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