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