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