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