]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_sao.cpp
054e4944fda1800f1dc7e5106d921ef0fcc71da9
[dragonfireclient.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 void LuaEntitySAO::setVelocity(v3f velocity)
647 {
648         m_velocity = velocity;
649 }
650
651 v3f LuaEntitySAO::getVelocity()
652 {
653         return m_velocity;
654 }
655
656 void LuaEntitySAO::setAcceleration(v3f acceleration)
657 {
658         m_acceleration = acceleration;
659 }
660
661 v3f LuaEntitySAO::getAcceleration()
662 {
663         return m_acceleration;
664 }
665
666 void LuaEntitySAO::setYaw(float yaw)
667 {
668         m_yaw = yaw;
669 }
670
671 float LuaEntitySAO::getYaw()
672 {
673         return m_yaw;
674 }
675
676 void LuaEntitySAO::setTextureMod(const std::string &mod)
677 {
678         std::string str = gob_cmd_set_texture_mod(mod);
679         // create message and add to list
680         ActiveObjectMessage aom(getId(), true, str);
681         m_messages_out.push_back(aom);
682 }
683
684 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
685                 bool select_horiz_by_yawpitch)
686 {
687         std::string str = gob_cmd_set_sprite(
688                 p,
689                 num_frames,
690                 framelength,
691                 select_horiz_by_yawpitch
692         );
693         // create message and add to list
694         ActiveObjectMessage aom(getId(), true, str);
695         m_messages_out.push_back(aom);
696 }
697
698 std::string LuaEntitySAO::getName()
699 {
700         return m_init_name;
701 }
702
703 std::string LuaEntitySAO::getPropertyPacket()
704 {
705         return gob_cmd_set_properties(m_prop);
706 }
707
708 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
709 {
710         m_last_sent_move_precision = m_base_position.getDistanceFrom(
711                         m_last_sent_position);
712         m_last_sent_position_timer = 0;
713         m_last_sent_yaw = m_yaw;
714         m_last_sent_position = m_base_position;
715         m_last_sent_velocity = m_velocity;
716         //m_last_sent_acceleration = m_acceleration;
717
718         float update_interval = m_env->getSendRecommendedInterval();
719
720         std::string str = gob_cmd_update_position(
721                 m_base_position,
722                 m_velocity,
723                 m_acceleration,
724                 m_yaw,
725                 do_interpolate,
726                 is_movement_end,
727                 update_interval
728         );
729         // create message and add to list
730         ActiveObjectMessage aom(getId(), false, str);
731 }
732
733 /*
734         PlayerSAO
735 */
736
737 // No prototype, PlayerSAO does not need to be deserialized
738
739 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_):
740         ServerActiveObject(env_, v3f(0,0,0)),
741         m_player(player_),
742         m_peer_id(peer_id_),
743         m_inventory(NULL),
744         m_last_good_position(0,0,0),
745         m_last_good_position_age(0),
746         m_time_from_last_punch(0),
747         m_wield_index(0),
748         m_position_not_sent(false),
749         m_armor_groups_sent(false),
750         m_properties_sent(true),
751         m_teleported(false),
752         m_inventory_not_sent(false),
753         m_hp_not_sent(false),
754         m_wielded_item_not_sent(false)
755 {
756         assert(m_player);
757         assert(m_peer_id != 0);
758         setBasePosition(m_player->getPosition());
759         m_inventory = &m_player->inventory;
760         m_armor_groups["choppy"] = 2;
761         m_armor_groups["fleshy"] = 3;
762
763         m_prop.hp_max = PLAYER_MAX_HP;
764         m_prop.physical = false;
765         m_prop.weight = 75;
766         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
767         m_prop.visual = "upright_sprite";
768         m_prop.visual_size = v2f(1, 2);
769         m_prop.textures.clear();
770         m_prop.textures.push_back("player.png");
771         m_prop.textures.push_back("player_back.png");
772         m_prop.spritediv = v2s16(1,1);
773         m_prop.is_visible = (getHP() != 0);
774         m_prop.makes_footstep_sound = true;
775 }
776
777 PlayerSAO::~PlayerSAO()
778 {
779         if(m_inventory != &m_player->inventory)
780                 delete m_inventory;
781
782 }
783
784 std::string PlayerSAO::getDescription()
785 {
786         return std::string("player ") + m_player->getName();
787 }
788
789 // Called after id has been set and has been inserted in environment
790 void PlayerSAO::addedToEnvironment()
791 {
792         ServerActiveObject::addedToEnvironment();
793         ServerActiveObject::setBasePosition(m_player->getPosition());
794         m_player->setPlayerSAO(this);
795         m_player->peer_id = m_peer_id;
796         m_last_good_position = m_player->getPosition();
797         m_last_good_position_age = 0.0;
798 }
799
800 // Called before removing from environment
801 void PlayerSAO::removingFromEnvironment()
802 {
803         ServerActiveObject::removingFromEnvironment();
804         if(m_player->getPlayerSAO() == this)
805         {
806                 m_player->setPlayerSAO(NULL);
807                 m_player->peer_id = 0;
808         }
809 }
810
811 bool PlayerSAO::isStaticAllowed() const
812 {
813         return false;
814 }
815
816 bool PlayerSAO::unlimitedTransferDistance() const
817 {
818         return g_settings->getBool("unlimited_player_transfer_distance");
819 }
820
821 std::string PlayerSAO::getClientInitializationData()
822 {
823         std::ostringstream os(std::ios::binary);
824         writeU8(os, 0); // version
825         os<<serializeString(m_player->getName()); // name
826         writeU8(os, 1); // is_player
827         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
828         writeF1000(os, m_player->getYaw());
829         writeS16(os, getHP());
830         writeU8(os, 2); // number of messages stuffed in here
831         os<<serializeLongString(getPropertyPacket()); // message 1
832         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
833         return os.str();
834 }
835
836 std::string PlayerSAO::getStaticData()
837 {
838         assert(0);
839         return "";
840 }
841
842 void PlayerSAO::step(float dtime, bool send_recommended)
843 {
844         if(!m_properties_sent)
845         {
846                 m_properties_sent = true;
847                 std::string str = getPropertyPacket();
848                 // create message and add to list
849                 ActiveObjectMessage aom(getId(), true, str);
850                 m_messages_out.push_back(aom);
851         }
852
853         m_time_from_last_punch += dtime;
854
855         /*
856                 Check player movements
857
858                 NOTE: Actually the server should handle player physics like the
859                 client does and compare player's position to what is calculated
860                 on our side. This is required when eg. players fly due to an
861                 explosion.
862         */
863
864         //float player_max_speed = BS * 4.0; // Normal speed
865         float player_max_speed = BS * 20; // Fast speed
866         float player_max_speed_up = BS * 20;
867         player_max_speed *= 2.5; // Tolerance
868         player_max_speed_up *= 2.5;
869
870         m_last_good_position_age += dtime;
871         if(m_last_good_position_age >= 1.0){
872                 float age = m_last_good_position_age;
873                 v3f diff = (m_player->getPosition() - m_last_good_position);
874                 float d_vert = diff.Y;
875                 diff.Y = 0;
876                 float d_horiz = diff.getLength();
877                 /*infostream<<m_player->getName()<<"'s horizontal speed is "
878                                 <<(d_horiz/age)<<std::endl;*/
879                 if(d_horiz <= age * player_max_speed &&
880                                 (d_vert < 0 || d_vert < age * player_max_speed_up)){
881                         m_last_good_position = m_player->getPosition();
882                 } else {
883                         actionstream<<"Player "<<m_player->getName()
884                                         <<" moved too fast; resetting position"
885                                         <<std::endl;
886                         m_player->setPosition(m_last_good_position);
887                         m_teleported = true;
888                 }
889                 m_last_good_position_age = 0;
890         }
891
892         if(send_recommended == false)
893                 return;
894
895         if(m_position_not_sent)
896         {
897                 m_position_not_sent = false;
898                 float update_interval = m_env->getSendRecommendedInterval();
899                 std::string str = gob_cmd_update_position(
900                         m_player->getPosition() + v3f(0,BS*1,0),
901                         v3f(0,0,0),
902                         v3f(0,0,0),
903                         m_player->getYaw(),
904                         true,
905                         false,
906                         update_interval
907                 );
908                 // create message and add to list
909                 ActiveObjectMessage aom(getId(), false, str);
910                 m_messages_out.push_back(aom);
911         }
912
913         if(m_wielded_item_not_sent)
914         {
915                 m_wielded_item_not_sent = false;
916                 // GenericCAO has no special way to show this
917         }
918
919         if(m_armor_groups_sent == false){
920                 m_armor_groups_sent = true;
921                 std::string str = gob_cmd_update_armor_groups(
922                                 m_armor_groups);
923                 // create message and add to list
924                 ActiveObjectMessage aom(getId(), true, str);
925                 m_messages_out.push_back(aom);
926         }
927 }
928
929 void PlayerSAO::setBasePosition(const v3f &position)
930 {
931         ServerActiveObject::setBasePosition(position);
932         m_position_not_sent = true;
933 }
934
935 void PlayerSAO::setPos(v3f pos)
936 {
937         m_player->setPosition(pos);
938         // Movement caused by this command is always valid
939         m_last_good_position = pos;
940         m_last_good_position_age = 0;
941         // Force position change on client
942         m_teleported = true;
943 }
944
945 void PlayerSAO::moveTo(v3f pos, bool continuous)
946 {
947         m_player->setPosition(pos);
948         // Movement caused by this command is always valid
949         m_last_good_position = pos;
950         m_last_good_position_age = 0;
951         // Force position change on client
952         m_teleported = true;
953 }
954
955 int PlayerSAO::punch(v3f dir,
956         const ToolCapabilities *toolcap,
957         ServerActiveObject *puncher,
958         float time_from_last_punch)
959 {
960         if(!toolcap)
961                 return 0;
962
963         // No effect if PvP disabled
964         if(g_settings->getBool("enable_pvp") == false){
965                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
966                         std::string str = gob_cmd_punched(0, getHP());
967                         // create message and add to list
968                         ActiveObjectMessage aom(getId(), true, str);
969                         m_messages_out.push_back(aom);
970                         return 0;
971                 }
972         }
973
974         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
975                         time_from_last_punch);
976
977         actionstream<<"Player "<<m_player->getName()<<" punched by "
978                         <<puncher->getDescription()<<", damage "<<hitparams.hp
979                         <<" HP"<<std::endl;
980
981         setHP(getHP() - hitparams.hp);
982
983         if(hitparams.hp != 0)
984         {
985                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
986                 // create message and add to list
987                 ActiveObjectMessage aom(getId(), true, str);
988                 m_messages_out.push_back(aom);
989         }
990
991         return hitparams.wear;
992 }
993
994 void PlayerSAO::rightClick(ServerActiveObject *clicker)
995 {
996 }
997
998 s16 PlayerSAO::getHP() const
999 {
1000         return m_player->hp;
1001 }
1002
1003 void PlayerSAO::setHP(s16 hp)
1004 {
1005         s16 oldhp = m_player->hp;
1006
1007         if(hp < 0)
1008                 hp = 0;
1009         else if(hp > PLAYER_MAX_HP)
1010                 hp = PLAYER_MAX_HP;
1011
1012         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1013         {
1014                 m_hp_not_sent = true; // fix wrong prediction on client
1015                 return;
1016         }
1017
1018         m_player->hp = hp;
1019
1020         if(hp != oldhp)
1021                 m_hp_not_sent = true;
1022
1023         // On death or reincarnation send an active object message
1024         if((hp == 0) != (oldhp == 0))
1025         {
1026                 // Will send new is_visible value based on (getHP()!=0)
1027                 m_properties_sent = false;
1028                 // Send new HP
1029                 std::string str = gob_cmd_punched(0, getHP());
1030                 ActiveObjectMessage aom(getId(), true, str);
1031                 m_messages_out.push_back(aom);
1032         }
1033 }
1034
1035 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1036 {
1037         m_armor_groups = armor_groups;
1038         m_armor_groups_sent = false;
1039 }
1040
1041 Inventory* PlayerSAO::getInventory()
1042 {
1043         return m_inventory;
1044 }
1045 const Inventory* PlayerSAO::getInventory() const
1046 {
1047         return m_inventory;
1048 }
1049
1050 InventoryLocation PlayerSAO::getInventoryLocation() const
1051 {
1052         InventoryLocation loc;
1053         loc.setPlayer(m_player->getName());
1054         return loc;
1055 }
1056
1057 void PlayerSAO::setInventoryModified()
1058 {
1059         m_inventory_not_sent = true;
1060 }
1061
1062 std::string PlayerSAO::getWieldList() const
1063 {
1064         return "main";
1065 }
1066
1067 int PlayerSAO::getWieldIndex() const
1068 {
1069         return m_wield_index;
1070 }
1071
1072 void PlayerSAO::setWieldIndex(int i)
1073 {
1074         if(i != m_wield_index)
1075         {
1076                 m_wield_index = i;
1077                 m_wielded_item_not_sent = true;
1078         }
1079 }
1080
1081 void PlayerSAO::disconnected()
1082 {
1083         m_peer_id = 0;
1084         m_removed = true;
1085         if(m_player->getPlayerSAO() == this)
1086         {
1087                 m_player->setPlayerSAO(NULL);
1088                 m_player->peer_id = 0;
1089         }
1090 }
1091
1092 void PlayerSAO::createCreativeInventory()
1093 {
1094         if(m_inventory != &m_player->inventory)
1095                 delete m_inventory;
1096
1097         m_inventory = new Inventory(m_player->inventory);
1098         m_inventory->clearContents();
1099         scriptapi_get_creative_inventory(m_env->getLua(), this);
1100 }
1101
1102 std::string PlayerSAO::getPropertyPacket()
1103 {
1104         m_prop.is_visible = (getHP() != 0);
1105         return gob_cmd_set_properties(m_prop);
1106 }
1107