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