3289f9077f5ba8ac8641d6646635490b5aa3f81e
[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                         // TODO: We shouldn't be sending this when the object is attached, but we can't check m_parent here
222                         setBasePosition(pos_f);
223                         m_last_sent_position = pos_f;
224
225                         std::ostringstream os(std::ios::binary);
226                         // command (0 = update position)
227                         writeU8(os, 0);
228                         // pos
229                         writeV3F1000(os, m_base_position);
230                         // create message and add to list
231                         ActiveObjectMessage aom(getId(), false, os.str());
232                         m_messages_out.push_back(aom);
233                 }
234                 if(m_itemstring_changed)
235                 {
236                         m_itemstring_changed = false;
237
238                         std::ostringstream os(std::ios::binary);
239                         // command (1 = update itemstring)
240                         writeU8(os, 1);
241                         // itemstring
242                         os<<serializeString(m_itemstring);
243                         // create message and add to list
244                         ActiveObjectMessage aom(getId(), false, os.str());
245                         m_messages_out.push_back(aom);
246                 }
247         }
248
249         std::string getClientInitializationData()
250         {
251                 std::ostringstream os(std::ios::binary);
252                 // version
253                 writeU8(os, 0);
254                 // pos
255                 writeV3F1000(os, m_base_position);
256                 // itemstring
257                 os<<serializeString(m_itemstring);
258                 return os.str();
259         }
260
261         std::string getStaticData()
262         {
263                 infostream<<__FUNCTION_NAME<<std::endl;
264                 std::ostringstream os(std::ios::binary);
265                 // version
266                 writeU8(os, 0);
267                 // itemstring
268                 os<<serializeString(m_itemstring);
269                 return os.str();
270         }
271
272         ItemStack createItemStack()
273         {
274                 try{
275                         IItemDefManager *idef = m_env->getGameDef()->idef();
276                         ItemStack item;
277                         item.deSerialize(m_itemstring, idef);
278                         infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
279                                         <<"\" -> item=\""<<item.getItemString()<<"\""
280                                         <<std::endl;
281                         return item;
282                 }
283                 catch(SerializationError &e)
284                 {
285                         infostream<<__FUNCTION_NAME<<": serialization error: "
286                                         <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
287                         return ItemStack();
288                 }
289         }
290
291         int punch(v3f dir,
292                         const ToolCapabilities *toolcap,
293                         ServerActiveObject *puncher,
294                         float time_from_last_punch)
295         {
296                 // Take item into inventory
297                 ItemStack item = createItemStack();
298                 Inventory *inv = puncher->getInventory();
299                 if(inv != NULL)
300                 {
301                         std::string wieldlist = puncher->getWieldList();
302                         ItemStack leftover = inv->addItem(wieldlist, item);
303                         puncher->setInventoryModified();
304                         if(leftover.empty())
305                         {
306                                 m_removed = true;
307                         }
308                         else
309                         {
310                                 m_itemstring = leftover.getItemString();
311                                 m_itemstring_changed = true;
312                         }
313                 }
314                 
315                 return 0;
316         }
317
318
319 private:
320         std::string m_itemstring;
321         bool m_itemstring_changed;
322         v3f m_speed_f;
323         v3f m_last_sent_position;
324         IntervalLimiter m_move_interval;
325 };
326
327 // Prototype (registers item for deserialization)
328 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
329
330 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
331                 const std::string itemstring)
332 {
333         return new ItemSAO(env, pos, itemstring);
334 }
335
336 /*
337         LuaEntitySAO
338 */
339
340 // Prototype (registers item for deserialization)
341 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
342
343 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
344                 const std::string &name, const std::string &state):
345         ServerActiveObject(env, pos),
346         m_init_name(name),
347         m_init_state(state),
348         m_registered(false),
349         m_hp(-1),
350         m_velocity(0,0,0),
351         m_acceleration(0,0,0),
352         m_yaw(0),
353         m_properties_sent(true),
354         m_last_sent_yaw(0),
355         m_last_sent_position(0,0,0),
356         m_last_sent_velocity(0,0,0),
357         m_last_sent_position_timer(0),
358         m_last_sent_move_precision(0),
359         m_armor_groups_sent(false)
360 {
361         // Only register type if no environment supplied
362         if(env == NULL){
363                 ServerActiveObject::registerType(getType(), create);
364                 return;
365         }
366         
367         // Initialize something to armor groups
368         m_armor_groups["fleshy"] = 3;
369         m_armor_groups["snappy"] = 2;
370 }
371
372 LuaEntitySAO::~LuaEntitySAO()
373 {
374         if(m_registered){
375                 lua_State *L = m_env->getLua();
376                 scriptapi_luaentity_rm(L, m_id);
377         }
378 }
379
380 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
381 {
382         ServerActiveObject::addedToEnvironment(dtime_s);
383         
384         // Create entity from name
385         lua_State *L = m_env->getLua();
386         m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
387         m_parent = NULL;
388         
389         if(m_registered){
390                 // Get properties
391                 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
392                 // Initialize HP from properties
393                 m_hp = m_prop.hp_max;
394                 // Activate entity, supplying serialized state
395                 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
396         }
397 }
398
399 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
400                 const std::string &data)
401 {
402         std::string name;
403         std::string state;
404         s16 hp = 1;
405         v3f velocity;
406         float yaw = 0;
407         if(data != ""){
408                 std::istringstream is(data, std::ios::binary);
409                 // read version
410                 u8 version = readU8(is);
411                 // check if version is supported
412                 if(version == 0){
413                         name = deSerializeString(is);
414                         state = deSerializeLongString(is);
415                 }
416                 else if(version == 1){
417                         name = deSerializeString(is);
418                         state = deSerializeLongString(is);
419                         hp = readS16(is);
420                         velocity = readV3F1000(is);
421                         yaw = readF1000(is);
422                 }
423         }
424         // create object
425         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
426                         <<state<<"\")"<<std::endl;
427         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
428         sao->m_hp = hp;
429         sao->m_velocity = velocity;
430         sao->m_yaw = yaw;
431         return sao;
432 }
433
434 void LuaEntitySAO::step(float dtime, bool send_recommended)
435 {
436         if(!m_properties_sent)
437         {
438                 m_properties_sent = true;
439                 std::string str = getPropertyPacket();
440                 // create message and add to list
441                 ActiveObjectMessage aom(getId(), true, str);
442                 m_messages_out.push_back(aom);
443         }
444
445         m_last_sent_position_timer += dtime;
446         
447         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
448         // If the object gets detached this comes into effect automatically from the last known origin
449         if(m_parent != NULL)
450         {
451                 v3f pos = m_parent->getBasePosition();
452                 m_base_position = pos;
453                 m_velocity = v3f(0,0,0);
454                 m_acceleration = v3f(0,0,0);
455         }
456         else
457         {
458                 if(m_prop.physical){
459                         core::aabbox3d<f32> box = m_prop.collisionbox;
460                         box.MinEdge *= BS;
461                         box.MaxEdge *= BS;
462                         collisionMoveResult moveresult;
463                         f32 pos_max_d = BS*0.25; // Distance per iteration
464                         f32 stepheight = 0; // Maximum climbable step height
465                         v3f p_pos = m_base_position;
466                         v3f p_velocity = m_velocity;
467                         v3f p_acceleration = m_acceleration;
468                         IGameDef *gamedef = m_env->getGameDef();
469                         moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
470                                         pos_max_d, box, stepheight, dtime,
471                                         p_pos, p_velocity, p_acceleration);
472                         // Apply results
473                         m_base_position = p_pos;
474                         m_velocity = p_velocity;
475                         m_acceleration = p_acceleration;
476                 } else {
477                         m_base_position += dtime * m_velocity + 0.5 * dtime
478                                         * dtime * m_acceleration;
479                         m_velocity += dtime * m_acceleration;
480                 }
481         }
482
483         if(m_registered){
484                 lua_State *L = m_env->getLua();
485                 scriptapi_luaentity_step(L, m_id, dtime);
486         }
487
488         if(send_recommended == false)
489                 return;
490
491         if(m_parent != NULL)
492         {
493                 // TODO: force send when acceleration changes enough?
494                 float minchange = 0.2*BS;
495                 if(m_last_sent_position_timer > 1.0){
496                         minchange = 0.01*BS;
497                 } else if(m_last_sent_position_timer > 0.2){
498                         minchange = 0.05*BS;
499                 }
500                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
501                 move_d += m_last_sent_move_precision;
502                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
503                 if(move_d > minchange || vel_d > minchange ||
504                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
505                         sendPosition(true, false);
506                 }
507         }
508
509         if(m_armor_groups_sent == false){
510                 m_armor_groups_sent = true;
511                 std::string str = gob_cmd_update_armor_groups(
512                                 m_armor_groups);
513                 // create message and add to list
514                 ActiveObjectMessage aom(getId(), true, str);
515                 m_messages_out.push_back(aom);
516         }
517 }
518
519 std::string LuaEntitySAO::getClientInitializationData()
520 {
521         std::ostringstream os(std::ios::binary);
522         writeU8(os, 0); // version
523         os<<serializeString(""); // name
524         writeS16(os, getId()); //id
525         writeU8(os, 0); // is_player
526         writeV3F1000(os, m_base_position);
527         writeF1000(os, m_yaw);
528         writeS16(os, m_hp);
529         writeU8(os, 2); // number of messages stuffed in here
530         os<<serializeLongString(getPropertyPacket()); // message 1
531         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
532         // return result
533         return os.str();
534 }
535
536 std::string LuaEntitySAO::getStaticData()
537 {
538         verbosestream<<__FUNCTION_NAME<<std::endl;
539         std::ostringstream os(std::ios::binary);
540         // version
541         writeU8(os, 1);
542         // name
543         os<<serializeString(m_init_name);
544         // state
545         if(m_registered){
546                 lua_State *L = m_env->getLua();
547                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
548                 os<<serializeLongString(state);
549         } else {
550                 os<<serializeLongString(m_init_state);
551         }
552         // hp
553         writeS16(os, m_hp);
554         // velocity
555         writeV3F1000(os, m_velocity);
556         // yaw
557         writeF1000(os, m_yaw);
558         return os.str();
559 }
560
561 int LuaEntitySAO::punch(v3f dir,
562                 const ToolCapabilities *toolcap,
563                 ServerActiveObject *puncher,
564                 float time_from_last_punch)
565 {
566         if(!m_registered){
567                 // Delete unknown LuaEntities when punched
568                 m_removed = true;
569                 return 0;
570         }
571
572         // It's best that attachments cannot be punched 
573         if(m_parent != NULL)
574                 return 0;
575         
576         ItemStack *punchitem = NULL;
577         ItemStack punchitem_static;
578         if(puncher){
579                 punchitem_static = puncher->getWieldedItem();
580                 punchitem = &punchitem_static;
581         }
582
583         PunchDamageResult result = getPunchDamage(
584                         m_armor_groups,
585                         toolcap,
586                         punchitem,
587                         time_from_last_punch);
588         
589         if(result.did_punch)
590         {
591                 setHP(getHP() - result.damage);
592                 
593                 actionstream<<getDescription()<<" punched by "
594                                 <<puncher->getDescription()<<", damage "<<result.damage
595                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
596                 
597                 {
598                         std::string str = gob_cmd_punched(result.damage, getHP());
599                         // create message and add to list
600                         ActiveObjectMessage aom(getId(), true, str);
601                         m_messages_out.push_back(aom);
602                 }
603
604                 if(getHP() == 0)
605                         m_removed = true;
606         }
607
608         lua_State *L = m_env->getLua();
609         scriptapi_luaentity_punch(L, m_id, puncher,
610                         time_from_last_punch, toolcap, dir);
611
612         return result.wear;
613 }
614
615 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
616 {
617         if(!m_registered)
618                 return;
619         lua_State *L = m_env->getLua();
620         scriptapi_luaentity_rightclick(L, m_id, clicker);
621 }
622
623 void LuaEntitySAO::setPos(v3f pos)
624 {
625         if(m_parent != NULL)
626                 return;
627         m_base_position = pos;
628         sendPosition(false, true);
629 }
630
631 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
632 {
633         if(m_parent != NULL)
634                 return;
635         m_base_position = pos;
636         if(!continuous)
637                 sendPosition(true, true);
638 }
639
640 float LuaEntitySAO::getMinimumSavedMovement()
641 {
642         return 0.1 * BS;
643 }
644
645 std::string LuaEntitySAO::getDescription()
646 {
647         std::ostringstream os(std::ios::binary);
648         os<<"LuaEntitySAO at (";
649         os<<(m_base_position.X/BS)<<",";
650         os<<(m_base_position.Y/BS)<<",";
651         os<<(m_base_position.Z/BS);
652         os<<")";
653         return os.str();
654 }
655
656 void LuaEntitySAO::setHP(s16 hp)
657 {
658         if(hp < 0) hp = 0;
659         m_hp = hp;
660 }
661
662 s16 LuaEntitySAO::getHP() const
663 {
664         return m_hp;
665 }
666
667 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
668 {
669         m_armor_groups = armor_groups;
670         m_armor_groups_sent = false;
671 }
672
673 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
674 {
675         std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
676         // create message and add to list
677         ActiveObjectMessage aom(getId(), true, str);
678         m_messages_out.push_back(aom);
679 }
680
681 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
682 {
683         std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
684         // create message and add to list
685         ActiveObjectMessage aom(getId(), true, str);
686         m_messages_out.push_back(aom);
687 }
688
689 void LuaEntitySAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
690 {
691         // Attachments need to be handled on both the server and client.
692         // If we just attach on the server, we can only copy the position of the parent. Attachments
693         // are still sent to clients at an interval so players might see them lagging, plus we can't
694         // read and attach to skeletal bones.
695         // If we just attach on the client, the server still sees the child at its original location.
696         // This breaks some things so we also give the server the most accurate representation
697         // even if players only see the client changes.
698
699         // Server attachment:
700         m_parent = parent;
701
702         // Client attachment:
703         std::string str = gob_cmd_set_attachment(parent->getId(), bone, position, rotation);
704         // create message and add to list
705         ActiveObjectMessage aom(getId(), true, str);
706         m_messages_out.push_back(aom);
707 }
708
709 ObjectProperties* LuaEntitySAO::accessObjectProperties()
710 {
711         return &m_prop;
712 }
713
714 void LuaEntitySAO::notifyObjectPropertiesModified()
715 {
716         m_properties_sent = false;
717 }
718
719 void LuaEntitySAO::setVelocity(v3f velocity)
720 {
721         m_velocity = velocity;
722 }
723
724 v3f LuaEntitySAO::getVelocity()
725 {
726         return m_velocity;
727 }
728
729 void LuaEntitySAO::setAcceleration(v3f acceleration)
730 {
731         m_acceleration = acceleration;
732 }
733
734 v3f LuaEntitySAO::getAcceleration()
735 {
736         return m_acceleration;
737 }
738
739 void LuaEntitySAO::setYaw(float yaw)
740 {
741         m_yaw = yaw;
742 }
743
744 float LuaEntitySAO::getYaw()
745 {
746         return m_yaw;
747 }
748
749 void LuaEntitySAO::setTextureMod(const std::string &mod)
750 {
751         std::string str = gob_cmd_set_texture_mod(mod);
752         // create message and add to list
753         ActiveObjectMessage aom(getId(), true, str);
754         m_messages_out.push_back(aom);
755 }
756
757 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
758                 bool select_horiz_by_yawpitch)
759 {
760         std::string str = gob_cmd_set_sprite(
761                 p,
762                 num_frames,
763                 framelength,
764                 select_horiz_by_yawpitch
765         );
766         // create message and add to list
767         ActiveObjectMessage aom(getId(), true, str);
768         m_messages_out.push_back(aom);
769 }
770
771 std::string LuaEntitySAO::getName()
772 {
773         return m_init_name;
774 }
775
776 std::string LuaEntitySAO::getPropertyPacket()
777 {
778         return gob_cmd_set_properties(m_prop);
779 }
780
781 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
782 {
783         // If the object is attached client-side, don't waste bandwidth sending its position to clients
784         if(m_parent != NULL)
785                 return;
786         
787         m_last_sent_move_precision = m_base_position.getDistanceFrom(
788                         m_last_sent_position);
789         m_last_sent_position_timer = 0;
790         m_last_sent_yaw = m_yaw;
791         m_last_sent_position = m_base_position;
792         m_last_sent_velocity = m_velocity;
793         //m_last_sent_acceleration = m_acceleration;
794
795         float update_interval = m_env->getSendRecommendedInterval();
796
797         std::string str = gob_cmd_update_position(
798                 m_base_position,
799                 m_velocity,
800                 m_acceleration,
801                 m_yaw,
802                 do_interpolate,
803                 is_movement_end,
804                 update_interval
805         );
806         // create message and add to list
807         ActiveObjectMessage aom(getId(), false, str);
808         m_messages_out.push_back(aom);
809 }
810
811 /*
812         PlayerSAO
813 */
814
815 // No prototype, PlayerSAO does not need to be deserialized
816
817 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
818                 const std::set<std::string> &privs, bool is_singleplayer):
819         ServerActiveObject(env_, v3f(0,0,0)),
820         m_player(player_),
821         m_peer_id(peer_id_),
822         m_inventory(NULL),
823         m_last_good_position(0,0,0),
824         m_last_good_position_age(0),
825         m_time_from_last_punch(0),
826         m_nocheat_dig_pos(32767, 32767, 32767),
827         m_nocheat_dig_time(0),
828         m_wield_index(0),
829         m_position_not_sent(false),
830         m_armor_groups_sent(false),
831         m_properties_sent(true),
832         m_privs(privs),
833         m_is_singleplayer(is_singleplayer),
834         // public
835         m_teleported(false),
836         m_inventory_not_sent(false),
837         m_hp_not_sent(false),
838         m_wielded_item_not_sent(false)
839 {
840         assert(m_player);
841         assert(m_peer_id != 0);
842         setBasePosition(m_player->getPosition());
843         m_inventory = &m_player->inventory;
844         m_armor_groups["choppy"] = 2;
845         m_armor_groups["fleshy"] = 3;
846
847         m_prop.hp_max = PLAYER_MAX_HP;
848         m_prop.physical = false;
849         m_prop.weight = 75;
850         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
851         // start of default appearance, this should be overwritten by LUA
852         m_prop.visual = "upright_sprite";
853         m_prop.visual_size = v2f(1, 2);
854         m_prop.textures.clear();
855         m_prop.textures.push_back("player.png");
856         m_prop.textures.push_back("player_back.png");
857         m_prop.colors.clear();
858         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
859         m_prop.spritediv = v2s16(1,1);
860         // end of default appearance
861         m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
862         m_prop.makes_footstep_sound = true;
863 }
864
865 PlayerSAO::~PlayerSAO()
866 {
867         if(m_inventory != &m_player->inventory)
868                 delete m_inventory;
869
870 }
871
872 std::string PlayerSAO::getDescription()
873 {
874         return std::string("player ") + m_player->getName();
875 }
876
877 // Called after id has been set and has been inserted in environment
878 void PlayerSAO::addedToEnvironment(u32 dtime_s)
879 {
880         ServerActiveObject::addedToEnvironment(dtime_s);
881         ServerActiveObject::setBasePosition(m_player->getPosition());
882         m_parent = NULL;
883         m_player->setPlayerSAO(this);
884         m_player->peer_id = m_peer_id;
885         m_last_good_position = m_player->getPosition();
886         m_last_good_position_age = 0.0;
887 }
888
889 // Called before removing from environment
890 void PlayerSAO::removingFromEnvironment()
891 {
892         ServerActiveObject::removingFromEnvironment();
893         if(m_player->getPlayerSAO() == this)
894         {
895                 m_player->setPlayerSAO(NULL);
896                 m_player->peer_id = 0;
897         }
898 }
899
900 bool PlayerSAO::isStaticAllowed() const
901 {
902         return false;
903 }
904
905 bool PlayerSAO::unlimitedTransferDistance() const
906 {
907         return g_settings->getBool("unlimited_player_transfer_distance");
908 }
909
910 std::string PlayerSAO::getClientInitializationData()
911 {
912         std::ostringstream os(std::ios::binary);
913         writeU8(os, 0); // version
914         os<<serializeString(m_player->getName()); // name
915         writeU8(os, 1); // is_player
916         writeS16(os, getId()); //id
917         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
918         writeF1000(os, m_player->getYaw());
919         writeS16(os, getHP());
920         writeU8(os, 2); // number of messages stuffed in here
921         os<<serializeLongString(getPropertyPacket()); // message 1
922         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
923         return os.str();
924 }
925
926 std::string PlayerSAO::getStaticData()
927 {
928         assert(0);
929         return "";
930 }
931
932 void PlayerSAO::step(float dtime, bool send_recommended)
933 {
934         if(!m_properties_sent)
935         {
936                 m_properties_sent = true;
937                 std::string str = getPropertyPacket();
938                 // create message and add to list
939                 ActiveObjectMessage aom(getId(), true, str);
940                 m_messages_out.push_back(aom);
941         }
942
943         m_time_from_last_punch += dtime;
944         m_nocheat_dig_time += dtime;
945
946         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
947         // If the object gets detached this comes into effect automatically from the last known origin
948         if(m_parent != NULL)
949         {
950                 v3f pos = m_parent->getBasePosition();
951                 m_last_good_position = pos;
952                 m_last_good_position_age = 0;
953                 m_player->setPosition(pos);
954         }
955         else
956         {
957                 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
958                 {
959                         m_last_good_position = m_player->getPosition();
960                         m_last_good_position_age = 0;
961                 }
962                 else
963                 {
964                         /*
965                                 Check player movements
966
967                                 NOTE: Actually the server should handle player physics like the
968                                 client does and compare player's position to what is calculated
969                                 on our side. This is required when eg. players fly due to an
970                                 explosion. Altough a node-based alternative might be possible
971                                 too, and much more lightweight.
972                         */
973
974                         float player_max_speed = 0;
975                         float player_max_speed_up = 0;
976                         if(m_privs.count("fast") != 0){
977                                 // Fast speed
978                                 player_max_speed = BS * 20;
979                                 player_max_speed_up = BS * 20;
980                         } else {
981                                 // Normal speed
982                                 player_max_speed = BS * 4.0;
983                                 player_max_speed_up = BS * 4.0;
984                         }
985                         // Tolerance
986                         player_max_speed *= 2.5;
987                         player_max_speed_up *= 2.5;
988
989                         m_last_good_position_age += dtime;
990                         if(m_last_good_position_age >= 1.0){
991                                 float age = m_last_good_position_age;
992                                 v3f diff = (m_player->getPosition() - m_last_good_position);
993                                 float d_vert = diff.Y;
994                                 diff.Y = 0;
995                                 float d_horiz = diff.getLength();
996                                 /*infostream<<m_player->getName()<<"'s horizontal speed is "
997                                                 <<(d_horiz/age)<<std::endl;*/
998                                 if(d_horiz <= age * player_max_speed &&
999                                                 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1000                                         m_last_good_position = m_player->getPosition();
1001                                 } else {
1002                                         actionstream<<"Player "<<m_player->getName()
1003                                                         <<" moved too fast; resetting position"
1004                                                         <<std::endl;
1005                                         m_player->setPosition(m_last_good_position);
1006                                         m_teleported = true;
1007                                 }
1008                                 m_last_good_position_age = 0;
1009                         }
1010                 }
1011         }
1012
1013         if(send_recommended == false)
1014                 return;
1015
1016         // If the object is attached client-side, don't waste bandwidth sending its position to clients
1017         if(m_position_not_sent && m_parent == NULL)
1018         {
1019                 m_position_not_sent = false;
1020                 float update_interval = m_env->getSendRecommendedInterval();
1021                 v3f pos;
1022                 if(m_parent != NULL) // Just in case we ever do send attachment position too
1023                         pos = m_parent->getBasePosition();
1024                 else
1025                         pos = m_player->getPosition() + v3f(0,BS*1,0);
1026                 std::string str = gob_cmd_update_position(
1027                         pos,
1028                         v3f(0,0,0),
1029                         v3f(0,0,0),
1030                         m_player->getYaw(),
1031                         true,
1032                         false,
1033                         update_interval
1034                 );
1035                 // create message and add to list
1036                 ActiveObjectMessage aom(getId(), false, str);
1037                 m_messages_out.push_back(aom);
1038         }
1039
1040         if(m_wielded_item_not_sent)
1041         {
1042                 m_wielded_item_not_sent = false;
1043                 // GenericCAO has no special way to show this
1044         }
1045
1046         if(m_armor_groups_sent == false){
1047                 m_armor_groups_sent = true;
1048                 std::string str = gob_cmd_update_armor_groups(
1049                                 m_armor_groups);
1050                 // create message and add to list
1051                 ActiveObjectMessage aom(getId(), true, str);
1052                 m_messages_out.push_back(aom);
1053         }
1054 }
1055
1056 void PlayerSAO::setBasePosition(const v3f &position)
1057 {
1058         // This needs to be ran for attachments too
1059         ServerActiveObject::setBasePosition(position);
1060         m_position_not_sent = true;
1061 }
1062
1063 void PlayerSAO::setPos(v3f pos)
1064 {
1065         if(m_parent != NULL)
1066                 return;
1067         m_player->setPosition(pos);
1068         // Movement caused by this command is always valid
1069         m_last_good_position = pos;
1070         m_last_good_position_age = 0;
1071         // Force position change on client
1072         m_teleported = true;
1073 }
1074
1075 void PlayerSAO::moveTo(v3f pos, bool continuous)
1076 {
1077         if(m_parent != NULL)
1078                 return;
1079         m_player->setPosition(pos);
1080         // Movement caused by this command is always valid
1081         m_last_good_position = pos;
1082         m_last_good_position_age = 0;
1083         // Force position change on client
1084         m_teleported = true;
1085 }
1086
1087 int PlayerSAO::punch(v3f dir,
1088         const ToolCapabilities *toolcap,
1089         ServerActiveObject *puncher,
1090         float time_from_last_punch)
1091 {
1092         // It's best that attachments cannot be punched 
1093         if(m_parent != NULL)
1094                 return 0;
1095
1096         if(!toolcap)
1097                 return 0;
1098
1099         // No effect if PvP disabled
1100         if(g_settings->getBool("enable_pvp") == false){
1101                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1102                         std::string str = gob_cmd_punched(0, getHP());
1103                         // create message and add to list
1104                         ActiveObjectMessage aom(getId(), true, str);
1105                         m_messages_out.push_back(aom);
1106                         return 0;
1107                 }
1108         }
1109
1110         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1111                         time_from_last_punch);
1112
1113         actionstream<<"Player "<<m_player->getName()<<" punched by "
1114                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1115                         <<" HP"<<std::endl;
1116
1117         setHP(getHP() - hitparams.hp);
1118
1119         if(hitparams.hp != 0)
1120         {
1121                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1122                 // create message and add to list
1123                 ActiveObjectMessage aom(getId(), true, str);
1124                 m_messages_out.push_back(aom);
1125         }
1126
1127         return hitparams.wear;
1128 }
1129
1130 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1131 {
1132 }
1133
1134 s16 PlayerSAO::getHP() const
1135 {
1136         return m_player->hp;
1137 }
1138
1139 void PlayerSAO::setHP(s16 hp)
1140 {
1141         s16 oldhp = m_player->hp;
1142
1143         if(hp < 0)
1144                 hp = 0;
1145         else if(hp > PLAYER_MAX_HP)
1146                 hp = PLAYER_MAX_HP;
1147
1148         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1149         {
1150                 m_hp_not_sent = true; // fix wrong prediction on client
1151                 return;
1152         }
1153
1154         m_player->hp = hp;
1155
1156         if(hp != oldhp)
1157                 m_hp_not_sent = true;
1158
1159         // On death or reincarnation send an active object message
1160         if((hp == 0) != (oldhp == 0))
1161         {
1162                 // Will send new is_visible value based on (getHP()!=0)
1163                 m_properties_sent = false;
1164                 // Send new HP
1165                 std::string str = gob_cmd_punched(0, getHP());
1166                 ActiveObjectMessage aom(getId(), true, str);
1167                 m_messages_out.push_back(aom);
1168         }
1169 }
1170
1171 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1172 {
1173         m_armor_groups = armor_groups;
1174         m_armor_groups_sent = false;
1175 }
1176
1177 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1178 {
1179         std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
1180         // create message and add to list
1181         ActiveObjectMessage aom(getId(), true, str);
1182         m_messages_out.push_back(aom);
1183 }
1184
1185 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1186 {
1187         std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
1188         // create message and add to list
1189         ActiveObjectMessage aom(getId(), true, str);
1190         m_messages_out.push_back(aom);
1191 }
1192
1193 void PlayerSAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
1194 {
1195         // Attachments need to be handled on both the server and client.
1196         // If we just attach on the server, we can only copy the position of the parent. Attachments
1197         // are still sent to clients at an interval so players might see them lagging, plus we can't
1198         // read and attach to skeletal bones.
1199         // If we just attach on the client, the server still sees the child at its original location.
1200         // This breaks some things so we also give the server the most accurate representation
1201         // even if players only see the client changes.
1202
1203         // Server attachment:
1204         m_parent = parent;
1205
1206         // Client attachment:
1207         std::string str = gob_cmd_set_attachment(parent->getId(), bone, position, rotation);
1208         // create message and add to list
1209         ActiveObjectMessage aom(getId(), true, str);
1210         m_messages_out.push_back(aom);
1211 }
1212
1213 ObjectProperties* PlayerSAO::accessObjectProperties()
1214 {
1215         return &m_prop;
1216 }
1217
1218 void PlayerSAO::notifyObjectPropertiesModified()
1219 {
1220         m_properties_sent = false;
1221 }
1222
1223 Inventory* PlayerSAO::getInventory()
1224 {
1225         return m_inventory;
1226 }
1227 const Inventory* PlayerSAO::getInventory() const
1228 {
1229         return m_inventory;
1230 }
1231
1232 InventoryLocation PlayerSAO::getInventoryLocation() const
1233 {
1234         InventoryLocation loc;
1235         loc.setPlayer(m_player->getName());
1236         return loc;
1237 }
1238
1239 void PlayerSAO::setInventoryModified()
1240 {
1241         m_inventory_not_sent = true;
1242 }
1243
1244 std::string PlayerSAO::getWieldList() const
1245 {
1246         return "main";
1247 }
1248
1249 int PlayerSAO::getWieldIndex() const
1250 {
1251         return m_wield_index;
1252 }
1253
1254 void PlayerSAO::setWieldIndex(int i)
1255 {
1256         if(i != m_wield_index)
1257         {
1258                 m_wield_index = i;
1259                 m_wielded_item_not_sent = true;
1260         }
1261 }
1262
1263 void PlayerSAO::disconnected()
1264 {
1265         m_peer_id = 0;
1266         m_removed = true;
1267         if(m_player->getPlayerSAO() == this)
1268         {
1269                 m_player->setPlayerSAO(NULL);
1270                 m_player->peer_id = 0;
1271         }
1272 }
1273
1274 std::string PlayerSAO::getPropertyPacket()
1275 {
1276         m_prop.is_visible = (getHP() != 0);
1277         return gob_cmd_set_properties(m_prop);
1278 }
1279