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