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