]> git.lizzy.rs Git - minetest.git/blob - src/content_sao.cpp
621a64de0bdfe91ac88aaf043625cfd7da9c2da7
[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) {
756                 return false;
757         }
758
759         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
760         toset->MaxEdge = m_prop.collisionbox.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;
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         // start of default appearance, this should be overwritten by LUA
790         m_prop.visual = "upright_sprite";
791         m_prop.visual_size = v2f(1, 2);
792         m_prop.textures.clear();
793         m_prop.textures.emplace_back("player.png");
794         m_prop.textures.emplace_back("player_back.png");
795         m_prop.colors.clear();
796         m_prop.colors.emplace_back(255, 255, 255, 255);
797         m_prop.spritediv = v2s16(1,1);
798         // end of default appearance
799         m_prop.is_visible = true;
800         m_prop.makes_footstep_sound = true;
801         m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
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 (u32 attached_particle_spawner : m_attached_particle_spawners) {
846                         m_env->deleteParticleSpawner(attached_particle_spawner, false);
847                 }
848         }
849 }
850
851 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
852 {
853         std::ostringstream os(std::ios::binary);
854
855         // Protocol >= 15
856         writeU8(os, 1); // version
857         os << serializeString(m_player->getName()); // name
858         writeU8(os, 1); // is_player
859         writeS16(os, getId()); //id
860         writeV3F1000(os, m_base_position);
861         writeF1000(os, m_yaw);
862         writeS16(os, getHP());
863
864         std::ostringstream msg_os(std::ios::binary);
865         msg_os << serializeLongString(getPropertyPacket()); // message 1
866         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
867         msg_os << serializeLongString(gob_cmd_update_animation(
868                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
869         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
870                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
871                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
872                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
873         }
874         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
875                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
876         msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
877                         m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
878                         m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
879         // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
880         msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
881         int message_count = 6 + m_bone_position.size();
882         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
883                         ii != m_attachment_child_ids.end(); ++ii) {
884                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
885                         message_count++;
886                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
887                                 obj->getClientInitializationData(protocol_version)));
888                 }
889         }
890
891         writeU8(os, message_count);
892         os.write(msg_os.str().c_str(), msg_os.str().size());
893
894         // return result
895         return os.str();
896 }
897
898 void PlayerSAO::getStaticData(std::string * result) const
899 {
900         FATAL_ERROR("Deprecated function");
901 }
902
903 void PlayerSAO::step(float dtime, bool send_recommended)
904 {
905         if (m_drowning_interval.step(dtime, 2.0)) {
906                 // get head position
907                 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
908                 MapNode n = m_env->getMap().getNodeNoEx(p);
909                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
910                 // If node generates drown
911                 if (c.drowning > 0 && m_hp > 0) {
912                         if (m_breath > 0)
913                                 setBreath(m_breath - 1);
914
915                         // No more breath, damage player
916                         if (m_breath == 0) {
917                                 setHP(m_hp - c.drowning);
918                                 m_env->getGameDef()->SendPlayerHPOrDie(this);
919                         }
920                 }
921         }
922
923         if (m_breathing_interval.step(dtime, 0.5)) {
924                 // get head position
925                 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
926                 MapNode n = m_env->getMap().getNodeNoEx(p);
927                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
928                 // If player is alive & no drowning, breath
929                 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
930                         setBreath(m_breath + 1);
931         }
932
933         if (m_node_hurt_interval.step(dtime, 1.0)) {
934                 // Feet, middle and head
935                 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
936                 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
937                 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
938                 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
939                 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
940                 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
941
942                 u32 damage_per_second = 0;
943                 damage_per_second = MYMAX(damage_per_second,
944                         m_env->getGameDef()->ndef()->get(n1).damage_per_second);
945                 damage_per_second = MYMAX(damage_per_second,
946                         m_env->getGameDef()->ndef()->get(n2).damage_per_second);
947                 damage_per_second = MYMAX(damage_per_second,
948                         m_env->getGameDef()->ndef()->get(n3).damage_per_second);
949
950                 if (damage_per_second != 0 && m_hp > 0) {
951                         s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
952                         setHP(newhp);
953                         m_env->getGameDef()->SendPlayerHPOrDie(this);
954                 }
955         }
956
957         if (!m_properties_sent) {
958                 m_properties_sent = true;
959                 std::string str = getPropertyPacket();
960                 // create message and add to list
961                 ActiveObjectMessage aom(getId(), true, str);
962                 m_messages_out.push(aom);
963         }
964
965         // If attached, check that our parent is still there. If it isn't, detach.
966         if(m_attachment_parent_id && !isAttached())
967         {
968                 m_attachment_parent_id = 0;
969                 m_attachment_bone = "";
970                 m_attachment_position = v3f(0,0,0);
971                 m_attachment_rotation = v3f(0,0,0);
972                 setBasePosition(m_last_good_position);
973                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
974         }
975
976         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
977
978         // Set lag pool maximums based on estimated lag
979         const float LAG_POOL_MIN = 5.0;
980         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
981         if(lag_pool_max < LAG_POOL_MIN)
982                 lag_pool_max = LAG_POOL_MIN;
983         m_dig_pool.setMax(lag_pool_max);
984         m_move_pool.setMax(lag_pool_max);
985
986         // Increment cheat prevention timers
987         m_dig_pool.add(dtime);
988         m_move_pool.add(dtime);
989         m_time_from_last_teleport += dtime;
990         m_time_from_last_punch += dtime;
991         m_nocheat_dig_time += dtime;
992
993         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
994         // If the object gets detached this comes into effect automatically from the last known origin
995         if (isAttached()) {
996                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
997                 m_last_good_position = pos;
998                 setBasePosition(pos);
999         }
1000
1001         if (!send_recommended)
1002                 return;
1003
1004         // If the object is attached client-side, don't waste bandwidth sending its position to clients
1005         if(m_position_not_sent && !isAttached())
1006         {
1007                 m_position_not_sent = false;
1008                 float update_interval = m_env->getSendRecommendedInterval();
1009                 v3f pos;
1010                 if(isAttached()) // Just in case we ever do send attachment position too
1011                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1012                 else
1013                         pos = m_base_position;
1014                 std::string str = gob_cmd_update_position(
1015                         pos,
1016                         v3f(0,0,0),
1017                         v3f(0,0,0),
1018                         m_yaw,
1019                         true,
1020                         false,
1021                         update_interval
1022                 );
1023                 // create message and add to list
1024                 ActiveObjectMessage aom(getId(), false, str);
1025                 m_messages_out.push(aom);
1026         }
1027
1028         if (!m_armor_groups_sent) {
1029                 m_armor_groups_sent = true;
1030                 std::string str = gob_cmd_update_armor_groups(
1031                                 m_armor_groups);
1032                 // create message and add to list
1033                 ActiveObjectMessage aom(getId(), true, str);
1034                 m_messages_out.push(aom);
1035         }
1036
1037         if (!m_physics_override_sent) {
1038                 m_physics_override_sent = true;
1039                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1040                                 m_physics_override_jump, m_physics_override_gravity,
1041                                 m_physics_override_sneak, m_physics_override_sneak_glitch,
1042                                 m_physics_override_new_move);
1043                 // create message and add to list
1044                 ActiveObjectMessage aom(getId(), true, str);
1045                 m_messages_out.push(aom);
1046         }
1047
1048         if (!m_animation_sent) {
1049                 m_animation_sent = true;
1050                 std::string str = gob_cmd_update_animation(
1051                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1052                 // create message and add to list
1053                 ActiveObjectMessage aom(getId(), true, str);
1054                 m_messages_out.push(aom);
1055         }
1056
1057         if (!m_bone_position_sent) {
1058                 m_bone_position_sent = true;
1059                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1060                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1061                         std::string str = gob_cmd_update_bone_position((*ii).first,
1062                                         (*ii).second.X, (*ii).second.Y);
1063                         // create message and add to list
1064                         ActiveObjectMessage aom(getId(), true, str);
1065                         m_messages_out.push(aom);
1066                 }
1067         }
1068
1069         if (!m_attachment_sent){
1070                 m_attachment_sent = true;
1071                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1072                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1073                 // create message and add to list
1074                 ActiveObjectMessage aom(getId(), true, str);
1075                 m_messages_out.push(aom);
1076         }
1077 }
1078
1079 void PlayerSAO::setBasePosition(const v3f &position)
1080 {
1081         if (m_player && position != m_base_position)
1082                 m_player->setDirty(true);
1083
1084         // This needs to be ran for attachments too
1085         ServerActiveObject::setBasePosition(position);
1086         m_position_not_sent = true;
1087 }
1088
1089 void PlayerSAO::setPos(const v3f &pos)
1090 {
1091         if(isAttached())
1092                 return;
1093
1094         setBasePosition(pos);
1095         // Movement caused by this command is always valid
1096         m_last_good_position = pos;
1097         m_move_pool.empty();
1098         m_time_from_last_teleport = 0.0;
1099         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1100 }
1101
1102 void PlayerSAO::moveTo(v3f pos, bool continuous)
1103 {
1104         if(isAttached())
1105                 return;
1106
1107         setBasePosition(pos);
1108         // Movement caused by this command is always valid
1109         m_last_good_position = pos;
1110         m_move_pool.empty();
1111         m_time_from_last_teleport = 0.0;
1112         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1113 }
1114
1115 void PlayerSAO::setYaw(const float yaw)
1116 {
1117         if (m_player && yaw != m_yaw)
1118                 m_player->setDirty(true);
1119
1120         UnitSAO::setYaw(yaw);
1121 }
1122
1123 void PlayerSAO::setFov(const float fov)
1124 {
1125         if (m_player && fov != m_fov)
1126                 m_player->setDirty(true);
1127
1128         m_fov = fov;
1129 }
1130
1131 void PlayerSAO::setWantedRange(const s16 range)
1132 {
1133         if (m_player && range != m_wanted_range)
1134                 m_player->setDirty(true);
1135
1136         m_wanted_range = range;
1137 }
1138
1139 void PlayerSAO::setYawAndSend(const float yaw)
1140 {
1141         setYaw(yaw);
1142         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1143 }
1144
1145 void PlayerSAO::setPitch(const float pitch)
1146 {
1147         if (m_player && pitch != m_pitch)
1148                 m_player->setDirty(true);
1149
1150         m_pitch = pitch;
1151 }
1152
1153 void PlayerSAO::setPitchAndSend(const float pitch)
1154 {
1155         setPitch(pitch);
1156         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1157 }
1158
1159 int PlayerSAO::punch(v3f dir,
1160         const ToolCapabilities *toolcap,
1161         ServerActiveObject *puncher,
1162         float time_from_last_punch)
1163 {
1164         // It's best that attachments cannot be punched
1165         if (isAttached())
1166                 return 0;
1167
1168         if (!toolcap)
1169                 return 0;
1170
1171         // No effect if PvP disabled
1172         if (!g_settings->getBool("enable_pvp")) {
1173                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1174                         std::string str = gob_cmd_punched(0, getHP());
1175                         // create message and add to list
1176                         ActiveObjectMessage aom(getId(), true, str);
1177                         m_messages_out.push(aom);
1178                         return 0;
1179                 }
1180         }
1181
1182         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1183                         time_from_last_punch);
1184
1185         std::string punchername = "nil";
1186
1187         if (puncher != 0)
1188                 punchername = puncher->getDescription();
1189
1190         PlayerSAO *playersao = m_player->getPlayerSAO();
1191
1192         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1193                                 puncher, time_from_last_punch, toolcap, dir,
1194                                 hitparams.hp);
1195
1196         if (!damage_handled) {
1197                 setHP(getHP() - hitparams.hp);
1198         } else { // override client prediction
1199                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1200                         std::string str = gob_cmd_punched(0, getHP());
1201                         // create message and add to list
1202                         ActiveObjectMessage aom(getId(), true, str);
1203                         m_messages_out.push(aom);
1204                 }
1205         }
1206
1207
1208         actionstream << "Player " << m_player->getName() << " punched by "
1209                         << punchername;
1210         if (!damage_handled) {
1211                 actionstream << ", damage " << hitparams.hp << " HP";
1212         } else {
1213                 actionstream << ", damage handled by lua";
1214         }
1215         actionstream << std::endl;
1216
1217         return hitparams.wear;
1218 }
1219
1220 s16 PlayerSAO::readDamage()
1221 {
1222         s16 damage = m_damage;
1223         m_damage = 0;
1224         return damage;
1225 }
1226
1227 void PlayerSAO::setHP(s16 hp)
1228 {
1229         s16 oldhp = m_hp;
1230
1231         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1232         if (hp_change == 0)
1233                 return;
1234         hp = oldhp + hp_change;
1235
1236         if (hp < 0)
1237                 hp = 0;
1238         else if (hp > PLAYER_MAX_HP)
1239                 hp = PLAYER_MAX_HP;
1240
1241         if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1242                 return;
1243         }
1244
1245         m_hp = hp;
1246
1247         if (oldhp > hp)
1248                 m_damage += (oldhp - hp);
1249
1250         // Update properties on death
1251         if ((hp == 0) != (oldhp == 0))
1252                 m_properties_sent = false;
1253 }
1254
1255 void PlayerSAO::setBreath(const u16 breath, bool send)
1256 {
1257         if (m_player && breath != m_breath)
1258                 m_player->setDirty(true);
1259
1260         m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1261
1262         if (send)
1263                 m_env->getGameDef()->SendPlayerBreath(this);
1264 }
1265
1266 Inventory* PlayerSAO::getInventory()
1267 {
1268         return m_inventory;
1269 }
1270 const Inventory* PlayerSAO::getInventory() const
1271 {
1272         return m_inventory;
1273 }
1274
1275 InventoryLocation PlayerSAO::getInventoryLocation() const
1276 {
1277         InventoryLocation loc;
1278         loc.setPlayer(m_player->getName());
1279         return loc;
1280 }
1281
1282 std::string PlayerSAO::getWieldList() const
1283 {
1284         return "main";
1285 }
1286
1287 ItemStack PlayerSAO::getWieldedItem() const
1288 {
1289         const Inventory *inv = getInventory();
1290         ItemStack ret;
1291         const InventoryList *mlist = inv->getList(getWieldList());
1292         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1293                 ret = mlist->getItem(getWieldIndex());
1294         return ret;
1295 }
1296
1297 ItemStack PlayerSAO::getWieldedItemOrHand() const
1298 {
1299         const Inventory *inv = getInventory();
1300         ItemStack ret;
1301         const InventoryList *mlist = inv->getList(getWieldList());
1302         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1303                 ret = mlist->getItem(getWieldIndex());
1304         if (ret.name.empty()) {
1305                 const InventoryList *hlist = inv->getList("hand");
1306                 if (hlist)
1307                         ret = hlist->getItem(0);
1308         }
1309         return ret;
1310 }
1311
1312 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1313 {
1314         Inventory *inv = getInventory();
1315         if (inv) {
1316                 InventoryList *mlist = inv->getList(getWieldList());
1317                 if (mlist) {
1318                         mlist->changeItem(getWieldIndex(), item);
1319                         return true;
1320                 }
1321         }
1322         return false;
1323 }
1324
1325 int PlayerSAO::getWieldIndex() const
1326 {
1327         return m_wield_index;
1328 }
1329
1330 void PlayerSAO::setWieldIndex(int i)
1331 {
1332         if(i != m_wield_index) {
1333                 m_wield_index = i;
1334         }
1335 }
1336
1337 // Erase the peer id and make the object for removal
1338 void PlayerSAO::disconnected()
1339 {
1340         m_peer_id = 0;
1341         m_removed = true;
1342 }
1343
1344 void PlayerSAO::unlinkPlayerSessionAndSave()
1345 {
1346         assert(m_player->getPlayerSAO() == this);
1347         m_player->peer_id = 0;
1348         m_env->savePlayer(m_player);
1349         m_player->setPlayerSAO(NULL);
1350         m_env->removePlayer(m_player);
1351 }
1352
1353 std::string PlayerSAO::getPropertyPacket()
1354 {
1355         m_prop.is_visible = (true);
1356         return gob_cmd_set_properties(m_prop);
1357 }
1358
1359 bool PlayerSAO::checkMovementCheat()
1360 {
1361         if (isAttached() || m_is_singleplayer ||
1362                         g_settings->getBool("disable_anticheat")) {
1363                 m_last_good_position = m_base_position;
1364                 return false;
1365         }
1366
1367         bool cheated = false;
1368         /*
1369                 Check player movements
1370
1371                 NOTE: Actually the server should handle player physics like the
1372                 client does and compare player's position to what is calculated
1373                 on our side. This is required when eg. players fly due to an
1374                 explosion. Altough a node-based alternative might be possible
1375                 too, and much more lightweight.
1376         */
1377
1378         float player_max_speed = 0;
1379
1380         if (m_privs.count("fast") != 0) {
1381                 // Fast speed
1382                 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1383         } else {
1384                 // Normal speed
1385                 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1386         }
1387         // Tolerance. The lag pool does this a bit.
1388         //player_max_speed *= 2.5;
1389
1390         v3f diff = (m_base_position - m_last_good_position);
1391         float d_vert = diff.Y;
1392         diff.Y = 0;
1393         float d_horiz = diff.getLength();
1394         float required_time = d_horiz / player_max_speed;
1395
1396         if (d_vert > 0 && d_vert / player_max_speed > required_time)
1397                 required_time = d_vert / player_max_speed; // Moving upwards
1398
1399         if (m_move_pool.grab(required_time)) {
1400                 m_last_good_position = m_base_position;
1401         } else {
1402                 const float LAG_POOL_MIN = 5.0;
1403                 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1404                 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1405                 if (m_time_from_last_teleport > lag_pool_max) {
1406                         actionstream << "Player " << m_player->getName()
1407                                         << " moved too fast; resetting position"
1408                                         << std::endl;
1409                         cheated = true;
1410                 }
1411                 setBasePosition(m_last_good_position);
1412         }
1413         return cheated;
1414 }
1415
1416 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1417 {
1418         //update collision box
1419         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1420         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1421
1422         toset->MinEdge += m_base_position;
1423         toset->MaxEdge += m_base_position;
1424         return true;
1425 }
1426
1427 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1428 {
1429         if (!m_prop.is_visible) {
1430                 return false;
1431         }
1432
1433         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1434         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1435
1436         return true;
1437 }