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