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