b23d287f406bbb70de2e02bba2056556b867bb60
[oweals/minetest.git] / src / content_sao.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_sao.h"
21 #include "collision.h"
22 #include "environment.h"
23 #include "settings.h"
24 #include "main.h" // For g_profiler
25 #include "profiler.h"
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
28 #include "gamedef.h"
29 #include "player.h"
30 #include "scriptapi.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
33
34 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
35
36 /*
37         DummyLoadSAO
38 */
39
40 class DummyLoadSAO : public ServerActiveObject
41 {
42 public:
43         DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
44                 ServerActiveObject(env, pos)
45         {
46                 ServerActiveObject::registerType(type, create);
47         }
48         // Pretend to be the test object (to fool the client)
49         u8 getType() const
50         { return ACTIVEOBJECT_TYPE_TEST; }
51         // And never save to disk
52         bool isStaticAllowed() const
53         { return false; }
54         
55         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56                         const std::string &data)
57         {
58                 return new DummyLoadSAO(env, pos, 0);
59         }
60
61         void step(float dtime, bool send_recommended)
62         {
63                 m_removed = true;
64                 infostream<<"DummyLoadSAO step"<<std::endl;
65         }
66
67 private:
68 };
69
70 // Prototype (registers item for deserialization)
71 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
72 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
73 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
74 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
75
76 /*
77         TestSAO
78 */
79
80 class TestSAO : public ServerActiveObject
81 {
82 public:
83         TestSAO(ServerEnvironment *env, v3f pos):
84                 ServerActiveObject(env, pos),
85                 m_timer1(0),
86                 m_age(0)
87         {
88                 ServerActiveObject::registerType(getType(), create);
89         }
90         u8 getType() const
91         { return ACTIVEOBJECT_TYPE_TEST; }
92         
93         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
94                         const std::string &data)
95         {
96                 return new TestSAO(env, pos);
97         }
98
99         void step(float dtime, bool send_recommended)
100         {
101                 m_age += dtime;
102                 if(m_age > 10)
103                 {
104                         m_removed = true;
105                         return;
106                 }
107
108                 m_base_position.Y += dtime * BS * 2;
109                 if(m_base_position.Y > 8*BS)
110                         m_base_position.Y = 2*BS;
111
112                 if(send_recommended == false)
113                         return;
114
115                 m_timer1 -= dtime;
116                 if(m_timer1 < 0.0)
117                 {
118                         m_timer1 += 0.125;
119
120                         std::string data;
121
122                         data += itos(0); // 0 = position
123                         data += " ";
124                         data += itos(m_base_position.X);
125                         data += " ";
126                         data += itos(m_base_position.Y);
127                         data += " ";
128                         data += itos(m_base_position.Z);
129
130                         ActiveObjectMessage aom(getId(), false, data);
131                         m_messages_out.push_back(aom);
132                 }
133         }
134
135 private:
136         float m_timer1;
137         float m_age;
138 };
139
140 // Prototype (registers item for deserialization)
141 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
142
143 /*
144         ItemSAO
145
146         DEPRECATED: New dropped items are implemented in Lua; see
147                     builtin/item_entity.lua.
148 */
149
150 class ItemSAO : public ServerActiveObject
151 {
152 public:
153         u8 getType() const
154         { return ACTIVEOBJECT_TYPE_ITEM; }
155         
156         float getMinimumSavedMovement()
157         { return 0.1*BS; }
158
159         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
160                         const std::string &data)
161         {
162                 std::istringstream is(data, std::ios::binary);
163                 char buf[1];
164                 // read version
165                 is.read(buf, 1);
166                 u8 version = buf[0];
167                 // check if version is supported
168                 if(version != 0)
169                         return NULL;
170                 std::string itemstring = deSerializeString(is);
171                 infostream<<"create(): Creating item \""
172                                 <<itemstring<<"\""<<std::endl;
173                 return new ItemSAO(env, pos, itemstring);
174         }
175
176         ItemSAO(ServerEnvironment *env, v3f pos,
177                         const std::string itemstring):
178                 ServerActiveObject(env, pos),
179                 m_itemstring(itemstring),
180                 m_itemstring_changed(false),
181                 m_speed_f(0,0,0),
182                 m_last_sent_position(0,0,0)
183         {
184                 ServerActiveObject::registerType(getType(), create);
185         }
186
187         void step(float dtime, bool send_recommended)
188         {
189                 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
190
191                 assert(m_env);
192
193                 const float interval = 0.2;
194                 if(m_move_interval.step(dtime, interval)==false)
195                         return;
196                 dtime = interval;
197                 
198                 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
199                 collisionMoveResult moveresult;
200                 // Apply gravity
201                 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
202                 // Maximum movement without glitches
203                 f32 pos_max_d = BS*0.25;
204                 // Limit speed
205                 if(m_speed_f.getLength()*dtime > pos_max_d)
206                         m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
207                 v3f pos_f = getBasePosition();
208                 v3f pos_f_old = pos_f;
209                 v3f accel_f = v3f(0,0,0);
210                 f32 stepheight = 0;
211                 IGameDef *gamedef = m_env->getGameDef();
212                 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
213                                 pos_max_d, box, stepheight, dtime,
214                                 pos_f, m_speed_f, accel_f);
215                 
216                 if(send_recommended == false)
217                         return;
218
219                 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
220                 {
221                         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 {
360         // Only register type if no environment supplied
361         if(env == NULL){
362                 ServerActiveObject::registerType(getType(), create);
363                 return;
364         }
365         
366         // Initialize something to armor groups
367         m_armor_groups["fleshy"] = 3;
368         m_armor_groups["snappy"] = 2;
369 }
370
371 LuaEntitySAO::~LuaEntitySAO()
372 {
373         if(m_registered){
374                 lua_State *L = m_env->getLua();
375                 scriptapi_luaentity_rm(L, m_id);
376         }
377 }
378
379 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
380 {
381         ServerActiveObject::addedToEnvironment(dtime_s);
382         
383         // Create entity from name
384         lua_State *L = m_env->getLua();
385         m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
386         
387         if(m_registered){
388                 // Get properties
389                 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
390                 // Initialize HP from properties
391                 m_hp = m_prop.hp_max;
392                 // Activate entity, supplying serialized state
393                 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
394         }
395 }
396
397 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
398                 const std::string &data)
399 {
400         std::string name;
401         std::string state;
402         s16 hp = 1;
403         v3f velocity;
404         float yaw = 0;
405         if(data != ""){
406                 std::istringstream is(data, std::ios::binary);
407                 // read version
408                 u8 version = readU8(is);
409                 // check if version is supported
410                 if(version == 0){
411                         name = deSerializeString(is);
412                         state = deSerializeLongString(is);
413                 }
414                 else if(version == 1){
415                         name = deSerializeString(is);
416                         state = deSerializeLongString(is);
417                         hp = readS16(is);
418                         velocity = readV3F1000(is);
419                         yaw = readF1000(is);
420                 }
421         }
422         // create object
423         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
424                         <<state<<"\")"<<std::endl;
425         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
426         sao->m_hp = hp;
427         sao->m_velocity = velocity;
428         sao->m_yaw = yaw;
429         return sao;
430 }
431
432 void LuaEntitySAO::step(float dtime, bool send_recommended)
433 {
434         if(!m_properties_sent)
435         {
436                 m_properties_sent = true;
437                 std::string str = getPropertyPacket();
438                 // create message and add to list
439                 ActiveObjectMessage aom(getId(), true, str);
440                 m_messages_out.push_back(aom);
441         }
442
443         m_last_sent_position_timer += dtime;
444         
445         if(m_prop.physical){
446                 core::aabbox3d<f32> box = m_prop.collisionbox;
447                 box.MinEdge *= BS;
448                 box.MaxEdge *= BS;
449                 collisionMoveResult moveresult;
450                 f32 pos_max_d = BS*0.25; // Distance per iteration
451                 f32 stepheight = 0; // Maximum climbable step height
452                 v3f p_pos = m_base_position;
453                 v3f p_velocity = m_velocity;
454                 v3f p_acceleration = m_acceleration;
455                 IGameDef *gamedef = m_env->getGameDef();
456                 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
457                                 pos_max_d, box, stepheight, dtime,
458                                 p_pos, p_velocity, p_acceleration);
459                 // Apply results
460                 m_base_position = p_pos;
461                 m_velocity = p_velocity;
462                 m_acceleration = p_acceleration;
463         } else {
464                 m_base_position += dtime * m_velocity + 0.5 * dtime
465                                 * dtime * m_acceleration;
466                 m_velocity += dtime * m_acceleration;
467         }
468
469         if(m_registered){
470                 lua_State *L = m_env->getLua();
471                 scriptapi_luaentity_step(L, m_id, dtime);
472         }
473
474         if(send_recommended == false)
475                 return;
476         
477         // TODO: force send when acceleration changes enough?
478         float minchange = 0.2*BS;
479         if(m_last_sent_position_timer > 1.0){
480                 minchange = 0.01*BS;
481         } else if(m_last_sent_position_timer > 0.2){
482                 minchange = 0.05*BS;
483         }
484         float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
485         move_d += m_last_sent_move_precision;
486         float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
487         if(move_d > minchange || vel_d > minchange ||
488                         fabs(m_yaw - m_last_sent_yaw) > 1.0){
489                 sendPosition(true, false);
490         }
491
492         if(m_armor_groups_sent == false){
493                 m_armor_groups_sent = true;
494                 std::string str = gob_cmd_update_armor_groups(
495                                 m_armor_groups);
496                 // create message and add to list
497                 ActiveObjectMessage aom(getId(), true, str);
498                 m_messages_out.push_back(aom);
499         }
500 }
501
502 std::string LuaEntitySAO::getClientInitializationData()
503 {
504         std::ostringstream os(std::ios::binary);
505         writeU8(os, 0); // version
506         os<<serializeString(""); // name
507         writeS16(os, getId()); //id
508         writeU8(os, 0); // is_player
509         writeV3F1000(os, m_base_position);
510         writeF1000(os, m_yaw);
511         writeS16(os, m_hp);
512         writeU8(os, 2); // number of messages stuffed in here
513         os<<serializeLongString(getPropertyPacket()); // message 1
514         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
515         // return result
516         return os.str();
517 }
518
519 std::string LuaEntitySAO::getStaticData()
520 {
521         verbosestream<<__FUNCTION_NAME<<std::endl;
522         std::ostringstream os(std::ios::binary);
523         // version
524         writeU8(os, 1);
525         // name
526         os<<serializeString(m_init_name);
527         // state
528         if(m_registered){
529                 lua_State *L = m_env->getLua();
530                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
531                 os<<serializeLongString(state);
532         } else {
533                 os<<serializeLongString(m_init_state);
534         }
535         // hp
536         writeS16(os, m_hp);
537         // velocity
538         writeV3F1000(os, m_velocity);
539         // yaw
540         writeF1000(os, m_yaw);
541         return os.str();
542 }
543
544 int LuaEntitySAO::punch(v3f dir,
545                 const ToolCapabilities *toolcap,
546                 ServerActiveObject *puncher,
547                 float time_from_last_punch)
548 {
549         if(!m_registered){
550                 // Delete unknown LuaEntities when punched
551                 m_removed = true;
552                 return 0;
553         }
554         
555         ItemStack *punchitem = NULL;
556         ItemStack punchitem_static;
557         if(puncher){
558                 punchitem_static = puncher->getWieldedItem();
559                 punchitem = &punchitem_static;
560         }
561
562         PunchDamageResult result = getPunchDamage(
563                         m_armor_groups,
564                         toolcap,
565                         punchitem,
566                         time_from_last_punch);
567         
568         if(result.did_punch)
569         {
570                 setHP(getHP() - result.damage);
571                 
572                 actionstream<<getDescription()<<" punched by "
573                                 <<puncher->getDescription()<<", damage "<<result.damage
574                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
575                 
576                 {
577                         std::string str = gob_cmd_punched(result.damage, getHP());
578                         // create message and add to list
579                         ActiveObjectMessage aom(getId(), true, str);
580                         m_messages_out.push_back(aom);
581                 }
582
583                 if(getHP() == 0)
584                         m_removed = true;
585         }
586
587         lua_State *L = m_env->getLua();
588         scriptapi_luaentity_punch(L, m_id, puncher,
589                         time_from_last_punch, toolcap, dir);
590
591         return result.wear;
592 }
593
594 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
595 {
596         if(!m_registered)
597                 return;
598         lua_State *L = m_env->getLua();
599         scriptapi_luaentity_rightclick(L, m_id, clicker);
600 }
601
602 void LuaEntitySAO::setPos(v3f pos)
603 {
604         m_base_position = pos;
605         sendPosition(false, true);
606 }
607
608 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
609 {
610         m_base_position = pos;
611         if(!continuous)
612                 sendPosition(true, true);
613 }
614
615 float LuaEntitySAO::getMinimumSavedMovement()
616 {
617         return 0.1 * BS;
618 }
619
620 std::string LuaEntitySAO::getDescription()
621 {
622         std::ostringstream os(std::ios::binary);
623         os<<"LuaEntitySAO at (";
624         os<<(m_base_position.X/BS)<<",";
625         os<<(m_base_position.Y/BS)<<",";
626         os<<(m_base_position.Z/BS);
627         os<<")";
628         return os.str();
629 }
630
631 void LuaEntitySAO::setHP(s16 hp)
632 {
633         if(hp < 0) hp = 0;
634         m_hp = hp;
635 }
636
637 s16 LuaEntitySAO::getHP() const
638 {
639         return m_hp;
640 }
641
642 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
643 {
644         m_armor_groups = armor_groups;
645         m_armor_groups_sent = false;
646 }
647
648 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
649 {
650         std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
651         // create message and add to list
652         ActiveObjectMessage aom(getId(), true, str);
653         m_messages_out.push_back(aom);
654 }
655
656 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
657 {
658         std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
659         // create message and add to list
660         ActiveObjectMessage aom(getId(), true, str);
661         m_messages_out.push_back(aom);
662 }
663
664 // Part of the attachment structure, not used yet!
665 void LuaEntitySAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
666 {
667         // Parent should be translated from a ServerActiveObject into something
668         // the client will recognize (as a ClientActiveObject) then sent in
669         // gob_cmd_set_attachment that way.
670
671         std::string str = gob_cmd_set_attachment(); // <- parameters here
672         // create message and add to list
673         ActiveObjectMessage aom(getId(), true, str);
674         m_messages_out.push_back(aom);
675 }
676
677 ObjectProperties* LuaEntitySAO::accessObjectProperties()
678 {
679         return &m_prop;
680 }
681
682 void LuaEntitySAO::notifyObjectPropertiesModified()
683 {
684         m_properties_sent = false;
685 }
686
687 void LuaEntitySAO::setVelocity(v3f velocity)
688 {
689         m_velocity = velocity;
690 }
691
692 v3f LuaEntitySAO::getVelocity()
693 {
694         return m_velocity;
695 }
696
697 void LuaEntitySAO::setAcceleration(v3f acceleration)
698 {
699         m_acceleration = acceleration;
700 }
701
702 v3f LuaEntitySAO::getAcceleration()
703 {
704         return m_acceleration;
705 }
706
707 void LuaEntitySAO::setYaw(float yaw)
708 {
709         m_yaw = yaw;
710 }
711
712 float LuaEntitySAO::getYaw()
713 {
714         return m_yaw;
715 }
716
717 void LuaEntitySAO::setTextureMod(const std::string &mod)
718 {
719         std::string str = gob_cmd_set_texture_mod(mod);
720         // create message and add to list
721         ActiveObjectMessage aom(getId(), true, str);
722         m_messages_out.push_back(aom);
723 }
724
725 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
726                 bool select_horiz_by_yawpitch)
727 {
728         std::string str = gob_cmd_set_sprite(
729                 p,
730                 num_frames,
731                 framelength,
732                 select_horiz_by_yawpitch
733         );
734         // create message and add to list
735         ActiveObjectMessage aom(getId(), true, str);
736         m_messages_out.push_back(aom);
737 }
738
739 std::string LuaEntitySAO::getName()
740 {
741         return m_init_name;
742 }
743
744 std::string LuaEntitySAO::getPropertyPacket()
745 {
746         return gob_cmd_set_properties(m_prop);
747 }
748
749 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
750 {
751         m_last_sent_move_precision = m_base_position.getDistanceFrom(
752                         m_last_sent_position);
753         m_last_sent_position_timer = 0;
754         m_last_sent_yaw = m_yaw;
755         m_last_sent_position = m_base_position;
756         m_last_sent_velocity = m_velocity;
757         //m_last_sent_acceleration = m_acceleration;
758
759         float update_interval = m_env->getSendRecommendedInterval();
760
761         std::string str = gob_cmd_update_position(
762                 m_base_position,
763                 m_velocity,
764                 m_acceleration,
765                 m_yaw,
766                 do_interpolate,
767                 is_movement_end,
768                 update_interval
769         );
770         // create message and add to list
771         ActiveObjectMessage aom(getId(), false, str);
772         m_messages_out.push_back(aom);
773 }
774
775 /*
776         PlayerSAO
777 */
778
779 // No prototype, PlayerSAO does not need to be deserialized
780
781 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
782                 const std::set<std::string> &privs, bool is_singleplayer):
783         ServerActiveObject(env_, v3f(0,0,0)),
784         m_player(player_),
785         m_peer_id(peer_id_),
786         m_inventory(NULL),
787         m_last_good_position(0,0,0),
788         m_last_good_position_age(0),
789         m_time_from_last_punch(0),
790         m_nocheat_dig_pos(32767, 32767, 32767),
791         m_nocheat_dig_time(0),
792         m_wield_index(0),
793         m_position_not_sent(false),
794         m_armor_groups_sent(false),
795         m_properties_sent(true),
796         m_privs(privs),
797         m_is_singleplayer(is_singleplayer),
798         // public
799         m_teleported(false),
800         m_inventory_not_sent(false),
801         m_hp_not_sent(false),
802         m_wielded_item_not_sent(false)
803 {
804         assert(m_player);
805         assert(m_peer_id != 0);
806         setBasePosition(m_player->getPosition());
807         m_inventory = &m_player->inventory;
808         m_armor_groups["choppy"] = 2;
809         m_armor_groups["fleshy"] = 3;
810
811         m_prop.hp_max = PLAYER_MAX_HP;
812         m_prop.physical = false;
813         m_prop.weight = 75;
814         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
815         // start of default appearance, this should be overwritten by LUA
816         m_prop.visual = "upright_sprite";
817         m_prop.visual_size = v2f(1, 2);
818         m_prop.textures.clear();
819         m_prop.textures.push_back("player.png");
820         m_prop.textures.push_back("player_back.png");
821         m_prop.colors.clear();
822         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
823         m_prop.spritediv = v2s16(1,1);
824         // end of default appearance
825         m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
826         m_prop.makes_footstep_sound = true;
827 }
828
829 PlayerSAO::~PlayerSAO()
830 {
831         if(m_inventory != &m_player->inventory)
832                 delete m_inventory;
833
834 }
835
836 std::string PlayerSAO::getDescription()
837 {
838         return std::string("player ") + m_player->getName();
839 }
840
841 // Called after id has been set and has been inserted in environment
842 void PlayerSAO::addedToEnvironment(u32 dtime_s)
843 {
844         ServerActiveObject::addedToEnvironment(dtime_s);
845         ServerActiveObject::setBasePosition(m_player->getPosition());
846         m_player->setPlayerSAO(this);
847         m_player->peer_id = m_peer_id;
848         m_last_good_position = m_player->getPosition();
849         m_last_good_position_age = 0.0;
850 }
851
852 // Called before removing from environment
853 void PlayerSAO::removingFromEnvironment()
854 {
855         ServerActiveObject::removingFromEnvironment();
856         if(m_player->getPlayerSAO() == this)
857         {
858                 m_player->setPlayerSAO(NULL);
859                 m_player->peer_id = 0;
860         }
861 }
862
863 bool PlayerSAO::isStaticAllowed() const
864 {
865         return false;
866 }
867
868 bool PlayerSAO::unlimitedTransferDistance() const
869 {
870         return g_settings->getBool("unlimited_player_transfer_distance");
871 }
872
873 std::string PlayerSAO::getClientInitializationData()
874 {
875         std::ostringstream os(std::ios::binary);
876         writeU8(os, 0); // version
877         os<<serializeString(m_player->getName()); // name
878         writeU8(os, 1); // is_player
879         writeS16(os, getId()); //id
880         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
881         writeF1000(os, m_player->getYaw());
882         writeS16(os, getHP());
883         writeU8(os, 2); // number of messages stuffed in here
884         os<<serializeLongString(getPropertyPacket()); // message 1
885         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
886         return os.str();
887 }
888
889 std::string PlayerSAO::getStaticData()
890 {
891         assert(0);
892         return "";
893 }
894
895 void PlayerSAO::step(float dtime, bool send_recommended)
896 {
897         if(!m_properties_sent)
898         {
899                 m_properties_sent = true;
900                 std::string str = getPropertyPacket();
901                 // create message and add to list
902                 ActiveObjectMessage aom(getId(), true, str);
903                 m_messages_out.push_back(aom);
904         }
905
906         m_time_from_last_punch += dtime;
907         m_nocheat_dig_time += dtime;
908         
909         if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
910         {
911                 m_last_good_position = m_player->getPosition();
912                 m_last_good_position_age = 0;
913         }
914         else
915         {
916                 /*
917                         Check player movements
918
919                         NOTE: Actually the server should handle player physics like the
920                         client does and compare player's position to what is calculated
921                         on our side. This is required when eg. players fly due to an
922                         explosion. Altough a node-based alternative might be possible
923                         too, and much more lightweight.
924                 */
925
926                 float player_max_speed = 0;
927                 float player_max_speed_up = 0;
928                 if(m_privs.count("fast") != 0){
929                         // Fast speed
930                         player_max_speed = BS * 20;
931                         player_max_speed_up = BS * 20;
932                 } else {
933                         // Normal speed
934                         player_max_speed = BS * 4.0;
935                         player_max_speed_up = BS * 4.0;
936                 }
937                 // Tolerance
938                 player_max_speed *= 2.5;
939                 player_max_speed_up *= 2.5;
940
941                 m_last_good_position_age += dtime;
942                 if(m_last_good_position_age >= 1.0){
943                         float age = m_last_good_position_age;
944                         v3f diff = (m_player->getPosition() - m_last_good_position);
945                         float d_vert = diff.Y;
946                         diff.Y = 0;
947                         float d_horiz = diff.getLength();
948                         /*infostream<<m_player->getName()<<"'s horizontal speed is "
949                                         <<(d_horiz/age)<<std::endl;*/
950                         if(d_horiz <= age * player_max_speed &&
951                                         (d_vert < 0 || d_vert < age * player_max_speed_up)){
952                                 m_last_good_position = m_player->getPosition();
953                         } else {
954                                 actionstream<<"Player "<<m_player->getName()
955                                                 <<" moved too fast; resetting position"
956                                                 <<std::endl;
957                                 m_player->setPosition(m_last_good_position);
958                                 m_teleported = true;
959                         }
960                         m_last_good_position_age = 0;
961                 }
962         }
963
964         if(send_recommended == false)
965                 return;
966
967         if(m_position_not_sent)
968         {
969                 m_position_not_sent = false;
970                 float update_interval = m_env->getSendRecommendedInterval();
971                 std::string str = gob_cmd_update_position(
972                         m_player->getPosition() + v3f(0,BS*1,0),
973                         v3f(0,0,0),
974                         v3f(0,0,0),
975                         m_player->getYaw(),
976                         true,
977                         false,
978                         update_interval
979                 );
980                 // create message and add to list
981                 ActiveObjectMessage aom(getId(), false, str);
982                 m_messages_out.push_back(aom);
983         }
984
985         if(m_wielded_item_not_sent)
986         {
987                 m_wielded_item_not_sent = false;
988                 // GenericCAO has no special way to show this
989         }
990
991         if(m_armor_groups_sent == false){
992                 m_armor_groups_sent = true;
993                 std::string str = gob_cmd_update_armor_groups(
994                                 m_armor_groups);
995                 // create message and add to list
996                 ActiveObjectMessage aom(getId(), true, str);
997                 m_messages_out.push_back(aom);
998         }
999 }
1000
1001 void PlayerSAO::setBasePosition(const v3f &position)
1002 {
1003         ServerActiveObject::setBasePosition(position);
1004         m_position_not_sent = true;
1005 }
1006
1007 void PlayerSAO::setPos(v3f pos)
1008 {
1009         m_player->setPosition(pos);
1010         // Movement caused by this command is always valid
1011         m_last_good_position = pos;
1012         m_last_good_position_age = 0;
1013         // Force position change on client
1014         m_teleported = true;
1015 }
1016
1017 void PlayerSAO::moveTo(v3f pos, bool continuous)
1018 {
1019         m_player->setPosition(pos);
1020         // Movement caused by this command is always valid
1021         m_last_good_position = pos;
1022         m_last_good_position_age = 0;
1023         // Force position change on client
1024         m_teleported = true;
1025 }
1026
1027 int PlayerSAO::punch(v3f dir,
1028         const ToolCapabilities *toolcap,
1029         ServerActiveObject *puncher,
1030         float time_from_last_punch)
1031 {
1032         if(!toolcap)
1033                 return 0;
1034
1035         // No effect if PvP disabled
1036         if(g_settings->getBool("enable_pvp") == false){
1037                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1038                         std::string str = gob_cmd_punched(0, getHP());
1039                         // create message and add to list
1040                         ActiveObjectMessage aom(getId(), true, str);
1041                         m_messages_out.push_back(aom);
1042                         return 0;
1043                 }
1044         }
1045
1046         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1047                         time_from_last_punch);
1048
1049         actionstream<<"Player "<<m_player->getName()<<" punched by "
1050                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1051                         <<" HP"<<std::endl;
1052
1053         setHP(getHP() - hitparams.hp);
1054
1055         if(hitparams.hp != 0)
1056         {
1057                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1058                 // create message and add to list
1059                 ActiveObjectMessage aom(getId(), true, str);
1060                 m_messages_out.push_back(aom);
1061         }
1062
1063         return hitparams.wear;
1064 }
1065
1066 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1067 {
1068 }
1069
1070 s16 PlayerSAO::getHP() const
1071 {
1072         return m_player->hp;
1073 }
1074
1075 void PlayerSAO::setHP(s16 hp)
1076 {
1077         s16 oldhp = m_player->hp;
1078
1079         if(hp < 0)
1080                 hp = 0;
1081         else if(hp > PLAYER_MAX_HP)
1082                 hp = PLAYER_MAX_HP;
1083
1084         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1085         {
1086                 m_hp_not_sent = true; // fix wrong prediction on client
1087                 return;
1088         }
1089
1090         m_player->hp = hp;
1091
1092         if(hp != oldhp)
1093                 m_hp_not_sent = true;
1094
1095         // On death or reincarnation send an active object message
1096         if((hp == 0) != (oldhp == 0))
1097         {
1098                 // Will send new is_visible value based on (getHP()!=0)
1099                 m_properties_sent = false;
1100                 // Send new HP
1101                 std::string str = gob_cmd_punched(0, getHP());
1102                 ActiveObjectMessage aom(getId(), true, str);
1103                 m_messages_out.push_back(aom);
1104         }
1105 }
1106
1107 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1108 {
1109         m_armor_groups = armor_groups;
1110         m_armor_groups_sent = false;
1111 }
1112
1113 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1114 {
1115         std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
1116         // create message and add to list
1117         ActiveObjectMessage aom(getId(), true, str);
1118         m_messages_out.push_back(aom);
1119 }
1120
1121 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1122 {
1123         std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
1124         // create message and add to list
1125         ActiveObjectMessage aom(getId(), true, str);
1126         m_messages_out.push_back(aom);
1127 }
1128
1129 // Part of the attachment structure, not used yet!
1130 void PlayerSAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
1131 {       
1132         std::string str = gob_cmd_set_attachment(); // <- parameters here
1133         // create message and add to list
1134         ActiveObjectMessage aom(getId(), true, str);
1135         m_messages_out.push_back(aom);
1136 }
1137
1138 ObjectProperties* PlayerSAO::accessObjectProperties()
1139 {
1140         return &m_prop;
1141 }
1142
1143 void PlayerSAO::notifyObjectPropertiesModified()
1144 {
1145         m_properties_sent = false;
1146 }
1147
1148 Inventory* PlayerSAO::getInventory()
1149 {
1150         return m_inventory;
1151 }
1152 const Inventory* PlayerSAO::getInventory() const
1153 {
1154         return m_inventory;
1155 }
1156
1157 InventoryLocation PlayerSAO::getInventoryLocation() const
1158 {
1159         InventoryLocation loc;
1160         loc.setPlayer(m_player->getName());
1161         return loc;
1162 }
1163
1164 void PlayerSAO::setInventoryModified()
1165 {
1166         m_inventory_not_sent = true;
1167 }
1168
1169 std::string PlayerSAO::getWieldList() const
1170 {
1171         return "main";
1172 }
1173
1174 int PlayerSAO::getWieldIndex() const
1175 {
1176         return m_wield_index;
1177 }
1178
1179 void PlayerSAO::setWieldIndex(int i)
1180 {
1181         if(i != m_wield_index)
1182         {
1183                 m_wield_index = i;
1184                 m_wielded_item_not_sent = true;
1185         }
1186 }
1187
1188 void PlayerSAO::disconnected()
1189 {
1190         m_peer_id = 0;
1191         m_removed = true;
1192         if(m_player->getPlayerSAO() == this)
1193         {
1194                 m_player->setPlayerSAO(NULL);
1195                 m_player->peer_id = 0;
1196         }
1197 }
1198
1199 std::string PlayerSAO::getPropertyPacket()
1200 {
1201         m_prop.is_visible = (getHP() != 0);
1202         return gob_cmd_set_properties(m_prop);
1203 }
1204