]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_sao.cpp
Add server-side enforcement of the 'fast' privilege; also fix client checking 'fly...
[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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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
33 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
34
35 /*
36         DummyLoadSAO
37 */
38
39 class DummyLoadSAO : public ServerActiveObject
40 {
41 public:
42         DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
43                 ServerActiveObject(env, pos)
44         {
45                 ServerActiveObject::registerType(type, create);
46         }
47         // Pretend to be the test object (to fool the client)
48         u8 getType() const
49         { return ACTIVEOBJECT_TYPE_TEST; }
50         // And never save to disk
51         bool isStaticAllowed() const
52         { return false; }
53         
54         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55                         const std::string &data)
56         {
57                 return new DummyLoadSAO(env, pos, 0);
58         }
59
60         void step(float dtime, bool send_recommended)
61         {
62                 m_removed = true;
63                 infostream<<"DummyLoadSAO step"<<std::endl;
64         }
65
66 private:
67 };
68
69 // Prototype (registers item for deserialization)
70 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
71 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
72 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
73 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
74
75 /*
76         TestSAO
77 */
78
79 class TestSAO : public ServerActiveObject
80 {
81 public:
82         TestSAO(ServerEnvironment *env, v3f pos):
83                 ServerActiveObject(env, pos),
84                 m_timer1(0),
85                 m_age(0)
86         {
87                 ServerActiveObject::registerType(getType(), create);
88         }
89         u8 getType() const
90         { return ACTIVEOBJECT_TYPE_TEST; }
91         
92         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
93                         const std::string &data)
94         {
95                 return new TestSAO(env, pos);
96         }
97
98         void step(float dtime, bool send_recommended)
99         {
100                 m_age += dtime;
101                 if(m_age > 10)
102                 {
103                         m_removed = true;
104                         return;
105                 }
106
107                 m_base_position.Y += dtime * BS * 2;
108                 if(m_base_position.Y > 8*BS)
109                         m_base_position.Y = 2*BS;
110
111                 if(send_recommended == false)
112                         return;
113
114                 m_timer1 -= dtime;
115                 if(m_timer1 < 0.0)
116                 {
117                         m_timer1 += 0.125;
118
119                         std::string data;
120
121                         data += itos(0); // 0 = position
122                         data += " ";
123                         data += itos(m_base_position.X);
124                         data += " ";
125                         data += itos(m_base_position.Y);
126                         data += " ";
127                         data += itos(m_base_position.Z);
128
129                         ActiveObjectMessage aom(getId(), false, data);
130                         m_messages_out.push_back(aom);
131                 }
132         }
133
134 private:
135         float m_timer1;
136         float m_age;
137 };
138
139 // Prototype (registers item for deserialization)
140 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
141
142 /*
143         ItemSAO
144 */
145
146 class ItemSAO : public ServerActiveObject
147 {
148 public:
149         u8 getType() const
150         { return ACTIVEOBJECT_TYPE_ITEM; }
151         
152         float getMinimumSavedMovement()
153         { return 0.1*BS; }
154
155         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
156                         const std::string &data)
157         {
158                 std::istringstream is(data, std::ios::binary);
159                 char buf[1];
160                 // read version
161                 is.read(buf, 1);
162                 u8 version = buf[0];
163                 // check if version is supported
164                 if(version != 0)
165                         return NULL;
166                 std::string itemstring = deSerializeString(is);
167                 infostream<<"create(): Creating item \""
168                                 <<itemstring<<"\""<<std::endl;
169                 return new ItemSAO(env, pos, itemstring);
170         }
171
172         ItemSAO(ServerEnvironment *env, v3f pos,
173                         const std::string itemstring):
174                 ServerActiveObject(env, pos),
175                 m_itemstring(itemstring),
176                 m_itemstring_changed(false),
177                 m_speed_f(0,0,0),
178                 m_last_sent_position(0,0,0)
179         {
180                 ServerActiveObject::registerType(getType(), create);
181         }
182
183         void step(float dtime, bool send_recommended)
184         {
185                 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
186
187                 assert(m_env);
188
189                 const float interval = 0.2;
190                 if(m_move_interval.step(dtime, interval)==false)
191                         return;
192                 dtime = interval;
193                 
194                 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
195                 collisionMoveResult moveresult;
196                 // Apply gravity
197                 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
198                 // Maximum movement without glitches
199                 f32 pos_max_d = BS*0.25;
200                 // Limit speed
201                 if(m_speed_f.getLength()*dtime > pos_max_d)
202                         m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
203                 v3f pos_f = getBasePosition();
204                 v3f pos_f_old = pos_f;
205                 IGameDef *gamedef = m_env->getGameDef();
206                 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
207                                 pos_max_d, box, dtime, pos_f, m_speed_f);
208                 
209                 if(send_recommended == false)
210                         return;
211
212                 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
213                 {
214                         setBasePosition(pos_f);
215                         m_last_sent_position = pos_f;
216
217                         std::ostringstream os(std::ios::binary);
218                         // command (0 = update position)
219                         writeU8(os, 0);
220                         // pos
221                         writeV3F1000(os, m_base_position);
222                         // create message and add to list
223                         ActiveObjectMessage aom(getId(), false, os.str());
224                         m_messages_out.push_back(aom);
225                 }
226                 if(m_itemstring_changed)
227                 {
228                         m_itemstring_changed = false;
229
230                         std::ostringstream os(std::ios::binary);
231                         // command (1 = update itemstring)
232                         writeU8(os, 1);
233                         // itemstring
234                         os<<serializeString(m_itemstring);
235                         // create message and add to list
236                         ActiveObjectMessage aom(getId(), false, os.str());
237                         m_messages_out.push_back(aom);
238                 }
239         }
240
241         std::string getClientInitializationData()
242         {
243                 std::ostringstream os(std::ios::binary);
244                 // version
245                 writeU8(os, 0);
246                 // pos
247                 writeV3F1000(os, m_base_position);
248                 // itemstring
249                 os<<serializeString(m_itemstring);
250                 return os.str();
251         }
252
253         std::string getStaticData()
254         {
255                 infostream<<__FUNCTION_NAME<<std::endl;
256                 std::ostringstream os(std::ios::binary);
257                 // version
258                 writeU8(os, 0);
259                 // itemstring
260                 os<<serializeString(m_itemstring);
261                 return os.str();
262         }
263
264         ItemStack createItemStack()
265         {
266                 try{
267                         IItemDefManager *idef = m_env->getGameDef()->idef();
268                         ItemStack item;
269                         item.deSerialize(m_itemstring, idef);
270                         infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
271                                         <<"\" -> item=\""<<item.getItemString()<<"\""
272                                         <<std::endl;
273                         return item;
274                 }
275                 catch(SerializationError &e)
276                 {
277                         infostream<<__FUNCTION_NAME<<": serialization error: "
278                                         <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
279                         return ItemStack();
280                 }
281         }
282
283         int punch(v3f dir,
284                         const ToolCapabilities *toolcap,
285                         ServerActiveObject *puncher,
286                         float time_from_last_punch)
287         {
288                 // Directly delete item in creative mode
289                 if(g_settings->getBool("creative_mode") == true)
290                 {
291                         m_removed = true;
292                         return 0;
293                 }
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()
380 {
381         ServerActiveObject::addedToEnvironment();
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         }
393         
394         // Activate entity, supplying serialized state
395         scriptapi_luaentity_activate(L, m_id, m_init_state.c_str());
396 }
397
398 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
399                 const std::string &data)
400 {
401         std::string name;
402         std::string state;
403         s16 hp = 1;
404         v3f velocity;
405         float yaw = 0;
406         if(data != ""){
407                 std::istringstream is(data, std::ios::binary);
408                 // read version
409                 u8 version = readU8(is);
410                 // check if version is supported
411                 if(version == 0){
412                         name = deSerializeString(is);
413                         state = deSerializeLongString(is);
414                 }
415                 else if(version == 1){
416                         name = deSerializeString(is);
417                         state = deSerializeLongString(is);
418                         hp = readS16(is);
419                         velocity = readV3F1000(is);
420                         yaw = readF1000(is);
421                 }
422         }
423         // create object
424         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
425                         <<state<<"\")"<<std::endl;
426         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
427         sao->m_hp = hp;
428         sao->m_velocity = velocity;
429         sao->m_yaw = yaw;
430         return sao;
431 }
432
433 void LuaEntitySAO::step(float dtime, bool send_recommended)
434 {
435         if(!m_properties_sent)
436         {
437                 m_properties_sent = true;
438                 std::string str = getPropertyPacket();
439                 // create message and add to list
440                 ActiveObjectMessage aom(getId(), true, str);
441                 m_messages_out.push_back(aom);
442         }
443
444         m_last_sent_position_timer += dtime;
445         
446         if(m_prop.physical){
447                 core::aabbox3d<f32> box = m_prop.collisionbox;
448                 box.MinEdge *= BS;
449                 box.MaxEdge *= BS;
450                 collisionMoveResult moveresult;
451                 f32 pos_max_d = BS*0.25; // Distance per iteration
452                 v3f p_pos = getBasePosition();
453                 v3f p_velocity = m_velocity;
454                 IGameDef *gamedef = m_env->getGameDef();
455                 moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
456                                 pos_max_d, box, dtime, p_pos, p_velocity);
457                 // Apply results
458                 setBasePosition(p_pos);
459                 m_velocity = p_velocity;
460
461                 m_velocity += dtime * m_acceleration;
462         } else {
463                 m_base_position += dtime * m_velocity + 0.5 * dtime
464                                 * dtime * m_acceleration;
465                 m_velocity += dtime * m_acceleration;
466         }
467
468         if(m_registered){
469                 lua_State *L = m_env->getLua();
470                 scriptapi_luaentity_step(L, m_id, dtime);
471         }
472
473         if(send_recommended == false)
474                 return;
475         
476         // TODO: force send when acceleration changes enough?
477         float minchange = 0.2*BS;
478         if(m_last_sent_position_timer > 1.0){
479                 minchange = 0.01*BS;
480         } else if(m_last_sent_position_timer > 0.2){
481                 minchange = 0.05*BS;
482         }
483         float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
484         move_d += m_last_sent_move_precision;
485         float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
486         if(move_d > minchange || vel_d > minchange ||
487                         fabs(m_yaw - m_last_sent_yaw) > 1.0){
488                 sendPosition(true, false);
489         }
490
491         if(m_armor_groups_sent == false){
492                 m_armor_groups_sent = true;
493                 std::string str = gob_cmd_update_armor_groups(
494                                 m_armor_groups);
495                 // create message and add to list
496                 ActiveObjectMessage aom(getId(), true, str);
497                 m_messages_out.push_back(aom);
498         }
499 }
500
501 std::string LuaEntitySAO::getClientInitializationData()
502 {
503         std::ostringstream os(std::ios::binary);
504         writeU8(os, 0); // version
505         os<<serializeString(""); // name
506         writeU8(os, 0); // is_player
507         writeV3F1000(os, m_base_position);
508         writeF1000(os, m_yaw);
509         writeS16(os, m_hp);
510         writeU8(os, 2); // number of messages stuffed in here
511         os<<serializeLongString(getPropertyPacket()); // message 1
512         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
513         // return result
514         return os.str();
515 }
516
517 std::string LuaEntitySAO::getStaticData()
518 {
519         verbosestream<<__FUNCTION_NAME<<std::endl;
520         std::ostringstream os(std::ios::binary);
521         // version
522         writeU8(os, 1);
523         // name
524         os<<serializeString(m_init_name);
525         // state
526         if(m_registered){
527                 lua_State *L = m_env->getLua();
528                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
529                 os<<serializeLongString(state);
530         } else {
531                 os<<serializeLongString(m_init_state);
532         }
533         // hp
534         writeS16(os, m_hp);
535         // velocity
536         writeV3F1000(os, m_velocity);
537         // yaw
538         writeF1000(os, m_yaw);
539         return os.str();
540 }
541
542 int LuaEntitySAO::punch(v3f dir,
543                 const ToolCapabilities *toolcap,
544                 ServerActiveObject *puncher,
545                 float time_from_last_punch)
546 {
547         if(!m_registered){
548                 // Delete unknown LuaEntities when punched
549                 m_removed = true;
550                 return 0;
551         }
552         
553         ItemStack *punchitem = NULL;
554         ItemStack punchitem_static;
555         if(puncher){
556                 punchitem_static = puncher->getWieldedItem();
557                 punchitem = &punchitem_static;
558         }
559
560         PunchDamageResult result = getPunchDamage(
561                         m_armor_groups,
562                         toolcap,
563                         punchitem,
564                         time_from_last_punch);
565         
566         if(result.did_punch)
567         {
568                 setHP(getHP() - result.damage);
569                 
570                 actionstream<<getDescription()<<" punched by "
571                                 <<puncher->getDescription()<<", damage "<<result.damage
572                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
573                 
574                 {
575                         std::string str = gob_cmd_punched(result.damage, getHP());
576                         // create message and add to list
577                         ActiveObjectMessage aom(getId(), true, str);
578                         m_messages_out.push_back(aom);
579                 }
580
581                 if(getHP() == 0)
582                         m_removed = true;
583         }
584
585         lua_State *L = m_env->getLua();
586         scriptapi_luaentity_punch(L, m_id, puncher,
587                         time_from_last_punch, toolcap, dir);
588
589         return result.wear;
590 }
591
592 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
593 {
594         if(!m_registered)
595                 return;
596         lua_State *L = m_env->getLua();
597         scriptapi_luaentity_rightclick(L, m_id, clicker);
598 }
599
600 void LuaEntitySAO::setPos(v3f pos)
601 {
602         m_base_position = pos;
603         sendPosition(false, true);
604 }
605
606 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
607 {
608         m_base_position = pos;
609         if(!continuous)
610                 sendPosition(true, true);
611 }
612
613 float LuaEntitySAO::getMinimumSavedMovement()
614 {
615         return 0.1 * BS;
616 }
617
618 std::string LuaEntitySAO::getDescription()
619 {
620         std::ostringstream os(std::ios::binary);
621         os<<"LuaEntitySAO at (";
622         os<<(m_base_position.X/BS)<<",";
623         os<<(m_base_position.Y/BS)<<",";
624         os<<(m_base_position.Z/BS);
625         os<<")";
626         return os.str();
627 }
628
629 void LuaEntitySAO::setHP(s16 hp)
630 {
631         if(hp < 0) hp = 0;
632         m_hp = hp;
633 }
634
635 s16 LuaEntitySAO::getHP() const
636 {
637         return m_hp;
638 }
639
640 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
641 {
642         m_armor_groups = armor_groups;
643         m_armor_groups_sent = false;
644 }
645
646 ObjectProperties* LuaEntitySAO::accessObjectProperties()
647 {
648         return &m_prop;
649 }
650
651 void LuaEntitySAO::notifyObjectPropertiesModified()
652 {
653         m_properties_sent = false;
654 }
655
656 void LuaEntitySAO::setVelocity(v3f velocity)
657 {
658         m_velocity = velocity;
659 }
660
661 v3f LuaEntitySAO::getVelocity()
662 {
663         return m_velocity;
664 }
665
666 void LuaEntitySAO::setAcceleration(v3f acceleration)
667 {
668         m_acceleration = acceleration;
669 }
670
671 v3f LuaEntitySAO::getAcceleration()
672 {
673         return m_acceleration;
674 }
675
676 void LuaEntitySAO::setYaw(float yaw)
677 {
678         m_yaw = yaw;
679 }
680
681 float LuaEntitySAO::getYaw()
682 {
683         return m_yaw;
684 }
685
686 void LuaEntitySAO::setTextureMod(const std::string &mod)
687 {
688         std::string str = gob_cmd_set_texture_mod(mod);
689         // create message and add to list
690         ActiveObjectMessage aom(getId(), true, str);
691         m_messages_out.push_back(aom);
692 }
693
694 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
695                 bool select_horiz_by_yawpitch)
696 {
697         std::string str = gob_cmd_set_sprite(
698                 p,
699                 num_frames,
700                 framelength,
701                 select_horiz_by_yawpitch
702         );
703         // create message and add to list
704         ActiveObjectMessage aom(getId(), true, str);
705         m_messages_out.push_back(aom);
706 }
707
708 std::string LuaEntitySAO::getName()
709 {
710         return m_init_name;
711 }
712
713 std::string LuaEntitySAO::getPropertyPacket()
714 {
715         return gob_cmd_set_properties(m_prop);
716 }
717
718 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
719 {
720         m_last_sent_move_precision = m_base_position.getDistanceFrom(
721                         m_last_sent_position);
722         m_last_sent_position_timer = 0;
723         m_last_sent_yaw = m_yaw;
724         m_last_sent_position = m_base_position;
725         m_last_sent_velocity = m_velocity;
726         //m_last_sent_acceleration = m_acceleration;
727
728         float update_interval = m_env->getSendRecommendedInterval();
729
730         std::string str = gob_cmd_update_position(
731                 m_base_position,
732                 m_velocity,
733                 m_acceleration,
734                 m_yaw,
735                 do_interpolate,
736                 is_movement_end,
737                 update_interval
738         );
739         // create message and add to list
740         ActiveObjectMessage aom(getId(), false, str);
741         m_messages_out.push_back(aom);
742 }
743
744 /*
745         PlayerSAO
746 */
747
748 // No prototype, PlayerSAO does not need to be deserialized
749
750 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
751                 const std::set<std::string> &privs):
752         ServerActiveObject(env_, v3f(0,0,0)),
753         m_player(player_),
754         m_peer_id(peer_id_),
755         m_inventory(NULL),
756         m_last_good_position(0,0,0),
757         m_last_good_position_age(0),
758         m_time_from_last_punch(0),
759         m_wield_index(0),
760         m_position_not_sent(false),
761         m_armor_groups_sent(false),
762         m_properties_sent(true),
763         m_privs(privs),
764         // public
765         m_teleported(false),
766         m_inventory_not_sent(false),
767         m_hp_not_sent(false),
768         m_wielded_item_not_sent(false)
769 {
770         assert(m_player);
771         assert(m_peer_id != 0);
772         setBasePosition(m_player->getPosition());
773         m_inventory = &m_player->inventory;
774         m_armor_groups["choppy"] = 2;
775         m_armor_groups["fleshy"] = 3;
776
777         m_prop.hp_max = PLAYER_MAX_HP;
778         m_prop.physical = false;
779         m_prop.weight = 75;
780         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
781         m_prop.visual = "upright_sprite";
782         m_prop.visual_size = v2f(1, 2);
783         m_prop.textures.clear();
784         m_prop.textures.push_back("player.png");
785         m_prop.textures.push_back("player_back.png");
786         m_prop.spritediv = v2s16(1,1);
787         m_prop.is_visible = (getHP() != 0);
788         m_prop.makes_footstep_sound = true;
789 }
790
791 PlayerSAO::~PlayerSAO()
792 {
793         if(m_inventory != &m_player->inventory)
794                 delete m_inventory;
795
796 }
797
798 std::string PlayerSAO::getDescription()
799 {
800         return std::string("player ") + m_player->getName();
801 }
802
803 // Called after id has been set and has been inserted in environment
804 void PlayerSAO::addedToEnvironment()
805 {
806         ServerActiveObject::addedToEnvironment();
807         ServerActiveObject::setBasePosition(m_player->getPosition());
808         m_player->setPlayerSAO(this);
809         m_player->peer_id = m_peer_id;
810         m_last_good_position = m_player->getPosition();
811         m_last_good_position_age = 0.0;
812 }
813
814 // Called before removing from environment
815 void PlayerSAO::removingFromEnvironment()
816 {
817         ServerActiveObject::removingFromEnvironment();
818         if(m_player->getPlayerSAO() == this)
819         {
820                 m_player->setPlayerSAO(NULL);
821                 m_player->peer_id = 0;
822         }
823 }
824
825 bool PlayerSAO::isStaticAllowed() const
826 {
827         return false;
828 }
829
830 bool PlayerSAO::unlimitedTransferDistance() const
831 {
832         return g_settings->getBool("unlimited_player_transfer_distance");
833 }
834
835 std::string PlayerSAO::getClientInitializationData()
836 {
837         std::ostringstream os(std::ios::binary);
838         writeU8(os, 0); // version
839         os<<serializeString(m_player->getName()); // name
840         writeU8(os, 1); // is_player
841         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
842         writeF1000(os, m_player->getYaw());
843         writeS16(os, getHP());
844         writeU8(os, 2); // number of messages stuffed in here
845         os<<serializeLongString(getPropertyPacket()); // message 1
846         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
847         return os.str();
848 }
849
850 std::string PlayerSAO::getStaticData()
851 {
852         assert(0);
853         return "";
854 }
855
856 void PlayerSAO::step(float dtime, bool send_recommended)
857 {
858         if(!m_properties_sent)
859         {
860                 m_properties_sent = true;
861                 std::string str = getPropertyPacket();
862                 // create message and add to list
863                 ActiveObjectMessage aom(getId(), true, str);
864                 m_messages_out.push_back(aom);
865         }
866
867         m_time_from_last_punch += dtime;
868
869         /*
870                 Check player movements
871
872                 NOTE: Actually the server should handle player physics like the
873                 client does and compare player's position to what is calculated
874                 on our side. This is required when eg. players fly due to an
875                 explosion.
876         */
877
878         float player_max_speed = 0;
879         float player_max_speed_up = 0;
880         if(m_privs.count("fast") != 0){
881                 // Fast speed
882                 player_max_speed = BS * 20;
883                 player_max_speed_up = BS * 20;
884         } else {
885                 // Normal speed
886                 player_max_speed = BS * 4.0;
887                 player_max_speed_up = BS * 4.0;
888         }
889         // Tolerance
890         player_max_speed *= 2.5;
891         player_max_speed_up *= 2.5;
892
893         m_last_good_position_age += dtime;
894         if(m_last_good_position_age >= 1.0){
895                 float age = m_last_good_position_age;
896                 v3f diff = (m_player->getPosition() - m_last_good_position);
897                 float d_vert = diff.Y;
898                 diff.Y = 0;
899                 float d_horiz = diff.getLength();
900                 /*infostream<<m_player->getName()<<"'s horizontal speed is "
901                                 <<(d_horiz/age)<<std::endl;*/
902                 if(d_horiz <= age * player_max_speed &&
903                                 (d_vert < 0 || d_vert < age * player_max_speed_up)){
904                         m_last_good_position = m_player->getPosition();
905                 } else {
906                         actionstream<<"Player "<<m_player->getName()
907                                         <<" moved too fast; resetting position"
908                                         <<std::endl;
909                         m_player->setPosition(m_last_good_position);
910                         m_teleported = true;
911                 }
912                 m_last_good_position_age = 0;
913         }
914
915         if(send_recommended == false)
916                 return;
917
918         if(m_position_not_sent)
919         {
920                 m_position_not_sent = false;
921                 float update_interval = m_env->getSendRecommendedInterval();
922                 std::string str = gob_cmd_update_position(
923                         m_player->getPosition() + v3f(0,BS*1,0),
924                         v3f(0,0,0),
925                         v3f(0,0,0),
926                         m_player->getYaw(),
927                         true,
928                         false,
929                         update_interval
930                 );
931                 // create message and add to list
932                 ActiveObjectMessage aom(getId(), false, str);
933                 m_messages_out.push_back(aom);
934         }
935
936         if(m_wielded_item_not_sent)
937         {
938                 m_wielded_item_not_sent = false;
939                 // GenericCAO has no special way to show this
940         }
941
942         if(m_armor_groups_sent == false){
943                 m_armor_groups_sent = true;
944                 std::string str = gob_cmd_update_armor_groups(
945                                 m_armor_groups);
946                 // create message and add to list
947                 ActiveObjectMessage aom(getId(), true, str);
948                 m_messages_out.push_back(aom);
949         }
950 }
951
952 void PlayerSAO::setBasePosition(const v3f &position)
953 {
954         ServerActiveObject::setBasePosition(position);
955         m_position_not_sent = true;
956 }
957
958 void PlayerSAO::setPos(v3f pos)
959 {
960         m_player->setPosition(pos);
961         // Movement caused by this command is always valid
962         m_last_good_position = pos;
963         m_last_good_position_age = 0;
964         // Force position change on client
965         m_teleported = true;
966 }
967
968 void PlayerSAO::moveTo(v3f pos, bool continuous)
969 {
970         m_player->setPosition(pos);
971         // Movement caused by this command is always valid
972         m_last_good_position = pos;
973         m_last_good_position_age = 0;
974         // Force position change on client
975         m_teleported = true;
976 }
977
978 int PlayerSAO::punch(v3f dir,
979         const ToolCapabilities *toolcap,
980         ServerActiveObject *puncher,
981         float time_from_last_punch)
982 {
983         if(!toolcap)
984                 return 0;
985
986         // No effect if PvP disabled
987         if(g_settings->getBool("enable_pvp") == false){
988                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
989                         std::string str = gob_cmd_punched(0, getHP());
990                         // create message and add to list
991                         ActiveObjectMessage aom(getId(), true, str);
992                         m_messages_out.push_back(aom);
993                         return 0;
994                 }
995         }
996
997         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
998                         time_from_last_punch);
999
1000         actionstream<<"Player "<<m_player->getName()<<" punched by "
1001                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1002                         <<" HP"<<std::endl;
1003
1004         setHP(getHP() - hitparams.hp);
1005
1006         if(hitparams.hp != 0)
1007         {
1008                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1009                 // create message and add to list
1010                 ActiveObjectMessage aom(getId(), true, str);
1011                 m_messages_out.push_back(aom);
1012         }
1013
1014         return hitparams.wear;
1015 }
1016
1017 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1018 {
1019 }
1020
1021 s16 PlayerSAO::getHP() const
1022 {
1023         return m_player->hp;
1024 }
1025
1026 void PlayerSAO::setHP(s16 hp)
1027 {
1028         s16 oldhp = m_player->hp;
1029
1030         if(hp < 0)
1031                 hp = 0;
1032         else if(hp > PLAYER_MAX_HP)
1033                 hp = PLAYER_MAX_HP;
1034
1035         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1036         {
1037                 m_hp_not_sent = true; // fix wrong prediction on client
1038                 return;
1039         }
1040
1041         m_player->hp = hp;
1042
1043         if(hp != oldhp)
1044                 m_hp_not_sent = true;
1045
1046         // On death or reincarnation send an active object message
1047         if((hp == 0) != (oldhp == 0))
1048         {
1049                 // Will send new is_visible value based on (getHP()!=0)
1050                 m_properties_sent = false;
1051                 // Send new HP
1052                 std::string str = gob_cmd_punched(0, getHP());
1053                 ActiveObjectMessage aom(getId(), true, str);
1054                 m_messages_out.push_back(aom);
1055         }
1056 }
1057
1058 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1059 {
1060         m_armor_groups = armor_groups;
1061         m_armor_groups_sent = false;
1062 }
1063
1064 ObjectProperties* PlayerSAO::accessObjectProperties()
1065 {
1066         return &m_prop;
1067 }
1068
1069 void PlayerSAO::notifyObjectPropertiesModified()
1070 {
1071         m_properties_sent = false;
1072 }
1073
1074 Inventory* PlayerSAO::getInventory()
1075 {
1076         return m_inventory;
1077 }
1078 const Inventory* PlayerSAO::getInventory() const
1079 {
1080         return m_inventory;
1081 }
1082
1083 InventoryLocation PlayerSAO::getInventoryLocation() const
1084 {
1085         InventoryLocation loc;
1086         loc.setPlayer(m_player->getName());
1087         return loc;
1088 }
1089
1090 void PlayerSAO::setInventoryModified()
1091 {
1092         m_inventory_not_sent = true;
1093 }
1094
1095 std::string PlayerSAO::getWieldList() const
1096 {
1097         return "main";
1098 }
1099
1100 int PlayerSAO::getWieldIndex() const
1101 {
1102         return m_wield_index;
1103 }
1104
1105 void PlayerSAO::setWieldIndex(int i)
1106 {
1107         if(i != m_wield_index)
1108         {
1109                 m_wield_index = i;
1110                 m_wielded_item_not_sent = true;
1111         }
1112 }
1113
1114 void PlayerSAO::disconnected()
1115 {
1116         m_peer_id = 0;
1117         m_removed = true;
1118         if(m_player->getPlayerSAO() == this)
1119         {
1120                 m_player->setPlayerSAO(NULL);
1121                 m_player->peer_id = 0;
1122         }
1123 }
1124
1125 void PlayerSAO::createCreativeInventory()
1126 {
1127         if(m_inventory != &m_player->inventory)
1128                 delete m_inventory;
1129
1130         m_inventory = new Inventory(m_player->inventory);
1131         m_inventory->clearContents();
1132         scriptapi_get_creative_inventory(m_env->getLua(), this);
1133 }
1134
1135 std::string PlayerSAO::getPropertyPacket()
1136 {
1137         m_prop.is_visible = (getHP() != 0);
1138         return gob_cmd_set_properties(m_prop);
1139 }
1140