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