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