]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_sao.cpp
Don't leak textures all over the place
[dragonfireclient.git] / src / content_sao.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU 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         m_prop.visual = "upright_sprite";
786         m_prop.visual_size = v2f(1, 2);
787         m_prop.textures.clear();
788         m_prop.textures.push_back("player.png");
789         m_prop.textures.push_back("player_back.png");
790         m_prop.spritediv = v2s16(1,1);
791         m_prop.is_visible = (getHP() != 0);
792         m_prop.makes_footstep_sound = true;
793 }
794
795 PlayerSAO::~PlayerSAO()
796 {
797         if(m_inventory != &m_player->inventory)
798                 delete m_inventory;
799
800 }
801
802 std::string PlayerSAO::getDescription()
803 {
804         return std::string("player ") + m_player->getName();
805 }
806
807 // Called after id has been set and has been inserted in environment
808 void PlayerSAO::addedToEnvironment(u32 dtime_s)
809 {
810         ServerActiveObject::addedToEnvironment(dtime_s);
811         ServerActiveObject::setBasePosition(m_player->getPosition());
812         m_player->setPlayerSAO(this);
813         m_player->peer_id = m_peer_id;
814         m_last_good_position = m_player->getPosition();
815         m_last_good_position_age = 0.0;
816 }
817
818 // Called before removing from environment
819 void PlayerSAO::removingFromEnvironment()
820 {
821         ServerActiveObject::removingFromEnvironment();
822         if(m_player->getPlayerSAO() == this)
823         {
824                 m_player->setPlayerSAO(NULL);
825                 m_player->peer_id = 0;
826         }
827 }
828
829 bool PlayerSAO::isStaticAllowed() const
830 {
831         return false;
832 }
833
834 bool PlayerSAO::unlimitedTransferDistance() const
835 {
836         return g_settings->getBool("unlimited_player_transfer_distance");
837 }
838
839 std::string PlayerSAO::getClientInitializationData()
840 {
841         std::ostringstream os(std::ios::binary);
842         writeU8(os, 0); // version
843         os<<serializeString(m_player->getName()); // name
844         writeU8(os, 1); // is_player
845         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
846         writeF1000(os, m_player->getYaw());
847         writeS16(os, getHP());
848         writeU8(os, 2); // number of messages stuffed in here
849         os<<serializeLongString(getPropertyPacket()); // message 1
850         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
851         return os.str();
852 }
853
854 std::string PlayerSAO::getStaticData()
855 {
856         assert(0);
857         return "";
858 }
859
860 void PlayerSAO::step(float dtime, bool send_recommended)
861 {
862         if(!m_properties_sent)
863         {
864                 m_properties_sent = true;
865                 std::string str = getPropertyPacket();
866                 // create message and add to list
867                 ActiveObjectMessage aom(getId(), true, str);
868                 m_messages_out.push_back(aom);
869         }
870
871         m_time_from_last_punch += dtime;
872         m_nocheat_dig_time += dtime;
873         
874         if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
875         {
876                 m_last_good_position = m_player->getPosition();
877                 m_last_good_position_age = 0;
878         }
879         else
880         {
881                 /*
882                         Check player movements
883
884                         NOTE: Actually the server should handle player physics like the
885                         client does and compare player's position to what is calculated
886                         on our side. This is required when eg. players fly due to an
887                         explosion. Altough a node-based alternative might be possible
888                         too, and much more lightweight.
889                 */
890
891                 float player_max_speed = 0;
892                 float player_max_speed_up = 0;
893                 if(m_privs.count("fast") != 0){
894                         // Fast speed
895                         player_max_speed = BS * 20;
896                         player_max_speed_up = BS * 20;
897                 } else {
898                         // Normal speed
899                         player_max_speed = BS * 4.0;
900                         player_max_speed_up = BS * 4.0;
901                 }
902                 // Tolerance
903                 player_max_speed *= 2.5;
904                 player_max_speed_up *= 2.5;
905
906                 m_last_good_position_age += dtime;
907                 if(m_last_good_position_age >= 1.0){
908                         float age = m_last_good_position_age;
909                         v3f diff = (m_player->getPosition() - m_last_good_position);
910                         float d_vert = diff.Y;
911                         diff.Y = 0;
912                         float d_horiz = diff.getLength();
913                         /*infostream<<m_player->getName()<<"'s horizontal speed is "
914                                         <<(d_horiz/age)<<std::endl;*/
915                         if(d_horiz <= age * player_max_speed &&
916                                         (d_vert < 0 || d_vert < age * player_max_speed_up)){
917                                 m_last_good_position = m_player->getPosition();
918                         } else {
919                                 actionstream<<"Player "<<m_player->getName()
920                                                 <<" moved too fast; resetting position"
921                                                 <<std::endl;
922                                 m_player->setPosition(m_last_good_position);
923                                 m_teleported = true;
924                         }
925                         m_last_good_position_age = 0;
926                 }
927         }
928
929         if(send_recommended == false)
930                 return;
931
932         if(m_position_not_sent)
933         {
934                 m_position_not_sent = false;
935                 float update_interval = m_env->getSendRecommendedInterval();
936                 std::string str = gob_cmd_update_position(
937                         m_player->getPosition() + v3f(0,BS*1,0),
938                         v3f(0,0,0),
939                         v3f(0,0,0),
940                         m_player->getYaw(),
941                         true,
942                         false,
943                         update_interval
944                 );
945                 // create message and add to list
946                 ActiveObjectMessage aom(getId(), false, str);
947                 m_messages_out.push_back(aom);
948         }
949
950         if(m_wielded_item_not_sent)
951         {
952                 m_wielded_item_not_sent = false;
953                 // GenericCAO has no special way to show this
954         }
955
956         if(m_armor_groups_sent == false){
957                 m_armor_groups_sent = true;
958                 std::string str = gob_cmd_update_armor_groups(
959                                 m_armor_groups);
960                 // create message and add to list
961                 ActiveObjectMessage aom(getId(), true, str);
962                 m_messages_out.push_back(aom);
963         }
964 }
965
966 void PlayerSAO::setBasePosition(const v3f &position)
967 {
968         ServerActiveObject::setBasePosition(position);
969         m_position_not_sent = true;
970 }
971
972 void PlayerSAO::setPos(v3f pos)
973 {
974         m_player->setPosition(pos);
975         // Movement caused by this command is always valid
976         m_last_good_position = pos;
977         m_last_good_position_age = 0;
978         // Force position change on client
979         m_teleported = true;
980 }
981
982 void PlayerSAO::moveTo(v3f pos, bool continuous)
983 {
984         m_player->setPosition(pos);
985         // Movement caused by this command is always valid
986         m_last_good_position = pos;
987         m_last_good_position_age = 0;
988         // Force position change on client
989         m_teleported = true;
990 }
991
992 int PlayerSAO::punch(v3f dir,
993         const ToolCapabilities *toolcap,
994         ServerActiveObject *puncher,
995         float time_from_last_punch)
996 {
997         if(!toolcap)
998                 return 0;
999
1000         // No effect if PvP disabled
1001         if(g_settings->getBool("enable_pvp") == false){
1002                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1003                         std::string str = gob_cmd_punched(0, getHP());
1004                         // create message and add to list
1005                         ActiveObjectMessage aom(getId(), true, str);
1006                         m_messages_out.push_back(aom);
1007                         return 0;
1008                 }
1009         }
1010
1011         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1012                         time_from_last_punch);
1013
1014         actionstream<<"Player "<<m_player->getName()<<" punched by "
1015                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1016                         <<" HP"<<std::endl;
1017
1018         setHP(getHP() - hitparams.hp);
1019
1020         if(hitparams.hp != 0)
1021         {
1022                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1023                 // create message and add to list
1024                 ActiveObjectMessage aom(getId(), true, str);
1025                 m_messages_out.push_back(aom);
1026         }
1027
1028         return hitparams.wear;
1029 }
1030
1031 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1032 {
1033 }
1034
1035 s16 PlayerSAO::getHP() const
1036 {
1037         return m_player->hp;
1038 }
1039
1040 void PlayerSAO::setHP(s16 hp)
1041 {
1042         s16 oldhp = m_player->hp;
1043
1044         if(hp < 0)
1045                 hp = 0;
1046         else if(hp > PLAYER_MAX_HP)
1047                 hp = PLAYER_MAX_HP;
1048
1049         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1050         {
1051                 m_hp_not_sent = true; // fix wrong prediction on client
1052                 return;
1053         }
1054
1055         m_player->hp = hp;
1056
1057         if(hp != oldhp)
1058                 m_hp_not_sent = true;
1059
1060         // On death or reincarnation send an active object message
1061         if((hp == 0) != (oldhp == 0))
1062         {
1063                 // Will send new is_visible value based on (getHP()!=0)
1064                 m_properties_sent = false;
1065                 // Send new HP
1066                 std::string str = gob_cmd_punched(0, getHP());
1067                 ActiveObjectMessage aom(getId(), true, str);
1068                 m_messages_out.push_back(aom);
1069         }
1070 }
1071
1072 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1073 {
1074         m_armor_groups = armor_groups;
1075         m_armor_groups_sent = false;
1076 }
1077
1078 ObjectProperties* PlayerSAO::accessObjectProperties()
1079 {
1080         return &m_prop;
1081 }
1082
1083 void PlayerSAO::notifyObjectPropertiesModified()
1084 {
1085         m_properties_sent = false;
1086 }
1087
1088 Inventory* PlayerSAO::getInventory()
1089 {
1090         return m_inventory;
1091 }
1092 const Inventory* PlayerSAO::getInventory() const
1093 {
1094         return m_inventory;
1095 }
1096
1097 InventoryLocation PlayerSAO::getInventoryLocation() const
1098 {
1099         InventoryLocation loc;
1100         loc.setPlayer(m_player->getName());
1101         return loc;
1102 }
1103
1104 void PlayerSAO::setInventoryModified()
1105 {
1106         m_inventory_not_sent = true;
1107 }
1108
1109 std::string PlayerSAO::getWieldList() const
1110 {
1111         return "main";
1112 }
1113
1114 int PlayerSAO::getWieldIndex() const
1115 {
1116         return m_wield_index;
1117 }
1118
1119 void PlayerSAO::setWieldIndex(int i)
1120 {
1121         if(i != m_wield_index)
1122         {
1123                 m_wield_index = i;
1124                 m_wielded_item_not_sent = true;
1125         }
1126 }
1127
1128 void PlayerSAO::disconnected()
1129 {
1130         m_peer_id = 0;
1131         m_removed = true;
1132         if(m_player->getPlayerSAO() == this)
1133         {
1134                 m_player->setPlayerSAO(NULL);
1135                 m_player->peer_id = 0;
1136         }
1137 }
1138
1139 std::string PlayerSAO::getPropertyPacket()
1140 {
1141         m_prop.is_visible = (getHP() != 0);
1142         return gob_cmd_set_properties(m_prop);
1143 }
1144