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