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