Mesh generation: Fix performance regression caused by 'plantlike_rooted' PR
[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
588
589
590         return result.wear;
591 }
592
593 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
594 {
595         if (!m_registered)
596                 return;
597         // It's best that attachments cannot be clicked
598         if (isAttached())
599                 return;
600         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
601 }
602
603 void LuaEntitySAO::setPos(const v3f &pos)
604 {
605         if(isAttached())
606                 return;
607         m_base_position = pos;
608         sendPosition(false, true);
609 }
610
611 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
612 {
613         if(isAttached())
614                 return;
615         m_base_position = pos;
616         if(!continuous)
617                 sendPosition(true, true);
618 }
619
620 float LuaEntitySAO::getMinimumSavedMovement()
621 {
622         return 0.1 * BS;
623 }
624
625 std::string LuaEntitySAO::getDescription()
626 {
627         std::ostringstream os(std::ios::binary);
628         os<<"LuaEntitySAO at (";
629         os<<(m_base_position.X/BS)<<",";
630         os<<(m_base_position.Y/BS)<<",";
631         os<<(m_base_position.Z/BS);
632         os<<")";
633         return os.str();
634 }
635
636 void LuaEntitySAO::setHP(s16 hp)
637 {
638         if(hp < 0) hp = 0;
639         m_hp = hp;
640 }
641
642 s16 LuaEntitySAO::getHP() const
643 {
644         return m_hp;
645 }
646
647 void LuaEntitySAO::setVelocity(v3f velocity)
648 {
649         m_velocity = velocity;
650 }
651
652 v3f LuaEntitySAO::getVelocity()
653 {
654         return m_velocity;
655 }
656
657 void LuaEntitySAO::setAcceleration(v3f acceleration)
658 {
659         m_acceleration = acceleration;
660 }
661
662 v3f LuaEntitySAO::getAcceleration()
663 {
664         return m_acceleration;
665 }
666
667 void LuaEntitySAO::setTextureMod(const std::string &mod)
668 {
669         std::string str = gob_cmd_set_texture_mod(mod);
670         m_current_texture_modifier = mod;
671         // create message and add to list
672         ActiveObjectMessage aom(getId(), true, str);
673         m_messages_out.push(aom);
674 }
675
676 std::string LuaEntitySAO::getTextureMod() const
677 {
678         return m_current_texture_modifier;
679 }
680
681 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
682                 bool select_horiz_by_yawpitch)
683 {
684         std::string str = gob_cmd_set_sprite(
685                 p,
686                 num_frames,
687                 framelength,
688                 select_horiz_by_yawpitch
689         );
690         // create message and add to list
691         ActiveObjectMessage aom(getId(), true, str);
692         m_messages_out.push(aom);
693 }
694
695 std::string LuaEntitySAO::getName()
696 {
697         return m_init_name;
698 }
699
700 std::string LuaEntitySAO::getPropertyPacket()
701 {
702         return gob_cmd_set_properties(m_prop);
703 }
704
705 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
706 {
707         // If the object is attached client-side, don't waste bandwidth sending its position to clients
708         if(isAttached())
709                 return;
710
711         m_last_sent_move_precision = m_base_position.getDistanceFrom(
712                         m_last_sent_position);
713         m_last_sent_position_timer = 0;
714         m_last_sent_yaw = m_yaw;
715         m_last_sent_position = m_base_position;
716         m_last_sent_velocity = m_velocity;
717         //m_last_sent_acceleration = m_acceleration;
718
719         float update_interval = m_env->getSendRecommendedInterval();
720
721         std::string str = gob_cmd_update_position(
722                 m_base_position,
723                 m_velocity,
724                 m_acceleration,
725                 m_yaw,
726                 do_interpolate,
727                 is_movement_end,
728                 update_interval
729         );
730         // create message and add to list
731         ActiveObjectMessage aom(getId(), false, str);
732         m_messages_out.push(aom);
733 }
734
735 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
736 {
737         if (m_prop.physical)
738         {
739                 //update collision box
740                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
741                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
742
743                 toset->MinEdge += m_base_position;
744                 toset->MaxEdge += m_base_position;
745
746                 return true;
747         }
748
749         return false;
750 }
751
752 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
753 {
754         if (!m_prop.is_visible) {
755                 return false;
756         }
757
758         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
759         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
760
761         return true;
762 }
763
764 bool LuaEntitySAO::collideWithObjects() const
765 {
766         return m_prop.collideWithObjects;
767 }
768
769 /*
770         PlayerSAO
771 */
772
773 // No prototype, PlayerSAO does not need to be deserialized
774
775 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
776                 bool is_singleplayer):
777         UnitSAO(env_, v3f(0,0,0)),
778         m_player(player_),
779         m_peer_id(peer_id_),
780         m_is_singleplayer(is_singleplayer)
781 {
782         assert(m_peer_id != 0); // pre-condition
783
784         m_prop.hp_max = PLAYER_MAX_HP;
785         m_prop.physical = false;
786         m_prop.weight = 75;
787         m_prop.collisionbox = aabb3f(-0.3f, -1.0f, -0.3f, 0.3f, 0.75f, 0.3f);
788         // start of default appearance, this should be overwritten by LUA
789         m_prop.visual = "upright_sprite";
790         m_prop.visual_size = v2f(1, 2);
791         m_prop.textures.clear();
792         m_prop.textures.push_back("player.png");
793         m_prop.textures.push_back("player_back.png");
794         m_prop.colors.clear();
795         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
796         m_prop.spritediv = v2s16(1,1);
797         // end of default appearance
798         m_prop.is_visible = true;
799         m_prop.makes_footstep_sound = true;
800         m_hp = PLAYER_MAX_HP;
801 }
802
803 PlayerSAO::~PlayerSAO()
804 {
805         if(m_inventory != &m_player->inventory)
806                 delete m_inventory;
807 }
808
809 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
810 {
811         assert(player);
812         m_player = player;
813         m_privs = privs;
814         m_inventory = &m_player->inventory;
815 }
816
817 v3f PlayerSAO::getEyeOffset() const
818 {
819         return v3f(0, BS * 1.625f, 0);
820 }
821
822 std::string PlayerSAO::getDescription()
823 {
824         return std::string("player ") + m_player->getName();
825 }
826
827 // Called after id has been set and has been inserted in environment
828 void PlayerSAO::addedToEnvironment(u32 dtime_s)
829 {
830         ServerActiveObject::addedToEnvironment(dtime_s);
831         ServerActiveObject::setBasePosition(m_base_position);
832         m_player->setPlayerSAO(this);
833         m_player->peer_id = m_peer_id;
834         m_last_good_position = m_base_position;
835 }
836
837 // Called before removing from environment
838 void PlayerSAO::removingFromEnvironment()
839 {
840         ServerActiveObject::removingFromEnvironment();
841         if (m_player->getPlayerSAO() == this) {
842                 unlinkPlayerSessionAndSave();
843                 for (std::unordered_set<u32>::iterator it = m_attached_particle_spawners.begin();
844                         it != m_attached_particle_spawners.end(); ++it) {
845                         m_env->deleteParticleSpawner(*it, false);
846                 }
847         }
848 }
849
850 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
851 {
852         std::ostringstream os(std::ios::binary);
853
854         // Protocol >= 15
855         writeU8(os, 1); // version
856         os << serializeString(m_player->getName()); // name
857         writeU8(os, 1); // is_player
858         writeS16(os, getId()); //id
859         writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
860         writeF1000(os, m_yaw);
861         writeS16(os, getHP());
862
863         std::ostringstream msg_os(std::ios::binary);
864         msg_os << serializeLongString(getPropertyPacket()); // message 1
865         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
866         msg_os << serializeLongString(gob_cmd_update_animation(
867                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
868         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
869                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
870                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
871                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
872         }
873         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
874                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
875         msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
876                         m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
877                         m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
878         // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
879         msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
880         int message_count = 6 + m_bone_position.size();
881         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
882                         ii != m_attachment_child_ids.end(); ++ii) {
883                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
884                         message_count++;
885                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
886                                 obj->getClientInitializationData(protocol_version)));
887                 }
888         }
889
890         writeU8(os, message_count);
891         os.write(msg_os.str().c_str(), msg_os.str().size());
892
893         // return result
894         return os.str();
895 }
896
897 void PlayerSAO::getStaticData(std::string *result) const
898 {
899         FATAL_ERROR("Deprecated function");
900 }
901
902 void PlayerSAO::step(float dtime, bool send_recommended)
903 {
904         if (m_drowning_interval.step(dtime, 2.0)) {
905                 // get head position
906                 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
907                 MapNode n = m_env->getMap().getNodeNoEx(p);
908                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
909                 // If node generates drown
910                 if (c.drowning > 0 && m_hp > 0) {
911                         if (m_breath > 0)
912                                 setBreath(m_breath - 1);
913
914                         // No more breath, damage player
915                         if (m_breath == 0) {
916                                 setHP(m_hp - c.drowning);
917                                 m_env->getGameDef()->SendPlayerHPOrDie(this);
918                         }
919                 }
920         }
921
922         if (m_breathing_interval.step(dtime, 0.5)) {
923                 // get head position
924                 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
925                 MapNode n = m_env->getMap().getNodeNoEx(p);
926                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
927                 // If player is alive & no drowning, breath
928                 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
929                         setBreath(m_breath + 1);
930         }
931
932         if (m_node_hurt_interval.step(dtime, 1.0)) {
933                 // Feet, middle and head
934                 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
935                 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
936                 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
937                 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
938                 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
939                 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
940
941                 u32 damage_per_second = 0;
942                 damage_per_second = MYMAX(damage_per_second,
943                         m_env->getGameDef()->ndef()->get(n1).damage_per_second);
944                 damage_per_second = MYMAX(damage_per_second,
945                         m_env->getGameDef()->ndef()->get(n2).damage_per_second);
946                 damage_per_second = MYMAX(damage_per_second,
947                         m_env->getGameDef()->ndef()->get(n3).damage_per_second);
948
949                 if (damage_per_second != 0 && m_hp > 0) {
950                         s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
951                         setHP(newhp);
952                         m_env->getGameDef()->SendPlayerHPOrDie(this);
953                 }
954         }
955
956         if (!m_properties_sent) {
957                 m_properties_sent = true;
958                 std::string str = getPropertyPacket();
959                 // create message and add to list
960                 ActiveObjectMessage aom(getId(), true, str);
961                 m_messages_out.push(aom);
962         }
963
964         // If attached, check that our parent is still there. If it isn't, detach.
965         if(m_attachment_parent_id && !isAttached())
966         {
967                 m_attachment_parent_id = 0;
968                 m_attachment_bone = "";
969                 m_attachment_position = v3f(0,0,0);
970                 m_attachment_rotation = v3f(0,0,0);
971                 setBasePosition(m_last_good_position);
972                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
973         }
974
975         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
976
977         // Set lag pool maximums based on estimated lag
978         const float LAG_POOL_MIN = 5.0;
979         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
980         if(lag_pool_max < LAG_POOL_MIN)
981                 lag_pool_max = LAG_POOL_MIN;
982         m_dig_pool.setMax(lag_pool_max);
983         m_move_pool.setMax(lag_pool_max);
984
985         // Increment cheat prevention timers
986         m_dig_pool.add(dtime);
987         m_move_pool.add(dtime);
988         m_time_from_last_teleport += dtime;
989         m_time_from_last_punch += dtime;
990         m_nocheat_dig_time += dtime;
991
992         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
993         // If the object gets detached this comes into effect automatically from the last known origin
994         if (isAttached()) {
995                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
996                 m_last_good_position = pos;
997                 setBasePosition(pos);
998         }
999
1000         if (!send_recommended)
1001                 return;
1002
1003         // If the object is attached client-side, don't waste bandwidth sending its position to clients
1004         if(m_position_not_sent && !isAttached())
1005         {
1006                 m_position_not_sent = false;
1007                 float update_interval = m_env->getSendRecommendedInterval();
1008                 v3f pos;
1009                 if(isAttached()) // Just in case we ever do send attachment position too
1010                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1011                 else
1012                         pos = m_base_position + v3f(0,BS*1,0);
1013                 std::string str = gob_cmd_update_position(
1014                         pos,
1015                         v3f(0,0,0),
1016                         v3f(0,0,0),
1017                         m_yaw,
1018                         true,
1019                         false,
1020                         update_interval
1021                 );
1022                 // create message and add to list
1023                 ActiveObjectMessage aom(getId(), false, str);
1024                 m_messages_out.push(aom);
1025         }
1026
1027         if (!m_armor_groups_sent) {
1028                 m_armor_groups_sent = true;
1029                 std::string str = gob_cmd_update_armor_groups(
1030                                 m_armor_groups);
1031                 // create message and add to list
1032                 ActiveObjectMessage aom(getId(), true, str);
1033                 m_messages_out.push(aom);
1034         }
1035
1036         if (!m_physics_override_sent) {
1037                 m_physics_override_sent = true;
1038                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1039                                 m_physics_override_jump, m_physics_override_gravity,
1040                                 m_physics_override_sneak, m_physics_override_sneak_glitch,
1041                                 m_physics_override_new_move);
1042                 // create message and add to list
1043                 ActiveObjectMessage aom(getId(), true, str);
1044                 m_messages_out.push(aom);
1045         }
1046
1047         if (!m_animation_sent) {
1048                 m_animation_sent = true;
1049                 std::string str = gob_cmd_update_animation(
1050                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1051                 // create message and add to list
1052                 ActiveObjectMessage aom(getId(), true, str);
1053                 m_messages_out.push(aom);
1054         }
1055
1056         if (!m_bone_position_sent) {
1057                 m_bone_position_sent = true;
1058                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1059                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1060                         std::string str = gob_cmd_update_bone_position((*ii).first,
1061                                         (*ii).second.X, (*ii).second.Y);
1062                         // create message and add to list
1063                         ActiveObjectMessage aom(getId(), true, str);
1064                         m_messages_out.push(aom);
1065                 }
1066         }
1067
1068         if (!m_attachment_sent){
1069                 m_attachment_sent = true;
1070                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1071                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1072                 // create message and add to list
1073                 ActiveObjectMessage aom(getId(), true, str);
1074                 m_messages_out.push(aom);
1075         }
1076 }
1077
1078 void PlayerSAO::setBasePosition(const v3f &position)
1079 {
1080         if (m_player && position != m_base_position)
1081                 m_player->setDirty(true);
1082
1083         // This needs to be ran for attachments too
1084         ServerActiveObject::setBasePosition(position);
1085         m_position_not_sent = true;
1086 }
1087
1088 void PlayerSAO::setPos(const v3f &pos)
1089 {
1090         if(isAttached())
1091                 return;
1092
1093         setBasePosition(pos);
1094         // Movement caused by this command is always valid
1095         m_last_good_position = pos;
1096         m_move_pool.empty();
1097         m_time_from_last_teleport = 0.0;
1098         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1099 }
1100
1101 void PlayerSAO::moveTo(v3f pos, bool continuous)
1102 {
1103         if(isAttached())
1104                 return;
1105
1106         setBasePosition(pos);
1107         // Movement caused by this command is always valid
1108         m_last_good_position = pos;
1109         m_move_pool.empty();
1110         m_time_from_last_teleport = 0.0;
1111         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1112 }
1113
1114 void PlayerSAO::setYaw(const float yaw)
1115 {
1116         if (m_player && yaw != m_yaw)
1117                 m_player->setDirty(true);
1118
1119         UnitSAO::setYaw(yaw);
1120 }
1121
1122 void PlayerSAO::setFov(const float fov)
1123 {
1124         if (m_player && fov != m_fov)
1125                 m_player->setDirty(true);
1126
1127         m_fov = fov;
1128 }
1129
1130 void PlayerSAO::setWantedRange(const s16 range)
1131 {
1132         if (m_player && range != m_wanted_range)
1133                 m_player->setDirty(true);
1134
1135         m_wanted_range = range;
1136 }
1137
1138 void PlayerSAO::setYawAndSend(const float yaw)
1139 {
1140         setYaw(yaw);
1141         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1142 }
1143
1144 void PlayerSAO::setPitch(const float pitch)
1145 {
1146         if (m_player && pitch != m_pitch)
1147                 m_player->setDirty(true);
1148
1149         m_pitch = pitch;
1150 }
1151
1152 void PlayerSAO::setPitchAndSend(const float pitch)
1153 {
1154         setPitch(pitch);
1155         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1156 }
1157
1158 int PlayerSAO::punch(v3f dir,
1159         const ToolCapabilities *toolcap,
1160         ServerActiveObject *puncher,
1161         float time_from_last_punch)
1162 {
1163         // It's best that attachments cannot be punched
1164         if (isAttached())
1165                 return 0;
1166
1167         if (!toolcap)
1168                 return 0;
1169
1170         // No effect if PvP disabled
1171         if (g_settings->getBool("enable_pvp") == false) {
1172                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1173                         std::string str = gob_cmd_punched(0, getHP());
1174                         // create message and add to list
1175                         ActiveObjectMessage aom(getId(), true, str);
1176                         m_messages_out.push(aom);
1177                         return 0;
1178                 }
1179         }
1180
1181         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1182                         time_from_last_punch);
1183
1184         std::string punchername = "nil";
1185
1186         if (puncher != 0)
1187                 punchername = puncher->getDescription();
1188
1189         PlayerSAO *playersao = m_player->getPlayerSAO();
1190
1191         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1192                                 puncher, time_from_last_punch, toolcap, dir,
1193                                 hitparams.hp);
1194
1195         if (!damage_handled) {
1196                 setHP(getHP() - hitparams.hp);
1197         } else { // override client prediction
1198                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1199                         std::string str = gob_cmd_punched(0, getHP());
1200                         // create message and add to list
1201                         ActiveObjectMessage aom(getId(), true, str);
1202                         m_messages_out.push(aom);
1203                 }
1204         }
1205
1206
1207         actionstream << "Player " << m_player->getName() << " punched by "
1208                         << punchername;
1209         if (!damage_handled) {
1210                 actionstream << ", damage " << hitparams.hp << " HP";
1211         } else {
1212                 actionstream << ", damage handled by lua";
1213         }
1214         actionstream << std::endl;
1215
1216         return hitparams.wear;
1217 }
1218
1219 s16 PlayerSAO::readDamage()
1220 {
1221         s16 damage = m_damage;
1222         m_damage = 0;
1223         return damage;
1224 }
1225
1226 void PlayerSAO::setHP(s16 hp)
1227 {
1228         s16 oldhp = m_hp;
1229
1230         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1231         if (hp_change == 0)
1232                 return;
1233         hp = oldhp + hp_change;
1234
1235         if (hp < 0)
1236                 hp = 0;
1237         else if (hp > PLAYER_MAX_HP)
1238                 hp = PLAYER_MAX_HP;
1239
1240         if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1241                 return;
1242         }
1243
1244         m_hp = hp;
1245
1246         if (oldhp > hp)
1247                 m_damage += (oldhp - hp);
1248
1249         // Update properties on death
1250         if ((hp == 0) != (oldhp == 0))
1251                 m_properties_sent = false;
1252 }
1253
1254 void PlayerSAO::setBreath(const u16 breath, bool send)
1255 {
1256         if (m_player && breath != m_breath)
1257                 m_player->setDirty(true);
1258
1259         m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1260
1261         if (send)
1262                 m_env->getGameDef()->SendPlayerBreath(this);
1263 }
1264
1265 Inventory* PlayerSAO::getInventory()
1266 {
1267         return m_inventory;
1268 }
1269 const Inventory* PlayerSAO::getInventory() const
1270 {
1271         return m_inventory;
1272 }
1273
1274 InventoryLocation PlayerSAO::getInventoryLocation() const
1275 {
1276         InventoryLocation loc;
1277         loc.setPlayer(m_player->getName());
1278         return loc;
1279 }
1280
1281 std::string PlayerSAO::getWieldList() const
1282 {
1283         return "main";
1284 }
1285
1286 ItemStack PlayerSAO::getWieldedItem() const
1287 {
1288         const Inventory *inv = getInventory();
1289         ItemStack ret;
1290         const InventoryList *mlist = inv->getList(getWieldList());
1291         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1292                 ret = mlist->getItem(getWieldIndex());
1293         return ret;
1294 }
1295
1296 ItemStack PlayerSAO::getWieldedItemOrHand() const
1297 {
1298         const Inventory *inv = getInventory();
1299         ItemStack ret;
1300         const InventoryList *mlist = inv->getList(getWieldList());
1301         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1302                 ret = mlist->getItem(getWieldIndex());
1303         if (ret.name.empty()) {
1304                 const InventoryList *hlist = inv->getList("hand");
1305                 if (hlist)
1306                         ret = hlist->getItem(0);
1307         }
1308         return ret;
1309 }
1310
1311 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1312 {
1313         Inventory *inv = getInventory();
1314         if (inv) {
1315                 InventoryList *mlist = inv->getList(getWieldList());
1316                 if (mlist) {
1317                         mlist->changeItem(getWieldIndex(), item);
1318                         return true;
1319                 }
1320         }
1321         return false;
1322 }
1323
1324 int PlayerSAO::getWieldIndex() const
1325 {
1326         return m_wield_index;
1327 }
1328
1329 void PlayerSAO::setWieldIndex(int i)
1330 {
1331         if(i != m_wield_index) {
1332                 m_wield_index = i;
1333         }
1334 }
1335
1336 // Erase the peer id and make the object for removal
1337 void PlayerSAO::disconnected()
1338 {
1339         m_peer_id = 0;
1340         m_removed = true;
1341 }
1342
1343 void PlayerSAO::unlinkPlayerSessionAndSave()
1344 {
1345         assert(m_player->getPlayerSAO() == this);
1346         m_player->peer_id = 0;
1347         m_env->savePlayer(m_player);
1348         m_player->setPlayerSAO(NULL);
1349         m_env->removePlayer(m_player);
1350 }
1351
1352 std::string PlayerSAO::getPropertyPacket()
1353 {
1354         m_prop.is_visible = (true);
1355         return gob_cmd_set_properties(m_prop);
1356 }
1357
1358 bool PlayerSAO::checkMovementCheat()
1359 {
1360         if (isAttached() || m_is_singleplayer ||
1361                         g_settings->getBool("disable_anticheat")) {
1362                 m_last_good_position = m_base_position;
1363                 return false;
1364         }
1365
1366         bool cheated = false;
1367         /*
1368                 Check player movements
1369
1370                 NOTE: Actually the server should handle player physics like the
1371                 client does and compare player's position to what is calculated
1372                 on our side. This is required when eg. players fly due to an
1373                 explosion. Altough a node-based alternative might be possible
1374                 too, and much more lightweight.
1375         */
1376
1377         float player_max_speed = 0;
1378
1379         if (m_privs.count("fast") != 0) {
1380                 // Fast speed
1381                 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1382         } else {
1383                 // Normal speed
1384                 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1385         }
1386         // Tolerance. The lag pool does this a bit.
1387         //player_max_speed *= 2.5;
1388
1389         v3f diff = (m_base_position - m_last_good_position);
1390         float d_vert = diff.Y;
1391         diff.Y = 0;
1392         float d_horiz = diff.getLength();
1393         float required_time = d_horiz / player_max_speed;
1394
1395         if (d_vert > 0 && d_vert / player_max_speed > required_time)
1396                 required_time = d_vert / player_max_speed; // Moving upwards
1397
1398         if (m_move_pool.grab(required_time)) {
1399                 m_last_good_position = m_base_position;
1400         } else {
1401                 const float LAG_POOL_MIN = 5.0;
1402                 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1403                 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1404                 if (m_time_from_last_teleport > lag_pool_max) {
1405                         actionstream << "Player " << m_player->getName()
1406                                         << " moved too fast; resetting position"
1407                                         << std::endl;
1408                         cheated = true;
1409                 }
1410                 setBasePosition(m_last_good_position);
1411         }
1412         return cheated;
1413 }
1414
1415 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1416 {
1417         *toset = aabb3f(-0.3f * BS, 0.0f, -0.3f * BS, 0.3f * BS, 1.75f * BS, 0.3f * BS);
1418
1419         toset->MinEdge += m_base_position;
1420         toset->MaxEdge += m_base_position;
1421         return true;
1422 }
1423
1424 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1425 {
1426         if (!m_prop.is_visible) {
1427                 return false;
1428         }
1429
1430         getCollisionBox(toset);
1431
1432         return true;
1433 }