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