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