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