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