Get the new animation framework properly working
[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         writeU8(os, 0); // is_player
508         writeV3F1000(os, m_base_position);
509         writeF1000(os, m_yaw);
510         writeS16(os, m_hp);
511         writeU8(os, 2); // number of messages stuffed in here
512         os<<serializeLongString(getPropertyPacket()); // message 1
513         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
514         // return result
515         return os.str();
516 }
517
518 std::string LuaEntitySAO::getStaticData()
519 {
520         verbosestream<<__FUNCTION_NAME<<std::endl;
521         std::ostringstream os(std::ios::binary);
522         // version
523         writeU8(os, 1);
524         // name
525         os<<serializeString(m_init_name);
526         // state
527         if(m_registered){
528                 lua_State *L = m_env->getLua();
529                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
530                 os<<serializeLongString(state);
531         } else {
532                 os<<serializeLongString(m_init_state);
533         }
534         // hp
535         writeS16(os, m_hp);
536         // velocity
537         writeV3F1000(os, m_velocity);
538         // yaw
539         writeF1000(os, m_yaw);
540         return os.str();
541 }
542
543 int LuaEntitySAO::punch(v3f dir,
544                 const ToolCapabilities *toolcap,
545                 ServerActiveObject *puncher,
546                 float time_from_last_punch)
547 {
548         if(!m_registered){
549                 // Delete unknown LuaEntities when punched
550                 m_removed = true;
551                 return 0;
552         }
553         
554         ItemStack *punchitem = NULL;
555         ItemStack punchitem_static;
556         if(puncher){
557                 punchitem_static = puncher->getWieldedItem();
558                 punchitem = &punchitem_static;
559         }
560
561         PunchDamageResult result = getPunchDamage(
562                         m_armor_groups,
563                         toolcap,
564                         punchitem,
565                         time_from_last_punch);
566         
567         if(result.did_punch)
568         {
569                 setHP(getHP() - result.damage);
570                 
571                 actionstream<<getDescription()<<" punched by "
572                                 <<puncher->getDescription()<<", damage "<<result.damage
573                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
574                 
575                 {
576                         std::string str = gob_cmd_punched(result.damage, getHP());
577                         // create message and add to list
578                         ActiveObjectMessage aom(getId(), true, str);
579                         m_messages_out.push_back(aom);
580                 }
581
582                 if(getHP() == 0)
583                         m_removed = true;
584         }
585
586         lua_State *L = m_env->getLua();
587         scriptapi_luaentity_punch(L, m_id, puncher,
588                         time_from_last_punch, toolcap, dir);
589
590         return result.wear;
591 }
592
593 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
594 {
595         if(!m_registered)
596                 return;
597         lua_State *L = m_env->getLua();
598         scriptapi_luaentity_rightclick(L, m_id, clicker);
599 }
600
601 void LuaEntitySAO::setPos(v3f pos)
602 {
603         m_base_position = pos;
604         sendPosition(false, true);
605 }
606
607 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
608 {
609         m_base_position = pos;
610         if(!continuous)
611                 sendPosition(true, true);
612 }
613
614 float LuaEntitySAO::getMinimumSavedMovement()
615 {
616         return 0.1 * BS;
617 }
618
619 std::string LuaEntitySAO::getDescription()
620 {
621         std::ostringstream os(std::ios::binary);
622         os<<"LuaEntitySAO at (";
623         os<<(m_base_position.X/BS)<<",";
624         os<<(m_base_position.Y/BS)<<",";
625         os<<(m_base_position.Z/BS);
626         os<<")";
627         return os.str();
628 }
629
630 void LuaEntitySAO::setHP(s16 hp)
631 {
632         if(hp < 0) hp = 0;
633         m_hp = hp;
634 }
635
636 s16 LuaEntitySAO::getHP() const
637 {
638         return m_hp;
639 }
640
641 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
642 {
643         m_armor_groups = armor_groups;
644         m_armor_groups_sent = false;
645 }
646
647 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
648 {
649         std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
650         // create message and add to list
651         ActiveObjectMessage aom(getId(), true, str);
652         m_messages_out.push_back(aom);
653 }
654
655 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
656 {
657         std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
658         // create message and add to list
659         ActiveObjectMessage aom(getId(), true, str);
660         m_messages_out.push_back(aom);
661 }
662
663 ObjectProperties* LuaEntitySAO::accessObjectProperties()
664 {
665         return &m_prop;
666 }
667
668 void LuaEntitySAO::notifyObjectPropertiesModified()
669 {
670         m_properties_sent = false;
671 }
672
673 void LuaEntitySAO::setVelocity(v3f velocity)
674 {
675         m_velocity = velocity;
676 }
677
678 v3f LuaEntitySAO::getVelocity()
679 {
680         return m_velocity;
681 }
682
683 void LuaEntitySAO::setAcceleration(v3f acceleration)
684 {
685         m_acceleration = acceleration;
686 }
687
688 v3f LuaEntitySAO::getAcceleration()
689 {
690         return m_acceleration;
691 }
692
693 void LuaEntitySAO::setYaw(float yaw)
694 {
695         m_yaw = yaw;
696 }
697
698 float LuaEntitySAO::getYaw()
699 {
700         return m_yaw;
701 }
702
703 void LuaEntitySAO::setTextureMod(const std::string &mod)
704 {
705         std::string str = gob_cmd_set_texture_mod(mod);
706         // create message and add to list
707         ActiveObjectMessage aom(getId(), true, str);
708         m_messages_out.push_back(aom);
709 }
710
711 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
712                 bool select_horiz_by_yawpitch)
713 {
714         std::string str = gob_cmd_set_sprite(
715                 p,
716                 num_frames,
717                 framelength,
718                 select_horiz_by_yawpitch
719         );
720         // create message and add to list
721         ActiveObjectMessage aom(getId(), true, str);
722         m_messages_out.push_back(aom);
723 }
724
725 std::string LuaEntitySAO::getName()
726 {
727         return m_init_name;
728 }
729
730 std::string LuaEntitySAO::getPropertyPacket()
731 {
732         return gob_cmd_set_properties(m_prop);
733 }
734
735 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
736 {
737         m_last_sent_move_precision = m_base_position.getDistanceFrom(
738                         m_last_sent_position);
739         m_last_sent_position_timer = 0;
740         m_last_sent_yaw = m_yaw;
741         m_last_sent_position = m_base_position;
742         m_last_sent_velocity = m_velocity;
743         //m_last_sent_acceleration = m_acceleration;
744
745         float update_interval = m_env->getSendRecommendedInterval();
746
747         std::string str = gob_cmd_update_position(
748                 m_base_position,
749                 m_velocity,
750                 m_acceleration,
751                 m_yaw,
752                 do_interpolate,
753                 is_movement_end,
754                 update_interval
755         );
756         // create message and add to list
757         ActiveObjectMessage aom(getId(), false, str);
758         m_messages_out.push_back(aom);
759 }
760
761 /*
762         PlayerSAO
763 */
764
765 // No prototype, PlayerSAO does not need to be deserialized
766
767 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
768                 const std::set<std::string> &privs, bool is_singleplayer):
769         ServerActiveObject(env_, v3f(0,0,0)),
770         m_player(player_),
771         m_peer_id(peer_id_),
772         m_inventory(NULL),
773         m_last_good_position(0,0,0),
774         m_last_good_position_age(0),
775         m_time_from_last_punch(0),
776         m_nocheat_dig_pos(32767, 32767, 32767),
777         m_nocheat_dig_time(0),
778         m_wield_index(0),
779         m_position_not_sent(false),
780         m_armor_groups_sent(false),
781         m_properties_sent(true),
782         m_privs(privs),
783         m_is_singleplayer(is_singleplayer),
784         // public
785         m_teleported(false),
786         m_inventory_not_sent(false),
787         m_hp_not_sent(false),
788         m_wielded_item_not_sent(false)
789 {
790         assert(m_player);
791         assert(m_peer_id != 0);
792         setBasePosition(m_player->getPosition());
793         m_inventory = &m_player->inventory;
794         m_armor_groups["choppy"] = 2;
795         m_armor_groups["fleshy"] = 3;
796
797         m_prop.hp_max = PLAYER_MAX_HP;
798         m_prop.physical = false;
799         m_prop.weight = 75;
800         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
801         // start of default appearance, this should be overwritten by LUA
802         m_prop.visual = "upright_sprite";
803         m_prop.visual_size = v2f(1, 2);
804         m_prop.textures.clear();
805         m_prop.textures.push_back("player.png");
806         m_prop.textures.push_back("player_back.png");
807         m_prop.spritediv = v2s16(1,1);
808         // end of default appearance
809         m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
810         m_prop.makes_footstep_sound = true;
811 }
812
813 PlayerSAO::~PlayerSAO()
814 {
815         if(m_inventory != &m_player->inventory)
816                 delete m_inventory;
817
818 }
819
820 std::string PlayerSAO::getDescription()
821 {
822         return std::string("player ") + m_player->getName();
823 }
824
825 // Called after id has been set and has been inserted in environment
826 void PlayerSAO::addedToEnvironment(u32 dtime_s)
827 {
828         ServerActiveObject::addedToEnvironment(dtime_s);
829         ServerActiveObject::setBasePosition(m_player->getPosition());
830         m_player->setPlayerSAO(this);
831         m_player->peer_id = m_peer_id;
832         m_last_good_position = m_player->getPosition();
833         m_last_good_position_age = 0.0;
834 }
835
836 // Called before removing from environment
837 void PlayerSAO::removingFromEnvironment()
838 {
839         ServerActiveObject::removingFromEnvironment();
840         if(m_player->getPlayerSAO() == this)
841         {
842                 m_player->setPlayerSAO(NULL);
843                 m_player->peer_id = 0;
844         }
845 }
846
847 bool PlayerSAO::isStaticAllowed() const
848 {
849         return false;
850 }
851
852 bool PlayerSAO::unlimitedTransferDistance() const
853 {
854         return g_settings->getBool("unlimited_player_transfer_distance");
855 }
856
857 std::string PlayerSAO::getClientInitializationData()
858 {
859         std::ostringstream os(std::ios::binary);
860         writeU8(os, 0); // version
861         os<<serializeString(m_player->getName()); // name
862         writeU8(os, 1); // is_player
863         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
864         writeF1000(os, m_player->getYaw());
865         writeS16(os, getHP());
866         writeU8(os, 2); // number of messages stuffed in here
867         os<<serializeLongString(getPropertyPacket()); // message 1
868         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
869         return os.str();
870 }
871
872 std::string PlayerSAO::getStaticData()
873 {
874         assert(0);
875         return "";
876 }
877
878 void PlayerSAO::step(float dtime, bool send_recommended)
879 {
880         if(!m_properties_sent)
881         {
882                 m_properties_sent = true;
883                 std::string str = getPropertyPacket();
884                 // create message and add to list
885                 ActiveObjectMessage aom(getId(), true, str);
886                 m_messages_out.push_back(aom);
887         }
888
889         m_time_from_last_punch += dtime;
890         m_nocheat_dig_time += dtime;
891         
892         if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
893         {
894                 m_last_good_position = m_player->getPosition();
895                 m_last_good_position_age = 0;
896         }
897         else
898         {
899                 /*
900                         Check player movements
901
902                         NOTE: Actually the server should handle player physics like the
903                         client does and compare player's position to what is calculated
904                         on our side. This is required when eg. players fly due to an
905                         explosion. Altough a node-based alternative might be possible
906                         too, and much more lightweight.
907                 */
908
909                 float player_max_speed = 0;
910                 float player_max_speed_up = 0;
911                 if(m_privs.count("fast") != 0){
912                         // Fast speed
913                         player_max_speed = BS * 20;
914                         player_max_speed_up = BS * 20;
915                 } else {
916                         // Normal speed
917                         player_max_speed = BS * 4.0;
918                         player_max_speed_up = BS * 4.0;
919                 }
920                 // Tolerance
921                 player_max_speed *= 2.5;
922                 player_max_speed_up *= 2.5;
923
924                 m_last_good_position_age += dtime;
925                 if(m_last_good_position_age >= 1.0){
926                         float age = m_last_good_position_age;
927                         v3f diff = (m_player->getPosition() - m_last_good_position);
928                         float d_vert = diff.Y;
929                         diff.Y = 0;
930                         float d_horiz = diff.getLength();
931                         /*infostream<<m_player->getName()<<"'s horizontal speed is "
932                                         <<(d_horiz/age)<<std::endl;*/
933                         if(d_horiz <= age * player_max_speed &&
934                                         (d_vert < 0 || d_vert < age * player_max_speed_up)){
935                                 m_last_good_position = m_player->getPosition();
936                         } else {
937                                 actionstream<<"Player "<<m_player->getName()
938                                                 <<" moved too fast; resetting position"
939                                                 <<std::endl;
940                                 m_player->setPosition(m_last_good_position);
941                                 m_teleported = true;
942                         }
943                         m_last_good_position_age = 0;
944                 }
945         }
946
947         if(send_recommended == false)
948                 return;
949
950         if(m_position_not_sent)
951         {
952                 m_position_not_sent = false;
953                 float update_interval = m_env->getSendRecommendedInterval();
954                 std::string str = gob_cmd_update_position(
955                         m_player->getPosition() + v3f(0,BS*1,0),
956                         v3f(0,0,0),
957                         v3f(0,0,0),
958                         m_player->getYaw(),
959                         true,
960                         false,
961                         update_interval
962                 );
963                 // create message and add to list
964                 ActiveObjectMessage aom(getId(), false, str);
965                 m_messages_out.push_back(aom);
966         }
967
968         if(m_wielded_item_not_sent)
969         {
970                 m_wielded_item_not_sent = false;
971                 // GenericCAO has no special way to show this
972         }
973
974         if(m_armor_groups_sent == false){
975                 m_armor_groups_sent = true;
976                 std::string str = gob_cmd_update_armor_groups(
977                                 m_armor_groups);
978                 // create message and add to list
979                 ActiveObjectMessage aom(getId(), true, str);
980                 m_messages_out.push_back(aom);
981         }
982 }
983
984 void PlayerSAO::setBasePosition(const v3f &position)
985 {
986         ServerActiveObject::setBasePosition(position);
987         m_position_not_sent = true;
988 }
989
990 void PlayerSAO::setPos(v3f pos)
991 {
992         m_player->setPosition(pos);
993         // Movement caused by this command is always valid
994         m_last_good_position = pos;
995         m_last_good_position_age = 0;
996         // Force position change on client
997         m_teleported = true;
998 }
999
1000 void PlayerSAO::moveTo(v3f pos, bool continuous)
1001 {
1002         m_player->setPosition(pos);
1003         // Movement caused by this command is always valid
1004         m_last_good_position = pos;
1005         m_last_good_position_age = 0;
1006         // Force position change on client
1007         m_teleported = true;
1008 }
1009
1010 int PlayerSAO::punch(v3f dir,
1011         const ToolCapabilities *toolcap,
1012         ServerActiveObject *puncher,
1013         float time_from_last_punch)
1014 {
1015         if(!toolcap)
1016                 return 0;
1017
1018         // No effect if PvP disabled
1019         if(g_settings->getBool("enable_pvp") == false){
1020                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1021                         std::string str = gob_cmd_punched(0, getHP());
1022                         // create message and add to list
1023                         ActiveObjectMessage aom(getId(), true, str);
1024                         m_messages_out.push_back(aom);
1025                         return 0;
1026                 }
1027         }
1028
1029         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1030                         time_from_last_punch);
1031
1032         actionstream<<"Player "<<m_player->getName()<<" punched by "
1033                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1034                         <<" HP"<<std::endl;
1035
1036         setHP(getHP() - hitparams.hp);
1037
1038         if(hitparams.hp != 0)
1039         {
1040                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1041                 // create message and add to list
1042                 ActiveObjectMessage aom(getId(), true, str);
1043                 m_messages_out.push_back(aom);
1044         }
1045
1046         return hitparams.wear;
1047 }
1048
1049 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1050 {
1051 }
1052
1053 s16 PlayerSAO::getHP() const
1054 {
1055         return m_player->hp;
1056 }
1057
1058 void PlayerSAO::setHP(s16 hp)
1059 {
1060         s16 oldhp = m_player->hp;
1061
1062         if(hp < 0)
1063                 hp = 0;
1064         else if(hp > PLAYER_MAX_HP)
1065                 hp = PLAYER_MAX_HP;
1066
1067         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1068         {
1069                 m_hp_not_sent = true; // fix wrong prediction on client
1070                 return;
1071         }
1072
1073         m_player->hp = hp;
1074
1075         if(hp != oldhp)
1076                 m_hp_not_sent = true;
1077
1078         // On death or reincarnation send an active object message
1079         if((hp == 0) != (oldhp == 0))
1080         {
1081                 // Will send new is_visible value based on (getHP()!=0)
1082                 m_properties_sent = false;
1083                 // Send new HP
1084                 std::string str = gob_cmd_punched(0, getHP());
1085                 ActiveObjectMessage aom(getId(), true, str);
1086                 m_messages_out.push_back(aom);
1087         }
1088 }
1089
1090 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1091 {
1092         m_armor_groups = armor_groups;
1093         m_armor_groups_sent = false;
1094 }
1095
1096 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1097 {
1098         std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
1099         // create message and add to list
1100         ActiveObjectMessage aom(getId(), true, str);
1101         m_messages_out.push_back(aom);
1102 }
1103
1104 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1105 {
1106         std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
1107         // create message and add to list
1108         ActiveObjectMessage aom(getId(), true, str);
1109         m_messages_out.push_back(aom);
1110 }
1111
1112 ObjectProperties* PlayerSAO::accessObjectProperties()
1113 {
1114         return &m_prop;
1115 }
1116
1117 void PlayerSAO::notifyObjectPropertiesModified()
1118 {
1119         m_properties_sent = false;
1120 }
1121
1122 Inventory* PlayerSAO::getInventory()
1123 {
1124         return m_inventory;
1125 }
1126 const Inventory* PlayerSAO::getInventory() const
1127 {
1128         return m_inventory;
1129 }
1130
1131 InventoryLocation PlayerSAO::getInventoryLocation() const
1132 {
1133         InventoryLocation loc;
1134         loc.setPlayer(m_player->getName());
1135         return loc;
1136 }
1137
1138 void PlayerSAO::setInventoryModified()
1139 {
1140         m_inventory_not_sent = true;
1141 }
1142
1143 std::string PlayerSAO::getWieldList() const
1144 {
1145         return "main";
1146 }
1147
1148 int PlayerSAO::getWieldIndex() const
1149 {
1150         return m_wield_index;
1151 }
1152
1153 void PlayerSAO::setWieldIndex(int i)
1154 {
1155         if(i != m_wield_index)
1156         {
1157                 m_wield_index = i;
1158                 m_wielded_item_not_sent = true;
1159         }
1160 }
1161
1162 void PlayerSAO::disconnected()
1163 {
1164         m_peer_id = 0;
1165         m_removed = true;
1166         if(m_player->getPlayerSAO() == this)
1167         {
1168                 m_player->setPlayerSAO(NULL);
1169                 m_player->peer_id = 0;
1170         }
1171 }
1172
1173 std::string PlayerSAO::getPropertyPacket()
1174 {
1175         m_prop.is_visible = (getHP() != 0);
1176         return gob_cmd_set_properties(m_prop);
1177 }
1178