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