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