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