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