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