Don't apply player movement cheat detection in singleplayer
[oweals/minetest.git] / src / content_sao.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "scriptapi.h"
31 #include "genericobject.h"
32
33 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
34
35 /*
36         DummyLoadSAO
37 */
38
39 class DummyLoadSAO : public ServerActiveObject
40 {
41 public:
42         DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
43                 ServerActiveObject(env, pos)
44         {
45                 ServerActiveObject::registerType(type, create);
46         }
47         // Pretend to be the test object (to fool the client)
48         u8 getType() const
49         { return ACTIVEOBJECT_TYPE_TEST; }
50         // And never save to disk
51         bool isStaticAllowed() const
52         { return false; }
53         
54         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55                         const std::string &data)
56         {
57                 return new DummyLoadSAO(env, pos, 0);
58         }
59
60         void step(float dtime, bool send_recommended)
61         {
62                 m_removed = true;
63                 infostream<<"DummyLoadSAO step"<<std::endl;
64         }
65
66 private:
67 };
68
69 // Prototype (registers item for deserialization)
70 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
71 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
72 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
73 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
74
75 /*
76         TestSAO
77 */
78
79 class TestSAO : public ServerActiveObject
80 {
81 public:
82         TestSAO(ServerEnvironment *env, v3f pos):
83                 ServerActiveObject(env, pos),
84                 m_timer1(0),
85                 m_age(0)
86         {
87                 ServerActiveObject::registerType(getType(), create);
88         }
89         u8 getType() const
90         { return ACTIVEOBJECT_TYPE_TEST; }
91         
92         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
93                         const std::string &data)
94         {
95                 return new TestSAO(env, pos);
96         }
97
98         void step(float dtime, bool send_recommended)
99         {
100                 m_age += dtime;
101                 if(m_age > 10)
102                 {
103                         m_removed = true;
104                         return;
105                 }
106
107                 m_base_position.Y += dtime * BS * 2;
108                 if(m_base_position.Y > 8*BS)
109                         m_base_position.Y = 2*BS;
110
111                 if(send_recommended == false)
112                         return;
113
114                 m_timer1 -= dtime;
115                 if(m_timer1 < 0.0)
116                 {
117                         m_timer1 += 0.125;
118
119                         std::string data;
120
121                         data += itos(0); // 0 = position
122                         data += " ";
123                         data += itos(m_base_position.X);
124                         data += " ";
125                         data += itos(m_base_position.Y);
126                         data += " ";
127                         data += itos(m_base_position.Z);
128
129                         ActiveObjectMessage aom(getId(), false, data);
130                         m_messages_out.push_back(aom);
131                 }
132         }
133
134 private:
135         float m_timer1;
136         float m_age;
137 };
138
139 // Prototype (registers item for deserialization)
140 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
141
142 /*
143         ItemSAO
144 */
145
146 class ItemSAO : public ServerActiveObject
147 {
148 public:
149         u8 getType() const
150         { return ACTIVEOBJECT_TYPE_ITEM; }
151         
152         float getMinimumSavedMovement()
153         { return 0.1*BS; }
154
155         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
156                         const std::string &data)
157         {
158                 std::istringstream is(data, std::ios::binary);
159                 char buf[1];
160                 // read version
161                 is.read(buf, 1);
162                 u8 version = buf[0];
163                 // check if version is supported
164                 if(version != 0)
165                         return NULL;
166                 std::string itemstring = deSerializeString(is);
167                 infostream<<"create(): Creating item \""
168                                 <<itemstring<<"\""<<std::endl;
169                 return new ItemSAO(env, pos, itemstring);
170         }
171
172         ItemSAO(ServerEnvironment *env, v3f pos,
173                         const std::string itemstring):
174                 ServerActiveObject(env, pos),
175                 m_itemstring(itemstring),
176                 m_itemstring_changed(false),
177                 m_speed_f(0,0,0),
178                 m_last_sent_position(0,0,0)
179         {
180                 ServerActiveObject::registerType(getType(), create);
181         }
182
183         void step(float dtime, bool send_recommended)
184         {
185                 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
186
187                 assert(m_env);
188
189                 const float interval = 0.2;
190                 if(m_move_interval.step(dtime, interval)==false)
191                         return;
192                 dtime = interval;
193                 
194                 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
195                 collisionMoveResult moveresult;
196                 // Apply gravity
197                 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
198                 // Maximum movement without glitches
199                 f32 pos_max_d = BS*0.25;
200                 // Limit speed
201                 if(m_speed_f.getLength()*dtime > pos_max_d)
202                         m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
203                 v3f pos_f = getBasePosition();
204                 v3f pos_f_old = pos_f;
205                 IGameDef *gamedef = m_env->getGameDef();
206                 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
207                                 pos_max_d, box, dtime, pos_f, m_speed_f);
208                 
209                 if(send_recommended == false)
210                         return;
211
212                 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
213                 {
214                         setBasePosition(pos_f);
215                         m_last_sent_position = pos_f;
216
217                         std::ostringstream os(std::ios::binary);
218                         // command (0 = update position)
219                         writeU8(os, 0);
220                         // pos
221                         writeV3F1000(os, m_base_position);
222                         // create message and add to list
223                         ActiveObjectMessage aom(getId(), false, os.str());
224                         m_messages_out.push_back(aom);
225                 }
226                 if(m_itemstring_changed)
227                 {
228                         m_itemstring_changed = false;
229
230                         std::ostringstream os(std::ios::binary);
231                         // command (1 = update itemstring)
232                         writeU8(os, 1);
233                         // itemstring
234                         os<<serializeString(m_itemstring);
235                         // create message and add to list
236                         ActiveObjectMessage aom(getId(), false, os.str());
237                         m_messages_out.push_back(aom);
238                 }
239         }
240
241         std::string getClientInitializationData()
242         {
243                 std::ostringstream os(std::ios::binary);
244                 // version
245                 writeU8(os, 0);
246                 // pos
247                 writeV3F1000(os, m_base_position);
248                 // itemstring
249                 os<<serializeString(m_itemstring);
250                 return os.str();
251         }
252
253         std::string getStaticData()
254         {
255                 infostream<<__FUNCTION_NAME<<std::endl;
256                 std::ostringstream os(std::ios::binary);
257                 // version
258                 writeU8(os, 0);
259                 // itemstring
260                 os<<serializeString(m_itemstring);
261                 return os.str();
262         }
263
264         ItemStack createItemStack()
265         {
266                 try{
267                         IItemDefManager *idef = m_env->getGameDef()->idef();
268                         ItemStack item;
269                         item.deSerialize(m_itemstring, idef);
270                         infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
271                                         <<"\" -> item=\""<<item.getItemString()<<"\""
272                                         <<std::endl;
273                         return item;
274                 }
275                 catch(SerializationError &e)
276                 {
277                         infostream<<__FUNCTION_NAME<<": serialization error: "
278                                         <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
279                         return ItemStack();
280                 }
281         }
282
283         int punch(v3f dir,
284                         const ToolCapabilities *toolcap,
285                         ServerActiveObject *puncher,
286                         float time_from_last_punch)
287         {
288                 // Directly delete item in creative mode
289                 if(g_settings->getBool("creative_mode") == true)
290                 {
291                         m_removed = true;
292                         return 0;
293                 }
294                 
295                 // Take item into inventory
296                 ItemStack item = createItemStack();
297                 Inventory *inv = puncher->getInventory();
298                 if(inv != NULL)
299                 {
300                         std::string wieldlist = puncher->getWieldList();
301                         ItemStack leftover = inv->addItem(wieldlist, item);
302                         puncher->setInventoryModified();
303                         if(leftover.empty())
304                         {
305                                 m_removed = true;
306                         }
307                         else
308                         {
309                                 m_itemstring = leftover.getItemString();
310                                 m_itemstring_changed = true;
311                         }
312                 }
313                 
314                 return 0;
315         }
316
317
318 private:
319         std::string m_itemstring;
320         bool m_itemstring_changed;
321         v3f m_speed_f;
322         v3f m_last_sent_position;
323         IntervalLimiter m_move_interval;
324 };
325
326 // Prototype (registers item for deserialization)
327 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
328
329 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
330                 const std::string itemstring)
331 {
332         return new ItemSAO(env, pos, itemstring);
333 }
334
335 /*
336         LuaEntitySAO
337 */
338
339 // Prototype (registers item for deserialization)
340 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
341
342 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
343                 const std::string &name, const std::string &state):
344         ServerActiveObject(env, pos),
345         m_init_name(name),
346         m_init_state(state),
347         m_registered(false),
348         m_hp(-1),
349         m_velocity(0,0,0),
350         m_acceleration(0,0,0),
351         m_yaw(0),
352         m_properties_sent(true),
353         m_last_sent_yaw(0),
354         m_last_sent_position(0,0,0),
355         m_last_sent_velocity(0,0,0),
356         m_last_sent_position_timer(0),
357         m_last_sent_move_precision(0),
358         m_armor_groups_sent(false)
359 {
360         // Only register type if no environment supplied
361         if(env == NULL){
362                 ServerActiveObject::registerType(getType(), create);
363                 return;
364         }
365         
366         // Initialize something to armor groups
367         m_armor_groups["fleshy"] = 3;
368         m_armor_groups["snappy"] = 2;
369 }
370
371 LuaEntitySAO::~LuaEntitySAO()
372 {
373         if(m_registered){
374                 lua_State *L = m_env->getLua();
375                 scriptapi_luaentity_rm(L, m_id);
376         }
377 }
378
379 void LuaEntitySAO::addedToEnvironment()
380 {
381         ServerActiveObject::addedToEnvironment();
382         
383         // Create entity from name
384         lua_State *L = m_env->getLua();
385         m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
386         
387         if(m_registered){
388                 // Get properties
389                 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
390                 // Initialize HP from properties
391                 m_hp = m_prop.hp_max;
392         }
393         
394         // Activate entity, supplying serialized state
395         scriptapi_luaentity_activate(L, m_id, m_init_state.c_str());
396 }
397
398 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
399                 const std::string &data)
400 {
401         std::string name;
402         std::string state;
403         s16 hp = 1;
404         v3f velocity;
405         float yaw = 0;
406         if(data != ""){
407                 std::istringstream is(data, std::ios::binary);
408                 // read version
409                 u8 version = readU8(is);
410                 // check if version is supported
411                 if(version == 0){
412                         name = deSerializeString(is);
413                         state = deSerializeLongString(is);
414                 }
415                 else if(version == 1){
416                         name = deSerializeString(is);
417                         state = deSerializeLongString(is);
418                         hp = readS16(is);
419                         velocity = readV3F1000(is);
420                         yaw = readF1000(is);
421                 }
422         }
423         // create object
424         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
425                         <<state<<"\")"<<std::endl;
426         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
427         sao->m_hp = hp;
428         sao->m_velocity = velocity;
429         sao->m_yaw = yaw;
430         return sao;
431 }
432
433 void LuaEntitySAO::step(float dtime, bool send_recommended)
434 {
435         if(!m_properties_sent)
436         {
437                 m_properties_sent = true;
438                 std::string str = getPropertyPacket();
439                 // create message and add to list
440                 ActiveObjectMessage aom(getId(), true, str);
441                 m_messages_out.push_back(aom);
442         }
443
444         m_last_sent_position_timer += dtime;
445         
446         if(m_prop.physical){
447                 core::aabbox3d<f32> box = m_prop.collisionbox;
448                 box.MinEdge *= BS;
449                 box.MaxEdge *= BS;
450                 collisionMoveResult moveresult;
451                 f32 pos_max_d = BS*0.25; // Distance per iteration
452                 v3f p_pos = getBasePosition();
453                 v3f p_velocity = m_velocity;
454                 IGameDef *gamedef = m_env->getGameDef();
455                 moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
456                                 pos_max_d, box, dtime, p_pos, p_velocity);
457                 // Apply results
458                 setBasePosition(p_pos);
459                 m_velocity = p_velocity;
460
461                 m_velocity += dtime * m_acceleration;
462         } else {
463                 m_base_position += dtime * m_velocity + 0.5 * dtime
464                                 * dtime * m_acceleration;
465                 m_velocity += dtime * m_acceleration;
466         }
467
468         if(m_registered){
469                 lua_State *L = m_env->getLua();
470                 scriptapi_luaentity_step(L, m_id, dtime);
471         }
472
473         if(send_recommended == false)
474                 return;
475         
476         // TODO: force send when acceleration changes enough?
477         float minchange = 0.2*BS;
478         if(m_last_sent_position_timer > 1.0){
479                 minchange = 0.01*BS;
480         } else if(m_last_sent_position_timer > 0.2){
481                 minchange = 0.05*BS;
482         }
483         float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
484         move_d += m_last_sent_move_precision;
485         float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
486         if(move_d > minchange || vel_d > minchange ||
487                         fabs(m_yaw - m_last_sent_yaw) > 1.0){
488                 sendPosition(true, false);
489         }
490
491         if(m_armor_groups_sent == false){
492                 m_armor_groups_sent = true;
493                 std::string str = gob_cmd_update_armor_groups(
494                                 m_armor_groups);
495                 // create message and add to list
496                 ActiveObjectMessage aom(getId(), true, str);
497                 m_messages_out.push_back(aom);
498         }
499 }
500
501 std::string LuaEntitySAO::getClientInitializationData()
502 {
503         std::ostringstream os(std::ios::binary);
504         writeU8(os, 0); // version
505         os<<serializeString(""); // name
506         writeU8(os, 0); // is_player
507         writeV3F1000(os, m_base_position);
508         writeF1000(os, m_yaw);
509         writeS16(os, m_hp);
510         writeU8(os, 2); // number of messages stuffed in here
511         os<<serializeLongString(getPropertyPacket()); // message 1
512         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
513         // return result
514         return os.str();
515 }
516
517 std::string LuaEntitySAO::getStaticData()
518 {
519         verbosestream<<__FUNCTION_NAME<<std::endl;
520         std::ostringstream os(std::ios::binary);
521         // version
522         writeU8(os, 1);
523         // name
524         os<<serializeString(m_init_name);
525         // state
526         if(m_registered){
527                 lua_State *L = m_env->getLua();
528                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
529                 os<<serializeLongString(state);
530         } else {
531                 os<<serializeLongString(m_init_state);
532         }
533         // hp
534         writeS16(os, m_hp);
535         // velocity
536         writeV3F1000(os, m_velocity);
537         // yaw
538         writeF1000(os, m_yaw);
539         return os.str();
540 }
541
542 int LuaEntitySAO::punch(v3f dir,
543                 const ToolCapabilities *toolcap,
544                 ServerActiveObject *puncher,
545                 float time_from_last_punch)
546 {
547         if(!m_registered){
548                 // Delete unknown LuaEntities when punched
549                 m_removed = true;
550                 return 0;
551         }
552         
553         ItemStack *punchitem = NULL;
554         ItemStack punchitem_static;
555         if(puncher){
556                 punchitem_static = puncher->getWieldedItem();
557                 punchitem = &punchitem_static;
558         }
559
560         PunchDamageResult result = getPunchDamage(
561                         m_armor_groups,
562                         toolcap,
563                         punchitem,
564                         time_from_last_punch);
565         
566         if(result.did_punch)
567         {
568                 setHP(getHP() - result.damage);
569                 
570                 actionstream<<getDescription()<<" punched by "
571                                 <<puncher->getDescription()<<", damage "<<result.damage
572                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
573                 
574                 {
575                         std::string str = gob_cmd_punched(result.damage, getHP());
576                         // create message and add to list
577                         ActiveObjectMessage aom(getId(), true, str);
578                         m_messages_out.push_back(aom);
579                 }
580
581                 if(getHP() == 0)
582                         m_removed = true;
583         }
584
585         lua_State *L = m_env->getLua();
586         scriptapi_luaentity_punch(L, m_id, puncher,
587                         time_from_last_punch, toolcap, dir);
588
589         return result.wear;
590 }
591
592 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
593 {
594         if(!m_registered)
595                 return;
596         lua_State *L = m_env->getLua();
597         scriptapi_luaentity_rightclick(L, m_id, clicker);
598 }
599
600 void LuaEntitySAO::setPos(v3f pos)
601 {
602         m_base_position = pos;
603         sendPosition(false, true);
604 }
605
606 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
607 {
608         m_base_position = pos;
609         if(!continuous)
610                 sendPosition(true, true);
611 }
612
613 float LuaEntitySAO::getMinimumSavedMovement()
614 {
615         return 0.1 * BS;
616 }
617
618 std::string LuaEntitySAO::getDescription()
619 {
620         std::ostringstream os(std::ios::binary);
621         os<<"LuaEntitySAO at (";
622         os<<(m_base_position.X/BS)<<",";
623         os<<(m_base_position.Y/BS)<<",";
624         os<<(m_base_position.Z/BS);
625         os<<")";
626         return os.str();
627 }
628
629 void LuaEntitySAO::setHP(s16 hp)
630 {
631         if(hp < 0) hp = 0;
632         m_hp = hp;
633 }
634
635 s16 LuaEntitySAO::getHP() const
636 {
637         return m_hp;
638 }
639
640 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
641 {
642         m_armor_groups = armor_groups;
643         m_armor_groups_sent = false;
644 }
645
646 ObjectProperties* LuaEntitySAO::accessObjectProperties()
647 {
648         return &m_prop;
649 }
650
651 void LuaEntitySAO::notifyObjectPropertiesModified()
652 {
653         m_properties_sent = false;
654 }
655
656 void LuaEntitySAO::setVelocity(v3f velocity)
657 {
658         m_velocity = velocity;
659 }
660
661 v3f LuaEntitySAO::getVelocity()
662 {
663         return m_velocity;
664 }
665
666 void LuaEntitySAO::setAcceleration(v3f acceleration)
667 {
668         m_acceleration = acceleration;
669 }
670
671 v3f LuaEntitySAO::getAcceleration()
672 {
673         return m_acceleration;
674 }
675
676 void LuaEntitySAO::setYaw(float yaw)
677 {
678         m_yaw = yaw;
679 }
680
681 float LuaEntitySAO::getYaw()
682 {
683         return m_yaw;
684 }
685
686 void LuaEntitySAO::setTextureMod(const std::string &mod)
687 {
688         std::string str = gob_cmd_set_texture_mod(mod);
689         // create message and add to list
690         ActiveObjectMessage aom(getId(), true, str);
691         m_messages_out.push_back(aom);
692 }
693
694 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
695                 bool select_horiz_by_yawpitch)
696 {
697         std::string str = gob_cmd_set_sprite(
698                 p,
699                 num_frames,
700                 framelength,
701                 select_horiz_by_yawpitch
702         );
703         // create message and add to list
704         ActiveObjectMessage aom(getId(), true, str);
705         m_messages_out.push_back(aom);
706 }
707
708 std::string LuaEntitySAO::getName()
709 {
710         return m_init_name;
711 }
712
713 std::string LuaEntitySAO::getPropertyPacket()
714 {
715         return gob_cmd_set_properties(m_prop);
716 }
717
718 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
719 {
720         m_last_sent_move_precision = m_base_position.getDistanceFrom(
721                         m_last_sent_position);
722         m_last_sent_position_timer = 0;
723         m_last_sent_yaw = m_yaw;
724         m_last_sent_position = m_base_position;
725         m_last_sent_velocity = m_velocity;
726         //m_last_sent_acceleration = m_acceleration;
727
728         float update_interval = m_env->getSendRecommendedInterval();
729
730         std::string str = gob_cmd_update_position(
731                 m_base_position,
732                 m_velocity,
733                 m_acceleration,
734                 m_yaw,
735                 do_interpolate,
736                 is_movement_end,
737                 update_interval
738         );
739         // create message and add to list
740         ActiveObjectMessage aom(getId(), false, str);
741         m_messages_out.push_back(aom);
742 }
743
744 /*
745         PlayerSAO
746 */
747
748 // No prototype, PlayerSAO does not need to be deserialized
749
750 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
751                 const std::set<std::string> &privs, bool is_singleplayer):
752         ServerActiveObject(env_, v3f(0,0,0)),
753         m_player(player_),
754         m_peer_id(peer_id_),
755         m_inventory(NULL),
756         m_last_good_position(0,0,0),
757         m_last_good_position_age(0),
758         m_time_from_last_punch(0),
759         m_wield_index(0),
760         m_position_not_sent(false),
761         m_armor_groups_sent(false),
762         m_properties_sent(true),
763         m_privs(privs),
764         m_is_singleplayer(is_singleplayer),
765         // public
766         m_teleported(false),
767         m_inventory_not_sent(false),
768         m_hp_not_sent(false),
769         m_wielded_item_not_sent(false)
770 {
771         assert(m_player);
772         assert(m_peer_id != 0);
773         setBasePosition(m_player->getPosition());
774         m_inventory = &m_player->inventory;
775         m_armor_groups["choppy"] = 2;
776         m_armor_groups["fleshy"] = 3;
777
778         m_prop.hp_max = PLAYER_MAX_HP;
779         m_prop.physical = false;
780         m_prop.weight = 75;
781         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
782         m_prop.visual = "upright_sprite";
783         m_prop.visual_size = v2f(1, 2);
784         m_prop.textures.clear();
785         m_prop.textures.push_back("player.png");
786         m_prop.textures.push_back("player_back.png");
787         m_prop.spritediv = v2s16(1,1);
788         m_prop.is_visible = (getHP() != 0);
789         m_prop.makes_footstep_sound = true;
790 }
791
792 PlayerSAO::~PlayerSAO()
793 {
794         if(m_inventory != &m_player->inventory)
795                 delete m_inventory;
796
797 }
798
799 std::string PlayerSAO::getDescription()
800 {
801         return std::string("player ") + m_player->getName();
802 }
803
804 // Called after id has been set and has been inserted in environment
805 void PlayerSAO::addedToEnvironment()
806 {
807         ServerActiveObject::addedToEnvironment();
808         ServerActiveObject::setBasePosition(m_player->getPosition());
809         m_player->setPlayerSAO(this);
810         m_player->peer_id = m_peer_id;
811         m_last_good_position = m_player->getPosition();
812         m_last_good_position_age = 0.0;
813 }
814
815 // Called before removing from environment
816 void PlayerSAO::removingFromEnvironment()
817 {
818         ServerActiveObject::removingFromEnvironment();
819         if(m_player->getPlayerSAO() == this)
820         {
821                 m_player->setPlayerSAO(NULL);
822                 m_player->peer_id = 0;
823         }
824 }
825
826 bool PlayerSAO::isStaticAllowed() const
827 {
828         return false;
829 }
830
831 bool PlayerSAO::unlimitedTransferDistance() const
832 {
833         return g_settings->getBool("unlimited_player_transfer_distance");
834 }
835
836 std::string PlayerSAO::getClientInitializationData()
837 {
838         std::ostringstream os(std::ios::binary);
839         writeU8(os, 0); // version
840         os<<serializeString(m_player->getName()); // name
841         writeU8(os, 1); // is_player
842         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
843         writeF1000(os, m_player->getYaw());
844         writeS16(os, getHP());
845         writeU8(os, 2); // number of messages stuffed in here
846         os<<serializeLongString(getPropertyPacket()); // message 1
847         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
848         return os.str();
849 }
850
851 std::string PlayerSAO::getStaticData()
852 {
853         assert(0);
854         return "";
855 }
856
857 void PlayerSAO::step(float dtime, bool send_recommended)
858 {
859         if(!m_properties_sent)
860         {
861                 m_properties_sent = true;
862                 std::string str = getPropertyPacket();
863                 // create message and add to list
864                 ActiveObjectMessage aom(getId(), true, str);
865                 m_messages_out.push_back(aom);
866         }
867
868         m_time_from_last_punch += dtime;
869         
870         if(!m_is_singleplayer)
871         {
872                 /*
873                         Check player movements
874
875                         NOTE: Actually the server should handle player physics like the
876                         client does and compare player's position to what is calculated
877                         on our side. This is required when eg. players fly due to an
878                         explosion.
879                 */
880
881                 float player_max_speed = 0;
882                 float player_max_speed_up = 0;
883                 if(m_privs.count("fast") != 0){
884                         // Fast speed
885                         player_max_speed = BS * 20;
886                         player_max_speed_up = BS * 20;
887                 } else {
888                         // Normal speed
889                         player_max_speed = BS * 4.0;
890                         player_max_speed_up = BS * 4.0;
891                 }
892                 // Tolerance
893                 player_max_speed *= 2.5;
894                 player_max_speed_up *= 2.5;
895
896                 m_last_good_position_age += dtime;
897                 if(m_last_good_position_age >= 1.0){
898                         float age = m_last_good_position_age;
899                         v3f diff = (m_player->getPosition() - m_last_good_position);
900                         float d_vert = diff.Y;
901                         diff.Y = 0;
902                         float d_horiz = diff.getLength();
903                         /*infostream<<m_player->getName()<<"'s horizontal speed is "
904                                         <<(d_horiz/age)<<std::endl;*/
905                         if(d_horiz <= age * player_max_speed &&
906                                         (d_vert < 0 || d_vert < age * player_max_speed_up)){
907                                 m_last_good_position = m_player->getPosition();
908                         } else {
909                                 actionstream<<"Player "<<m_player->getName()
910                                                 <<" moved too fast; resetting position"
911                                                 <<std::endl;
912                                 m_player->setPosition(m_last_good_position);
913                                 m_teleported = true;
914                         }
915                         m_last_good_position_age = 0;
916                 }
917         }
918
919         if(send_recommended == false)
920                 return;
921
922         if(m_position_not_sent)
923         {
924                 m_position_not_sent = false;
925                 float update_interval = m_env->getSendRecommendedInterval();
926                 std::string str = gob_cmd_update_position(
927                         m_player->getPosition() + v3f(0,BS*1,0),
928                         v3f(0,0,0),
929                         v3f(0,0,0),
930                         m_player->getYaw(),
931                         true,
932                         false,
933                         update_interval
934                 );
935                 // create message and add to list
936                 ActiveObjectMessage aom(getId(), false, str);
937                 m_messages_out.push_back(aom);
938         }
939
940         if(m_wielded_item_not_sent)
941         {
942                 m_wielded_item_not_sent = false;
943                 // GenericCAO has no special way to show this
944         }
945
946         if(m_armor_groups_sent == false){
947                 m_armor_groups_sent = true;
948                 std::string str = gob_cmd_update_armor_groups(
949                                 m_armor_groups);
950                 // create message and add to list
951                 ActiveObjectMessage aom(getId(), true, str);
952                 m_messages_out.push_back(aom);
953         }
954 }
955
956 void PlayerSAO::setBasePosition(const v3f &position)
957 {
958         ServerActiveObject::setBasePosition(position);
959         m_position_not_sent = true;
960 }
961
962 void PlayerSAO::setPos(v3f pos)
963 {
964         m_player->setPosition(pos);
965         // Movement caused by this command is always valid
966         m_last_good_position = pos;
967         m_last_good_position_age = 0;
968         // Force position change on client
969         m_teleported = true;
970 }
971
972 void PlayerSAO::moveTo(v3f pos, bool continuous)
973 {
974         m_player->setPosition(pos);
975         // Movement caused by this command is always valid
976         m_last_good_position = pos;
977         m_last_good_position_age = 0;
978         // Force position change on client
979         m_teleported = true;
980 }
981
982 int PlayerSAO::punch(v3f dir,
983         const ToolCapabilities *toolcap,
984         ServerActiveObject *puncher,
985         float time_from_last_punch)
986 {
987         if(!toolcap)
988                 return 0;
989
990         // No effect if PvP disabled
991         if(g_settings->getBool("enable_pvp") == false){
992                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
993                         std::string str = gob_cmd_punched(0, getHP());
994                         // create message and add to list
995                         ActiveObjectMessage aom(getId(), true, str);
996                         m_messages_out.push_back(aom);
997                         return 0;
998                 }
999         }
1000
1001         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1002                         time_from_last_punch);
1003
1004         actionstream<<"Player "<<m_player->getName()<<" punched by "
1005                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1006                         <<" HP"<<std::endl;
1007
1008         setHP(getHP() - hitparams.hp);
1009
1010         if(hitparams.hp != 0)
1011         {
1012                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1013                 // create message and add to list
1014                 ActiveObjectMessage aom(getId(), true, str);
1015                 m_messages_out.push_back(aom);
1016         }
1017
1018         return hitparams.wear;
1019 }
1020
1021 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1022 {
1023 }
1024
1025 s16 PlayerSAO::getHP() const
1026 {
1027         return m_player->hp;
1028 }
1029
1030 void PlayerSAO::setHP(s16 hp)
1031 {
1032         s16 oldhp = m_player->hp;
1033
1034         if(hp < 0)
1035                 hp = 0;
1036         else if(hp > PLAYER_MAX_HP)
1037                 hp = PLAYER_MAX_HP;
1038
1039         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1040         {
1041                 m_hp_not_sent = true; // fix wrong prediction on client
1042                 return;
1043         }
1044
1045         m_player->hp = hp;
1046
1047         if(hp != oldhp)
1048                 m_hp_not_sent = true;
1049
1050         // On death or reincarnation send an active object message
1051         if((hp == 0) != (oldhp == 0))
1052         {
1053                 // Will send new is_visible value based on (getHP()!=0)
1054                 m_properties_sent = false;
1055                 // Send new HP
1056                 std::string str = gob_cmd_punched(0, getHP());
1057                 ActiveObjectMessage aom(getId(), true, str);
1058                 m_messages_out.push_back(aom);
1059         }
1060 }
1061
1062 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1063 {
1064         m_armor_groups = armor_groups;
1065         m_armor_groups_sent = false;
1066 }
1067
1068 ObjectProperties* PlayerSAO::accessObjectProperties()
1069 {
1070         return &m_prop;
1071 }
1072
1073 void PlayerSAO::notifyObjectPropertiesModified()
1074 {
1075         m_properties_sent = false;
1076 }
1077
1078 Inventory* PlayerSAO::getInventory()
1079 {
1080         return m_inventory;
1081 }
1082 const Inventory* PlayerSAO::getInventory() const
1083 {
1084         return m_inventory;
1085 }
1086
1087 InventoryLocation PlayerSAO::getInventoryLocation() const
1088 {
1089         InventoryLocation loc;
1090         loc.setPlayer(m_player->getName());
1091         return loc;
1092 }
1093
1094 void PlayerSAO::setInventoryModified()
1095 {
1096         m_inventory_not_sent = true;
1097 }
1098
1099 std::string PlayerSAO::getWieldList() const
1100 {
1101         return "main";
1102 }
1103
1104 int PlayerSAO::getWieldIndex() const
1105 {
1106         return m_wield_index;
1107 }
1108
1109 void PlayerSAO::setWieldIndex(int i)
1110 {
1111         if(i != m_wield_index)
1112         {
1113                 m_wield_index = i;
1114                 m_wielded_item_not_sent = true;
1115         }
1116 }
1117
1118 void PlayerSAO::disconnected()
1119 {
1120         m_peer_id = 0;
1121         m_removed = true;
1122         if(m_player->getPlayerSAO() == this)
1123         {
1124                 m_player->setPlayerSAO(NULL);
1125                 m_player->peer_id = 0;
1126         }
1127 }
1128
1129 void PlayerSAO::createCreativeInventory()
1130 {
1131         if(m_inventory != &m_player->inventory)
1132                 delete m_inventory;
1133
1134         m_inventory = new Inventory(m_player->inventory);
1135         m_inventory->clearContents();
1136         scriptapi_get_creative_inventory(m_env->getLua(), this);
1137 }
1138
1139 std::string PlayerSAO::getPropertyPacket()
1140 {
1141         m_prop.is_visible = (getHP() != 0);
1142         return gob_cmd_set_properties(m_prop);
1143 }
1144