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