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