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