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