]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_sao.cpp
895c2604433df6a0254a0acbccab6d729a3f55b8
[dragonfireclient.git] / src / content_sao.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "util/mathconstants.h"
23 #include "collision.h"
24 #include "environment.h"
25 #include "settings.h"
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
28 #include "gamedef.h"
29 #include "player.h"
30 #include "server.h"
31 #include "scripting_game.h"
32 #include "genericobject.h"
33 #include "log.h"
34
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
36
37 /*
38         TestSAO
39 */
40
41 class TestSAO : public ServerActiveObject
42 {
43 public:
44         TestSAO(ServerEnvironment *env, v3f pos):
45                 ServerActiveObject(env, pos),
46                 m_timer1(0),
47                 m_age(0)
48         {
49                 ServerActiveObject::registerType(getType(), create);
50         }
51         ActiveObjectType getType() const
52         { return ACTIVEOBJECT_TYPE_TEST; }
53
54         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55                         const std::string &data)
56         {
57                 return new TestSAO(env, pos);
58         }
59
60         void step(float dtime, bool send_recommended)
61         {
62                 m_age += dtime;
63                 if(m_age > 10)
64                 {
65                         m_removed = true;
66                         return;
67                 }
68
69                 m_base_position.Y += dtime * BS * 2;
70                 if(m_base_position.Y > 8*BS)
71                         m_base_position.Y = 2*BS;
72
73                 if(send_recommended == false)
74                         return;
75
76                 m_timer1 -= dtime;
77                 if(m_timer1 < 0.0)
78                 {
79                         m_timer1 += 0.125;
80
81                         std::string data;
82
83                         data += itos(0); // 0 = position
84                         data += " ";
85                         data += itos(m_base_position.X);
86                         data += " ";
87                         data += itos(m_base_position.Y);
88                         data += " ";
89                         data += itos(m_base_position.Z);
90
91                         ActiveObjectMessage aom(getId(), false, data);
92                         m_messages_out.push(aom);
93                 }
94         }
95
96         bool getCollisionBox(aabb3f *toset) {
97                 return false;
98         }
99
100         bool collideWithObjects() {
101                 return false;
102         }
103
104 private:
105         float m_timer1;
106         float m_age;
107 };
108
109 // Prototype (registers item for deserialization)
110 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
111
112 /*
113         LuaEntitySAO
114 */
115
116 // Prototype (registers item for deserialization)
117 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
118
119 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
120                 const std::string &name, const std::string &state):
121         ServerActiveObject(env, pos),
122         m_init_name(name),
123         m_init_state(state),
124         m_registered(false),
125         m_hp(-1),
126         m_velocity(0,0,0),
127         m_acceleration(0,0,0),
128         m_yaw(0),
129         m_properties_sent(true),
130         m_last_sent_yaw(0),
131         m_last_sent_position(0,0,0),
132         m_last_sent_velocity(0,0,0),
133         m_last_sent_position_timer(0),
134         m_last_sent_move_precision(0),
135         m_armor_groups_sent(false),
136         m_animation_speed(0),
137         m_animation_blend(0),
138         m_animation_loop(true),
139         m_animation_sent(false),
140         m_bone_position_sent(false),
141         m_attachment_parent_id(0),
142         m_attachment_sent(false)
143 {
144         // Only register type if no environment supplied
145         if(env == NULL){
146                 ServerActiveObject::registerType(getType(), create);
147                 return;
148         }
149
150         // Initialize something to armor groups
151         m_armor_groups["fleshy"] = 100;
152 }
153
154 LuaEntitySAO::~LuaEntitySAO()
155 {
156         if(m_registered){
157                 m_env->getScriptIface()->luaentity_Remove(m_id);
158         }
159 }
160
161 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
162 {
163         ServerActiveObject::addedToEnvironment(dtime_s);
164
165         // Create entity from name
166         m_registered = m_env->getScriptIface()->
167                 luaentity_Add(m_id, m_init_name.c_str());
168
169         if(m_registered){
170                 // Get properties
171                 m_env->getScriptIface()->
172                         luaentity_GetProperties(m_id, &m_prop);
173                 // Initialize HP from properties
174                 m_hp = m_prop.hp_max;
175                 // Activate entity, supplying serialized state
176                 m_env->getScriptIface()->
177                         luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
178         } else {
179                 m_prop.infotext = m_init_name;
180         }
181 }
182
183 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
184                 const std::string &data)
185 {
186         std::string name;
187         std::string state;
188         s16 hp = 1;
189         v3f velocity;
190         float yaw = 0;
191         if(data != ""){
192                 std::istringstream is(data, std::ios::binary);
193                 // read version
194                 u8 version = readU8(is);
195                 // check if version is supported
196                 if(version == 0){
197                         name = deSerializeString(is);
198                         state = deSerializeLongString(is);
199                 }
200                 else if(version == 1){
201                         name = deSerializeString(is);
202                         state = deSerializeLongString(is);
203                         hp = readS16(is);
204                         velocity = readV3F1000(is);
205                         yaw = readF1000(is);
206                 }
207         }
208         // create object
209         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
210                         <<state<<"\")"<<std::endl;
211         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
212         sao->m_hp = hp;
213         sao->m_velocity = velocity;
214         sao->m_yaw = yaw;
215         return sao;
216 }
217
218 bool LuaEntitySAO::isAttached()
219 {
220         if(!m_attachment_parent_id)
221                 return false;
222         // Check if the parent still exists
223         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
224         if(obj)
225                 return true;
226         return false;
227 }
228
229 void LuaEntitySAO::step(float dtime, bool send_recommended)
230 {
231         if(!m_properties_sent)
232         {
233                 m_properties_sent = true;
234                 std::string str = getPropertyPacket();
235                 // create message and add to list
236                 ActiveObjectMessage aom(getId(), true, str);
237                 m_messages_out.push(aom);
238         }
239
240         // If attached, check that our parent is still there. If it isn't, detach.
241         if(m_attachment_parent_id && !isAttached())
242         {
243                 m_attachment_parent_id = 0;
244                 m_attachment_bone = "";
245                 m_attachment_position = v3f(0,0,0);
246                 m_attachment_rotation = v3f(0,0,0);
247                 sendPosition(false, true);
248         }
249
250         m_last_sent_position_timer += dtime;
251
252         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
253         // If the object gets detached this comes into effect automatically from the last known origin
254         if(isAttached())
255         {
256                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
257                 m_base_position = pos;
258                 m_velocity = v3f(0,0,0);
259                 m_acceleration = v3f(0,0,0);
260         }
261         else
262         {
263                 if(m_prop.physical){
264                         aabb3f box = m_prop.collisionbox;
265                         box.MinEdge *= BS;
266                         box.MaxEdge *= BS;
267                         collisionMoveResult moveresult;
268                         f32 pos_max_d = BS*0.25; // Distance per iteration
269                         v3f p_pos = m_base_position;
270                         v3f p_velocity = m_velocity;
271                         v3f p_acceleration = m_acceleration;
272                         moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
273                                         pos_max_d, box, m_prop.stepheight, dtime,
274                                         &p_pos, &p_velocity, p_acceleration,
275                                         this, m_prop.collideWithObjects);
276
277                         // Apply results
278                         m_base_position = p_pos;
279                         m_velocity = p_velocity;
280                         m_acceleration = p_acceleration;
281                 } else {
282                         m_base_position += dtime * m_velocity + 0.5 * dtime
283                                         * dtime * m_acceleration;
284                         m_velocity += dtime * m_acceleration;
285                 }
286
287                 if((m_prop.automatic_face_movement_dir) &&
288                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
289                 {
290                         float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
291                                         + m_prop.automatic_face_movement_dir_offset;
292                         float max_rotation_delta =
293                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
294
295                         if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
296                                 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
297
298                                 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
299                         } else {
300                                 m_yaw = optimal_yaw;
301                         }
302                 }
303         }
304
305         if(m_registered){
306                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
307         }
308
309         if(send_recommended == false)
310                 return;
311
312         if(!isAttached())
313         {
314                 // TODO: force send when acceleration changes enough?
315                 float minchange = 0.2*BS;
316                 if(m_last_sent_position_timer > 1.0){
317                         minchange = 0.01*BS;
318                 } else if(m_last_sent_position_timer > 0.2){
319                         minchange = 0.05*BS;
320                 }
321                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
322                 move_d += m_last_sent_move_precision;
323                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
324                 if(move_d > minchange || vel_d > minchange ||
325                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
326                         sendPosition(true, false);
327                 }
328         }
329
330         if(m_armor_groups_sent == false){
331                 m_armor_groups_sent = true;
332                 std::string str = gob_cmd_update_armor_groups(
333                                 m_armor_groups);
334                 // create message and add to list
335                 ActiveObjectMessage aom(getId(), true, str);
336                 m_messages_out.push(aom);
337         }
338
339         if(m_animation_sent == false){
340                 m_animation_sent = true;
341                 std::string str = gob_cmd_update_animation(
342                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
343                 // create message and add to list
344                 ActiveObjectMessage aom(getId(), true, str);
345                 m_messages_out.push(aom);
346         }
347
348         if(m_bone_position_sent == false){
349                 m_bone_position_sent = true;
350                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
351                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
352                         std::string str = gob_cmd_update_bone_position((*ii).first,
353                                         (*ii).second.X, (*ii).second.Y);
354                         // create message and add to list
355                         ActiveObjectMessage aom(getId(), true, str);
356                         m_messages_out.push(aom);
357                 }
358         }
359
360         if(m_attachment_sent == false){
361                 m_attachment_sent = true;
362                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
363                 // create message and add to list
364                 ActiveObjectMessage aom(getId(), true, str);
365                 m_messages_out.push(aom);
366         }
367 }
368
369 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
370 {
371         std::ostringstream os(std::ios::binary);
372
373         if(protocol_version >= 14)
374         {
375                 writeU8(os, 1); // version
376                 os<<serializeString(""); // name
377                 writeU8(os, 0); // is_player
378                 writeS16(os, getId()); //id
379                 writeV3F1000(os, m_base_position);
380                 writeF1000(os, m_yaw);
381                 writeS16(os, m_hp);
382
383                 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
384                 os<<serializeLongString(getPropertyPacket()); // message 1
385                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
386                 os<<serializeLongString(gob_cmd_update_animation(
387                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
388                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
389                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
390                         os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
391                                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
392                 }
393                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
394         }
395         else
396         {
397                 writeU8(os, 0); // version
398                 os<<serializeString(""); // name
399                 writeU8(os, 0); // is_player
400                 writeV3F1000(os, m_base_position);
401                 writeF1000(os, m_yaw);
402                 writeS16(os, m_hp);
403                 writeU8(os, 2); // number of messages stuffed in here
404                 os<<serializeLongString(getPropertyPacket()); // message 1
405                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
406         }
407
408         // return result
409         return os.str();
410 }
411
412 std::string LuaEntitySAO::getStaticData()
413 {
414         verbosestream<<FUNCTION_NAME<<std::endl;
415         std::ostringstream os(std::ios::binary);
416         // version
417         writeU8(os, 1);
418         // name
419         os<<serializeString(m_init_name);
420         // state
421         if(m_registered){
422                 std::string state = m_env->getScriptIface()->
423                         luaentity_GetStaticdata(m_id);
424                 os<<serializeLongString(state);
425         } else {
426                 os<<serializeLongString(m_init_state);
427         }
428         // hp
429         writeS16(os, m_hp);
430         // velocity
431         writeV3F1000(os, m_velocity);
432         // yaw
433         writeF1000(os, m_yaw);
434         return os.str();
435 }
436
437 int LuaEntitySAO::punch(v3f dir,
438                 const ToolCapabilities *toolcap,
439                 ServerActiveObject *puncher,
440                 float time_from_last_punch)
441 {
442         if (!m_registered){
443                 // Delete unknown LuaEntities when punched
444                 m_removed = true;
445                 return 0;
446         }
447
448         // It's best that attachments cannot be punched
449         if (isAttached())
450                 return 0;
451
452         ItemStack *punchitem = NULL;
453         ItemStack punchitem_static;
454         if (puncher) {
455                 punchitem_static = puncher->getWieldedItem();
456                 punchitem = &punchitem_static;
457         }
458
459         PunchDamageResult result = getPunchDamage(
460                         m_armor_groups,
461                         toolcap,
462                         punchitem,
463                         time_from_last_punch);
464
465         if (result.did_punch) {
466                 setHP(getHP() - result.damage);
467
468                 if (result.damage > 0) {
469                         std::string punchername = puncher ? puncher->getDescription() : "nil";
470
471                         actionstream << getDescription() << " punched by "
472                                         << punchername << ", damage " << result.damage
473                                         << " hp, health now " << getHP() << " hp" << std::endl;
474                 }
475
476                 std::string str = gob_cmd_punched(result.damage, getHP());
477                 // create message and add to list
478                 ActiveObjectMessage aom(getId(), true, str);
479                 m_messages_out.push(aom);
480         }
481
482         if (getHP() == 0)
483                 m_removed = true;
484
485         m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
486                         time_from_last_punch, toolcap, dir);
487
488         return result.wear;
489 }
490
491 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
492 {
493         if (!m_registered)
494                 return;
495         // It's best that attachments cannot be clicked
496         if (isAttached())
497                 return;
498         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
499 }
500
501 void LuaEntitySAO::setPos(v3f pos)
502 {
503         if(isAttached())
504                 return;
505         m_base_position = pos;
506         sendPosition(false, true);
507 }
508
509 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
510 {
511         if(isAttached())
512                 return;
513         m_base_position = pos;
514         if(!continuous)
515                 sendPosition(true, true);
516 }
517
518 float LuaEntitySAO::getMinimumSavedMovement()
519 {
520         return 0.1 * BS;
521 }
522
523 std::string LuaEntitySAO::getDescription()
524 {
525         std::ostringstream os(std::ios::binary);
526         os<<"LuaEntitySAO at (";
527         os<<(m_base_position.X/BS)<<",";
528         os<<(m_base_position.Y/BS)<<",";
529         os<<(m_base_position.Z/BS);
530         os<<")";
531         return os.str();
532 }
533
534 void LuaEntitySAO::setHP(s16 hp)
535 {
536         if(hp < 0) hp = 0;
537         m_hp = hp;
538 }
539
540 s16 LuaEntitySAO::getHP() const
541 {
542         return m_hp;
543 }
544
545 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
546 {
547         m_armor_groups = armor_groups;
548         m_armor_groups_sent = false;
549 }
550
551 ItemGroupList LuaEntitySAO::getArmorGroups()
552 {
553         return m_armor_groups;
554 }
555
556 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
557 {
558         m_animation_range = frame_range;
559         m_animation_speed = frame_speed;
560         m_animation_blend = frame_blend;
561         m_animation_loop = frame_loop;
562         m_animation_sent = false;
563 }
564
565 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
566 {
567         *frame_range = m_animation_range;
568         *frame_speed = m_animation_speed;
569         *frame_blend = m_animation_blend;
570         *frame_loop = m_animation_loop;
571 }
572
573 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
574 {
575         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
576         m_bone_position_sent = false;
577 }
578
579 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
580 {
581         *position = m_bone_position[bone].X;
582         *rotation = m_bone_position[bone].Y;
583 }
584
585 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
586 {
587         // Attachments need to be handled on both the server and client.
588         // If we just attach on the server, we can only copy the position of the parent. Attachments
589         // are still sent to clients at an interval so players might see them lagging, plus we can't
590         // read and attach to skeletal bones.
591         // If we just attach on the client, the server still sees the child at its original location.
592         // This breaks some things so we also give the server the most accurate representation
593         // even if players only see the client changes.
594
595         m_attachment_parent_id = parent_id;
596         m_attachment_bone = bone;
597         m_attachment_position = position;
598         m_attachment_rotation = rotation;
599         m_attachment_sent = false;
600 }
601
602 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
603         v3f *rotation)
604 {
605         *parent_id = m_attachment_parent_id;
606         *bone = m_attachment_bone;
607         *position = m_attachment_position;
608         *rotation = m_attachment_rotation;
609 }
610
611 void LuaEntitySAO::addAttachmentChild(int child_id)
612 {
613         m_attachment_child_ids.insert(child_id);
614 }
615
616 void LuaEntitySAO::removeAttachmentChild(int child_id)
617 {
618         m_attachment_child_ids.erase(child_id);
619 }
620
621 std::set<int> LuaEntitySAO::getAttachmentChildIds()
622 {
623         return m_attachment_child_ids;
624 }
625
626 ObjectProperties* LuaEntitySAO::accessObjectProperties()
627 {
628         return &m_prop;
629 }
630
631 void LuaEntitySAO::notifyObjectPropertiesModified()
632 {
633         m_properties_sent = false;
634 }
635
636 void LuaEntitySAO::setVelocity(v3f velocity)
637 {
638         m_velocity = velocity;
639 }
640
641 v3f LuaEntitySAO::getVelocity()
642 {
643         return m_velocity;
644 }
645
646 void LuaEntitySAO::setAcceleration(v3f acceleration)
647 {
648         m_acceleration = acceleration;
649 }
650
651 v3f LuaEntitySAO::getAcceleration()
652 {
653         return m_acceleration;
654 }
655
656 void LuaEntitySAO::setYaw(float yaw)
657 {
658         m_yaw = yaw;
659 }
660
661 float LuaEntitySAO::getYaw()
662 {
663         return m_yaw;
664 }
665
666 void LuaEntitySAO::setTextureMod(const std::string &mod)
667 {
668         std::string str = gob_cmd_set_texture_mod(mod);
669         // create message and add to list
670         ActiveObjectMessage aom(getId(), true, str);
671         m_messages_out.push(aom);
672 }
673
674 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
675                 bool select_horiz_by_yawpitch)
676 {
677         std::string str = gob_cmd_set_sprite(
678                 p,
679                 num_frames,
680                 framelength,
681                 select_horiz_by_yawpitch
682         );
683         // create message and add to list
684         ActiveObjectMessage aom(getId(), true, str);
685         m_messages_out.push(aom);
686 }
687
688 std::string LuaEntitySAO::getName()
689 {
690         return m_init_name;
691 }
692
693 std::string LuaEntitySAO::getPropertyPacket()
694 {
695         return gob_cmd_set_properties(m_prop);
696 }
697
698 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
699 {
700         // If the object is attached client-side, don't waste bandwidth sending its position to clients
701         if(isAttached())
702                 return;
703
704         m_last_sent_move_precision = m_base_position.getDistanceFrom(
705                         m_last_sent_position);
706         m_last_sent_position_timer = 0;
707         m_last_sent_yaw = m_yaw;
708         m_last_sent_position = m_base_position;
709         m_last_sent_velocity = m_velocity;
710         //m_last_sent_acceleration = m_acceleration;
711
712         float update_interval = m_env->getSendRecommendedInterval();
713
714         std::string str = gob_cmd_update_position(
715                 m_base_position,
716                 m_velocity,
717                 m_acceleration,
718                 m_yaw,
719                 do_interpolate,
720                 is_movement_end,
721                 update_interval
722         );
723         // create message and add to list
724         ActiveObjectMessage aom(getId(), false, str);
725         m_messages_out.push(aom);
726 }
727
728 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
729         if (m_prop.physical)
730         {
731                 //update collision box
732                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
733                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
734
735                 toset->MinEdge += m_base_position;
736                 toset->MaxEdge += m_base_position;
737
738                 return true;
739         }
740
741         return false;
742 }
743
744 bool LuaEntitySAO::collideWithObjects(){
745         return m_prop.collideWithObjects;
746 }
747
748 /*
749         PlayerSAO
750 */
751
752 // No prototype, PlayerSAO does not need to be deserialized
753
754 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
755                 const std::set<std::string> &privs, bool is_singleplayer):
756         ServerActiveObject(env_, v3f(0,0,0)),
757         m_player(player_),
758         m_peer_id(peer_id_),
759         m_inventory(NULL),
760         m_damage(0),
761         m_last_good_position(0,0,0),
762         m_time_from_last_punch(0),
763         m_nocheat_dig_pos(32767, 32767, 32767),
764         m_nocheat_dig_time(0),
765         m_wield_index(0),
766         m_position_not_sent(false),
767         m_armor_groups_sent(false),
768         m_properties_sent(true),
769         m_privs(privs),
770         m_is_singleplayer(is_singleplayer),
771         m_animation_speed(0),
772         m_animation_blend(0),
773         m_animation_loop(true),
774         m_animation_sent(false),
775         m_bone_position_sent(false),
776         m_attachment_parent_id(0),
777         m_attachment_sent(false),
778         // public
779         m_physics_override_speed(1),
780         m_physics_override_jump(1),
781         m_physics_override_gravity(1),
782         m_physics_override_sneak(true),
783         m_physics_override_sneak_glitch(true),
784         m_physics_override_sent(false)
785 {
786         assert(m_player);       // pre-condition
787         assert(m_peer_id != 0); // pre-condition
788         setBasePosition(m_player->getPosition());
789         m_inventory = &m_player->inventory;
790         m_armor_groups["fleshy"] = 100;
791
792         m_prop.hp_max = PLAYER_MAX_HP;
793         m_prop.physical = false;
794         m_prop.weight = 75;
795         m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
796         // start of default appearance, this should be overwritten by LUA
797         m_prop.visual = "upright_sprite";
798         m_prop.visual_size = v2f(1, 2);
799         m_prop.textures.clear();
800         m_prop.textures.push_back("player.png");
801         m_prop.textures.push_back("player_back.png");
802         m_prop.colors.clear();
803         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
804         m_prop.spritediv = v2s16(1,1);
805         // end of default appearance
806         m_prop.is_visible = true;
807         m_prop.makes_footstep_sound = true;
808 }
809
810 PlayerSAO::~PlayerSAO()
811 {
812         if(m_inventory != &m_player->inventory)
813                 delete m_inventory;
814
815 }
816
817 std::string PlayerSAO::getDescription()
818 {
819         return std::string("player ") + m_player->getName();
820 }
821
822 // Called after id has been set and has been inserted in environment
823 void PlayerSAO::addedToEnvironment(u32 dtime_s)
824 {
825         ServerActiveObject::addedToEnvironment(dtime_s);
826         ServerActiveObject::setBasePosition(m_player->getPosition());
827         m_player->setPlayerSAO(this);
828         m_player->peer_id = m_peer_id;
829         m_last_good_position = m_player->getPosition();
830 }
831
832 // Called before removing from environment
833 void PlayerSAO::removingFromEnvironment()
834 {
835         ServerActiveObject::removingFromEnvironment();
836         if(m_player->getPlayerSAO() == this)
837         {
838                 m_player->setPlayerSAO(NULL);
839                 m_player->peer_id = 0;
840                 m_env->savePlayer((RemotePlayer*)m_player);
841                 m_env->removePlayer(m_player);
842         }
843 }
844
845 bool PlayerSAO::isStaticAllowed() const
846 {
847         return false;
848 }
849
850 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
851 {
852         std::ostringstream os(std::ios::binary);
853
854         if(protocol_version >= 15)
855         {
856                 writeU8(os, 1); // version
857                 os<<serializeString(m_player->getName()); // name
858                 writeU8(os, 1); // is_player
859                 writeS16(os, getId()); //id
860                 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
861                 writeF1000(os, m_player->getYaw());
862                 writeS16(os, getHP());
863
864                 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
865                 os<<serializeLongString(getPropertyPacket()); // message 1
866                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
867                 os<<serializeLongString(gob_cmd_update_animation(
868                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
869                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
870                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
871                         os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
872                 }
873                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
874                 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
875                                 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
876                                 m_physics_override_sneak_glitch)); // 5
877                 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
878         }
879         else
880         {
881                 writeU8(os, 0); // version
882                 os<<serializeString(m_player->getName()); // name
883                 writeU8(os, 1); // is_player
884                 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
885                 writeF1000(os, m_player->getYaw());
886                 writeS16(os, getHP());
887                 writeU8(os, 2); // number of messages stuffed in here
888                 os<<serializeLongString(getPropertyPacket()); // message 1
889                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
890         }
891
892         // return result
893         return os.str();
894 }
895
896 std::string PlayerSAO::getStaticData()
897 {
898         FATAL_ERROR("Deprecated function (?)");
899         return "";
900 }
901
902 bool PlayerSAO::isAttached()
903 {
904         if(!m_attachment_parent_id)
905                 return false;
906         // Check if the parent still exists
907         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
908         if(obj)
909                 return true;
910         return false;
911 }
912
913 void PlayerSAO::step(float dtime, bool send_recommended)
914 {
915         if(!m_properties_sent)
916         {
917                 m_properties_sent = true;
918                 std::string str = getPropertyPacket();
919                 // create message and add to list
920                 ActiveObjectMessage aom(getId(), true, str);
921                 m_messages_out.push(aom);
922         }
923
924         // If attached, check that our parent is still there. If it isn't, detach.
925         if(m_attachment_parent_id && !isAttached())
926         {
927                 m_attachment_parent_id = 0;
928                 m_attachment_bone = "";
929                 m_attachment_position = v3f(0,0,0);
930                 m_attachment_rotation = v3f(0,0,0);
931                 m_player->setPosition(m_last_good_position);
932                 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
933         }
934
935         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
936
937         // Set lag pool maximums based on estimated lag
938         const float LAG_POOL_MIN = 5.0;
939         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
940         if(lag_pool_max < LAG_POOL_MIN)
941                 lag_pool_max = LAG_POOL_MIN;
942         m_dig_pool.setMax(lag_pool_max);
943         m_move_pool.setMax(lag_pool_max);
944
945         // Increment cheat prevention timers
946         m_dig_pool.add(dtime);
947         m_move_pool.add(dtime);
948         m_time_from_last_punch += dtime;
949         m_nocheat_dig_time += dtime;
950
951         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
952         // If the object gets detached this comes into effect automatically from the last known origin
953         if(isAttached())
954         {
955                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
956                 m_last_good_position = pos;
957                 m_player->setPosition(pos);
958         }
959
960         if(send_recommended == false)
961                 return;
962
963         // If the object is attached client-side, don't waste bandwidth sending its position to clients
964         if(m_position_not_sent && !isAttached())
965         {
966                 m_position_not_sent = false;
967                 float update_interval = m_env->getSendRecommendedInterval();
968                 v3f pos;
969                 if(isAttached()) // Just in case we ever do send attachment position too
970                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
971                 else
972                         pos = m_player->getPosition() + v3f(0,BS*1,0);
973                 std::string str = gob_cmd_update_position(
974                         pos,
975                         v3f(0,0,0),
976                         v3f(0,0,0),
977                         m_player->getYaw(),
978                         true,
979                         false,
980                         update_interval
981                 );
982                 // create message and add to list
983                 ActiveObjectMessage aom(getId(), false, str);
984                 m_messages_out.push(aom);
985         }
986
987         if(m_armor_groups_sent == false) {
988                 m_armor_groups_sent = true;
989                 std::string str = gob_cmd_update_armor_groups(
990                                 m_armor_groups);
991                 // create message and add to list
992                 ActiveObjectMessage aom(getId(), true, str);
993                 m_messages_out.push(aom);
994         }
995
996         if(m_physics_override_sent == false){
997                 m_physics_override_sent = true;
998                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
999                                 m_physics_override_jump, m_physics_override_gravity,
1000                                 m_physics_override_sneak, m_physics_override_sneak_glitch);
1001                 // create message and add to list
1002                 ActiveObjectMessage aom(getId(), true, str);
1003                 m_messages_out.push(aom);
1004         }
1005
1006         if(m_animation_sent == false){
1007                 m_animation_sent = true;
1008                 std::string str = gob_cmd_update_animation(
1009                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1010                 // create message and add to list
1011                 ActiveObjectMessage aom(getId(), true, str);
1012                 m_messages_out.push(aom);
1013         }
1014
1015         if (!m_bone_position_sent) {
1016                 m_bone_position_sent = true;
1017                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1018                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1019                         std::string str = gob_cmd_update_bone_position((*ii).first,
1020                                         (*ii).second.X, (*ii).second.Y);
1021                         // create message and add to list
1022                         ActiveObjectMessage aom(getId(), true, str);
1023                         m_messages_out.push(aom);
1024                 }
1025         }
1026
1027         if (!m_attachment_sent){
1028                 m_attachment_sent = true;
1029                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1030                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1031                 // create message and add to list
1032                 ActiveObjectMessage aom(getId(), true, str);
1033                 m_messages_out.push(aom);
1034         }
1035 }
1036
1037 void PlayerSAO::setBasePosition(const v3f &position)
1038 {
1039         // This needs to be ran for attachments too
1040         ServerActiveObject::setBasePosition(position);
1041         m_position_not_sent = true;
1042 }
1043
1044 void PlayerSAO::setPos(v3f pos)
1045 {
1046         if(isAttached())
1047                 return;
1048         m_player->setPosition(pos);
1049         // Movement caused by this command is always valid
1050         m_last_good_position = pos;
1051         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1052 }
1053
1054 void PlayerSAO::moveTo(v3f pos, bool continuous)
1055 {
1056         if(isAttached())
1057                 return;
1058         m_player->setPosition(pos);
1059         // Movement caused by this command is always valid
1060         m_last_good_position = pos;
1061         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1062 }
1063
1064 void PlayerSAO::setYaw(float yaw)
1065 {
1066         m_player->setYaw(yaw);
1067         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1068 }
1069
1070 void PlayerSAO::setPitch(float pitch)
1071 {
1072         m_player->setPitch(pitch);
1073         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1074 }
1075
1076 int PlayerSAO::punch(v3f dir,
1077         const ToolCapabilities *toolcap,
1078         ServerActiveObject *puncher,
1079         float time_from_last_punch)
1080 {
1081         // It's best that attachments cannot be punched
1082         if (isAttached())
1083                 return 0;
1084
1085         if (!toolcap)
1086                 return 0;
1087
1088         // No effect if PvP disabled
1089         if (g_settings->getBool("enable_pvp") == false) {
1090                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1091                         std::string str = gob_cmd_punched(0, getHP());
1092                         // create message and add to list
1093                         ActiveObjectMessage aom(getId(), true, str);
1094                         m_messages_out.push(aom);
1095                         return 0;
1096                 }
1097         }
1098
1099         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1100                         time_from_last_punch);
1101
1102         std::string punchername = "nil";
1103
1104         if (puncher != 0)
1105                 punchername = puncher->getDescription();
1106
1107         PlayerSAO *playersao = m_player->getPlayerSAO();
1108
1109         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1110                                 puncher, time_from_last_punch, toolcap, dir,
1111                                 hitparams.hp);
1112
1113         if (!damage_handled) {
1114                 setHP(getHP() - hitparams.hp);
1115         } else { // override client prediction
1116                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1117                         std::string str = gob_cmd_punched(0, getHP());
1118                         // create message and add to list
1119                         ActiveObjectMessage aom(getId(), true, str);
1120                         m_messages_out.push(aom);
1121                 }
1122         }
1123
1124
1125         actionstream << "Player " << m_player->getName() << " punched by "
1126                         << punchername;
1127         if (!damage_handled) {
1128                 actionstream << ", damage " << hitparams.hp << " HP";
1129         } else {
1130                 actionstream << ", damage handled by lua";
1131         }
1132         actionstream << std::endl;
1133
1134         return hitparams.wear;
1135 }
1136
1137 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1138 {
1139 }
1140
1141 s16 PlayerSAO::getHP() const
1142 {
1143         return m_player->hp;
1144 }
1145
1146 s16 PlayerSAO::readDamage()
1147 {
1148         s16 damage = m_damage;
1149         m_damage = 0;
1150         return damage;
1151 }
1152
1153 void PlayerSAO::setHP(s16 hp)
1154 {
1155         s16 oldhp = m_player->hp;
1156
1157         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1158                 hp - oldhp);
1159         if (hp_change == 0)
1160                 return;
1161         hp = oldhp + hp_change;
1162
1163         if (hp < 0)
1164                 hp = 0;
1165         else if (hp > PLAYER_MAX_HP)
1166                 hp = PLAYER_MAX_HP;
1167
1168         if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1169                 return;
1170         }
1171
1172         m_player->hp = hp;
1173
1174         if (oldhp > hp)
1175                 m_damage += (oldhp - hp);
1176
1177         // Update properties on death
1178         if ((hp == 0) != (oldhp == 0))
1179                 m_properties_sent = false;
1180 }
1181
1182 u16 PlayerSAO::getBreath() const
1183 {
1184         return m_player->getBreath();
1185 }
1186
1187 void PlayerSAO::setBreath(u16 breath)
1188 {
1189         m_player->setBreath(breath);
1190 }
1191
1192 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1193 {
1194         m_armor_groups = armor_groups;
1195         m_armor_groups_sent = false;
1196 }
1197
1198 ItemGroupList PlayerSAO::getArmorGroups()
1199 {
1200         return m_armor_groups;
1201 }
1202
1203 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1204 {
1205         // store these so they can be updated to clients
1206         m_animation_range = frame_range;
1207         m_animation_speed = frame_speed;
1208         m_animation_blend = frame_blend;
1209         m_animation_loop = frame_loop;
1210         m_animation_sent = false;
1211 }
1212
1213 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1214 {
1215         *frame_range = m_animation_range;
1216         *frame_speed = m_animation_speed;
1217         *frame_blend = m_animation_blend;
1218         *frame_loop = m_animation_loop;
1219 }
1220
1221 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1222 {
1223         // store these so they can be updated to clients
1224         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1225         m_bone_position_sent = false;
1226 }
1227
1228 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1229 {
1230         *position = m_bone_position[bone].X;
1231         *rotation = m_bone_position[bone].Y;
1232 }
1233
1234 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1235 {
1236         // Attachments need to be handled on both the server and client.
1237         // If we just attach on the server, we can only copy the position of the parent. Attachments
1238         // are still sent to clients at an interval so players might see them lagging, plus we can't
1239         // read and attach to skeletal bones.
1240         // If we just attach on the client, the server still sees the child at its original location.
1241         // This breaks some things so we also give the server the most accurate representation
1242         // even if players only see the client changes.
1243
1244         m_attachment_parent_id = parent_id;
1245         m_attachment_bone = bone;
1246         m_attachment_position = position;
1247         m_attachment_rotation = rotation;
1248         m_attachment_sent = false;
1249 }
1250
1251 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1252         v3f *rotation)
1253 {
1254         *parent_id = m_attachment_parent_id;
1255         *bone = m_attachment_bone;
1256         *position = m_attachment_position;
1257         *rotation = m_attachment_rotation;
1258 }
1259
1260 void PlayerSAO::addAttachmentChild(int child_id)
1261 {
1262         m_attachment_child_ids.insert(child_id);
1263 }
1264
1265 void PlayerSAO::removeAttachmentChild(int child_id)
1266 {
1267         m_attachment_child_ids.erase(child_id);
1268 }
1269
1270 std::set<int> PlayerSAO::getAttachmentChildIds()
1271 {
1272         return m_attachment_child_ids;
1273 }
1274
1275 ObjectProperties* PlayerSAO::accessObjectProperties()
1276 {
1277         return &m_prop;
1278 }
1279
1280 void PlayerSAO::notifyObjectPropertiesModified()
1281 {
1282         m_properties_sent = false;
1283 }
1284
1285 Inventory* PlayerSAO::getInventory()
1286 {
1287         return m_inventory;
1288 }
1289 const Inventory* PlayerSAO::getInventory() const
1290 {
1291         return m_inventory;
1292 }
1293
1294 InventoryLocation PlayerSAO::getInventoryLocation() const
1295 {
1296         InventoryLocation loc;
1297         loc.setPlayer(m_player->getName());
1298         return loc;
1299 }
1300
1301 std::string PlayerSAO::getWieldList() const
1302 {
1303         return "main";
1304 }
1305
1306 int PlayerSAO::getWieldIndex() const
1307 {
1308         return m_wield_index;
1309 }
1310
1311 void PlayerSAO::setWieldIndex(int i)
1312 {
1313         if(i != m_wield_index) {
1314                 m_wield_index = i;
1315         }
1316 }
1317
1318 void PlayerSAO::disconnected()
1319 {
1320         m_peer_id = 0;
1321         m_removed = true;
1322         if(m_player->getPlayerSAO() == this)
1323         {
1324                 m_player->setPlayerSAO(NULL);
1325                 m_player->peer_id = 0;
1326         }
1327 }
1328
1329 std::string PlayerSAO::getPropertyPacket()
1330 {
1331         m_prop.is_visible = (true);
1332         return gob_cmd_set_properties(m_prop);
1333 }
1334
1335 bool PlayerSAO::checkMovementCheat()
1336 {
1337         if (isAttached() || m_is_singleplayer ||
1338                         g_settings->getBool("disable_anticheat")) {
1339                 m_last_good_position = m_player->getPosition();
1340                 return false;
1341         }
1342
1343         bool cheated = false;
1344         /*
1345                 Check player movements
1346
1347                 NOTE: Actually the server should handle player physics like the
1348                 client does and compare player's position to what is calculated
1349                 on our side. This is required when eg. players fly due to an
1350                 explosion. Altough a node-based alternative might be possible
1351                 too, and much more lightweight.
1352         */
1353
1354         float player_max_speed = 0;
1355
1356         if (m_privs.count("fast") != 0) {
1357                 // Fast speed
1358                 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1359         } else {
1360                 // Normal speed
1361                 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1362         }
1363         // Tolerance. The lag pool does this a bit.
1364         //player_max_speed *= 2.5;
1365
1366         v3f diff = (m_player->getPosition() - m_last_good_position);
1367         float d_vert = diff.Y;
1368         diff.Y = 0;
1369         float d_horiz = diff.getLength();
1370         float required_time = d_horiz / player_max_speed;
1371
1372         if (d_vert > 0 && d_vert / player_max_speed > required_time)
1373                 required_time = d_vert / player_max_speed; // Moving upwards
1374
1375         if (m_move_pool.grab(required_time)) {
1376                 m_last_good_position = m_player->getPosition();
1377         } else {
1378                 actionstream << "Player " << m_player->getName()
1379                                 << " moved too fast; resetting position"
1380                                 << std::endl;
1381                 m_player->setPosition(m_last_good_position);
1382                 cheated = true;
1383         }
1384         return cheated;
1385 }
1386
1387 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1388         //update collision box
1389         *toset = m_player->getCollisionbox();
1390
1391         toset->MinEdge += m_base_position;
1392         toset->MaxEdge += m_base_position;
1393
1394         return true;
1395 }
1396
1397 bool PlayerSAO::collideWithObjects(){
1398         return true;
1399 }