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