Add congestion control settings to minetest.conf
[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(u16 protocol_version)
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         m_animation_sent(false),
360         m_bone_position_sent(false),
361         m_attachment_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(u32 dtime_s)
383 {
384         ServerActiveObject::addedToEnvironment(dtime_s);
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(), dtime_s);
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 bool LuaEntitySAO::isAttached()
436 {
437         if(!m_attachment_parent_id)
438                 return false;
439         // Check if the parent still exists
440         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
441         if(obj)
442                 return true;
443         return false;
444 }
445
446 void LuaEntitySAO::step(float dtime, bool send_recommended)
447 {
448         if(!m_properties_sent)
449         {
450                 m_properties_sent = true;
451                 std::string str = getPropertyPacket();
452                 // create message and add to list
453                 ActiveObjectMessage aom(getId(), true, str);
454                 m_messages_out.push_back(aom);
455         }
456
457         // If attached, check that our parent is still there. If it isn't, detach.
458         if(m_attachment_parent_id && !isAttached())
459         {
460                 m_attachment_parent_id = 0;
461                 m_attachment_bone = "";
462                 m_attachment_position = v3f(0,0,0);
463                 m_attachment_rotation = v3f(0,0,0);
464                 sendPosition(false, true);
465         }
466
467         m_last_sent_position_timer += dtime;
468
469         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
470         // If the object gets detached this comes into effect automatically from the last known origin
471         if(isAttached())
472         {
473                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
474                 m_base_position = pos;
475                 m_velocity = v3f(0,0,0);
476                 m_acceleration = v3f(0,0,0);
477         }
478         else
479         {
480                 if(m_prop.physical){
481                         core::aabbox3d<f32> box = m_prop.collisionbox;
482                         box.MinEdge *= BS;
483                         box.MaxEdge *= BS;
484                         collisionMoveResult moveresult;
485                         f32 pos_max_d = BS*0.25; // Distance per iteration
486                         f32 stepheight = 0; // Maximum climbable step height
487                         v3f p_pos = m_base_position;
488                         v3f p_velocity = m_velocity;
489                         v3f p_acceleration = m_acceleration;
490                         IGameDef *gamedef = m_env->getGameDef();
491                         moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
492                                         pos_max_d, box, stepheight, dtime,
493                                         p_pos, p_velocity, p_acceleration);
494                         // Apply results
495                         m_base_position = p_pos;
496                         m_velocity = p_velocity;
497                         m_acceleration = p_acceleration;
498                 } else {
499                         m_base_position += dtime * m_velocity + 0.5 * dtime
500                                         * dtime * m_acceleration;
501                         m_velocity += dtime * m_acceleration;
502                 }
503         }
504
505         if(m_registered){
506                 lua_State *L = m_env->getLua();
507                 scriptapi_luaentity_step(L, m_id, dtime);
508         }
509
510         if(send_recommended == false)
511                 return;
512
513         if(!isAttached())
514         {
515                 // TODO: force send when acceleration changes enough?
516                 float minchange = 0.2*BS;
517                 if(m_last_sent_position_timer > 1.0){
518                         minchange = 0.01*BS;
519                 } else if(m_last_sent_position_timer > 0.2){
520                         minchange = 0.05*BS;
521                 }
522                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
523                 move_d += m_last_sent_move_precision;
524                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
525                 if(move_d > minchange || vel_d > minchange ||
526                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
527                         sendPosition(true, false);
528                 }
529         }
530
531         if(m_armor_groups_sent == false){
532                 m_armor_groups_sent = true;
533                 std::string str = gob_cmd_update_armor_groups(
534                                 m_armor_groups);
535                 // create message and add to list
536                 ActiveObjectMessage aom(getId(), true, str);
537                 m_messages_out.push_back(aom);
538         }
539
540         if(m_animation_sent == false){
541                 m_animation_sent = true;
542                 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
543                 // create message and add to list
544                 ActiveObjectMessage aom(getId(), true, str);
545                 m_messages_out.push_back(aom);
546         }
547
548         if(m_bone_position_sent == false){
549                 m_bone_position_sent = true;
550                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
551                         std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
552                         // create message and add to list
553                         ActiveObjectMessage aom(getId(), true, str);
554                         m_messages_out.push_back(aom);
555                 }
556         }
557
558         if(m_attachment_sent == false){
559                 m_attachment_sent = true;
560                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
561                 // create message and add to list
562                 ActiveObjectMessage aom(getId(), true, str);
563                 m_messages_out.push_back(aom);
564         }
565 }
566
567 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
568 {
569         std::ostringstream os(std::ios::binary);
570
571         if(protocol_version >= 14)
572         {
573                 writeU8(os, 1); // version
574                 os<<serializeString(""); // name
575                 writeU8(os, 0); // is_player
576                 writeS16(os, getId()); //id
577                 writeV3F1000(os, m_base_position);
578                 writeF1000(os, m_yaw);
579                 writeS16(os, m_hp);
580
581                 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
582                 os<<serializeLongString(getPropertyPacket()); // message 1
583                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
584                 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
585                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
586                         os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
587                 }
588                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
589         }
590         else
591         {
592                 writeU8(os, 0); // version
593                 os<<serializeString(""); // name
594                 writeU8(os, 0); // is_player
595                 writeV3F1000(os, m_base_position);
596                 writeF1000(os, m_yaw);
597                 writeS16(os, m_hp);
598                 writeU8(os, 2); // number of messages stuffed in here
599                 os<<serializeLongString(getPropertyPacket()); // message 1
600                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
601         }
602
603         // return result
604         return os.str();
605 }
606
607 std::string LuaEntitySAO::getStaticData()
608 {
609         verbosestream<<__FUNCTION_NAME<<std::endl;
610         std::ostringstream os(std::ios::binary);
611         // version
612         writeU8(os, 1);
613         // name
614         os<<serializeString(m_init_name);
615         // state
616         if(m_registered){
617                 lua_State *L = m_env->getLua();
618                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
619                 os<<serializeLongString(state);
620         } else {
621                 os<<serializeLongString(m_init_state);
622         }
623         // hp
624         writeS16(os, m_hp);
625         // velocity
626         writeV3F1000(os, m_velocity);
627         // yaw
628         writeF1000(os, m_yaw);
629         return os.str();
630 }
631
632 int LuaEntitySAO::punch(v3f dir,
633                 const ToolCapabilities *toolcap,
634                 ServerActiveObject *puncher,
635                 float time_from_last_punch)
636 {
637         if(!m_registered){
638                 // Delete unknown LuaEntities when punched
639                 m_removed = true;
640                 return 0;
641         }
642
643         // It's best that attachments cannot be punched 
644         if(isAttached())
645                 return 0;
646         
647         ItemStack *punchitem = NULL;
648         ItemStack punchitem_static;
649         if(puncher){
650                 punchitem_static = puncher->getWieldedItem();
651                 punchitem = &punchitem_static;
652         }
653
654         PunchDamageResult result = getPunchDamage(
655                         m_armor_groups,
656                         toolcap,
657                         punchitem,
658                         time_from_last_punch);
659         
660         if(result.did_punch)
661         {
662                 setHP(getHP() - result.damage);
663                 
664                 actionstream<<getDescription()<<" punched by "
665                                 <<puncher->getDescription()<<", damage "<<result.damage
666                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
667                 
668                 {
669                         std::string str = gob_cmd_punched(result.damage, getHP());
670                         // create message and add to list
671                         ActiveObjectMessage aom(getId(), true, str);
672                         m_messages_out.push_back(aom);
673                 }
674
675                 if(getHP() == 0)
676                         m_removed = true;
677         }
678
679         lua_State *L = m_env->getLua();
680         scriptapi_luaentity_punch(L, m_id, puncher,
681                         time_from_last_punch, toolcap, dir);
682
683         return result.wear;
684 }
685
686 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
687 {
688         if(!m_registered)
689                 return;
690         // It's best that attachments cannot be clicked
691         if(isAttached())
692                 return;
693         lua_State *L = m_env->getLua();
694         scriptapi_luaentity_rightclick(L, m_id, clicker);
695 }
696
697 void LuaEntitySAO::setPos(v3f pos)
698 {
699         if(isAttached())
700                 return;
701         m_base_position = pos;
702         sendPosition(false, true);
703 }
704
705 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
706 {
707         if(isAttached())
708                 return;
709         m_base_position = pos;
710         if(!continuous)
711                 sendPosition(true, true);
712 }
713
714 float LuaEntitySAO::getMinimumSavedMovement()
715 {
716         return 0.1 * BS;
717 }
718
719 std::string LuaEntitySAO::getDescription()
720 {
721         std::ostringstream os(std::ios::binary);
722         os<<"LuaEntitySAO at (";
723         os<<(m_base_position.X/BS)<<",";
724         os<<(m_base_position.Y/BS)<<",";
725         os<<(m_base_position.Z/BS);
726         os<<")";
727         return os.str();
728 }
729
730 void LuaEntitySAO::setHP(s16 hp)
731 {
732         if(hp < 0) hp = 0;
733         m_hp = hp;
734 }
735
736 s16 LuaEntitySAO::getHP() const
737 {
738         return m_hp;
739 }
740
741 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
742 {
743         m_armor_groups = armor_groups;
744         m_armor_groups_sent = false;
745 }
746
747 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
748 {
749         m_animation_range = frame_range;
750         m_animation_speed = frame_speed;
751         m_animation_blend = frame_blend;
752         m_animation_sent = false;
753 }
754
755 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
756 {
757         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
758         m_bone_position_sent = false;
759 }
760
761 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
762 {
763         // Attachments need to be handled on both the server and client.
764         // If we just attach on the server, we can only copy the position of the parent. Attachments
765         // are still sent to clients at an interval so players might see them lagging, plus we can't
766         // read and attach to skeletal bones.
767         // If we just attach on the client, the server still sees the child at its original location.
768         // This breaks some things so we also give the server the most accurate representation
769         // even if players only see the client changes.
770
771         m_attachment_parent_id = parent_id;
772         m_attachment_bone = bone;
773         m_attachment_position = position;
774         m_attachment_rotation = rotation;
775         m_attachment_sent = false;
776 }
777
778 ObjectProperties* LuaEntitySAO::accessObjectProperties()
779 {
780         return &m_prop;
781 }
782
783 void LuaEntitySAO::notifyObjectPropertiesModified()
784 {
785         m_properties_sent = false;
786 }
787
788 void LuaEntitySAO::setVelocity(v3f velocity)
789 {
790         m_velocity = velocity;
791 }
792
793 v3f LuaEntitySAO::getVelocity()
794 {
795         return m_velocity;
796 }
797
798 void LuaEntitySAO::setAcceleration(v3f acceleration)
799 {
800         m_acceleration = acceleration;
801 }
802
803 v3f LuaEntitySAO::getAcceleration()
804 {
805         return m_acceleration;
806 }
807
808 void LuaEntitySAO::setYaw(float yaw)
809 {
810         m_yaw = yaw;
811 }
812
813 float LuaEntitySAO::getYaw()
814 {
815         return m_yaw;
816 }
817
818 void LuaEntitySAO::setTextureMod(const std::string &mod)
819 {
820         std::string str = gob_cmd_set_texture_mod(mod);
821         // create message and add to list
822         ActiveObjectMessage aom(getId(), true, str);
823         m_messages_out.push_back(aom);
824 }
825
826 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
827                 bool select_horiz_by_yawpitch)
828 {
829         std::string str = gob_cmd_set_sprite(
830                 p,
831                 num_frames,
832                 framelength,
833                 select_horiz_by_yawpitch
834         );
835         // create message and add to list
836         ActiveObjectMessage aom(getId(), true, str);
837         m_messages_out.push_back(aom);
838 }
839
840 std::string LuaEntitySAO::getName()
841 {
842         return m_init_name;
843 }
844
845 std::string LuaEntitySAO::getPropertyPacket()
846 {
847         return gob_cmd_set_properties(m_prop);
848 }
849
850 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
851 {
852         // If the object is attached client-side, don't waste bandwidth sending its position to clients
853         if(isAttached())
854                 return;
855         
856         m_last_sent_move_precision = m_base_position.getDistanceFrom(
857                         m_last_sent_position);
858         m_last_sent_position_timer = 0;
859         m_last_sent_yaw = m_yaw;
860         m_last_sent_position = m_base_position;
861         m_last_sent_velocity = m_velocity;
862         //m_last_sent_acceleration = m_acceleration;
863
864         float update_interval = m_env->getSendRecommendedInterval();
865
866         std::string str = gob_cmd_update_position(
867                 m_base_position,
868                 m_velocity,
869                 m_acceleration,
870                 m_yaw,
871                 do_interpolate,
872                 is_movement_end,
873                 update_interval
874         );
875         // create message and add to list
876         ActiveObjectMessage aom(getId(), false, str);
877         m_messages_out.push_back(aom);
878 }
879
880 /*
881         PlayerSAO
882 */
883
884 // No prototype, PlayerSAO does not need to be deserialized
885
886 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
887                 const std::set<std::string> &privs, bool is_singleplayer):
888         ServerActiveObject(env_, v3f(0,0,0)),
889         m_player(player_),
890         m_peer_id(peer_id_),
891         m_inventory(NULL),
892         m_last_good_position(0,0,0),
893         m_last_good_position_age(0),
894         m_time_from_last_punch(0),
895         m_nocheat_dig_pos(32767, 32767, 32767),
896         m_nocheat_dig_time(0),
897         m_wield_index(0),
898         m_position_not_sent(false),
899         m_armor_groups_sent(false),
900         m_properties_sent(true),
901         m_privs(privs),
902         m_is_singleplayer(is_singleplayer),
903         m_animation_sent(false),
904         m_bone_position_sent(false),
905         m_attachment_sent(false),
906         // public
907         m_moved(false),
908         m_inventory_not_sent(false),
909         m_hp_not_sent(false),
910         m_wielded_item_not_sent(false)
911 {
912         assert(m_player);
913         assert(m_peer_id != 0);
914         setBasePosition(m_player->getPosition());
915         m_inventory = &m_player->inventory;
916         m_armor_groups["choppy"] = 2;
917         m_armor_groups["fleshy"] = 3;
918
919         m_prop.hp_max = PLAYER_MAX_HP;
920         m_prop.physical = false;
921         m_prop.weight = 75;
922         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
923         // start of default appearance, this should be overwritten by LUA
924         m_prop.visual = "upright_sprite";
925         m_prop.visual_size = v2f(1, 2);
926         m_prop.textures.clear();
927         m_prop.textures.push_back("player.png");
928         m_prop.textures.push_back("player_back.png");
929         m_prop.colors.clear();
930         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
931         m_prop.spritediv = v2s16(1,1);
932         // end of default appearance
933         m_prop.is_visible = true;
934         m_prop.makes_footstep_sound = true;
935 }
936
937 PlayerSAO::~PlayerSAO()
938 {
939         if(m_inventory != &m_player->inventory)
940                 delete m_inventory;
941
942 }
943
944 std::string PlayerSAO::getDescription()
945 {
946         return std::string("player ") + m_player->getName();
947 }
948
949 // Called after id has been set and has been inserted in environment
950 void PlayerSAO::addedToEnvironment(u32 dtime_s)
951 {
952         ServerActiveObject::addedToEnvironment(dtime_s);
953         ServerActiveObject::setBasePosition(m_player->getPosition());
954         m_player->setPlayerSAO(this);
955         m_player->peer_id = m_peer_id;
956         m_last_good_position = m_player->getPosition();
957         m_last_good_position_age = 0.0;
958 }
959
960 // Called before removing from environment
961 void PlayerSAO::removingFromEnvironment()
962 {
963         ServerActiveObject::removingFromEnvironment();
964         if(m_player->getPlayerSAO() == this)
965         {
966                 m_player->setPlayerSAO(NULL);
967                 m_player->peer_id = 0;
968         }
969 }
970
971 bool PlayerSAO::isStaticAllowed() const
972 {
973         return false;
974 }
975
976 bool PlayerSAO::unlimitedTransferDistance() const
977 {
978         return g_settings->getBool("unlimited_player_transfer_distance");
979 }
980
981 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
982 {
983         std::ostringstream os(std::ios::binary);
984
985         if(protocol_version >= 15)
986         {
987                 writeU8(os, 1); // version
988                 os<<serializeString(m_player->getName()); // name
989                 writeU8(os, 1); // is_player
990                 writeS16(os, getId()); //id
991                 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
992                 writeF1000(os, m_player->getYaw());
993                 writeS16(os, getHP());
994
995                 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
996                 os<<serializeLongString(getPropertyPacket()); // message 1
997                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
998                 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
999                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1000                         os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1001                 }
1002                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1003         }
1004         else
1005         {
1006                 writeU8(os, 0); // version
1007                 os<<serializeString(m_player->getName()); // name
1008                 writeU8(os, 1); // is_player
1009                 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1010                 writeF1000(os, m_player->getYaw());
1011                 writeS16(os, getHP());
1012                 writeU8(os, 2); // number of messages stuffed in here
1013                 os<<serializeLongString(getPropertyPacket()); // message 1
1014                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1015         }
1016
1017         // return result
1018         return os.str();
1019 }
1020
1021 std::string PlayerSAO::getStaticData()
1022 {
1023         assert(0);
1024         return "";
1025 }
1026
1027 bool PlayerSAO::isAttached()
1028 {
1029         if(!m_attachment_parent_id)
1030                 return false;
1031         // Check if the parent still exists
1032         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1033         if(obj)
1034                 return true;
1035         return false;
1036 }
1037
1038 void PlayerSAO::step(float dtime, bool send_recommended)
1039 {
1040         if(!m_properties_sent)
1041         {
1042                 m_properties_sent = true;
1043                 std::string str = getPropertyPacket();
1044                 // create message and add to list
1045                 ActiveObjectMessage aom(getId(), true, str);
1046                 m_messages_out.push_back(aom);
1047         }
1048
1049         // If attached, check that our parent is still there. If it isn't, detach.
1050         if(m_attachment_parent_id && !isAttached())
1051         {
1052                 m_attachment_parent_id = 0;
1053                 m_attachment_bone = "";
1054                 m_attachment_position = v3f(0,0,0);
1055                 m_attachment_rotation = v3f(0,0,0);
1056                 m_player->setPosition(m_last_good_position);
1057                 m_moved = true;
1058         }
1059
1060         m_time_from_last_punch += dtime;
1061         m_nocheat_dig_time += dtime;
1062
1063         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1064         // If the object gets detached this comes into effect automatically from the last known origin
1065         if(isAttached())
1066         {
1067                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1068                 m_last_good_position = pos;
1069                 m_last_good_position_age = 0;
1070                 m_player->setPosition(pos);
1071         }
1072         else
1073         {
1074                 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1075                 {
1076                         m_last_good_position = m_player->getPosition();
1077                         m_last_good_position_age = 0;
1078                 }
1079                 else
1080                 {
1081                         /*
1082                                 Check player movements
1083
1084                                 NOTE: Actually the server should handle player physics like the
1085                                 client does and compare player's position to what is calculated
1086                                 on our side. This is required when eg. players fly due to an
1087                                 explosion. Altough a node-based alternative might be possible
1088                                 too, and much more lightweight.
1089                         */
1090
1091                         float player_max_speed = 0;
1092                         float player_max_speed_up = 0;
1093                         if(m_privs.count("fast") != 0){
1094                                 // Fast speed
1095                                 player_max_speed = BS * 20;
1096                                 player_max_speed_up = BS * 20;
1097                         } else {
1098                                 // Normal speed
1099                                 player_max_speed = BS * 4.0;
1100                                 player_max_speed_up = BS * 4.0;
1101                         }
1102                         // Tolerance
1103                         player_max_speed *= 2.5;
1104                         player_max_speed_up *= 2.5;
1105
1106                         m_last_good_position_age += dtime;
1107                         if(m_last_good_position_age >= 1.0){
1108                                 float age = m_last_good_position_age;
1109                                 v3f diff = (m_player->getPosition() - m_last_good_position);
1110                                 float d_vert = diff.Y;
1111                                 diff.Y = 0;
1112                                 float d_horiz = diff.getLength();
1113                                 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1114                                                 <<(d_horiz/age)<<std::endl;*/
1115                                 if(d_horiz <= age * player_max_speed &&
1116                                                 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1117                                         m_last_good_position = m_player->getPosition();
1118                                 } else {
1119                                         actionstream<<"Player "<<m_player->getName()
1120                                                         <<" moved too fast; resetting position"
1121                                                         <<std::endl;
1122                                         m_player->setPosition(m_last_good_position);
1123                                         m_moved = true;
1124                                 }
1125                                 m_last_good_position_age = 0;
1126                         }
1127                 }
1128         }
1129
1130         if(send_recommended == false)
1131                 return;
1132
1133         // If the object is attached client-side, don't waste bandwidth sending its position to clients
1134         if(m_position_not_sent && !isAttached())
1135         {
1136                 m_position_not_sent = false;
1137                 float update_interval = m_env->getSendRecommendedInterval();
1138                 v3f pos;
1139                 if(isAttached()) // Just in case we ever do send attachment position too
1140                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1141                 else
1142                         pos = m_player->getPosition() + v3f(0,BS*1,0);
1143                 std::string str = gob_cmd_update_position(
1144                         pos,
1145                         v3f(0,0,0),
1146                         v3f(0,0,0),
1147                         m_player->getYaw(),
1148                         true,
1149                         false,
1150                         update_interval
1151                 );
1152                 // create message and add to list
1153                 ActiveObjectMessage aom(getId(), false, str);
1154                 m_messages_out.push_back(aom);
1155         }
1156
1157         if(m_wielded_item_not_sent)
1158         {
1159                 m_wielded_item_not_sent = false;
1160                 // GenericCAO has no special way to show this
1161         }
1162
1163         if(m_armor_groups_sent == false){
1164                 m_armor_groups_sent = true;
1165                 std::string str = gob_cmd_update_armor_groups(
1166                                 m_armor_groups);
1167                 // create message and add to list
1168                 ActiveObjectMessage aom(getId(), true, str);
1169                 m_messages_out.push_back(aom);
1170         }
1171
1172         if(m_animation_sent == false){
1173                 m_animation_sent = true;
1174                 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1175                 // create message and add to list
1176                 ActiveObjectMessage aom(getId(), true, str);
1177                 m_messages_out.push_back(aom);
1178         }
1179
1180         if(m_bone_position_sent == false){
1181                 m_bone_position_sent = true;
1182                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1183                         std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1184                         // create message and add to list
1185                         ActiveObjectMessage aom(getId(), true, str);
1186                         m_messages_out.push_back(aom);
1187                 }
1188         }
1189
1190         if(m_attachment_sent == false){
1191                 m_attachment_sent = true;
1192                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1193                 // create message and add to list
1194                 ActiveObjectMessage aom(getId(), true, str);
1195                 m_messages_out.push_back(aom);
1196         }
1197 }
1198
1199 void PlayerSAO::setBasePosition(const v3f &position)
1200 {
1201         // This needs to be ran for attachments too
1202         ServerActiveObject::setBasePosition(position);
1203         m_position_not_sent = true;
1204 }
1205
1206 void PlayerSAO::setPos(v3f pos)
1207 {
1208         if(isAttached())
1209                 return;
1210         m_player->setPosition(pos);
1211         // Movement caused by this command is always valid
1212         m_last_good_position = pos;
1213         m_last_good_position_age = 0;
1214         // Force position change on client
1215         m_moved = true;
1216 }
1217
1218 void PlayerSAO::moveTo(v3f pos, bool continuous)
1219 {
1220         if(isAttached())
1221                 return;
1222         m_player->setPosition(pos);
1223         // Movement caused by this command is always valid
1224         m_last_good_position = pos;
1225         m_last_good_position_age = 0;
1226         // Force position change on client
1227         m_moved = true;
1228 }
1229
1230 int PlayerSAO::punch(v3f dir,
1231         const ToolCapabilities *toolcap,
1232         ServerActiveObject *puncher,
1233         float time_from_last_punch)
1234 {
1235         // It's best that attachments cannot be punched 
1236         if(isAttached())
1237                 return 0;
1238
1239         if(!toolcap)
1240                 return 0;
1241
1242         // No effect if PvP disabled
1243         if(g_settings->getBool("enable_pvp") == false){
1244                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1245                         std::string str = gob_cmd_punched(0, getHP());
1246                         // create message and add to list
1247                         ActiveObjectMessage aom(getId(), true, str);
1248                         m_messages_out.push_back(aom);
1249                         return 0;
1250                 }
1251         }
1252
1253         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1254                         time_from_last_punch);
1255
1256         actionstream<<"Player "<<m_player->getName()<<" punched by "
1257                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1258                         <<" HP"<<std::endl;
1259
1260         setHP(getHP() - hitparams.hp);
1261
1262         if(hitparams.hp != 0)
1263         {
1264                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1265                 // create message and add to list
1266                 ActiveObjectMessage aom(getId(), true, str);
1267                 m_messages_out.push_back(aom);
1268         }
1269
1270         return hitparams.wear;
1271 }
1272
1273 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1274 {
1275 }
1276
1277 s16 PlayerSAO::getHP() const
1278 {
1279         return m_player->hp;
1280 }
1281
1282 void PlayerSAO::setHP(s16 hp)
1283 {
1284         s16 oldhp = m_player->hp;
1285
1286         if(hp < 0)
1287                 hp = 0;
1288         else if(hp > PLAYER_MAX_HP)
1289                 hp = PLAYER_MAX_HP;
1290
1291         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1292         {
1293                 m_hp_not_sent = true; // fix wrong prediction on client
1294                 return;
1295         }
1296
1297         m_player->hp = hp;
1298
1299         if(hp != oldhp)
1300                 m_hp_not_sent = true;
1301
1302         // On death or reincarnation send an active object message
1303         if((hp == 0) != (oldhp == 0))
1304         {
1305                 // Will send new is_visible value based on (getHP()!=0)
1306                 m_properties_sent = false;
1307                 // Send new HP
1308                 std::string str = gob_cmd_punched(0, getHP());
1309                 ActiveObjectMessage aom(getId(), true, str);
1310                 m_messages_out.push_back(aom);
1311         }
1312 }
1313
1314 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1315 {
1316         m_armor_groups = armor_groups;
1317         m_armor_groups_sent = false;
1318 }
1319
1320 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1321 {
1322         // store these so they can be updated to clients
1323         m_animation_range = frame_range;
1324         m_animation_speed = frame_speed;
1325         m_animation_blend = frame_blend;
1326         m_animation_sent = false;
1327 }
1328
1329 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1330 {
1331         // store these so they can be updated to clients
1332         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1333         m_bone_position_sent = false;
1334 }
1335
1336 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1337 {
1338         // Attachments need to be handled on both the server and client.
1339         // If we just attach on the server, we can only copy the position of the parent. Attachments
1340         // are still sent to clients at an interval so players might see them lagging, plus we can't
1341         // read and attach to skeletal bones.
1342         // If we just attach on the client, the server still sees the child at its original location.
1343         // This breaks some things so we also give the server the most accurate representation
1344         // even if players only see the client changes.
1345
1346         m_attachment_parent_id = parent_id;
1347         m_attachment_bone = bone;
1348         m_attachment_position = position;
1349         m_attachment_rotation = rotation;
1350         m_attachment_sent = false;
1351 }
1352
1353 ObjectProperties* PlayerSAO::accessObjectProperties()
1354 {
1355         return &m_prop;
1356 }
1357
1358 void PlayerSAO::notifyObjectPropertiesModified()
1359 {
1360         m_properties_sent = false;
1361 }
1362
1363 Inventory* PlayerSAO::getInventory()
1364 {
1365         return m_inventory;
1366 }
1367 const Inventory* PlayerSAO::getInventory() const
1368 {
1369         return m_inventory;
1370 }
1371
1372 InventoryLocation PlayerSAO::getInventoryLocation() const
1373 {
1374         InventoryLocation loc;
1375         loc.setPlayer(m_player->getName());
1376         return loc;
1377 }
1378
1379 void PlayerSAO::setInventoryModified()
1380 {
1381         m_inventory_not_sent = true;
1382 }
1383
1384 std::string PlayerSAO::getWieldList() const
1385 {
1386         return "main";
1387 }
1388
1389 int PlayerSAO::getWieldIndex() const
1390 {
1391         return m_wield_index;
1392 }
1393
1394 void PlayerSAO::setWieldIndex(int i)
1395 {
1396         if(i != m_wield_index)
1397         {
1398                 m_wield_index = i;
1399                 m_wielded_item_not_sent = true;
1400         }
1401 }
1402
1403 void PlayerSAO::disconnected()
1404 {
1405         m_peer_id = 0;
1406         m_removed = true;
1407         if(m_player->getPlayerSAO() == this)
1408         {
1409                 m_player->setPlayerSAO(NULL);
1410                 m_player->peer_id = 0;
1411         }
1412 }
1413
1414 std::string PlayerSAO::getPropertyPacket()
1415 {
1416         m_prop.is_visible = (true);
1417         return gob_cmd_set_properties(m_prop);
1418 }
1419