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