205621d791264ac8307a436b43267165a7bb36ef
[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                 // Activate entity, supplying serialized state
393                 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str());
394         }
395 }
396
397 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
398                 const std::string &data)
399 {
400         std::string name;
401         std::string state;
402         s16 hp = 1;
403         v3f velocity;
404         float yaw = 0;
405         if(data != ""){
406                 std::istringstream is(data, std::ios::binary);
407                 // read version
408                 u8 version = readU8(is);
409                 // check if version is supported
410                 if(version == 0){
411                         name = deSerializeString(is);
412                         state = deSerializeLongString(is);
413                 }
414                 else if(version == 1){
415                         name = deSerializeString(is);
416                         state = deSerializeLongString(is);
417                         hp = readS16(is);
418                         velocity = readV3F1000(is);
419                         yaw = readF1000(is);
420                 }
421         }
422         // create object
423         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
424                         <<state<<"\")"<<std::endl;
425         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
426         sao->m_hp = hp;
427         sao->m_velocity = velocity;
428         sao->m_yaw = yaw;
429         return sao;
430 }
431
432 void LuaEntitySAO::step(float dtime, bool send_recommended)
433 {
434         if(!m_properties_sent)
435         {
436                 m_properties_sent = true;
437                 std::string str = getPropertyPacket();
438                 // create message and add to list
439                 ActiveObjectMessage aom(getId(), true, str);
440                 m_messages_out.push_back(aom);
441         }
442
443         m_last_sent_position_timer += dtime;
444         
445         if(m_prop.physical){
446                 core::aabbox3d<f32> box = m_prop.collisionbox;
447                 box.MinEdge *= BS;
448                 box.MaxEdge *= BS;
449                 collisionMoveResult moveresult;
450                 f32 pos_max_d = BS*0.25; // Distance per iteration
451                 v3f p_pos = getBasePosition();
452                 v3f p_velocity = m_velocity;
453                 IGameDef *gamedef = m_env->getGameDef();
454                 moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
455                                 pos_max_d, box, dtime, p_pos, p_velocity);
456                 // Apply results
457                 setBasePosition(p_pos);
458                 m_velocity = p_velocity;
459
460                 m_velocity += dtime * m_acceleration;
461         } else {
462                 m_base_position += dtime * m_velocity + 0.5 * dtime
463                                 * dtime * m_acceleration;
464                 m_velocity += dtime * m_acceleration;
465         }
466
467         if(m_registered){
468                 lua_State *L = m_env->getLua();
469                 scriptapi_luaentity_step(L, m_id, dtime);
470         }
471
472         if(send_recommended == false)
473                 return;
474         
475         // TODO: force send when acceleration changes enough?
476         float minchange = 0.2*BS;
477         if(m_last_sent_position_timer > 1.0){
478                 minchange = 0.01*BS;
479         } else if(m_last_sent_position_timer > 0.2){
480                 minchange = 0.05*BS;
481         }
482         float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
483         move_d += m_last_sent_move_precision;
484         float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
485         if(move_d > minchange || vel_d > minchange ||
486                         fabs(m_yaw - m_last_sent_yaw) > 1.0){
487                 sendPosition(true, false);
488         }
489
490         if(m_armor_groups_sent == false){
491                 m_armor_groups_sent = true;
492                 std::string str = gob_cmd_update_armor_groups(
493                                 m_armor_groups);
494                 // create message and add to list
495                 ActiveObjectMessage aom(getId(), true, str);
496                 m_messages_out.push_back(aom);
497         }
498 }
499
500 std::string LuaEntitySAO::getClientInitializationData()
501 {
502         std::ostringstream os(std::ios::binary);
503         writeU8(os, 0); // version
504         os<<serializeString(""); // name
505         writeU8(os, 0); // is_player
506         writeV3F1000(os, m_base_position);
507         writeF1000(os, m_yaw);
508         writeS16(os, m_hp);
509         writeU8(os, 2); // number of messages stuffed in here
510         os<<serializeLongString(getPropertyPacket()); // message 1
511         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
512         // return result
513         return os.str();
514 }
515
516 std::string LuaEntitySAO::getStaticData()
517 {
518         verbosestream<<__FUNCTION_NAME<<std::endl;
519         std::ostringstream os(std::ios::binary);
520         // version
521         writeU8(os, 1);
522         // name
523         os<<serializeString(m_init_name);
524         // state
525         if(m_registered){
526                 lua_State *L = m_env->getLua();
527                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
528                 os<<serializeLongString(state);
529         } else {
530                 os<<serializeLongString(m_init_state);
531         }
532         // hp
533         writeS16(os, m_hp);
534         // velocity
535         writeV3F1000(os, m_velocity);
536         // yaw
537         writeF1000(os, m_yaw);
538         return os.str();
539 }
540
541 int LuaEntitySAO::punch(v3f dir,
542                 const ToolCapabilities *toolcap,
543                 ServerActiveObject *puncher,
544                 float time_from_last_punch)
545 {
546         if(!m_registered){
547                 // Delete unknown LuaEntities when punched
548                 m_removed = true;
549                 return 0;
550         }
551         
552         ItemStack *punchitem = NULL;
553         ItemStack punchitem_static;
554         if(puncher){
555                 punchitem_static = puncher->getWieldedItem();
556                 punchitem = &punchitem_static;
557         }
558
559         PunchDamageResult result = getPunchDamage(
560                         m_armor_groups,
561                         toolcap,
562                         punchitem,
563                         time_from_last_punch);
564         
565         if(result.did_punch)
566         {
567                 setHP(getHP() - result.damage);
568                 
569                 actionstream<<getDescription()<<" punched by "
570                                 <<puncher->getDescription()<<", damage "<<result.damage
571                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
572                 
573                 {
574                         std::string str = gob_cmd_punched(result.damage, getHP());
575                         // create message and add to list
576                         ActiveObjectMessage aom(getId(), true, str);
577                         m_messages_out.push_back(aom);
578                 }
579
580                 if(getHP() == 0)
581                         m_removed = true;
582         }
583
584         lua_State *L = m_env->getLua();
585         scriptapi_luaentity_punch(L, m_id, puncher,
586                         time_from_last_punch, toolcap, dir);
587
588         return result.wear;
589 }
590
591 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
592 {
593         if(!m_registered)
594                 return;
595         lua_State *L = m_env->getLua();
596         scriptapi_luaentity_rightclick(L, m_id, clicker);
597 }
598
599 void LuaEntitySAO::setPos(v3f pos)
600 {
601         m_base_position = pos;
602         sendPosition(false, true);
603 }
604
605 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
606 {
607         m_base_position = pos;
608         if(!continuous)
609                 sendPosition(true, true);
610 }
611
612 float LuaEntitySAO::getMinimumSavedMovement()
613 {
614         return 0.1 * BS;
615 }
616
617 std::string LuaEntitySAO::getDescription()
618 {
619         std::ostringstream os(std::ios::binary);
620         os<<"LuaEntitySAO at (";
621         os<<(m_base_position.X/BS)<<",";
622         os<<(m_base_position.Y/BS)<<",";
623         os<<(m_base_position.Z/BS);
624         os<<")";
625         return os.str();
626 }
627
628 void LuaEntitySAO::setHP(s16 hp)
629 {
630         if(hp < 0) hp = 0;
631         m_hp = hp;
632 }
633
634 s16 LuaEntitySAO::getHP() const
635 {
636         return m_hp;
637 }
638
639 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
640 {
641         m_armor_groups = armor_groups;
642         m_armor_groups_sent = false;
643 }
644
645 ObjectProperties* LuaEntitySAO::accessObjectProperties()
646 {
647         return &m_prop;
648 }
649
650 void LuaEntitySAO::notifyObjectPropertiesModified()
651 {
652         m_properties_sent = false;
653 }
654
655 void LuaEntitySAO::setVelocity(v3f velocity)
656 {
657         m_velocity = velocity;
658 }
659
660 v3f LuaEntitySAO::getVelocity()
661 {
662         return m_velocity;
663 }
664
665 void LuaEntitySAO::setAcceleration(v3f acceleration)
666 {
667         m_acceleration = acceleration;
668 }
669
670 v3f LuaEntitySAO::getAcceleration()
671 {
672         return m_acceleration;
673 }
674
675 void LuaEntitySAO::setYaw(float yaw)
676 {
677         m_yaw = yaw;
678 }
679
680 float LuaEntitySAO::getYaw()
681 {
682         return m_yaw;
683 }
684
685 void LuaEntitySAO::setTextureMod(const std::string &mod)
686 {
687         std::string str = gob_cmd_set_texture_mod(mod);
688         // create message and add to list
689         ActiveObjectMessage aom(getId(), true, str);
690         m_messages_out.push_back(aom);
691 }
692
693 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
694                 bool select_horiz_by_yawpitch)
695 {
696         std::string str = gob_cmd_set_sprite(
697                 p,
698                 num_frames,
699                 framelength,
700                 select_horiz_by_yawpitch
701         );
702         // create message and add to list
703         ActiveObjectMessage aom(getId(), true, str);
704         m_messages_out.push_back(aom);
705 }
706
707 std::string LuaEntitySAO::getName()
708 {
709         return m_init_name;
710 }
711
712 std::string LuaEntitySAO::getPropertyPacket()
713 {
714         return gob_cmd_set_properties(m_prop);
715 }
716
717 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
718 {
719         m_last_sent_move_precision = m_base_position.getDistanceFrom(
720                         m_last_sent_position);
721         m_last_sent_position_timer = 0;
722         m_last_sent_yaw = m_yaw;
723         m_last_sent_position = m_base_position;
724         m_last_sent_velocity = m_velocity;
725         //m_last_sent_acceleration = m_acceleration;
726
727         float update_interval = m_env->getSendRecommendedInterval();
728
729         std::string str = gob_cmd_update_position(
730                 m_base_position,
731                 m_velocity,
732                 m_acceleration,
733                 m_yaw,
734                 do_interpolate,
735                 is_movement_end,
736                 update_interval
737         );
738         // create message and add to list
739         ActiveObjectMessage aom(getId(), false, str);
740         m_messages_out.push_back(aom);
741 }
742
743 /*
744         PlayerSAO
745 */
746
747 // No prototype, PlayerSAO does not need to be deserialized
748
749 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
750                 const std::set<std::string> &privs, bool is_singleplayer):
751         ServerActiveObject(env_, v3f(0,0,0)),
752         m_player(player_),
753         m_peer_id(peer_id_),
754         m_inventory(NULL),
755         m_last_good_position(0,0,0),
756         m_last_good_position_age(0),
757         m_time_from_last_punch(0),
758         m_wield_index(0),
759         m_position_not_sent(false),
760         m_armor_groups_sent(false),
761         m_properties_sent(true),
762         m_privs(privs),
763         m_is_singleplayer(is_singleplayer),
764         // public
765         m_teleported(false),
766         m_inventory_not_sent(false),
767         m_hp_not_sent(false),
768         m_wielded_item_not_sent(false)
769 {
770         assert(m_player);
771         assert(m_peer_id != 0);
772         setBasePosition(m_player->getPosition());
773         m_inventory = &m_player->inventory;
774         m_armor_groups["choppy"] = 2;
775         m_armor_groups["fleshy"] = 3;
776
777         m_prop.hp_max = PLAYER_MAX_HP;
778         m_prop.physical = false;
779         m_prop.weight = 75;
780         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
781         m_prop.visual = "upright_sprite";
782         m_prop.visual_size = v2f(1, 2);
783         m_prop.textures.clear();
784         m_prop.textures.push_back("player.png");
785         m_prop.textures.push_back("player_back.png");
786         m_prop.spritediv = v2s16(1,1);
787         m_prop.is_visible = (getHP() != 0);
788         m_prop.makes_footstep_sound = true;
789 }
790
791 PlayerSAO::~PlayerSAO()
792 {
793         if(m_inventory != &m_player->inventory)
794                 delete m_inventory;
795
796 }
797
798 std::string PlayerSAO::getDescription()
799 {
800         return std::string("player ") + m_player->getName();
801 }
802
803 // Called after id has been set and has been inserted in environment
804 void PlayerSAO::addedToEnvironment()
805 {
806         ServerActiveObject::addedToEnvironment();
807         ServerActiveObject::setBasePosition(m_player->getPosition());
808         m_player->setPlayerSAO(this);
809         m_player->peer_id = m_peer_id;
810         m_last_good_position = m_player->getPosition();
811         m_last_good_position_age = 0.0;
812 }
813
814 // Called before removing from environment
815 void PlayerSAO::removingFromEnvironment()
816 {
817         ServerActiveObject::removingFromEnvironment();
818         if(m_player->getPlayerSAO() == this)
819         {
820                 m_player->setPlayerSAO(NULL);
821                 m_player->peer_id = 0;
822         }
823 }
824
825 bool PlayerSAO::isStaticAllowed() const
826 {
827         return false;
828 }
829
830 bool PlayerSAO::unlimitedTransferDistance() const
831 {
832         return g_settings->getBool("unlimited_player_transfer_distance");
833 }
834
835 std::string PlayerSAO::getClientInitializationData()
836 {
837         std::ostringstream os(std::ios::binary);
838         writeU8(os, 0); // version
839         os<<serializeString(m_player->getName()); // name
840         writeU8(os, 1); // is_player
841         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
842         writeF1000(os, m_player->getYaw());
843         writeS16(os, getHP());
844         writeU8(os, 2); // number of messages stuffed in here
845         os<<serializeLongString(getPropertyPacket()); // message 1
846         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
847         return os.str();
848 }
849
850 std::string PlayerSAO::getStaticData()
851 {
852         assert(0);
853         return "";
854 }
855
856 void PlayerSAO::step(float dtime, bool send_recommended)
857 {
858         if(!m_properties_sent)
859         {
860                 m_properties_sent = true;
861                 std::string str = getPropertyPacket();
862                 // create message and add to list
863                 ActiveObjectMessage aom(getId(), true, str);
864                 m_messages_out.push_back(aom);
865         }
866
867         m_time_from_last_punch += dtime;
868         
869         if(m_is_singleplayer)
870         {
871                 m_last_good_position = m_player->getPosition();
872                 m_last_good_position_age = 0;
873         }
874         else
875         {
876                 /*
877                         Check player movements
878
879                         NOTE: Actually the server should handle player physics like the
880                         client does and compare player's position to what is calculated
881                         on our side. This is required when eg. players fly due to an
882                         explosion.
883                 */
884
885                 float player_max_speed = 0;
886                 float player_max_speed_up = 0;
887                 if(m_privs.count("fast") != 0){
888                         // Fast speed
889                         player_max_speed = BS * 20;
890                         player_max_speed_up = BS * 20;
891                 } else {
892                         // Normal speed
893                         player_max_speed = BS * 4.0;
894                         player_max_speed_up = BS * 4.0;
895                 }
896                 // Tolerance
897                 player_max_speed *= 2.5;
898                 player_max_speed_up *= 2.5;
899
900                 m_last_good_position_age += dtime;
901                 if(m_last_good_position_age >= 1.0){
902                         float age = m_last_good_position_age;
903                         v3f diff = (m_player->getPosition() - m_last_good_position);
904                         float d_vert = diff.Y;
905                         diff.Y = 0;
906                         float d_horiz = diff.getLength();
907                         /*infostream<<m_player->getName()<<"'s horizontal speed is "
908                                         <<(d_horiz/age)<<std::endl;*/
909                         if(d_horiz <= age * player_max_speed &&
910                                         (d_vert < 0 || d_vert < age * player_max_speed_up)){
911                                 m_last_good_position = m_player->getPosition();
912                         } else {
913                                 actionstream<<"Player "<<m_player->getName()
914                                                 <<" moved too fast; resetting position"
915                                                 <<std::endl;
916                                 m_player->setPosition(m_last_good_position);
917                                 m_teleported = true;
918                         }
919                         m_last_good_position_age = 0;
920                 }
921         }
922
923         if(send_recommended == false)
924                 return;
925
926         if(m_position_not_sent)
927         {
928                 m_position_not_sent = false;
929                 float update_interval = m_env->getSendRecommendedInterval();
930                 std::string str = gob_cmd_update_position(
931                         m_player->getPosition() + v3f(0,BS*1,0),
932                         v3f(0,0,0),
933                         v3f(0,0,0),
934                         m_player->getYaw(),
935                         true,
936                         false,
937                         update_interval
938                 );
939                 // create message and add to list
940                 ActiveObjectMessage aom(getId(), false, str);
941                 m_messages_out.push_back(aom);
942         }
943
944         if(m_wielded_item_not_sent)
945         {
946                 m_wielded_item_not_sent = false;
947                 // GenericCAO has no special way to show this
948         }
949
950         if(m_armor_groups_sent == false){
951                 m_armor_groups_sent = true;
952                 std::string str = gob_cmd_update_armor_groups(
953                                 m_armor_groups);
954                 // create message and add to list
955                 ActiveObjectMessage aom(getId(), true, str);
956                 m_messages_out.push_back(aom);
957         }
958 }
959
960 void PlayerSAO::setBasePosition(const v3f &position)
961 {
962         ServerActiveObject::setBasePosition(position);
963         m_position_not_sent = true;
964 }
965
966 void PlayerSAO::setPos(v3f pos)
967 {
968         m_player->setPosition(pos);
969         // Movement caused by this command is always valid
970         m_last_good_position = pos;
971         m_last_good_position_age = 0;
972         // Force position change on client
973         m_teleported = true;
974 }
975
976 void PlayerSAO::moveTo(v3f pos, bool continuous)
977 {
978         m_player->setPosition(pos);
979         // Movement caused by this command is always valid
980         m_last_good_position = pos;
981         m_last_good_position_age = 0;
982         // Force position change on client
983         m_teleported = true;
984 }
985
986 int PlayerSAO::punch(v3f dir,
987         const ToolCapabilities *toolcap,
988         ServerActiveObject *puncher,
989         float time_from_last_punch)
990 {
991         if(!toolcap)
992                 return 0;
993
994         // No effect if PvP disabled
995         if(g_settings->getBool("enable_pvp") == false){
996                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
997                         std::string str = gob_cmd_punched(0, getHP());
998                         // create message and add to list
999                         ActiveObjectMessage aom(getId(), true, str);
1000                         m_messages_out.push_back(aom);
1001                         return 0;
1002                 }
1003         }
1004
1005         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1006                         time_from_last_punch);
1007
1008         actionstream<<"Player "<<m_player->getName()<<" punched by "
1009                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1010                         <<" HP"<<std::endl;
1011
1012         setHP(getHP() - hitparams.hp);
1013
1014         if(hitparams.hp != 0)
1015         {
1016                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1017                 // create message and add to list
1018                 ActiveObjectMessage aom(getId(), true, str);
1019                 m_messages_out.push_back(aom);
1020         }
1021
1022         return hitparams.wear;
1023 }
1024
1025 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1026 {
1027 }
1028
1029 s16 PlayerSAO::getHP() const
1030 {
1031         return m_player->hp;
1032 }
1033
1034 void PlayerSAO::setHP(s16 hp)
1035 {
1036         s16 oldhp = m_player->hp;
1037
1038         if(hp < 0)
1039                 hp = 0;
1040         else if(hp > PLAYER_MAX_HP)
1041                 hp = PLAYER_MAX_HP;
1042
1043         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1044         {
1045                 m_hp_not_sent = true; // fix wrong prediction on client
1046                 return;
1047         }
1048
1049         m_player->hp = hp;
1050
1051         if(hp != oldhp)
1052                 m_hp_not_sent = true;
1053
1054         // On death or reincarnation send an active object message
1055         if((hp == 0) != (oldhp == 0))
1056         {
1057                 // Will send new is_visible value based on (getHP()!=0)
1058                 m_properties_sent = false;
1059                 // Send new HP
1060                 std::string str = gob_cmd_punched(0, getHP());
1061                 ActiveObjectMessage aom(getId(), true, str);
1062                 m_messages_out.push_back(aom);
1063         }
1064 }
1065
1066 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1067 {
1068         m_armor_groups = armor_groups;
1069         m_armor_groups_sent = false;
1070 }
1071
1072 ObjectProperties* PlayerSAO::accessObjectProperties()
1073 {
1074         return &m_prop;
1075 }
1076
1077 void PlayerSAO::notifyObjectPropertiesModified()
1078 {
1079         m_properties_sent = false;
1080 }
1081
1082 Inventory* PlayerSAO::getInventory()
1083 {
1084         return m_inventory;
1085 }
1086 const Inventory* PlayerSAO::getInventory() const
1087 {
1088         return m_inventory;
1089 }
1090
1091 InventoryLocation PlayerSAO::getInventoryLocation() const
1092 {
1093         InventoryLocation loc;
1094         loc.setPlayer(m_player->getName());
1095         return loc;
1096 }
1097
1098 void PlayerSAO::setInventoryModified()
1099 {
1100         m_inventory_not_sent = true;
1101 }
1102
1103 std::string PlayerSAO::getWieldList() const
1104 {
1105         return "main";
1106 }
1107
1108 int PlayerSAO::getWieldIndex() const
1109 {
1110         return m_wield_index;
1111 }
1112
1113 void PlayerSAO::setWieldIndex(int i)
1114 {
1115         if(i != m_wield_index)
1116         {
1117                 m_wield_index = i;
1118                 m_wielded_item_not_sent = true;
1119         }
1120 }
1121
1122 void PlayerSAO::disconnected()
1123 {
1124         m_peer_id = 0;
1125         m_removed = true;
1126         if(m_player->getPlayerSAO() == this)
1127         {
1128                 m_player->setPlayerSAO(NULL);
1129                 m_player->peer_id = 0;
1130         }
1131 }
1132
1133 void PlayerSAO::createCreativeInventory()
1134 {
1135         if(m_inventory != &m_player->inventory)
1136                 delete m_inventory;
1137
1138         m_inventory = new Inventory(m_player->inventory);
1139         m_inventory->clearContents();
1140         scriptapi_get_creative_inventory(m_env->getLua(), this);
1141 }
1142
1143 std::string PlayerSAO::getPropertyPacket()
1144 {
1145         m_prop.is_visible = (getHP() != 0);
1146         return gob_cmd_set_properties(m_prop);
1147 }
1148