31ee21e583542128e7c15ff266dd5b40d44dfcf7
[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_pending_removal = true;
63                         return;
64                 }
65
66                 m_base_position.Y += dtime * BS * 2;
67                 if(m_base_position.Y > 8*BS)
68                         m_base_position.Y = 2*BS;
69
70                 if (!send_recommended)
71                         return;
72
73                 m_timer1 -= dtime;
74                 if(m_timer1 < 0.0)
75                 {
76                         m_timer1 += 0.125;
77
78                         std::string data;
79
80                         data += itos(0); // 0 = position
81                         data += " ";
82                         data += itos(m_base_position.X);
83                         data += " ";
84                         data += itos(m_base_position.Y);
85                         data += " ";
86                         data += itos(m_base_position.Z);
87
88                         ActiveObjectMessage aom(getId(), false, data);
89                         m_messages_out.push(aom);
90                 }
91         }
92
93         bool getCollisionBox(aabb3f *toset) const { return false; }
94
95         virtual bool getSelectionBox(aabb3f *toset) const { return false; }
96
97         bool collideWithObjects() const { return false; }
98
99 private:
100         float m_timer1;
101         float m_age;
102 };
103
104 // Prototype (registers item for deserialization)
105 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
106
107 /*
108         UnitSAO
109  */
110
111 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
112         ServerActiveObject(env, pos)
113 {
114         // Initialize something to armor groups
115         m_armor_groups["fleshy"] = 100;
116 }
117
118 bool UnitSAO::isAttached() const
119 {
120         if (!m_attachment_parent_id)
121                 return false;
122         // Check if the parent still exists
123         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
124         if (obj)
125                 return true;
126         return false;
127 }
128
129 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
130 {
131         m_armor_groups = armor_groups;
132         m_armor_groups_sent = false;
133 }
134
135 const ItemGroupList &UnitSAO::getArmorGroups()
136 {
137         return m_armor_groups;
138 }
139
140 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
141 {
142         // store these so they can be updated to clients
143         m_animation_range = frame_range;
144         m_animation_speed = frame_speed;
145         m_animation_blend = frame_blend;
146         m_animation_loop = frame_loop;
147         m_animation_sent = false;
148 }
149
150 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
151 {
152         *frame_range = m_animation_range;
153         *frame_speed = m_animation_speed;
154         *frame_blend = m_animation_blend;
155         *frame_loop = m_animation_loop;
156 }
157
158 void UnitSAO::setAnimationSpeed(float frame_speed)
159 {
160         m_animation_speed = frame_speed;
161         m_animation_speed_sent = false;
162 }
163
164 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
165 {
166         // store these so they can be updated to clients
167         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
168         m_bone_position_sent = false;
169 }
170
171 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
172 {
173         *position = m_bone_position[bone].X;
174         *rotation = m_bone_position[bone].Y;
175 }
176
177 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
178 {
179         // Attachments need to be handled on both the server and client.
180         // If we just attach on the server, we can only copy the position of the parent. Attachments
181         // are still sent to clients at an interval so players might see them lagging, plus we can't
182         // read and attach to skeletal bones.
183         // If we just attach on the client, the server still sees the child at its original location.
184         // This breaks some things so we also give the server the most accurate representation
185         // even if players only see the client changes.
186
187         m_attachment_parent_id = parent_id;
188         m_attachment_bone = bone;
189         m_attachment_position = position;
190         m_attachment_rotation = rotation;
191         m_attachment_sent = false;
192 }
193
194 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
195         v3f *rotation)
196 {
197         *parent_id = m_attachment_parent_id;
198         *bone = m_attachment_bone;
199         *position = m_attachment_position;
200         *rotation = m_attachment_rotation;
201 }
202
203 void UnitSAO::addAttachmentChild(int child_id)
204 {
205         m_attachment_child_ids.insert(child_id);
206 }
207
208 void UnitSAO::removeAttachmentChild(int child_id)
209 {
210         m_attachment_child_ids.erase(child_id);
211 }
212
213 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
214 {
215         return m_attachment_child_ids;
216 }
217
218 ObjectProperties* UnitSAO::accessObjectProperties()
219 {
220         return &m_prop;
221 }
222
223 void UnitSAO::notifyObjectPropertiesModified()
224 {
225         m_properties_sent = false;
226 }
227
228 /*
229         LuaEntitySAO
230 */
231
232 // Prototype (registers item for deserialization)
233 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
234
235 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
236                 const std::string &name, const std::string &state):
237         UnitSAO(env, pos),
238         m_init_name(name),
239         m_init_state(state)
240 {
241         // Only register type if no environment supplied
242         if(env == NULL){
243                 ServerActiveObject::registerType(getType(), create);
244                 return;
245         }
246 }
247
248 LuaEntitySAO::~LuaEntitySAO()
249 {
250         if(m_registered){
251                 m_env->getScriptIface()->luaentity_Remove(m_id);
252         }
253
254         for (u32 attached_particle_spawner : m_attached_particle_spawners) {
255                 m_env->deleteParticleSpawner(attached_particle_spawner, false);
256         }
257 }
258
259 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
260 {
261         ServerActiveObject::addedToEnvironment(dtime_s);
262
263         // Create entity from name
264         m_registered = m_env->getScriptIface()->
265                 luaentity_Add(m_id, m_init_name.c_str());
266
267         if(m_registered){
268                 // Get properties
269                 m_env->getScriptIface()->
270                         luaentity_GetProperties(m_id, &m_prop);
271                 // Initialize HP from properties
272                 m_hp = m_prop.hp_max;
273                 // Activate entity, supplying serialized state
274                 m_env->getScriptIface()->
275                         luaentity_Activate(m_id, m_init_state, dtime_s);
276         } else {
277                 m_prop.infotext = m_init_name;
278         }
279 }
280
281 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
282                 const std::string &data)
283 {
284         std::string name;
285         std::string state;
286         s16 hp = 1;
287         v3f velocity;
288         float yaw = 0;
289         if (!data.empty()) {
290                 std::istringstream is(data, std::ios::binary);
291                 // read version
292                 u8 version = readU8(is);
293                 // check if version is supported
294                 if(version == 0){
295                         name = deSerializeString(is);
296                         state = deSerializeLongString(is);
297                 }
298                 else if(version == 1){
299                         name = deSerializeString(is);
300                         state = deSerializeLongString(is);
301                         hp = readS16(is);
302                         velocity = readV3F1000(is);
303                         yaw = readF1000(is);
304                 }
305         }
306         // create object
307         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
308                         <<state<<"\")"<<std::endl;
309         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
310         sao->m_hp = hp;
311         sao->m_velocity = velocity;
312         sao->m_yaw = yaw;
313         return sao;
314 }
315
316 void LuaEntitySAO::step(float dtime, bool send_recommended)
317 {
318         if(!m_properties_sent)
319         {
320                 m_properties_sent = true;
321                 std::string str = getPropertyPacket();
322                 // create message and add to list
323                 ActiveObjectMessage aom(getId(), true, str);
324                 m_messages_out.push(aom);
325         }
326
327         // If attached, check that our parent is still there. If it isn't, detach.
328         if(m_attachment_parent_id && !isAttached())
329         {
330                 m_attachment_parent_id = 0;
331                 m_attachment_bone = "";
332                 m_attachment_position = v3f(0,0,0);
333                 m_attachment_rotation = v3f(0,0,0);
334                 sendPosition(false, true);
335         }
336
337         m_last_sent_position_timer += dtime;
338
339         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
340         // If the object gets detached this comes into effect automatically from the last known origin
341         if(isAttached())
342         {
343                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
344                 m_base_position = pos;
345                 m_velocity = v3f(0,0,0);
346                 m_acceleration = v3f(0,0,0);
347         }
348         else
349         {
350                 if(m_prop.physical){
351                         aabb3f box = m_prop.collisionbox;
352                         box.MinEdge *= BS;
353                         box.MaxEdge *= BS;
354                         collisionMoveResult moveresult;
355                         f32 pos_max_d = BS*0.25; // Distance per iteration
356                         v3f p_pos = m_base_position;
357                         v3f p_velocity = m_velocity;
358                         v3f p_acceleration = m_acceleration;
359                         moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
360                                         pos_max_d, box, m_prop.stepheight, dtime,
361                                         &p_pos, &p_velocity, p_acceleration,
362                                         this, m_prop.collideWithObjects);
363
364                         // Apply results
365                         m_base_position = p_pos;
366                         m_velocity = p_velocity;
367                         m_acceleration = p_acceleration;
368                 } else {
369                         m_base_position += dtime * m_velocity + 0.5 * dtime
370                                         * dtime * m_acceleration;
371                         m_velocity += dtime * m_acceleration;
372                 }
373
374                 if((m_prop.automatic_face_movement_dir) &&
375                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
376                 {
377                         float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
378                                         + m_prop.automatic_face_movement_dir_offset;
379                         float max_rotation_delta =
380                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
381
382                         if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
383                                 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
384
385                                 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
386                         } else {
387                                 m_yaw = optimal_yaw;
388                         }
389                 }
390         }
391
392         if(m_registered){
393                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
394         }
395
396         // Remove LuaEntity beyond terrain edges
397         {
398                 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
399                 assert(map);
400                 if (!m_pending_removal &&
401                                 map->saoPositionOverLimit(m_base_position)) {
402                         infostream << "Removing SAO " << m_id << "(" << m_init_name
403                                 << "), outside of limits" << std::endl;
404                         m_pending_removal = true;
405                         return;
406                 }
407         }
408
409         if (!send_recommended)
410                 return;
411
412         if(!isAttached())
413         {
414                 // TODO: force send when acceleration changes enough?
415                 float minchange = 0.2*BS;
416                 if(m_last_sent_position_timer > 1.0){
417                         minchange = 0.01*BS;
418                 } else if(m_last_sent_position_timer > 0.2){
419                         minchange = 0.05*BS;
420                 }
421                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
422                 move_d += m_last_sent_move_precision;
423                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
424                 if(move_d > minchange || vel_d > minchange ||
425                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
426                         sendPosition(true, false);
427                 }
428         }
429
430         if (!m_armor_groups_sent) {
431                 m_armor_groups_sent = true;
432                 std::string str = gob_cmd_update_armor_groups(
433                                 m_armor_groups);
434                 // create message and add to list
435                 ActiveObjectMessage aom(getId(), true, str);
436                 m_messages_out.push(aom);
437         }
438
439         if (!m_animation_sent) {
440                 m_animation_sent = true;
441                 std::string str = gob_cmd_update_animation(
442                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
443                 // create message and add to list
444                 ActiveObjectMessage aom(getId(), true, str);
445                 m_messages_out.push(aom);
446         }
447
448         if (!m_animation_speed_sent) {
449                 m_animation_speed_sent = true;
450                 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
451                 // create message and add to list
452                 ActiveObjectMessage aom(getId(), true, str);
453                 m_messages_out.push(aom);
454         }
455
456         if (!m_bone_position_sent) {
457                 m_bone_position_sent = true;
458                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
459                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
460                         std::string str = gob_cmd_update_bone_position((*ii).first,
461                                         (*ii).second.X, (*ii).second.Y);
462                         // create message and add to list
463                         ActiveObjectMessage aom(getId(), true, str);
464                         m_messages_out.push(aom);
465                 }
466         }
467
468         if (!m_attachment_sent) {
469                 m_attachment_sent = true;
470                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
471                 // create message and add to list
472                 ActiveObjectMessage aom(getId(), true, str);
473                 m_messages_out.push(aom);
474         }
475 }
476
477 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
478 {
479         std::ostringstream os(std::ios::binary);
480
481         // protocol >= 14
482         writeU8(os, 1); // version
483         os << serializeString(""); // name
484         writeU8(os, 0); // is_player
485         writeS16(os, getId()); //id
486         writeV3F1000(os, m_base_position);
487         writeF1000(os, m_yaw);
488         writeS16(os, m_hp);
489
490         std::ostringstream msg_os(std::ios::binary);
491         msg_os << serializeLongString(getPropertyPacket()); // message 1
492         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
493         msg_os << serializeLongString(gob_cmd_update_animation(
494                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
495         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
496                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
497                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
498                                 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
499         }
500         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
501                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
502         int message_count = 4 + m_bone_position.size();
503         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
504                         (ii != m_attachment_child_ids.end()); ++ii) {
505                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
506                         message_count++;
507                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
508                                 obj->getClientInitializationData(protocol_version)));
509                 }
510         }
511
512         msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
513         message_count++;
514
515         writeU8(os, message_count);
516         os.write(msg_os.str().c_str(), msg_os.str().size());
517
518         // return result
519         return os.str();
520 }
521
522 void LuaEntitySAO::getStaticData(std::string *result) const
523 {
524         verbosestream<<FUNCTION_NAME<<std::endl;
525         std::ostringstream os(std::ios::binary);
526         // version
527         writeU8(os, 1);
528         // name
529         os<<serializeString(m_init_name);
530         // state
531         if(m_registered){
532                 std::string state = m_env->getScriptIface()->
533                         luaentity_GetStaticdata(m_id);
534                 os<<serializeLongString(state);
535         } else {
536                 os<<serializeLongString(m_init_state);
537         }
538         // hp
539         writeS16(os, m_hp);
540         // velocity
541         writeV3F1000(os, m_velocity);
542         // yaw
543         writeF1000(os, m_yaw);
544         *result = os.str();
545 }
546
547 int LuaEntitySAO::punch(v3f dir,
548                 const ToolCapabilities *toolcap,
549                 ServerActiveObject *puncher,
550                 float time_from_last_punch)
551 {
552         if (!m_registered) {
553                 // Delete unknown LuaEntities when punched
554                 m_pending_removal = true;
555                 return 0;
556         }
557
558         // It's best that attachments cannot be punched
559         if (isAttached())
560                 return 0;
561
562         ItemStack *punchitem = NULL;
563         ItemStack punchitem_static;
564         if (puncher) {
565                 punchitem_static = puncher->getWieldedItem();
566                 punchitem = &punchitem_static;
567         }
568
569         PunchDamageResult result = getPunchDamage(
570                         m_armor_groups,
571                         toolcap,
572                         punchitem,
573                         time_from_last_punch);
574
575         bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
576                         time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
577
578         if (!damage_handled) {
579                 if (result.did_punch) {
580                         setHP(getHP() - result.damage);
581
582                         if (result.damage > 0) {
583                                 std::string punchername = puncher ? puncher->getDescription() : "nil";
584
585                                 actionstream << getDescription() << " punched by "
586                                                 << punchername << ", damage " << result.damage
587                                                 << " hp, health now " << getHP() << " hp" << std::endl;
588                         }
589
590                         std::string str = gob_cmd_punched(result.damage, getHP());
591                         // create message and add to list
592                         ActiveObjectMessage aom(getId(), true, str);
593                         m_messages_out.push(aom);
594                 }
595         }
596
597         if (getHP() == 0) {
598                 m_pending_removal = true;
599                 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
600         }
601
602         return result.wear;
603 }
604
605 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
606 {
607         if (!m_registered)
608                 return;
609         // It's best that attachments cannot be clicked
610         if (isAttached())
611                 return;
612         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
613 }
614
615 void LuaEntitySAO::setPos(const v3f &pos)
616 {
617         if(isAttached())
618                 return;
619         m_base_position = pos;
620         sendPosition(false, true);
621 }
622
623 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
624 {
625         if(isAttached())
626                 return;
627         m_base_position = pos;
628         if(!continuous)
629                 sendPosition(true, true);
630 }
631
632 float LuaEntitySAO::getMinimumSavedMovement()
633 {
634         return 0.1 * BS;
635 }
636
637 std::string LuaEntitySAO::getDescription()
638 {
639         std::ostringstream os(std::ios::binary);
640         os<<"LuaEntitySAO at (";
641         os<<(m_base_position.X/BS)<<",";
642         os<<(m_base_position.Y/BS)<<",";
643         os<<(m_base_position.Z/BS);
644         os<<")";
645         return os.str();
646 }
647
648 void LuaEntitySAO::setHP(s16 hp)
649 {
650         if(hp < 0) hp = 0;
651         m_hp = hp;
652 }
653
654 s16 LuaEntitySAO::getHP() const
655 {
656         return m_hp;
657 }
658
659 void LuaEntitySAO::setVelocity(v3f velocity)
660 {
661         m_velocity = velocity;
662 }
663
664 v3f LuaEntitySAO::getVelocity()
665 {
666         return m_velocity;
667 }
668
669 void LuaEntitySAO::setAcceleration(v3f acceleration)
670 {
671         m_acceleration = acceleration;
672 }
673
674 v3f LuaEntitySAO::getAcceleration()
675 {
676         return m_acceleration;
677 }
678
679 void LuaEntitySAO::setTextureMod(const std::string &mod)
680 {
681         std::string str = gob_cmd_set_texture_mod(mod);
682         m_current_texture_modifier = mod;
683         // create message and add to list
684         ActiveObjectMessage aom(getId(), true, str);
685         m_messages_out.push(aom);
686 }
687
688 std::string LuaEntitySAO::getTextureMod() const
689 {
690         return m_current_texture_modifier;
691 }
692
693 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
694                 bool select_horiz_by_yawpitch)
695 {
696         std::string str = gob_cmd_set_sprite(
697                 p,
698                 num_frames,
699                 framelength,
700                 select_horiz_by_yawpitch
701         );
702         // create message and add to list
703         ActiveObjectMessage aom(getId(), true, str);
704         m_messages_out.push(aom);
705 }
706
707 std::string LuaEntitySAO::getName()
708 {
709         return m_init_name;
710 }
711
712 std::string LuaEntitySAO::getPropertyPacket()
713 {
714         return gob_cmd_set_properties(m_prop);
715 }
716
717 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
718 {
719         // If the object is attached client-side, don't waste bandwidth sending its position to clients
720         if(isAttached())
721                 return;
722
723         m_last_sent_move_precision = m_base_position.getDistanceFrom(
724                         m_last_sent_position);
725         m_last_sent_position_timer = 0;
726         m_last_sent_yaw = m_yaw;
727         m_last_sent_position = m_base_position;
728         m_last_sent_velocity = m_velocity;
729         //m_last_sent_acceleration = m_acceleration;
730
731         float update_interval = m_env->getSendRecommendedInterval();
732
733         std::string str = gob_cmd_update_position(
734                 m_base_position,
735                 m_velocity,
736                 m_acceleration,
737                 m_yaw,
738                 do_interpolate,
739                 is_movement_end,
740                 update_interval
741         );
742         // create message and add to list
743         ActiveObjectMessage aom(getId(), false, str);
744         m_messages_out.push(aom);
745 }
746
747 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
748 {
749         if (m_prop.physical)
750         {
751                 //update collision box
752                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
753                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
754
755                 toset->MinEdge += m_base_position;
756                 toset->MaxEdge += m_base_position;
757
758                 return true;
759         }
760
761         return false;
762 }
763
764 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
765 {
766         if (!m_prop.is_visible || !m_prop.pointable) {
767                 return false;
768         }
769
770         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
771         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
772
773         return true;
774 }
775
776 bool LuaEntitySAO::collideWithObjects() const
777 {
778         return m_prop.collideWithObjects;
779 }
780
781 /*
782         PlayerSAO
783 */
784
785 // No prototype, PlayerSAO does not need to be deserialized
786
787 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
788                 bool is_singleplayer):
789         UnitSAO(env_, v3f(0,0,0)),
790         m_player(player_),
791         m_peer_id(peer_id_),
792         m_is_singleplayer(is_singleplayer)
793 {
794         assert(m_peer_id != 0); // pre-condition
795
796         m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
797         m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
798         m_prop.physical = false;
799         m_prop.weight = 75;
800         m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
801         m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
802         m_prop.pointable = true;
803         // start of default appearance, this should be overwritten by LUA
804         m_prop.visual = "upright_sprite";
805         m_prop.visual_size = v2f(1, 2);
806         m_prop.textures.clear();
807         m_prop.textures.emplace_back("player.png");
808         m_prop.textures.emplace_back("player_back.png");
809         m_prop.colors.clear();
810         m_prop.colors.emplace_back(255, 255, 255, 255);
811         m_prop.spritediv = v2s16(1,1);
812         m_prop.eye_height = 1.625f;
813         // end of default appearance
814         m_prop.is_visible = true;
815         m_prop.makes_footstep_sound = true;
816         m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
817         m_prop.can_zoom = true;
818         m_hp = m_prop.hp_max;
819         m_breath = m_prop.breath_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 * m_prop.eye_height, 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->setPeerId(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 < m_prop.breath_max && 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, m_prop.breath_max);
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 void PlayerSAO::disconnected()
1355 {
1356         m_peer_id = 0;
1357         m_pending_removal = true;
1358 }
1359
1360 void PlayerSAO::unlinkPlayerSessionAndSave()
1361 {
1362         assert(m_player->getPlayerSAO() == this);
1363         m_player->setPeerId(PEER_ID_INEXISTENT);
1364         m_env->savePlayer(m_player);
1365         m_player->setPlayerSAO(NULL);
1366         m_env->removePlayer(m_player);
1367 }
1368
1369 std::string PlayerSAO::getPropertyPacket()
1370 {
1371         m_prop.is_visible = (true);
1372         return gob_cmd_set_properties(m_prop);
1373 }
1374
1375 bool PlayerSAO::checkMovementCheat()
1376 {
1377         if (isAttached() || m_is_singleplayer ||
1378                         g_settings->getBool("disable_anticheat")) {
1379                 m_last_good_position = m_base_position;
1380                 return false;
1381         }
1382
1383         bool cheated = false;
1384         /*
1385                 Check player movements
1386
1387                 NOTE: Actually the server should handle player physics like the
1388                 client does and compare player's position to what is calculated
1389                 on our side. This is required when eg. players fly due to an
1390                 explosion. Altough a node-based alternative might be possible
1391                 too, and much more lightweight.
1392         */
1393
1394         float player_max_speed = 0;
1395
1396         if (m_privs.count("fast") != 0) {
1397                 // Fast speed
1398                 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1399         } else {
1400                 // Normal speed
1401                 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1402         }
1403         // Tolerance. The lag pool does this a bit.
1404         //player_max_speed *= 2.5;
1405
1406         v3f diff = (m_base_position - m_last_good_position);
1407         float d_vert = diff.Y;
1408         diff.Y = 0;
1409         float d_horiz = diff.getLength();
1410         float required_time = d_horiz / player_max_speed;
1411
1412         if (d_vert > 0 && d_vert / player_max_speed > required_time)
1413                 required_time = d_vert / player_max_speed; // Moving upwards
1414
1415         if (m_move_pool.grab(required_time)) {
1416                 m_last_good_position = m_base_position;
1417         } else {
1418                 const float LAG_POOL_MIN = 5.0;
1419                 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1420                 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1421                 if (m_time_from_last_teleport > lag_pool_max) {
1422                         actionstream << "Player " << m_player->getName()
1423                                         << " moved too fast; resetting position"
1424                                         << std::endl;
1425                         cheated = true;
1426                 }
1427                 setBasePosition(m_last_good_position);
1428         }
1429         return cheated;
1430 }
1431
1432 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1433 {
1434         //update collision box
1435         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1436         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1437
1438         toset->MinEdge += m_base_position;
1439         toset->MaxEdge += m_base_position;
1440         return true;
1441 }
1442
1443 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1444 {
1445         if (!m_prop.is_visible || !m_prop.pointable) {
1446                 return false;
1447         }
1448
1449         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1450         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
1451
1452         return true;
1453 }