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