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