]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_sao.cpp
963e4b43a50854c0847658c8f7133c44ba3014d3
[dragonfireclient.git] / src / content_sao.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_sao.h"
21 #include "collision.h"
22 #include "environment.h"
23 #include "settings.h"
24 #include "main.h" // For g_profiler
25 #include "profiler.h"
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
28 #include "gamedef.h"
29 #include "player.h"
30 #include "scriptapi.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
33
34 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
35
36 /*
37         DummyLoadSAO
38 */
39
40 class DummyLoadSAO : public ServerActiveObject
41 {
42 public:
43         DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
44                 ServerActiveObject(env, pos)
45         {
46                 ServerActiveObject::registerType(type, create);
47         }
48         // Pretend to be the test object (to fool the client)
49         u8 getType() const
50         { return ACTIVEOBJECT_TYPE_TEST; }
51         // And never save to disk
52         bool isStaticAllowed() const
53         { return false; }
54         
55         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56                         const std::string &data)
57         {
58                 return new DummyLoadSAO(env, pos, 0);
59         }
60
61         void step(float dtime, bool send_recommended)
62         {
63                 m_removed = true;
64                 infostream<<"DummyLoadSAO step"<<std::endl;
65         }
66
67 private:
68 };
69
70 // Prototype (registers item for deserialization)
71 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
72 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
73 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
74 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
75
76 /*
77         TestSAO
78 */
79
80 class TestSAO : public ServerActiveObject
81 {
82 public:
83         TestSAO(ServerEnvironment *env, v3f pos):
84                 ServerActiveObject(env, pos),
85                 m_timer1(0),
86                 m_age(0)
87         {
88                 ServerActiveObject::registerType(getType(), create);
89         }
90         u8 getType() const
91         { return ACTIVEOBJECT_TYPE_TEST; }
92         
93         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
94                         const std::string &data)
95         {
96                 return new TestSAO(env, pos);
97         }
98
99         void step(float dtime, bool send_recommended)
100         {
101                 m_age += dtime;
102                 if(m_age > 10)
103                 {
104                         m_removed = true;
105                         return;
106                 }
107
108                 m_base_position.Y += dtime * BS * 2;
109                 if(m_base_position.Y > 8*BS)
110                         m_base_position.Y = 2*BS;
111
112                 if(send_recommended == false)
113                         return;
114
115                 m_timer1 -= dtime;
116                 if(m_timer1 < 0.0)
117                 {
118                         m_timer1 += 0.125;
119
120                         std::string data;
121
122                         data += itos(0); // 0 = position
123                         data += " ";
124                         data += itos(m_base_position.X);
125                         data += " ";
126                         data += itos(m_base_position.Y);
127                         data += " ";
128                         data += itos(m_base_position.Z);
129
130                         ActiveObjectMessage aom(getId(), false, data);
131                         m_messages_out.push_back(aom);
132                 }
133         }
134
135 private:
136         float m_timer1;
137         float m_age;
138 };
139
140 // Prototype (registers item for deserialization)
141 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
142
143 /*
144         ItemSAO
145
146         DEPRECATED: New dropped items are implemented in Lua; see
147                     builtin/item_entity.lua.
148 */
149
150 class ItemSAO : public ServerActiveObject
151 {
152 public:
153         u8 getType() const
154         { return ACTIVEOBJECT_TYPE_ITEM; }
155         
156         float getMinimumSavedMovement()
157         { return 0.1*BS; }
158
159         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
160                         const std::string &data)
161         {
162                 std::istringstream is(data, std::ios::binary);
163                 char buf[1];
164                 // read version
165                 is.read(buf, 1);
166                 u8 version = buf[0];
167                 // check if version is supported
168                 if(version != 0)
169                         return NULL;
170                 std::string itemstring = deSerializeString(is);
171                 infostream<<"create(): Creating item \""
172                                 <<itemstring<<"\""<<std::endl;
173                 return new ItemSAO(env, pos, itemstring);
174         }
175
176         ItemSAO(ServerEnvironment *env, v3f pos,
177                         const std::string itemstring):
178                 ServerActiveObject(env, pos),
179                 m_itemstring(itemstring),
180                 m_itemstring_changed(false),
181                 m_speed_f(0,0,0),
182                 m_last_sent_position(0,0,0)
183         {
184                 ServerActiveObject::registerType(getType(), create);
185         }
186
187         void step(float dtime, bool send_recommended)
188         {
189                 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
190
191                 assert(m_env);
192
193                 const float interval = 0.2;
194                 if(m_move_interval.step(dtime, interval)==false)
195                         return;
196                 dtime = interval;
197                 
198                 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
199                 collisionMoveResult moveresult;
200                 // Apply gravity
201                 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
202                 // Maximum movement without glitches
203                 f32 pos_max_d = BS*0.25;
204                 // Limit speed
205                 if(m_speed_f.getLength()*dtime > pos_max_d)
206                         m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
207                 v3f pos_f = getBasePosition();
208                 v3f pos_f_old = pos_f;
209                 v3f accel_f = v3f(0,0,0);
210                 f32 stepheight = 0;
211                 IGameDef *gamedef = m_env->getGameDef();
212                 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
213                                 pos_max_d, box, stepheight, dtime,
214                                 pos_f, m_speed_f, accel_f);
215                 
216                 if(send_recommended == false)
217                         return;
218
219                 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
220                 {
221                         setBasePosition(pos_f);
222                         m_last_sent_position = pos_f;
223
224                         std::ostringstream os(std::ios::binary);
225                         // command (0 = update position)
226                         writeU8(os, 0);
227                         // pos
228                         writeV3F1000(os, m_base_position);
229                         // create message and add to list
230                         ActiveObjectMessage aom(getId(), false, os.str());
231                         m_messages_out.push_back(aom);
232                 }
233                 if(m_itemstring_changed)
234                 {
235                         m_itemstring_changed = false;
236
237                         std::ostringstream os(std::ios::binary);
238                         // command (1 = update itemstring)
239                         writeU8(os, 1);
240                         // itemstring
241                         os<<serializeString(m_itemstring);
242                         // create message and add to list
243                         ActiveObjectMessage aom(getId(), false, os.str());
244                         m_messages_out.push_back(aom);
245                 }
246         }
247
248         std::string getClientInitializationData()
249         {
250                 std::ostringstream os(std::ios::binary);
251                 // version
252                 writeU8(os, 0);
253                 // pos
254                 writeV3F1000(os, m_base_position);
255                 // itemstring
256                 os<<serializeString(m_itemstring);
257                 return os.str();
258         }
259
260         std::string getStaticData()
261         {
262                 infostream<<__FUNCTION_NAME<<std::endl;
263                 std::ostringstream os(std::ios::binary);
264                 // version
265                 writeU8(os, 0);
266                 // itemstring
267                 os<<serializeString(m_itemstring);
268                 return os.str();
269         }
270
271         ItemStack createItemStack()
272         {
273                 try{
274                         IItemDefManager *idef = m_env->getGameDef()->idef();
275                         ItemStack item;
276                         item.deSerialize(m_itemstring, idef);
277                         infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
278                                         <<"\" -> item=\""<<item.getItemString()<<"\""
279                                         <<std::endl;
280                         return item;
281                 }
282                 catch(SerializationError &e)
283                 {
284                         infostream<<__FUNCTION_NAME<<": serialization error: "
285                                         <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
286                         return ItemStack();
287                 }
288         }
289
290         int punch(v3f dir,
291                         const ToolCapabilities *toolcap,
292                         ServerActiveObject *puncher,
293                         float time_from_last_punch)
294         {
295                 // Take item into inventory
296                 ItemStack item = createItemStack();
297                 Inventory *inv = puncher->getInventory();
298                 if(inv != NULL)
299                 {
300                         std::string wieldlist = puncher->getWieldList();
301                         ItemStack leftover = inv->addItem(wieldlist, item);
302                         puncher->setInventoryModified();
303                         if(leftover.empty())
304                         {
305                                 m_removed = true;
306                         }
307                         else
308                         {
309                                 m_itemstring = leftover.getItemString();
310                                 m_itemstring_changed = true;
311                         }
312                 }
313                 
314                 return 0;
315         }
316
317
318 private:
319         std::string m_itemstring;
320         bool m_itemstring_changed;
321         v3f m_speed_f;
322         v3f m_last_sent_position;
323         IntervalLimiter m_move_interval;
324 };
325
326 // Prototype (registers item for deserialization)
327 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
328
329 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
330                 const std::string itemstring)
331 {
332         return new ItemSAO(env, pos, itemstring);
333 }
334
335 /*
336         LuaEntitySAO
337 */
338
339 // Prototype (registers item for deserialization)
340 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
341
342 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
343                 const std::string &name, const std::string &state):
344         ServerActiveObject(env, pos),
345         m_init_name(name),
346         m_init_state(state),
347         m_registered(false),
348         m_hp(-1),
349         m_velocity(0,0,0),
350         m_acceleration(0,0,0),
351         m_yaw(0),
352         m_properties_sent(true),
353         m_last_sent_yaw(0),
354         m_last_sent_position(0,0,0),
355         m_last_sent_velocity(0,0,0),
356         m_last_sent_position_timer(0),
357         m_last_sent_move_precision(0),
358         m_armor_groups_sent(false),
359         m_animations_sent(false),
360         m_animations_bone_sent(false),
361         m_attachment_sent(false)
362 {
363         // Only register type if no environment supplied
364         if(env == NULL){
365                 ServerActiveObject::registerType(getType(), create);
366                 return;
367         }
368         
369         // Initialize something to armor groups
370         m_armor_groups["fleshy"] = 3;
371         m_armor_groups["snappy"] = 2;
372 }
373
374 LuaEntitySAO::~LuaEntitySAO()
375 {
376         if(m_registered){
377                 lua_State *L = m_env->getLua();
378                 scriptapi_luaentity_rm(L, m_id);
379         }
380 }
381
382 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
383 {
384         ServerActiveObject::addedToEnvironment(dtime_s);
385         
386         // Create entity from name
387         lua_State *L = m_env->getLua();
388         m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
389         
390         if(m_registered){
391                 // Get properties
392                 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
393                 // Initialize HP from properties
394                 m_hp = m_prop.hp_max;
395                 // Activate entity, supplying serialized state
396                 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
397         }
398 }
399
400 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
401                 const std::string &data)
402 {
403         std::string name;
404         std::string state;
405         s16 hp = 1;
406         v3f velocity;
407         float yaw = 0;
408         if(data != ""){
409                 std::istringstream is(data, std::ios::binary);
410                 // read version
411                 u8 version = readU8(is);
412                 // check if version is supported
413                 if(version == 0){
414                         name = deSerializeString(is);
415                         state = deSerializeLongString(is);
416                 }
417                 else if(version == 1){
418                         name = deSerializeString(is);
419                         state = deSerializeLongString(is);
420                         hp = readS16(is);
421                         velocity = readV3F1000(is);
422                         yaw = readF1000(is);
423                 }
424         }
425         // create object
426         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
427                         <<state<<"\")"<<std::endl;
428         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
429         sao->m_hp = hp;
430         sao->m_velocity = velocity;
431         sao->m_yaw = yaw;
432         return sao;
433 }
434
435 bool LuaEntitySAO::isAttached()
436 {
437         if(!m_attachment_parent_id)
438                 return false;
439         // Check if the parent still exists
440         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
441         if(obj)
442                 return true;
443         return false;
444 }
445
446 void LuaEntitySAO::step(float dtime, bool send_recommended)
447 {
448         if(!m_properties_sent)
449         {
450                 m_properties_sent = true;
451                 std::string str = getPropertyPacket();
452                 // create message and add to list
453                 ActiveObjectMessage aom(getId(), true, str);
454                 m_messages_out.push_back(aom);
455         }
456
457         // If attached, check that our parent is still there. If it isn't, detach.
458         if(m_attachment_parent_id && !isAttached())
459         {
460                 m_attachment_parent_id = 0;
461                 m_attachment_bone = "";
462                 m_attachment_position = v3f(0,0,0);
463                 m_attachment_rotation = v3f(0,0,0);
464                 sendPosition(false, true);
465         }
466
467         m_last_sent_position_timer += dtime;
468
469         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
470         // If the object gets detached this comes into effect automatically from the last known origin
471         if(isAttached())
472         {
473                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
474                 m_base_position = pos;
475                 m_velocity = v3f(0,0,0);
476                 m_acceleration = v3f(0,0,0);
477         }
478         else
479         {
480                 if(m_prop.physical){
481                         core::aabbox3d<f32> box = m_prop.collisionbox;
482                         box.MinEdge *= BS;
483                         box.MaxEdge *= BS;
484                         collisionMoveResult moveresult;
485                         f32 pos_max_d = BS*0.25; // Distance per iteration
486                         f32 stepheight = 0; // Maximum climbable step height
487                         v3f p_pos = m_base_position;
488                         v3f p_velocity = m_velocity;
489                         v3f p_acceleration = m_acceleration;
490                         IGameDef *gamedef = m_env->getGameDef();
491                         moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
492                                         pos_max_d, box, stepheight, dtime,
493                                         p_pos, p_velocity, p_acceleration);
494                         // Apply results
495                         m_base_position = p_pos;
496                         m_velocity = p_velocity;
497                         m_acceleration = p_acceleration;
498                 } else {
499                         m_base_position += dtime * m_velocity + 0.5 * dtime
500                                         * dtime * m_acceleration;
501                         m_velocity += dtime * m_acceleration;
502                 }
503         }
504
505         if(m_registered){
506                 lua_State *L = m_env->getLua();
507                 scriptapi_luaentity_step(L, m_id, dtime);
508         }
509
510         if(send_recommended == false)
511                 return;
512
513         if(!isAttached())
514         {
515                 // TODO: force send when acceleration changes enough?
516                 float minchange = 0.2*BS;
517                 if(m_last_sent_position_timer > 1.0){
518                         minchange = 0.01*BS;
519                 } else if(m_last_sent_position_timer > 0.2){
520                         minchange = 0.05*BS;
521                 }
522                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
523                 move_d += m_last_sent_move_precision;
524                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
525                 if(move_d > minchange || vel_d > minchange ||
526                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
527                         sendPosition(true, false);
528                 }
529         }
530
531         if(m_armor_groups_sent == false){
532                 m_armor_groups_sent = true;
533                 std::string str = gob_cmd_update_armor_groups(
534                                 m_armor_groups);
535                 // create message and add to list
536                 ActiveObjectMessage aom(getId(), true, str);
537                 m_messages_out.push_back(aom);
538         }
539
540         if(m_animations_sent == false){
541                 m_animations_sent = true;
542                 std::string str = gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend);
543                 // create message and add to list
544                 ActiveObjectMessage aom(getId(), true, str);
545                 m_messages_out.push_back(aom);
546         }
547
548         if(m_animations_bone_sent == false){
549                 m_animations_bone_sent = true;
550                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
551                         std::string str = gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y);
552                         // create message and add to list
553                         ActiveObjectMessage aom(getId(), true, str);
554                         m_messages_out.push_back(aom);
555                 }
556         }
557
558         if(m_attachment_sent == false){
559                 m_attachment_sent = true;
560                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
561                 // create message and add to list
562                 ActiveObjectMessage aom(getId(), true, str);
563                 m_messages_out.push_back(aom);
564         }
565 }
566
567 std::string LuaEntitySAO::getClientInitializationData()
568 {
569         std::ostringstream os(std::ios::binary);
570         writeU8(os, 0); // version
571         os<<serializeString(""); // name
572         writeS16(os, getId()); //id
573         writeU8(os, 0); // is_player
574         writeV3F1000(os, m_base_position);
575         writeF1000(os, m_yaw);
576         writeS16(os, m_hp);
577
578         writeU8(os, 4 + m_animation_bone.size()); // number of messages stuffed in here
579         os<<serializeLongString(getPropertyPacket()); // message 1
580         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
581         os<<serializeLongString(gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend)); // 3
582         if(m_animation_bone.size()){
583                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
584                         os<<serializeLongString(gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_animation_bone.size
585                 }
586         }
587         os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
588
589         // return result
590         return os.str();
591 }
592
593 std::string LuaEntitySAO::getStaticData()
594 {
595         verbosestream<<__FUNCTION_NAME<<std::endl;
596         std::ostringstream os(std::ios::binary);
597         // version
598         writeU8(os, 1);
599         // name
600         os<<serializeString(m_init_name);
601         // state
602         if(m_registered){
603                 lua_State *L = m_env->getLua();
604                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
605                 os<<serializeLongString(state);
606         } else {
607                 os<<serializeLongString(m_init_state);
608         }
609         // hp
610         writeS16(os, m_hp);
611         // velocity
612         writeV3F1000(os, m_velocity);
613         // yaw
614         writeF1000(os, m_yaw);
615         return os.str();
616 }
617
618 int LuaEntitySAO::punch(v3f dir,
619                 const ToolCapabilities *toolcap,
620                 ServerActiveObject *puncher,
621                 float time_from_last_punch)
622 {
623         if(!m_registered){
624                 // Delete unknown LuaEntities when punched
625                 m_removed = true;
626                 return 0;
627         }
628
629         // It's best that attachments cannot be punched 
630         if(isAttached())
631                 return 0;
632         
633         ItemStack *punchitem = NULL;
634         ItemStack punchitem_static;
635         if(puncher){
636                 punchitem_static = puncher->getWieldedItem();
637                 punchitem = &punchitem_static;
638         }
639
640         PunchDamageResult result = getPunchDamage(
641                         m_armor_groups,
642                         toolcap,
643                         punchitem,
644                         time_from_last_punch);
645         
646         if(result.did_punch)
647         {
648                 setHP(getHP() - result.damage);
649                 
650                 actionstream<<getDescription()<<" punched by "
651                                 <<puncher->getDescription()<<", damage "<<result.damage
652                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
653                 
654                 {
655                         std::string str = gob_cmd_punched(result.damage, getHP());
656                         // create message and add to list
657                         ActiveObjectMessage aom(getId(), true, str);
658                         m_messages_out.push_back(aom);
659                 }
660
661                 if(getHP() == 0)
662                         m_removed = true;
663         }
664
665         lua_State *L = m_env->getLua();
666         scriptapi_luaentity_punch(L, m_id, puncher,
667                         time_from_last_punch, toolcap, dir);
668
669         return result.wear;
670 }
671
672 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
673 {
674         if(!m_registered)
675                 return;
676         lua_State *L = m_env->getLua();
677         scriptapi_luaentity_rightclick(L, m_id, clicker);
678 }
679
680 void LuaEntitySAO::setPos(v3f pos)
681 {
682         if(isAttached())
683                 return;
684         m_base_position = pos;
685         sendPosition(false, true);
686 }
687
688 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
689 {
690         if(isAttached())
691                 return;
692         m_base_position = pos;
693         if(!continuous)
694                 sendPosition(true, true);
695 }
696
697 float LuaEntitySAO::getMinimumSavedMovement()
698 {
699         return 0.1 * BS;
700 }
701
702 std::string LuaEntitySAO::getDescription()
703 {
704         std::ostringstream os(std::ios::binary);
705         os<<"LuaEntitySAO at (";
706         os<<(m_base_position.X/BS)<<",";
707         os<<(m_base_position.Y/BS)<<",";
708         os<<(m_base_position.Z/BS);
709         os<<")";
710         return os.str();
711 }
712
713 void LuaEntitySAO::setHP(s16 hp)
714 {
715         if(hp < 0) hp = 0;
716         m_hp = hp;
717 }
718
719 s16 LuaEntitySAO::getHP() const
720 {
721         return m_hp;
722 }
723
724 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
725 {
726         m_armor_groups = armor_groups;
727         m_armor_groups_sent = false;
728 }
729
730 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
731 {
732         m_animation_frames = frames;
733         m_animation_speed = frame_speed;
734         m_animation_blend = frame_blend;
735         m_animations_sent = false;
736 }
737
738 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
739 {
740         m_animation_bone[bone] = core::vector2d<v3f>(position, rotation);
741         m_animations_bone_sent = false;
742 }
743
744 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
745 {
746         // Attachments need to be handled on both the server and client.
747         // If we just attach on the server, we can only copy the position of the parent. Attachments
748         // are still sent to clients at an interval so players might see them lagging, plus we can't
749         // read and attach to skeletal bones.
750         // If we just attach on the client, the server still sees the child at its original location.
751         // This breaks some things so we also give the server the most accurate representation
752         // even if players only see the client changes.
753
754         m_attachment_parent_id = parent_id;
755         m_attachment_bone = bone;
756         m_attachment_position = position;
757         m_attachment_rotation = rotation;
758         m_attachment_sent = false;
759 }
760
761 ObjectProperties* LuaEntitySAO::accessObjectProperties()
762 {
763         return &m_prop;
764 }
765
766 void LuaEntitySAO::notifyObjectPropertiesModified()
767 {
768         m_properties_sent = false;
769 }
770
771 void LuaEntitySAO::setVelocity(v3f velocity)
772 {
773         m_velocity = velocity;
774 }
775
776 v3f LuaEntitySAO::getVelocity()
777 {
778         return m_velocity;
779 }
780
781 void LuaEntitySAO::setAcceleration(v3f acceleration)
782 {
783         m_acceleration = acceleration;
784 }
785
786 v3f LuaEntitySAO::getAcceleration()
787 {
788         return m_acceleration;
789 }
790
791 void LuaEntitySAO::setYaw(float yaw)
792 {
793         m_yaw = yaw;
794 }
795
796 float LuaEntitySAO::getYaw()
797 {
798         return m_yaw;
799 }
800
801 void LuaEntitySAO::setTextureMod(const std::string &mod)
802 {
803         std::string str = gob_cmd_set_texture_mod(mod);
804         // create message and add to list
805         ActiveObjectMessage aom(getId(), true, str);
806         m_messages_out.push_back(aom);
807 }
808
809 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
810                 bool select_horiz_by_yawpitch)
811 {
812         std::string str = gob_cmd_set_sprite(
813                 p,
814                 num_frames,
815                 framelength,
816                 select_horiz_by_yawpitch
817         );
818         // create message and add to list
819         ActiveObjectMessage aom(getId(), true, str);
820         m_messages_out.push_back(aom);
821 }
822
823 std::string LuaEntitySAO::getName()
824 {
825         return m_init_name;
826 }
827
828 std::string LuaEntitySAO::getPropertyPacket()
829 {
830         return gob_cmd_set_properties(m_prop);
831 }
832
833 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
834 {
835         // If the object is attached client-side, don't waste bandwidth sending its position to clients
836         if(isAttached())
837                 return;
838         
839         m_last_sent_move_precision = m_base_position.getDistanceFrom(
840                         m_last_sent_position);
841         m_last_sent_position_timer = 0;
842         m_last_sent_yaw = m_yaw;
843         m_last_sent_position = m_base_position;
844         m_last_sent_velocity = m_velocity;
845         //m_last_sent_acceleration = m_acceleration;
846
847         float update_interval = m_env->getSendRecommendedInterval();
848
849         std::string str = gob_cmd_update_position(
850                 m_base_position,
851                 m_velocity,
852                 m_acceleration,
853                 m_yaw,
854                 do_interpolate,
855                 is_movement_end,
856                 update_interval
857         );
858         // create message and add to list
859         ActiveObjectMessage aom(getId(), false, str);
860         m_messages_out.push_back(aom);
861 }
862
863 /*
864         PlayerSAO
865 */
866
867 // No prototype, PlayerSAO does not need to be deserialized
868
869 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
870                 const std::set<std::string> &privs, bool is_singleplayer):
871         ServerActiveObject(env_, v3f(0,0,0)),
872         m_player(player_),
873         m_peer_id(peer_id_),
874         m_inventory(NULL),
875         m_last_good_position(0,0,0),
876         m_last_good_position_age(0),
877         m_time_from_last_punch(0),
878         m_nocheat_dig_pos(32767, 32767, 32767),
879         m_nocheat_dig_time(0),
880         m_wield_index(0),
881         m_position_not_sent(false),
882         m_armor_groups_sent(false),
883         m_properties_sent(true),
884         m_privs(privs),
885         m_is_singleplayer(is_singleplayer),
886         m_animations_sent(false),
887         m_animations_bone_sent(false),
888         m_attachment_sent(false),
889         // public
890         m_moved(false),
891         m_inventory_not_sent(false),
892         m_hp_not_sent(false),
893         m_wielded_item_not_sent(false)
894 {
895         assert(m_player);
896         assert(m_peer_id != 0);
897         setBasePosition(m_player->getPosition());
898         m_inventory = &m_player->inventory;
899         m_armor_groups["choppy"] = 2;
900         m_armor_groups["fleshy"] = 3;
901
902         m_prop.hp_max = PLAYER_MAX_HP;
903         m_prop.physical = false;
904         m_prop.weight = 75;
905         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
906         // start of default appearance, this should be overwritten by LUA
907         m_prop.visual = "upright_sprite";
908         m_prop.visual_size = v2f(1, 2);
909         m_prop.textures.clear();
910         m_prop.textures.push_back("player.png");
911         m_prop.textures.push_back("player_back.png");
912         m_prop.colors.clear();
913         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
914         m_prop.spritediv = v2s16(1,1);
915         // end of default appearance
916         m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
917         m_prop.makes_footstep_sound = true;
918 }
919
920 PlayerSAO::~PlayerSAO()
921 {
922         if(m_inventory != &m_player->inventory)
923                 delete m_inventory;
924
925 }
926
927 std::string PlayerSAO::getDescription()
928 {
929         return std::string("player ") + m_player->getName();
930 }
931
932 // Called after id has been set and has been inserted in environment
933 void PlayerSAO::addedToEnvironment(u32 dtime_s)
934 {
935         ServerActiveObject::addedToEnvironment(dtime_s);
936         ServerActiveObject::setBasePosition(m_player->getPosition());
937         m_player->setPlayerSAO(this);
938         m_player->peer_id = m_peer_id;
939         m_last_good_position = m_player->getPosition();
940         m_last_good_position_age = 0.0;
941 }
942
943 // Called before removing from environment
944 void PlayerSAO::removingFromEnvironment()
945 {
946         ServerActiveObject::removingFromEnvironment();
947         if(m_player->getPlayerSAO() == this)
948         {
949                 m_player->setPlayerSAO(NULL);
950                 m_player->peer_id = 0;
951         }
952 }
953
954 bool PlayerSAO::isStaticAllowed() const
955 {
956         return false;
957 }
958
959 bool PlayerSAO::unlimitedTransferDistance() const
960 {
961         return g_settings->getBool("unlimited_player_transfer_distance");
962 }
963
964 std::string PlayerSAO::getClientInitializationData()
965 {
966         std::ostringstream os(std::ios::binary);
967         writeU8(os, 0); // version
968         os<<serializeString(m_player->getName()); // name
969         writeU8(os, 1); // is_player
970         writeS16(os, getId()); //id
971         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
972         writeF1000(os, m_player->getYaw());
973         writeS16(os, getHP());
974
975         writeU8(os, 4 + m_animation_bone.size()); // number of messages stuffed in here
976         os<<serializeLongString(getPropertyPacket()); // message 1
977         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
978         os<<serializeLongString(gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend)); // 3
979         if(m_animation_bone.size()){
980                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
981                         os<<serializeLongString(gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_animation_bone.size
982                 }
983         }
984         os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
985
986         // return result
987         return os.str();
988 }
989
990 std::string PlayerSAO::getStaticData()
991 {
992         assert(0);
993         return "";
994 }
995
996 bool PlayerSAO::isAttached()
997 {
998         if(!m_attachment_parent_id)
999                 return false;
1000         // Check if the parent still exists
1001         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1002         if(obj)
1003                 return true;
1004         return false;
1005 }
1006
1007 void PlayerSAO::step(float dtime, bool send_recommended)
1008 {
1009         if(!m_properties_sent)
1010         {
1011                 m_properties_sent = true;
1012                 std::string str = getPropertyPacket();
1013                 // create message and add to list
1014                 ActiveObjectMessage aom(getId(), true, str);
1015                 m_messages_out.push_back(aom);
1016         }
1017
1018         // If attached, check that our parent is still there. If it isn't, detach.
1019         if(m_attachment_parent_id && !isAttached())
1020         {
1021                 m_attachment_parent_id = 0;
1022                 m_attachment_bone = "";
1023                 m_attachment_position = v3f(0,0,0);
1024                 m_attachment_rotation = v3f(0,0,0);
1025                 m_player->setPosition(m_last_good_position);
1026                 m_moved = true;
1027         }
1028
1029         m_time_from_last_punch += dtime;
1030         m_nocheat_dig_time += dtime;
1031
1032         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1033         // If the object gets detached this comes into effect automatically from the last known origin
1034         if(isAttached())
1035         {
1036                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1037                 m_last_good_position = pos;
1038                 m_last_good_position_age = 0;
1039                 m_player->setPosition(pos);
1040         }
1041         else
1042         {
1043                 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1044                 {
1045                         m_last_good_position = m_player->getPosition();
1046                         m_last_good_position_age = 0;
1047                 }
1048                 else
1049                 {
1050                         /*
1051                                 Check player movements
1052
1053                                 NOTE: Actually the server should handle player physics like the
1054                                 client does and compare player's position to what is calculated
1055                                 on our side. This is required when eg. players fly due to an
1056                                 explosion. Altough a node-based alternative might be possible
1057                                 too, and much more lightweight.
1058                         */
1059
1060                         float player_max_speed = 0;
1061                         float player_max_speed_up = 0;
1062                         if(m_privs.count("fast") != 0){
1063                                 // Fast speed
1064                                 player_max_speed = BS * 20;
1065                                 player_max_speed_up = BS * 20;
1066                         } else {
1067                                 // Normal speed
1068                                 player_max_speed = BS * 4.0;
1069                                 player_max_speed_up = BS * 4.0;
1070                         }
1071                         // Tolerance
1072                         player_max_speed *= 2.5;
1073                         player_max_speed_up *= 2.5;
1074
1075                         m_last_good_position_age += dtime;
1076                         if(m_last_good_position_age >= 1.0){
1077                                 float age = m_last_good_position_age;
1078                                 v3f diff = (m_player->getPosition() - m_last_good_position);
1079                                 float d_vert = diff.Y;
1080                                 diff.Y = 0;
1081                                 float d_horiz = diff.getLength();
1082                                 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1083                                                 <<(d_horiz/age)<<std::endl;*/
1084                                 if(d_horiz <= age * player_max_speed &&
1085                                                 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1086                                         m_last_good_position = m_player->getPosition();
1087                                 } else {
1088                                         actionstream<<"Player "<<m_player->getName()
1089                                                         <<" moved too fast; resetting position"
1090                                                         <<std::endl;
1091                                         m_player->setPosition(m_last_good_position);
1092                                         m_moved = true;
1093                                 }
1094                                 m_last_good_position_age = 0;
1095                         }
1096                 }
1097         }
1098
1099         if(send_recommended == false)
1100                 return;
1101
1102         // If the object is attached client-side, don't waste bandwidth sending its position to clients
1103         if(m_position_not_sent && !isAttached())
1104         {
1105                 m_position_not_sent = false;
1106                 float update_interval = m_env->getSendRecommendedInterval();
1107                 v3f pos;
1108                 if(isAttached()) // Just in case we ever do send attachment position too
1109                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1110                 else
1111                         pos = m_player->getPosition() + v3f(0,BS*1,0);
1112                 std::string str = gob_cmd_update_position(
1113                         pos,
1114                         v3f(0,0,0),
1115                         v3f(0,0,0),
1116                         m_player->getYaw(),
1117                         true,
1118                         false,
1119                         update_interval
1120                 );
1121                 // create message and add to list
1122                 ActiveObjectMessage aom(getId(), false, str);
1123                 m_messages_out.push_back(aom);
1124         }
1125
1126         if(m_wielded_item_not_sent)
1127         {
1128                 m_wielded_item_not_sent = false;
1129                 // GenericCAO has no special way to show this
1130         }
1131
1132         if(m_armor_groups_sent == false){
1133                 m_armor_groups_sent = true;
1134                 std::string str = gob_cmd_update_armor_groups(
1135                                 m_armor_groups);
1136                 // create message and add to list
1137                 ActiveObjectMessage aom(getId(), true, str);
1138                 m_messages_out.push_back(aom);
1139         }
1140
1141         if(m_animations_sent == false){
1142                 m_animations_sent = true;
1143                 std::string str = gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend);
1144                 // create message and add to list
1145                 ActiveObjectMessage aom(getId(), true, str);
1146                 m_messages_out.push_back(aom);
1147         }
1148
1149         if(m_animations_bone_sent == false){
1150                 m_animations_bone_sent = true;
1151                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
1152                         std::string str = gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y);
1153                         // create message and add to list
1154                         ActiveObjectMessage aom(getId(), true, str);
1155                         m_messages_out.push_back(aom);
1156                 }
1157         }
1158
1159         if(m_attachment_sent == false){
1160                 m_attachment_sent = true;
1161                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1162                 // create message and add to list
1163                 ActiveObjectMessage aom(getId(), true, str);
1164                 m_messages_out.push_back(aom);
1165         }
1166 }
1167
1168 void PlayerSAO::setBasePosition(const v3f &position)
1169 {
1170         // This needs to be ran for attachments too
1171         ServerActiveObject::setBasePosition(position);
1172         m_position_not_sent = true;
1173 }
1174
1175 void PlayerSAO::setPos(v3f pos)
1176 {
1177         if(isAttached())
1178                 return;
1179         m_player->setPosition(pos);
1180         // Movement caused by this command is always valid
1181         m_last_good_position = pos;
1182         m_last_good_position_age = 0;
1183         // Force position change on client
1184         m_moved = true;
1185 }
1186
1187 void PlayerSAO::moveTo(v3f pos, bool continuous)
1188 {
1189         if(isAttached())
1190                 return;
1191         m_player->setPosition(pos);
1192         // Movement caused by this command is always valid
1193         m_last_good_position = pos;
1194         m_last_good_position_age = 0;
1195         // Force position change on client
1196         m_moved = true;
1197 }
1198
1199 int PlayerSAO::punch(v3f dir,
1200         const ToolCapabilities *toolcap,
1201         ServerActiveObject *puncher,
1202         float time_from_last_punch)
1203 {
1204         // It's best that attachments cannot be punched 
1205         if(isAttached())
1206                 return 0;
1207
1208         if(!toolcap)
1209                 return 0;
1210
1211         // No effect if PvP disabled
1212         if(g_settings->getBool("enable_pvp") == false){
1213                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1214                         std::string str = gob_cmd_punched(0, getHP());
1215                         // create message and add to list
1216                         ActiveObjectMessage aom(getId(), true, str);
1217                         m_messages_out.push_back(aom);
1218                         return 0;
1219                 }
1220         }
1221
1222         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1223                         time_from_last_punch);
1224
1225         actionstream<<"Player "<<m_player->getName()<<" punched by "
1226                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1227                         <<" HP"<<std::endl;
1228
1229         setHP(getHP() - hitparams.hp);
1230
1231         if(hitparams.hp != 0)
1232         {
1233                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1234                 // create message and add to list
1235                 ActiveObjectMessage aom(getId(), true, str);
1236                 m_messages_out.push_back(aom);
1237         }
1238
1239         return hitparams.wear;
1240 }
1241
1242 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1243 {
1244 }
1245
1246 s16 PlayerSAO::getHP() const
1247 {
1248         return m_player->hp;
1249 }
1250
1251 void PlayerSAO::setHP(s16 hp)
1252 {
1253         s16 oldhp = m_player->hp;
1254
1255         if(hp < 0)
1256                 hp = 0;
1257         else if(hp > PLAYER_MAX_HP)
1258                 hp = PLAYER_MAX_HP;
1259
1260         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1261         {
1262                 m_hp_not_sent = true; // fix wrong prediction on client
1263                 return;
1264         }
1265
1266         m_player->hp = hp;
1267
1268         if(hp != oldhp)
1269                 m_hp_not_sent = true;
1270
1271         // On death or reincarnation send an active object message
1272         if((hp == 0) != (oldhp == 0))
1273         {
1274                 // Will send new is_visible value based on (getHP()!=0)
1275                 m_properties_sent = false;
1276                 // Send new HP
1277                 std::string str = gob_cmd_punched(0, getHP());
1278                 ActiveObjectMessage aom(getId(), true, str);
1279                 m_messages_out.push_back(aom);
1280         }
1281 }
1282
1283 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1284 {
1285         m_armor_groups = armor_groups;
1286         m_armor_groups_sent = false;
1287 }
1288
1289 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1290 {
1291         // store these so they can be updated to clients
1292         m_animation_frames = frames;
1293         m_animation_speed = frame_speed;
1294         m_animation_blend = frame_blend;
1295         m_animations_sent = false;
1296 }
1297
1298 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1299 {
1300         // store these so they can be updated to clients
1301         m_animation_bone[bone] = core::vector2d<v3f>(position, rotation);
1302         m_animations_bone_sent = false;
1303 }
1304
1305 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1306 {
1307         // Attachments need to be handled on both the server and client.
1308         // If we just attach on the server, we can only copy the position of the parent. Attachments
1309         // are still sent to clients at an interval so players might see them lagging, plus we can't
1310         // read and attach to skeletal bones.
1311         // If we just attach on the client, the server still sees the child at its original location.
1312         // This breaks some things so we also give the server the most accurate representation
1313         // even if players only see the client changes.
1314
1315         m_attachment_parent_id = parent_id;
1316         m_attachment_bone = bone;
1317         m_attachment_position = position;
1318         m_attachment_rotation = rotation;
1319         m_attachment_sent = false;
1320 }
1321
1322 ObjectProperties* PlayerSAO::accessObjectProperties()
1323 {
1324         return &m_prop;
1325 }
1326
1327 void PlayerSAO::notifyObjectPropertiesModified()
1328 {
1329         m_properties_sent = false;
1330 }
1331
1332 Inventory* PlayerSAO::getInventory()
1333 {
1334         return m_inventory;
1335 }
1336 const Inventory* PlayerSAO::getInventory() const
1337 {
1338         return m_inventory;
1339 }
1340
1341 InventoryLocation PlayerSAO::getInventoryLocation() const
1342 {
1343         InventoryLocation loc;
1344         loc.setPlayer(m_player->getName());
1345         return loc;
1346 }
1347
1348 void PlayerSAO::setInventoryModified()
1349 {
1350         m_inventory_not_sent = true;
1351 }
1352
1353 std::string PlayerSAO::getWieldList() const
1354 {
1355         return "main";
1356 }
1357
1358 int PlayerSAO::getWieldIndex() const
1359 {
1360         return m_wield_index;
1361 }
1362
1363 void PlayerSAO::setWieldIndex(int i)
1364 {
1365         if(i != m_wield_index)
1366         {
1367                 m_wield_index = i;
1368                 m_wielded_item_not_sent = true;
1369         }
1370 }
1371
1372 void PlayerSAO::disconnected()
1373 {
1374         m_peer_id = 0;
1375         m_removed = true;
1376         if(m_player->getPlayerSAO() == this)
1377         {
1378                 m_player->setPlayerSAO(NULL);
1379                 m_player->peer_id = 0;
1380         }
1381 }
1382
1383 std::string PlayerSAO::getPropertyPacket()
1384 {
1385         m_prop.is_visible = (getHP() != 0);
1386         return gob_cmd_set_properties(m_prop);
1387 }
1388