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