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