]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_sao.cpp
Update Lua API documentation to include minetest.get_modnames()
[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                 // Directly delete item in creative mode
296                 if(g_settings->getBool("creative_mode") == true)
297                 {
298                         m_removed = true;
299                         return 0;
300                 }
301                 
302                 // Take item into inventory
303                 ItemStack item = createItemStack();
304                 Inventory *inv = puncher->getInventory();
305                 if(inv != NULL)
306                 {
307                         std::string wieldlist = puncher->getWieldList();
308                         ItemStack leftover = inv->addItem(wieldlist, item);
309                         puncher->setInventoryModified();
310                         if(leftover.empty())
311                         {
312                                 m_removed = true;
313                         }
314                         else
315                         {
316                                 m_itemstring = leftover.getItemString();
317                                 m_itemstring_changed = true;
318                         }
319                 }
320                 
321                 return 0;
322         }
323
324
325 private:
326         std::string m_itemstring;
327         bool m_itemstring_changed;
328         v3f m_speed_f;
329         v3f m_last_sent_position;
330         IntervalLimiter m_move_interval;
331 };
332
333 // Prototype (registers item for deserialization)
334 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
335
336 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
337                 const std::string itemstring)
338 {
339         return new ItemSAO(env, pos, itemstring);
340 }
341
342 /*
343         LuaEntitySAO
344 */
345
346 // Prototype (registers item for deserialization)
347 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
348
349 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
350                 const std::string &name, const std::string &state):
351         ServerActiveObject(env, pos),
352         m_init_name(name),
353         m_init_state(state),
354         m_registered(false),
355         m_hp(-1),
356         m_velocity(0,0,0),
357         m_acceleration(0,0,0),
358         m_yaw(0),
359         m_properties_sent(true),
360         m_last_sent_yaw(0),
361         m_last_sent_position(0,0,0),
362         m_last_sent_velocity(0,0,0),
363         m_last_sent_position_timer(0),
364         m_last_sent_move_precision(0),
365         m_armor_groups_sent(false)
366 {
367         // Only register type if no environment supplied
368         if(env == NULL){
369                 ServerActiveObject::registerType(getType(), create);
370                 return;
371         }
372         
373         // Initialize something to armor groups
374         m_armor_groups["fleshy"] = 3;
375         m_armor_groups["snappy"] = 2;
376 }
377
378 LuaEntitySAO::~LuaEntitySAO()
379 {
380         if(m_registered){
381                 lua_State *L = m_env->getLua();
382                 scriptapi_luaentity_rm(L, m_id);
383         }
384 }
385
386 void LuaEntitySAO::addedToEnvironment()
387 {
388         ServerActiveObject::addedToEnvironment();
389         
390         // Create entity from name
391         lua_State *L = m_env->getLua();
392         m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
393         
394         if(m_registered){
395                 // Get properties
396                 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
397                 // Initialize HP from properties
398                 m_hp = m_prop.hp_max;
399                 // Activate entity, supplying serialized state
400                 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str());
401         }
402 }
403
404 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
405                 const std::string &data)
406 {
407         std::string name;
408         std::string state;
409         s16 hp = 1;
410         v3f velocity;
411         float yaw = 0;
412         if(data != ""){
413                 std::istringstream is(data, std::ios::binary);
414                 // read version
415                 u8 version = readU8(is);
416                 // check if version is supported
417                 if(version == 0){
418                         name = deSerializeString(is);
419                         state = deSerializeLongString(is);
420                 }
421                 else if(version == 1){
422                         name = deSerializeString(is);
423                         state = deSerializeLongString(is);
424                         hp = readS16(is);
425                         velocity = readV3F1000(is);
426                         yaw = readF1000(is);
427                 }
428         }
429         // create object
430         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
431                         <<state<<"\")"<<std::endl;
432         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
433         sao->m_hp = hp;
434         sao->m_velocity = velocity;
435         sao->m_yaw = yaw;
436         return sao;
437 }
438
439 void LuaEntitySAO::step(float dtime, bool send_recommended)
440 {
441         if(!m_properties_sent)
442         {
443                 m_properties_sent = true;
444                 std::string str = getPropertyPacket();
445                 // create message and add to list
446                 ActiveObjectMessage aom(getId(), true, str);
447                 m_messages_out.push_back(aom);
448         }
449
450         m_last_sent_position_timer += dtime;
451         
452         if(m_prop.physical){
453                 core::aabbox3d<f32> box = m_prop.collisionbox;
454                 box.MinEdge *= BS;
455                 box.MaxEdge *= BS;
456                 collisionMoveResult moveresult;
457                 f32 pos_max_d = BS*0.25; // Distance per iteration
458                 f32 stepheight = 0; // Maximum climbable step height
459                 v3f p_pos = m_base_position;
460                 v3f p_velocity = m_velocity;
461                 v3f p_acceleration = m_acceleration;
462                 IGameDef *gamedef = m_env->getGameDef();
463                 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
464                                 pos_max_d, box, stepheight, dtime,
465                                 p_pos, p_velocity, p_acceleration);
466                 // Apply results
467                 m_base_position = p_pos;
468                 m_velocity = p_velocity;
469                 m_acceleration = p_acceleration;
470         } else {
471                 m_base_position += dtime * m_velocity + 0.5 * dtime
472                                 * dtime * m_acceleration;
473                 m_velocity += dtime * m_acceleration;
474         }
475
476         if(m_registered){
477                 lua_State *L = m_env->getLua();
478                 scriptapi_luaentity_step(L, m_id, dtime);
479         }
480
481         if(send_recommended == false)
482                 return;
483         
484         // TODO: force send when acceleration changes enough?
485         float minchange = 0.2*BS;
486         if(m_last_sent_position_timer > 1.0){
487                 minchange = 0.01*BS;
488         } else if(m_last_sent_position_timer > 0.2){
489                 minchange = 0.05*BS;
490         }
491         float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
492         move_d += m_last_sent_move_precision;
493         float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
494         if(move_d > minchange || vel_d > minchange ||
495                         fabs(m_yaw - m_last_sent_yaw) > 1.0){
496                 sendPosition(true, false);
497         }
498
499         if(m_armor_groups_sent == false){
500                 m_armor_groups_sent = true;
501                 std::string str = gob_cmd_update_armor_groups(
502                                 m_armor_groups);
503                 // create message and add to list
504                 ActiveObjectMessage aom(getId(), true, str);
505                 m_messages_out.push_back(aom);
506         }
507 }
508
509 std::string LuaEntitySAO::getClientInitializationData()
510 {
511         std::ostringstream os(std::ios::binary);
512         writeU8(os, 0); // version
513         os<<serializeString(""); // name
514         writeU8(os, 0); // is_player
515         writeV3F1000(os, m_base_position);
516         writeF1000(os, m_yaw);
517         writeS16(os, m_hp);
518         writeU8(os, 2); // number of messages stuffed in here
519         os<<serializeLongString(getPropertyPacket()); // message 1
520         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
521         // return result
522         return os.str();
523 }
524
525 std::string LuaEntitySAO::getStaticData()
526 {
527         verbosestream<<__FUNCTION_NAME<<std::endl;
528         std::ostringstream os(std::ios::binary);
529         // version
530         writeU8(os, 1);
531         // name
532         os<<serializeString(m_init_name);
533         // state
534         if(m_registered){
535                 lua_State *L = m_env->getLua();
536                 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
537                 os<<serializeLongString(state);
538         } else {
539                 os<<serializeLongString(m_init_state);
540         }
541         // hp
542         writeS16(os, m_hp);
543         // velocity
544         writeV3F1000(os, m_velocity);
545         // yaw
546         writeF1000(os, m_yaw);
547         return os.str();
548 }
549
550 int LuaEntitySAO::punch(v3f dir,
551                 const ToolCapabilities *toolcap,
552                 ServerActiveObject *puncher,
553                 float time_from_last_punch)
554 {
555         if(!m_registered){
556                 // Delete unknown LuaEntities when punched
557                 m_removed = true;
558                 return 0;
559         }
560         
561         ItemStack *punchitem = NULL;
562         ItemStack punchitem_static;
563         if(puncher){
564                 punchitem_static = puncher->getWieldedItem();
565                 punchitem = &punchitem_static;
566         }
567
568         PunchDamageResult result = getPunchDamage(
569                         m_armor_groups,
570                         toolcap,
571                         punchitem,
572                         time_from_last_punch);
573         
574         if(result.did_punch)
575         {
576                 setHP(getHP() - result.damage);
577                 
578                 actionstream<<getDescription()<<" punched by "
579                                 <<puncher->getDescription()<<", damage "<<result.damage
580                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
581                 
582                 {
583                         std::string str = gob_cmd_punched(result.damage, getHP());
584                         // create message and add to list
585                         ActiveObjectMessage aom(getId(), true, str);
586                         m_messages_out.push_back(aom);
587                 }
588
589                 if(getHP() == 0)
590                         m_removed = true;
591         }
592
593         lua_State *L = m_env->getLua();
594         scriptapi_luaentity_punch(L, m_id, puncher,
595                         time_from_last_punch, toolcap, dir);
596
597         return result.wear;
598 }
599
600 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
601 {
602         if(!m_registered)
603                 return;
604         lua_State *L = m_env->getLua();
605         scriptapi_luaentity_rightclick(L, m_id, clicker);
606 }
607
608 void LuaEntitySAO::setPos(v3f pos)
609 {
610         m_base_position = pos;
611         sendPosition(false, true);
612 }
613
614 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
615 {
616         m_base_position = pos;
617         if(!continuous)
618                 sendPosition(true, true);
619 }
620
621 float LuaEntitySAO::getMinimumSavedMovement()
622 {
623         return 0.1 * BS;
624 }
625
626 std::string LuaEntitySAO::getDescription()
627 {
628         std::ostringstream os(std::ios::binary);
629         os<<"LuaEntitySAO at (";
630         os<<(m_base_position.X/BS)<<",";
631         os<<(m_base_position.Y/BS)<<",";
632         os<<(m_base_position.Z/BS);
633         os<<")";
634         return os.str();
635 }
636
637 void LuaEntitySAO::setHP(s16 hp)
638 {
639         if(hp < 0) hp = 0;
640         m_hp = hp;
641 }
642
643 s16 LuaEntitySAO::getHP() const
644 {
645         return m_hp;
646 }
647
648 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
649 {
650         m_armor_groups = armor_groups;
651         m_armor_groups_sent = false;
652 }
653
654 ObjectProperties* LuaEntitySAO::accessObjectProperties()
655 {
656         return &m_prop;
657 }
658
659 void LuaEntitySAO::notifyObjectPropertiesModified()
660 {
661         m_properties_sent = false;
662 }
663
664 void LuaEntitySAO::setVelocity(v3f velocity)
665 {
666         m_velocity = velocity;
667 }
668
669 v3f LuaEntitySAO::getVelocity()
670 {
671         return m_velocity;
672 }
673
674 void LuaEntitySAO::setAcceleration(v3f acceleration)
675 {
676         m_acceleration = acceleration;
677 }
678
679 v3f LuaEntitySAO::getAcceleration()
680 {
681         return m_acceleration;
682 }
683
684 void LuaEntitySAO::setYaw(float yaw)
685 {
686         m_yaw = yaw;
687 }
688
689 float LuaEntitySAO::getYaw()
690 {
691         return m_yaw;
692 }
693
694 void LuaEntitySAO::setTextureMod(const std::string &mod)
695 {
696         std::string str = gob_cmd_set_texture_mod(mod);
697         // create message and add to list
698         ActiveObjectMessage aom(getId(), true, str);
699         m_messages_out.push_back(aom);
700 }
701
702 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
703                 bool select_horiz_by_yawpitch)
704 {
705         std::string str = gob_cmd_set_sprite(
706                 p,
707                 num_frames,
708                 framelength,
709                 select_horiz_by_yawpitch
710         );
711         // create message and add to list
712         ActiveObjectMessage aom(getId(), true, str);
713         m_messages_out.push_back(aom);
714 }
715
716 std::string LuaEntitySAO::getName()
717 {
718         return m_init_name;
719 }
720
721 std::string LuaEntitySAO::getPropertyPacket()
722 {
723         return gob_cmd_set_properties(m_prop);
724 }
725
726 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
727 {
728         m_last_sent_move_precision = m_base_position.getDistanceFrom(
729                         m_last_sent_position);
730         m_last_sent_position_timer = 0;
731         m_last_sent_yaw = m_yaw;
732         m_last_sent_position = m_base_position;
733         m_last_sent_velocity = m_velocity;
734         //m_last_sent_acceleration = m_acceleration;
735
736         float update_interval = m_env->getSendRecommendedInterval();
737
738         std::string str = gob_cmd_update_position(
739                 m_base_position,
740                 m_velocity,
741                 m_acceleration,
742                 m_yaw,
743                 do_interpolate,
744                 is_movement_end,
745                 update_interval
746         );
747         // create message and add to list
748         ActiveObjectMessage aom(getId(), false, str);
749         m_messages_out.push_back(aom);
750 }
751
752 /*
753         PlayerSAO
754 */
755
756 // No prototype, PlayerSAO does not need to be deserialized
757
758 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
759                 const std::set<std::string> &privs, bool is_singleplayer):
760         ServerActiveObject(env_, v3f(0,0,0)),
761         m_player(player_),
762         m_peer_id(peer_id_),
763         m_inventory(NULL),
764         m_last_good_position(0,0,0),
765         m_last_good_position_age(0),
766         m_time_from_last_punch(0),
767         m_nocheat_dig_pos(32767, 32767, 32767),
768         m_nocheat_dig_time(0),
769         m_wield_index(0),
770         m_position_not_sent(false),
771         m_armor_groups_sent(false),
772         m_properties_sent(true),
773         m_privs(privs),
774         m_is_singleplayer(is_singleplayer),
775         // public
776         m_teleported(false),
777         m_inventory_not_sent(false),
778         m_hp_not_sent(false),
779         m_wielded_item_not_sent(false)
780 {
781         assert(m_player);
782         assert(m_peer_id != 0);
783         setBasePosition(m_player->getPosition());
784         m_inventory = &m_player->inventory;
785         m_armor_groups["choppy"] = 2;
786         m_armor_groups["fleshy"] = 3;
787
788         m_prop.hp_max = PLAYER_MAX_HP;
789         m_prop.physical = false;
790         m_prop.weight = 75;
791         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
792         m_prop.visual = "upright_sprite";
793         m_prop.visual_size = v2f(1, 2);
794         m_prop.textures.clear();
795         m_prop.textures.push_back("player.png");
796         m_prop.textures.push_back("player_back.png");
797         m_prop.spritediv = v2s16(1,1);
798         m_prop.is_visible = (getHP() != 0);
799         m_prop.makes_footstep_sound = true;
800 }
801
802 PlayerSAO::~PlayerSAO()
803 {
804         if(m_inventory != &m_player->inventory)
805                 delete m_inventory;
806
807 }
808
809 std::string PlayerSAO::getDescription()
810 {
811         return std::string("player ") + m_player->getName();
812 }
813
814 // Called after id has been set and has been inserted in environment
815 void PlayerSAO::addedToEnvironment()
816 {
817         ServerActiveObject::addedToEnvironment();
818         ServerActiveObject::setBasePosition(m_player->getPosition());
819         m_player->setPlayerSAO(this);
820         m_player->peer_id = m_peer_id;
821         m_last_good_position = m_player->getPosition();
822         m_last_good_position_age = 0.0;
823 }
824
825 // Called before removing from environment
826 void PlayerSAO::removingFromEnvironment()
827 {
828         ServerActiveObject::removingFromEnvironment();
829         if(m_player->getPlayerSAO() == this)
830         {
831                 m_player->setPlayerSAO(NULL);
832                 m_player->peer_id = 0;
833         }
834 }
835
836 bool PlayerSAO::isStaticAllowed() const
837 {
838         return false;
839 }
840
841 bool PlayerSAO::unlimitedTransferDistance() const
842 {
843         return g_settings->getBool("unlimited_player_transfer_distance");
844 }
845
846 std::string PlayerSAO::getClientInitializationData()
847 {
848         std::ostringstream os(std::ios::binary);
849         writeU8(os, 0); // version
850         os<<serializeString(m_player->getName()); // name
851         writeU8(os, 1); // is_player
852         writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
853         writeF1000(os, m_player->getYaw());
854         writeS16(os, getHP());
855         writeU8(os, 2); // number of messages stuffed in here
856         os<<serializeLongString(getPropertyPacket()); // message 1
857         os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
858         return os.str();
859 }
860
861 std::string PlayerSAO::getStaticData()
862 {
863         assert(0);
864         return "";
865 }
866
867 void PlayerSAO::step(float dtime, bool send_recommended)
868 {
869         if(!m_properties_sent)
870         {
871                 m_properties_sent = true;
872                 std::string str = getPropertyPacket();
873                 // create message and add to list
874                 ActiveObjectMessage aom(getId(), true, str);
875                 m_messages_out.push_back(aom);
876         }
877
878         m_time_from_last_punch += dtime;
879         m_nocheat_dig_time += dtime;
880         
881         if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
882         {
883                 m_last_good_position = m_player->getPosition();
884                 m_last_good_position_age = 0;
885         }
886         else
887         {
888                 /*
889                         Check player movements
890
891                         NOTE: Actually the server should handle player physics like the
892                         client does and compare player's position to what is calculated
893                         on our side. This is required when eg. players fly due to an
894                         explosion. Altough a node-based alternative might be possible
895                         too, and much more lightweight.
896                 */
897
898                 float player_max_speed = 0;
899                 float player_max_speed_up = 0;
900                 if(m_privs.count("fast") != 0){
901                         // Fast speed
902                         player_max_speed = BS * 20;
903                         player_max_speed_up = BS * 20;
904                 } else {
905                         // Normal speed
906                         player_max_speed = BS * 4.0;
907                         player_max_speed_up = BS * 4.0;
908                 }
909                 // Tolerance
910                 player_max_speed *= 2.5;
911                 player_max_speed_up *= 2.5;
912
913                 m_last_good_position_age += dtime;
914                 if(m_last_good_position_age >= 1.0){
915                         float age = m_last_good_position_age;
916                         v3f diff = (m_player->getPosition() - m_last_good_position);
917                         float d_vert = diff.Y;
918                         diff.Y = 0;
919                         float d_horiz = diff.getLength();
920                         /*infostream<<m_player->getName()<<"'s horizontal speed is "
921                                         <<(d_horiz/age)<<std::endl;*/
922                         if(d_horiz <= age * player_max_speed &&
923                                         (d_vert < 0 || d_vert < age * player_max_speed_up)){
924                                 m_last_good_position = m_player->getPosition();
925                         } else {
926                                 actionstream<<"Player "<<m_player->getName()
927                                                 <<" moved too fast; resetting position"
928                                                 <<std::endl;
929                                 m_player->setPosition(m_last_good_position);
930                                 m_teleported = true;
931                         }
932                         m_last_good_position_age = 0;
933                 }
934         }
935
936         if(send_recommended == false)
937                 return;
938
939         if(m_position_not_sent)
940         {
941                 m_position_not_sent = false;
942                 float update_interval = m_env->getSendRecommendedInterval();
943                 std::string str = gob_cmd_update_position(
944                         m_player->getPosition() + v3f(0,BS*1,0),
945                         v3f(0,0,0),
946                         v3f(0,0,0),
947                         m_player->getYaw(),
948                         true,
949                         false,
950                         update_interval
951                 );
952                 // create message and add to list
953                 ActiveObjectMessage aom(getId(), false, str);
954                 m_messages_out.push_back(aom);
955         }
956
957         if(m_wielded_item_not_sent)
958         {
959                 m_wielded_item_not_sent = false;
960                 // GenericCAO has no special way to show this
961         }
962
963         if(m_armor_groups_sent == false){
964                 m_armor_groups_sent = true;
965                 std::string str = gob_cmd_update_armor_groups(
966                                 m_armor_groups);
967                 // create message and add to list
968                 ActiveObjectMessage aom(getId(), true, str);
969                 m_messages_out.push_back(aom);
970         }
971 }
972
973 void PlayerSAO::setBasePosition(const v3f &position)
974 {
975         ServerActiveObject::setBasePosition(position);
976         m_position_not_sent = true;
977 }
978
979 void PlayerSAO::setPos(v3f pos)
980 {
981         m_player->setPosition(pos);
982         // Movement caused by this command is always valid
983         m_last_good_position = pos;
984         m_last_good_position_age = 0;
985         // Force position change on client
986         m_teleported = true;
987 }
988
989 void PlayerSAO::moveTo(v3f pos, bool continuous)
990 {
991         m_player->setPosition(pos);
992         // Movement caused by this command is always valid
993         m_last_good_position = pos;
994         m_last_good_position_age = 0;
995         // Force position change on client
996         m_teleported = true;
997 }
998
999 int PlayerSAO::punch(v3f dir,
1000         const ToolCapabilities *toolcap,
1001         ServerActiveObject *puncher,
1002         float time_from_last_punch)
1003 {
1004         if(!toolcap)
1005                 return 0;
1006
1007         // No effect if PvP disabled
1008         if(g_settings->getBool("enable_pvp") == false){
1009                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1010                         std::string str = gob_cmd_punched(0, getHP());
1011                         // create message and add to list
1012                         ActiveObjectMessage aom(getId(), true, str);
1013                         m_messages_out.push_back(aom);
1014                         return 0;
1015                 }
1016         }
1017
1018         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1019                         time_from_last_punch);
1020
1021         actionstream<<"Player "<<m_player->getName()<<" punched by "
1022                         <<puncher->getDescription()<<", damage "<<hitparams.hp
1023                         <<" HP"<<std::endl;
1024
1025         setHP(getHP() - hitparams.hp);
1026
1027         if(hitparams.hp != 0)
1028         {
1029                 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1030                 // create message and add to list
1031                 ActiveObjectMessage aom(getId(), true, str);
1032                 m_messages_out.push_back(aom);
1033         }
1034
1035         return hitparams.wear;
1036 }
1037
1038 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1039 {
1040 }
1041
1042 s16 PlayerSAO::getHP() const
1043 {
1044         return m_player->hp;
1045 }
1046
1047 void PlayerSAO::setHP(s16 hp)
1048 {
1049         s16 oldhp = m_player->hp;
1050
1051         if(hp < 0)
1052                 hp = 0;
1053         else if(hp > PLAYER_MAX_HP)
1054                 hp = PLAYER_MAX_HP;
1055
1056         if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1057         {
1058                 m_hp_not_sent = true; // fix wrong prediction on client
1059                 return;
1060         }
1061
1062         m_player->hp = hp;
1063
1064         if(hp != oldhp)
1065                 m_hp_not_sent = true;
1066
1067         // On death or reincarnation send an active object message
1068         if((hp == 0) != (oldhp == 0))
1069         {
1070                 // Will send new is_visible value based on (getHP()!=0)
1071                 m_properties_sent = false;
1072                 // Send new HP
1073                 std::string str = gob_cmd_punched(0, getHP());
1074                 ActiveObjectMessage aom(getId(), true, str);
1075                 m_messages_out.push_back(aom);
1076         }
1077 }
1078
1079 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1080 {
1081         m_armor_groups = armor_groups;
1082         m_armor_groups_sent = false;
1083 }
1084
1085 ObjectProperties* PlayerSAO::accessObjectProperties()
1086 {
1087         return &m_prop;
1088 }
1089
1090 void PlayerSAO::notifyObjectPropertiesModified()
1091 {
1092         m_properties_sent = false;
1093 }
1094
1095 Inventory* PlayerSAO::getInventory()
1096 {
1097         return m_inventory;
1098 }
1099 const Inventory* PlayerSAO::getInventory() const
1100 {
1101         return m_inventory;
1102 }
1103
1104 InventoryLocation PlayerSAO::getInventoryLocation() const
1105 {
1106         InventoryLocation loc;
1107         loc.setPlayer(m_player->getName());
1108         return loc;
1109 }
1110
1111 void PlayerSAO::setInventoryModified()
1112 {
1113         m_inventory_not_sent = true;
1114 }
1115
1116 std::string PlayerSAO::getWieldList() const
1117 {
1118         return "main";
1119 }
1120
1121 int PlayerSAO::getWieldIndex() const
1122 {
1123         return m_wield_index;
1124 }
1125
1126 void PlayerSAO::setWieldIndex(int i)
1127 {
1128         if(i != m_wield_index)
1129         {
1130                 m_wield_index = i;
1131                 m_wielded_item_not_sent = true;
1132         }
1133 }
1134
1135 void PlayerSAO::disconnected()
1136 {
1137         m_peer_id = 0;
1138         m_removed = true;
1139         if(m_player->getPlayerSAO() == this)
1140         {
1141                 m_player->setPlayerSAO(NULL);
1142                 m_player->peer_id = 0;
1143         }
1144 }
1145
1146 void PlayerSAO::createCreativeInventory()
1147 {
1148         if(m_inventory != &m_player->inventory)
1149                 delete m_inventory;
1150
1151         m_inventory = new Inventory(m_player->inventory);
1152         m_inventory->clearContents();
1153         scriptapi_get_creative_inventory(m_env->getLua(), this);
1154 }
1155
1156 std::string PlayerSAO::getPropertyPacket()
1157 {
1158         m_prop.is_visible = (getHP() != 0);
1159         return gob_cmd_set_properties(m_prop);
1160 }
1161