]> git.lizzy.rs Git - minetest.git/blob - src/content_sao.cpp
4debc354f15154a9966a355abc3f98ebe88a0e42
[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)
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 (u32 attached_particle_spawner : m_attached_particle_spawners) {
249                 m_env->deleteParticleSpawner(attached_particle_spawner, false);
250         }
251 }
252
253 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
254 {
255         ServerActiveObject::addedToEnvironment(dtime_s);
256
257         // Create entity from name
258         m_registered = m_env->getScriptIface()->
259                 luaentity_Add(m_id, m_init_name.c_str());
260
261         if(m_registered){
262                 // Get properties
263                 m_env->getScriptIface()->
264                         luaentity_GetProperties(m_id, &m_prop);
265                 // Initialize HP from properties
266                 m_hp = m_prop.hp_max;
267                 // Activate entity, supplying serialized state
268                 m_env->getScriptIface()->
269                         luaentity_Activate(m_id, m_init_state, dtime_s);
270         } else {
271                 m_prop.infotext = m_init_name;
272         }
273 }
274
275 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
276                 const std::string &data)
277 {
278         std::string name;
279         std::string state;
280         s16 hp = 1;
281         v3f velocity;
282         float yaw = 0;
283         if (!data.empty()) {
284                 std::istringstream is(data, std::ios::binary);
285                 // read version
286                 u8 version = readU8(is);
287                 // check if version is supported
288                 if(version == 0){
289                         name = deSerializeString(is);
290                         state = deSerializeLongString(is);
291                 }
292                 else if(version == 1){
293                         name = deSerializeString(is);
294                         state = deSerializeLongString(is);
295                         hp = readS16(is);
296                         velocity = readV3F1000(is);
297                         yaw = readF1000(is);
298                 }
299         }
300         // create object
301         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
302                         <<state<<"\")"<<std::endl;
303         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
304         sao->m_hp = hp;
305         sao->m_velocity = velocity;
306         sao->m_yaw = yaw;
307         return sao;
308 }
309
310 void LuaEntitySAO::step(float dtime, bool send_recommended)
311 {
312         if(!m_properties_sent)
313         {
314                 m_properties_sent = true;
315                 std::string str = getPropertyPacket();
316                 // create message and add to list
317                 ActiveObjectMessage aom(getId(), true, str);
318                 m_messages_out.push(aom);
319         }
320
321         // If attached, check that our parent is still there. If it isn't, detach.
322         if(m_attachment_parent_id && !isAttached())
323         {
324                 m_attachment_parent_id = 0;
325                 m_attachment_bone = "";
326                 m_attachment_position = v3f(0,0,0);
327                 m_attachment_rotation = v3f(0,0,0);
328                 sendPosition(false, true);
329         }
330
331         m_last_sent_position_timer += dtime;
332
333         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
334         // If the object gets detached this comes into effect automatically from the last known origin
335         if(isAttached())
336         {
337                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
338                 m_base_position = pos;
339                 m_velocity = v3f(0,0,0);
340                 m_acceleration = v3f(0,0,0);
341         }
342         else
343         {
344                 if(m_prop.physical){
345                         aabb3f box = m_prop.collisionbox;
346                         box.MinEdge *= BS;
347                         box.MaxEdge *= BS;
348                         collisionMoveResult moveresult;
349                         f32 pos_max_d = BS*0.25; // Distance per iteration
350                         v3f p_pos = m_base_position;
351                         v3f p_velocity = m_velocity;
352                         v3f p_acceleration = m_acceleration;
353                         moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
354                                         pos_max_d, box, m_prop.stepheight, dtime,
355                                         &p_pos, &p_velocity, p_acceleration,
356                                         this, m_prop.collideWithObjects);
357
358                         // Apply results
359                         m_base_position = p_pos;
360                         m_velocity = p_velocity;
361                         m_acceleration = p_acceleration;
362                 } else {
363                         m_base_position += dtime * m_velocity + 0.5 * dtime
364                                         * dtime * m_acceleration;
365                         m_velocity += dtime * m_acceleration;
366                 }
367
368                 if((m_prop.automatic_face_movement_dir) &&
369                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
370                 {
371                         float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
372                                         + m_prop.automatic_face_movement_dir_offset;
373                         float max_rotation_delta =
374                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
375
376                         if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
377                                 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
378
379                                 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
380                         } else {
381                                 m_yaw = optimal_yaw;
382                         }
383                 }
384         }
385
386         if(m_registered){
387                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
388         }
389
390         // Remove LuaEntity beyond terrain edges
391         {
392                 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
393                 assert(map);
394                 if (!m_pending_deactivation &&
395                                 map->saoPositionOverLimit(m_base_position)) {
396                         infostream << "Remove SAO " << m_id << "(" << m_init_name
397                                 << "), outside of limits" << std::endl;
398                         m_pending_deactivation = true;
399                         m_removed = true;
400                         return;
401                 }
402         }
403
404         if (!send_recommended)
405                 return;
406
407         if(!isAttached())
408         {
409                 // TODO: force send when acceleration changes enough?
410                 float minchange = 0.2*BS;
411                 if(m_last_sent_position_timer > 1.0){
412                         minchange = 0.01*BS;
413                 } else if(m_last_sent_position_timer > 0.2){
414                         minchange = 0.05*BS;
415                 }
416                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
417                 move_d += m_last_sent_move_precision;
418                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
419                 if(move_d > minchange || vel_d > minchange ||
420                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
421                         sendPosition(true, false);
422                 }
423         }
424
425         if (!m_armor_groups_sent) {
426                 m_armor_groups_sent = true;
427                 std::string str = gob_cmd_update_armor_groups(
428                                 m_armor_groups);
429                 // create message and add to list
430                 ActiveObjectMessage aom(getId(), true, str);
431                 m_messages_out.push(aom);
432         }
433
434         if (!m_animation_sent) {
435                 m_animation_sent = true;
436                 std::string str = gob_cmd_update_animation(
437                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
438                 // create message and add to list
439                 ActiveObjectMessage aom(getId(), true, str);
440                 m_messages_out.push(aom);
441         }
442
443         if (!m_bone_position_sent) {
444                 m_bone_position_sent = true;
445                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
446                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
447                         std::string str = gob_cmd_update_bone_position((*ii).first,
448                                         (*ii).second.X, (*ii).second.Y);
449                         // create message and add to list
450                         ActiveObjectMessage aom(getId(), true, str);
451                         m_messages_out.push(aom);
452                 }
453         }
454
455         if (!m_attachment_sent) {
456                 m_attachment_sent = true;
457                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
458                 // create message and add to list
459                 ActiveObjectMessage aom(getId(), true, str);
460                 m_messages_out.push(aom);
461         }
462 }
463
464 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
465 {
466         std::ostringstream os(std::ios::binary);
467
468         // protocol >= 14
469         writeU8(os, 1); // version
470         os << serializeString(""); // name
471         writeU8(os, 0); // is_player
472         writeS16(os, getId()); //id
473         writeV3F1000(os, m_base_position);
474         writeF1000(os, m_yaw);
475         writeS16(os, m_hp);
476
477         std::ostringstream msg_os(std::ios::binary);
478         msg_os << serializeLongString(getPropertyPacket()); // message 1
479         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
480         msg_os << serializeLongString(gob_cmd_update_animation(
481                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
482         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
483                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
484                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
485                                 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
486         }
487         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
488                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
489         int message_count = 4 + m_bone_position.size();
490         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
491                         (ii != m_attachment_child_ids.end()); ++ii) {
492                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
493                         message_count++;
494                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
495                                 obj->getClientInitializationData(protocol_version)));
496                 }
497         }
498
499         msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
500         message_count++;
501
502         writeU8(os, message_count);
503         os.write(msg_os.str().c_str(), msg_os.str().size());
504
505         // return result
506         return os.str();
507 }
508
509 void LuaEntitySAO::getStaticData(std::string *result) const
510 {
511         verbosestream<<FUNCTION_NAME<<std::endl;
512         std::ostringstream os(std::ios::binary);
513         // version
514         writeU8(os, 1);
515         // name
516         os<<serializeString(m_init_name);
517         // state
518         if(m_registered){
519                 std::string state = m_env->getScriptIface()->
520                         luaentity_GetStaticdata(m_id);
521                 os<<serializeLongString(state);
522         } else {
523                 os<<serializeLongString(m_init_state);
524         }
525         // hp
526         writeS16(os, m_hp);
527         // velocity
528         writeV3F1000(os, m_velocity);
529         // yaw
530         writeF1000(os, m_yaw);
531         *result = os.str();
532 }
533
534 int LuaEntitySAO::punch(v3f dir,
535                 const ToolCapabilities *toolcap,
536                 ServerActiveObject *puncher,
537                 float time_from_last_punch)
538 {
539         if (!m_registered){
540                 // Delete unknown LuaEntities when punched
541                 m_removed = true;
542                 return 0;
543         }
544
545         // It's best that attachments cannot be punched
546         if (isAttached())
547                 return 0;
548
549         ItemStack *punchitem = NULL;
550         ItemStack punchitem_static;
551         if (puncher) {
552                 punchitem_static = puncher->getWieldedItem();
553                 punchitem = &punchitem_static;
554         }
555
556         PunchDamageResult result = getPunchDamage(
557                         m_armor_groups,
558                         toolcap,
559                         punchitem,
560                         time_from_last_punch);
561
562         bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
563                         time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
564
565         if (!damage_handled) {
566                 if (result.did_punch) {
567                         setHP(getHP() - result.damage);
568
569                         if (result.damage > 0) {
570                                 std::string punchername = puncher ? puncher->getDescription() : "nil";
571
572                                 actionstream << getDescription() << " punched by "
573                                                 << punchername << ", damage " << result.damage
574                                                 << " hp, health now " << getHP() << " hp" << std::endl;
575                         }
576
577                         std::string str = gob_cmd_punched(result.damage, getHP());
578                         // create message and add to list
579                         ActiveObjectMessage aom(getId(), true, str);
580                         m_messages_out.push(aom);
581                 }
582         }
583
584         if (getHP() == 0) {
585                 m_removed = true;
586                 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
587         }
588
589
590
591         return result.wear;
592 }
593
594 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
595 {
596         if (!m_registered)
597                 return;
598         // It's best that attachments cannot be clicked
599         if (isAttached())
600                 return;
601         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
602 }
603
604 void LuaEntitySAO::setPos(const v3f &pos)
605 {
606         if(isAttached())
607                 return;
608         m_base_position = pos;
609         sendPosition(false, true);
610 }
611
612 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
613 {
614         if(isAttached())
615                 return;
616         m_base_position = pos;
617         if(!continuous)
618                 sendPosition(true, true);
619 }
620
621 float LuaEntitySAO::getMinimumSavedMovement()
622 {
623         return 0.1 * BS;
624 }
625
626 std::string LuaEntitySAO::getDescription()
627 {
628         std::ostringstream os(std::ios::binary);
629         os<<"LuaEntitySAO at (";
630         os<<(m_base_position.X/BS)<<",";
631         os<<(m_base_position.Y/BS)<<",";
632         os<<(m_base_position.Z/BS);
633         os<<")";
634         return os.str();
635 }
636
637 void LuaEntitySAO::setHP(s16 hp)
638 {
639         if(hp < 0) hp = 0;
640         m_hp = hp;
641 }
642
643 s16 LuaEntitySAO::getHP() const
644 {
645         return m_hp;
646 }
647
648 void LuaEntitySAO::setVelocity(v3f velocity)
649 {
650         m_velocity = velocity;
651 }
652
653 v3f LuaEntitySAO::getVelocity()
654 {
655         return m_velocity;
656 }
657
658 void LuaEntitySAO::setAcceleration(v3f acceleration)
659 {
660         m_acceleration = acceleration;
661 }
662
663 v3f LuaEntitySAO::getAcceleration()
664 {
665         return m_acceleration;
666 }
667
668 void LuaEntitySAO::setTextureMod(const std::string &mod)
669 {
670         std::string str = gob_cmd_set_texture_mod(mod);
671         m_current_texture_modifier = mod;
672         // create message and add to list
673         ActiveObjectMessage aom(getId(), true, str);
674         m_messages_out.push(aom);
675 }
676
677 std::string LuaEntitySAO::getTextureMod() const
678 {
679         return m_current_texture_modifier;
680 }
681
682 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
683                 bool select_horiz_by_yawpitch)
684 {
685         std::string str = gob_cmd_set_sprite(
686                 p,
687                 num_frames,
688                 framelength,
689                 select_horiz_by_yawpitch
690         );
691         // create message and add to list
692         ActiveObjectMessage aom(getId(), true, str);
693         m_messages_out.push(aom);
694 }
695
696 std::string LuaEntitySAO::getName()
697 {
698         return m_init_name;
699 }
700
701 std::string LuaEntitySAO::getPropertyPacket()
702 {
703         return gob_cmd_set_properties(m_prop);
704 }
705
706 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
707 {
708         // If the object is attached client-side, don't waste bandwidth sending its position to clients
709         if(isAttached())
710                 return;
711
712         m_last_sent_move_precision = m_base_position.getDistanceFrom(
713                         m_last_sent_position);
714         m_last_sent_position_timer = 0;
715         m_last_sent_yaw = m_yaw;
716         m_last_sent_position = m_base_position;
717         m_last_sent_velocity = m_velocity;
718         //m_last_sent_acceleration = m_acceleration;
719
720         float update_interval = m_env->getSendRecommendedInterval();
721
722         std::string str = gob_cmd_update_position(
723                 m_base_position,
724                 m_velocity,
725                 m_acceleration,
726                 m_yaw,
727                 do_interpolate,
728                 is_movement_end,
729                 update_interval
730         );
731         // create message and add to list
732         ActiveObjectMessage aom(getId(), false, str);
733         m_messages_out.push(aom);
734 }
735
736 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
737 {
738         if (m_prop.physical)
739         {
740                 //update collision box
741                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
742                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
743
744                 toset->MinEdge += m_base_position;
745                 toset->MaxEdge += m_base_position;
746
747                 return true;
748         }
749
750         return false;
751 }
752
753 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
754 {
755         if (!m_prop.is_visible || !m_prop.pointable) {
756                 return false;
757         }
758
759         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
760         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
761
762         return true;
763 }
764
765 bool LuaEntitySAO::collideWithObjects() const
766 {
767         return m_prop.collideWithObjects;
768 }
769
770 /*
771         PlayerSAO
772 */
773
774 // No prototype, PlayerSAO does not need to be deserialized
775
776 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
777                 bool is_singleplayer):
778         UnitSAO(env_, v3f(0,0,0)),
779         m_player(player_),
780         m_peer_id(peer_id_),
781         m_is_singleplayer(is_singleplayer)
782 {
783         assert(m_peer_id != 0); // pre-condition
784
785         m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
786         m_prop.physical = false;
787         m_prop.weight = 75;
788         m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
789         m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
790         m_prop.pointable = true;
791         // start of default appearance, this should be overwritten by LUA
792         m_prop.visual = "upright_sprite";
793         m_prop.visual_size = v2f(1, 2);
794         m_prop.textures.clear();
795         m_prop.textures.emplace_back("player.png");
796         m_prop.textures.emplace_back("player_back.png");
797         m_prop.colors.clear();
798         m_prop.colors.emplace_back(255, 255, 255, 255);
799         m_prop.spritediv = v2s16(1,1);
800         // end of default appearance
801         m_prop.is_visible = true;
802         m_prop.makes_footstep_sound = true;
803         m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
804         m_hp = m_prop.hp_max;
805 }
806
807 PlayerSAO::~PlayerSAO()
808 {
809         if(m_inventory != &m_player->inventory)
810                 delete m_inventory;
811 }
812
813 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
814 {
815         assert(player);
816         m_player = player;
817         m_privs = privs;
818         m_inventory = &m_player->inventory;
819 }
820
821 v3f PlayerSAO::getEyeOffset() const
822 {
823         return v3f(0, BS * 1.625f, 0);
824 }
825
826 std::string PlayerSAO::getDescription()
827 {
828         return std::string("player ") + m_player->getName();
829 }
830
831 // Called after id has been set and has been inserted in environment
832 void PlayerSAO::addedToEnvironment(u32 dtime_s)
833 {
834         ServerActiveObject::addedToEnvironment(dtime_s);
835         ServerActiveObject::setBasePosition(m_base_position);
836         m_player->setPlayerSAO(this);
837         m_player->peer_id = m_peer_id;
838         m_last_good_position = m_base_position;
839 }
840
841 // Called before removing from environment
842 void PlayerSAO::removingFromEnvironment()
843 {
844         ServerActiveObject::removingFromEnvironment();
845         if (m_player->getPlayerSAO() == this) {
846                 unlinkPlayerSessionAndSave();
847                 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
848                         m_env->deleteParticleSpawner(attached_particle_spawner, 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")) {
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 > m_prop.hp_max)
1241                 hp = m_prop.hp_max;
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 || !m_prop.pointable) {
1432                 return false;
1433         }
1434
1435         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1436         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
1437
1438         return true;
1439 }