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