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