]> git.lizzy.rs Git - minetest.git/blob - src/content_sao.cpp
Consistent HP and damage types (#8167)
[minetest.git] / src / content_sao.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 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 "util/serialize.h"
22 #include "collision.h"
23 #include "environment.h"
24 #include "tool.h" // For ToolCapabilities
25 #include "gamedef.h"
26 #include "nodedef.h"
27 #include "remoteplayer.h"
28 #include "server.h"
29 #include "scripting_server.h"
30 #include "genericobject.h"
31 #include "settings.h"
32 #include <algorithm>
33 #include <cmath>
34
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
36
37 /*
38         TestSAO
39 */
40
41 class TestSAO : public ServerActiveObject
42 {
43 public:
44         TestSAO(ServerEnvironment *env, v3f pos):
45                 ServerActiveObject(env, pos),
46                 m_timer1(0),
47                 m_age(0)
48         {
49                 ServerActiveObject::registerType(getType(), create);
50         }
51         ActiveObjectType getType() const
52         { return ACTIVEOBJECT_TYPE_TEST; }
53
54         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55                         const std::string &data)
56         {
57                 return new TestSAO(env, pos);
58         }
59
60         void step(float dtime, bool send_recommended)
61         {
62                 m_age += dtime;
63                 if(m_age > 10)
64                 {
65                         m_pending_removal = true;
66                         return;
67                 }
68
69                 m_base_position.Y += dtime * BS * 2;
70                 if(m_base_position.Y > 8*BS)
71                         m_base_position.Y = 2*BS;
72
73                 if (!send_recommended)
74                         return;
75
76                 m_timer1 -= dtime;
77                 if(m_timer1 < 0.0)
78                 {
79                         m_timer1 += 0.125;
80
81                         std::string data;
82
83                         data += itos(0); // 0 = position
84                         data += " ";
85                         data += itos(m_base_position.X);
86                         data += " ";
87                         data += itos(m_base_position.Y);
88                         data += " ";
89                         data += itos(m_base_position.Z);
90
91                         ActiveObjectMessage aom(getId(), false, data);
92                         m_messages_out.push(aom);
93                 }
94         }
95
96         bool getCollisionBox(aabb3f *toset) const { return false; }
97
98         virtual bool getSelectionBox(aabb3f *toset) const { return false; }
99
100         bool collideWithObjects() const { return false; }
101
102 private:
103         float m_timer1;
104         float m_age;
105 };
106
107 // Prototype (registers item for deserialization)
108 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
109
110 /*
111         UnitSAO
112  */
113
114 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
115         ServerActiveObject(env, pos)
116 {
117         // Initialize something to armor groups
118         m_armor_groups["fleshy"] = 100;
119 }
120
121 ServerActiveObject *UnitSAO::getParent() const
122 {
123         if (!m_attachment_parent_id)
124                 return nullptr;
125         // Check if the parent still exists
126         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
127
128         return obj;
129 }
130
131 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
132 {
133         m_armor_groups = armor_groups;
134         m_armor_groups_sent = false;
135 }
136
137 const ItemGroupList &UnitSAO::getArmorGroups()
138 {
139         return m_armor_groups;
140 }
141
142 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
143 {
144         // store these so they can be updated to clients
145         m_animation_range = frame_range;
146         m_animation_speed = frame_speed;
147         m_animation_blend = frame_blend;
148         m_animation_loop = frame_loop;
149         m_animation_sent = false;
150 }
151
152 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
153 {
154         *frame_range = m_animation_range;
155         *frame_speed = m_animation_speed;
156         *frame_blend = m_animation_blend;
157         *frame_loop = m_animation_loop;
158 }
159
160 void UnitSAO::setAnimationSpeed(float frame_speed)
161 {
162         m_animation_speed = frame_speed;
163         m_animation_speed_sent = false;
164 }
165
166 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
167 {
168         // store these so they can be updated to clients
169         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
170         m_bone_position_sent = false;
171 }
172
173 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
174 {
175         *position = m_bone_position[bone].X;
176         *rotation = m_bone_position[bone].Y;
177 }
178
179 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
180 {
181         // Attachments need to be handled on both the server and client.
182         // If we just attach on the server, we can only copy the position of the parent. Attachments
183         // are still sent to clients at an interval so players might see them lagging, plus we can't
184         // read and attach to skeletal bones.
185         // If we just attach on the client, the server still sees the child at its original location.
186         // This breaks some things so we also give the server the most accurate representation
187         // even if players only see the client changes.
188
189         int old_parent = m_attachment_parent_id;
190         m_attachment_parent_id = parent_id;
191         m_attachment_bone = bone;
192         m_attachment_position = position;
193         m_attachment_rotation = rotation;
194         m_attachment_sent = false;
195
196         if (parent_id != old_parent) {
197                 onDetach(old_parent);
198                 onAttach(parent_id);
199         }
200 }
201
202 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
203         v3f *rotation)
204 {
205         *parent_id = m_attachment_parent_id;
206         *bone = m_attachment_bone;
207         *position = m_attachment_position;
208         *rotation = m_attachment_rotation;
209 }
210
211 void UnitSAO::clearChildAttachments()
212 {
213         for (int child_id : m_attachment_child_ids) {
214                 // Child can be NULL if it was deleted earlier
215                 if (ServerActiveObject *child = m_env->getActiveObject(child_id))
216                         child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
217         }
218         m_attachment_child_ids.clear();
219 }
220
221 void UnitSAO::clearParentAttachment()
222 {
223         ServerActiveObject *parent = nullptr;
224         if (m_attachment_parent_id) {
225                 parent = m_env->getActiveObject(m_attachment_parent_id);
226                 setAttachment(0, "", m_attachment_position, m_attachment_rotation);
227         } else {
228                 setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
229         }
230         // Do it
231         if (parent)
232                 parent->removeAttachmentChild(m_id);
233 }
234
235 void UnitSAO::addAttachmentChild(int child_id)
236 {
237         m_attachment_child_ids.insert(child_id);
238 }
239
240 void UnitSAO::removeAttachmentChild(int child_id)
241 {
242         m_attachment_child_ids.erase(child_id);
243 }
244
245 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
246 {
247         return m_attachment_child_ids;
248 }
249
250 void UnitSAO::onAttach(int parent_id)
251 {
252         if (!parent_id)
253                 return;
254
255         ServerActiveObject *parent = m_env->getActiveObject(parent_id);
256
257         if (!parent || parent->isGone())
258                 return; // Do not try to notify soon gone parent
259
260         if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) {
261                 // Call parent's on_attach field
262                 m_env->getScriptIface()->luaentity_on_attach_child(parent_id, this);
263         }
264 }
265
266 void UnitSAO::onDetach(int parent_id)
267 {
268         if (!parent_id)
269                 return;
270
271         ServerActiveObject *parent = m_env->getActiveObject(parent_id);
272         if (getType() == ACTIVEOBJECT_TYPE_LUAENTITY)
273                 m_env->getScriptIface()->luaentity_on_detach(m_id, parent);
274
275         if (!parent || parent->isGone())
276                 return; // Do not try to notify soon gone parent
277
278         if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY)
279                 m_env->getScriptIface()->luaentity_on_detach_child(parent_id, this);
280 }
281
282 ObjectProperties* UnitSAO::accessObjectProperties()
283 {
284         return &m_prop;
285 }
286
287 void UnitSAO::notifyObjectPropertiesModified()
288 {
289         m_properties_sent = false;
290 }
291
292 /*
293         LuaEntitySAO
294 */
295
296 // Prototype (registers item for deserialization)
297 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
298
299 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
300                 const std::string &name, const std::string &state):
301         UnitSAO(env, pos),
302         m_init_name(name),
303         m_init_state(state)
304 {
305         // Only register type if no environment supplied
306         if(env == NULL){
307                 ServerActiveObject::registerType(getType(), create);
308                 return;
309         }
310 }
311
312 LuaEntitySAO::~LuaEntitySAO()
313 {
314         if(m_registered){
315                 m_env->getScriptIface()->luaentity_Remove(m_id);
316         }
317
318         for (u32 attached_particle_spawner : m_attached_particle_spawners) {
319                 m_env->deleteParticleSpawner(attached_particle_spawner, false);
320         }
321 }
322
323 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
324 {
325         ServerActiveObject::addedToEnvironment(dtime_s);
326
327         // Create entity from name
328         m_registered = m_env->getScriptIface()->
329                 luaentity_Add(m_id, m_init_name.c_str());
330
331         if(m_registered){
332                 // Get properties
333                 m_env->getScriptIface()->
334                         luaentity_GetProperties(m_id, &m_prop);
335                 // Initialize HP from properties
336                 m_hp = m_prop.hp_max;
337                 // Activate entity, supplying serialized state
338                 m_env->getScriptIface()->
339                         luaentity_Activate(m_id, m_init_state, dtime_s);
340         } else {
341                 m_prop.infotext = m_init_name;
342         }
343 }
344
345 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
346                 const std::string &data)
347 {
348         std::string name;
349         std::string state;
350         u16 hp = 1;
351         v3f velocity;
352         v3f rotation;
353
354         while (!data.empty()) { // breakable, run for one iteration
355                 std::istringstream is(data, std::ios::binary);
356                 // 'version' does not allow to incrementally extend the parameter list thus
357                 // we need another variable to build on top of 'version=1'. Ugly hack but worksâ„¢
358                 u8 version2 = 0;
359                 u8 version = readU8(is);
360
361                 name = deSerializeString(is);
362                 state = deSerializeLongString(is);
363
364                 if (version < 1)
365                         break;
366
367                 hp = readU16(is);
368                 velocity = readV3F1000(is);
369                 // yaw must be yaw to be backwards-compatible
370                 rotation.Y = readF1000(is);
371
372                 if (is.good()) // EOF for old formats
373                         version2 = readU8(is);
374
375                 if (version2 < 1) // PROTOCOL_VERSION < 37
376                         break;
377
378                 // version2 >= 1
379                 rotation.X = readF1000(is);
380                 rotation.Z = readF1000(is);
381
382                 // if (version2 < 2)
383                 //     break;
384                 // <read new values>
385                 break;
386         }
387         // create object
388         infostream << "LuaEntitySAO::create(name=\"" << name << "\" state=\""
389                          << state << "\")" << std::endl;
390         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
391         sao->m_hp = hp;
392         sao->m_velocity = velocity;
393         sao->m_rotation = rotation;
394         return sao;
395 }
396
397 void LuaEntitySAO::step(float dtime, bool send_recommended)
398 {
399         if(!m_properties_sent)
400         {
401                 m_properties_sent = true;
402                 std::string str = getPropertyPacket();
403                 // create message and add to list
404                 ActiveObjectMessage aom(getId(), true, str);
405                 m_messages_out.push(aom);
406         }
407
408         // If attached, check that our parent is still there. If it isn't, detach.
409         if(m_attachment_parent_id && !isAttached())
410         {
411                 m_attachment_parent_id = 0;
412                 m_attachment_bone = "";
413                 m_attachment_position = v3f(0,0,0);
414                 m_attachment_rotation = v3f(0,0,0);
415                 sendPosition(false, true);
416         }
417
418         m_last_sent_position_timer += dtime;
419
420         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
421         // If the object gets detached this comes into effect automatically from the last known origin
422         if(isAttached())
423         {
424                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
425                 m_base_position = pos;
426                 m_velocity = v3f(0,0,0);
427                 m_acceleration = v3f(0,0,0);
428         }
429         else
430         {
431                 if(m_prop.physical){
432                         aabb3f box = m_prop.collisionbox;
433                         box.MinEdge *= BS;
434                         box.MaxEdge *= BS;
435                         collisionMoveResult moveresult;
436                         f32 pos_max_d = BS*0.25; // Distance per iteration
437                         v3f p_pos = m_base_position;
438                         v3f p_velocity = m_velocity;
439                         v3f p_acceleration = m_acceleration;
440                         moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
441                                         pos_max_d, box, m_prop.stepheight, dtime,
442                                         &p_pos, &p_velocity, p_acceleration,
443                                         this, m_prop.collideWithObjects);
444
445                         // Apply results
446                         m_base_position = p_pos;
447                         m_velocity = p_velocity;
448                         m_acceleration = p_acceleration;
449                 } else {
450                         m_base_position += dtime * m_velocity + 0.5 * dtime
451                                         * dtime * m_acceleration;
452                         m_velocity += dtime * m_acceleration;
453                 }
454
455                 if (m_prop.automatic_face_movement_dir &&
456                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
457
458                         float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
459                                 + m_prop.automatic_face_movement_dir_offset;
460                         float max_rotation_delta =
461                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
462
463                         m_rotation.Y = wrapDegrees_0_360(m_rotation.Y);
464                         wrappedApproachShortest(m_rotation.Y, target_yaw, max_rotation_delta, 360.f);
465                 }
466         }
467
468         if(m_registered){
469                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
470         }
471
472         if (!send_recommended)
473                 return;
474
475         if(!isAttached())
476         {
477                 // TODO: force send when acceleration changes enough?
478                 float minchange = 0.2*BS;
479                 if(m_last_sent_position_timer > 1.0){
480                         minchange = 0.01*BS;
481                 } else if(m_last_sent_position_timer > 0.2){
482                         minchange = 0.05*BS;
483                 }
484                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
485                 move_d += m_last_sent_move_precision;
486                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
487                 if (move_d > minchange || vel_d > minchange ||
488                                 std::fabs(m_rotation.X - m_last_sent_rotation.X) > 1.0f ||
489                                 std::fabs(m_rotation.Y - m_last_sent_rotation.Y) > 1.0f ||
490                                 std::fabs(m_rotation.Z - m_last_sent_rotation.Z) > 1.0f) {
491
492                         sendPosition(true, false);
493                 }
494         }
495
496         if (!m_armor_groups_sent) {
497                 m_armor_groups_sent = true;
498                 std::string str = gob_cmd_update_armor_groups(
499                                 m_armor_groups);
500                 // create message and add to list
501                 ActiveObjectMessage aom(getId(), true, str);
502                 m_messages_out.push(aom);
503         }
504
505         if (!m_animation_sent) {
506                 m_animation_sent = true;
507                 std::string str = gob_cmd_update_animation(
508                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
509                 // create message and add to list
510                 ActiveObjectMessage aom(getId(), true, str);
511                 m_messages_out.push(aom);
512         }
513
514         if (!m_animation_speed_sent) {
515                 m_animation_speed_sent = true;
516                 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
517                 // create message and add to list
518                 ActiveObjectMessage aom(getId(), true, str);
519                 m_messages_out.push(aom);
520         }
521
522         if (!m_bone_position_sent) {
523                 m_bone_position_sent = true;
524                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
525                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
526                         std::string str = gob_cmd_update_bone_position((*ii).first,
527                                         (*ii).second.X, (*ii).second.Y);
528                         // create message and add to list
529                         ActiveObjectMessage aom(getId(), true, str);
530                         m_messages_out.push(aom);
531                 }
532         }
533
534         if (!m_attachment_sent) {
535                 m_attachment_sent = true;
536                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
537                 // create message and add to list
538                 ActiveObjectMessage aom(getId(), true, str);
539                 m_messages_out.push(aom);
540         }
541 }
542
543 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
544 {
545         std::ostringstream os(std::ios::binary);
546
547         // PROTOCOL_VERSION >= 37
548         writeU8(os, 1); // version
549         os << serializeString(""); // name
550         writeU8(os, 0); // is_player
551         writeU16(os, getId()); //id
552         writeV3F32(os, m_base_position);
553         writeV3F32(os, m_rotation);
554         writeU16(os, m_hp);
555
556         std::ostringstream msg_os(std::ios::binary);
557         msg_os << serializeLongString(getPropertyPacket()); // message 1
558         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
559         msg_os << serializeLongString(gob_cmd_update_animation(
560                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
561         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
562                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
563                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
564                                 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
565         }
566         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
567                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
568         int message_count = 4 + m_bone_position.size();
569         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
570                         (ii != m_attachment_child_ids.end()); ++ii) {
571                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
572                         message_count++;
573                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
574                                 obj->getClientInitializationData(protocol_version)));
575                 }
576         }
577
578         msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
579         message_count++;
580
581         writeU8(os, message_count);
582         os.write(msg_os.str().c_str(), msg_os.str().size());
583
584         // return result
585         return os.str();
586 }
587
588 void LuaEntitySAO::getStaticData(std::string *result) const
589 {
590         verbosestream<<FUNCTION_NAME<<std::endl;
591         std::ostringstream os(std::ios::binary);
592         // version must be 1 to keep backwards-compatibility. See version2
593         writeU8(os, 1);
594         // name
595         os<<serializeString(m_init_name);
596         // state
597         if(m_registered){
598                 std::string state = m_env->getScriptIface()->
599                         luaentity_GetStaticdata(m_id);
600                 os<<serializeLongString(state);
601         } else {
602                 os<<serializeLongString(m_init_state);
603         }
604         writeU16(os, m_hp);
605         writeV3F1000(os, m_velocity);
606         // yaw
607         writeF1000(os, m_rotation.Y);
608
609         // version2. Increase this variable for new values
610         writeU8(os, 1); // PROTOCOL_VERSION >= 37
611
612         writeF1000(os, m_rotation.X);
613         writeF1000(os, m_rotation.Z);
614
615         // <write new values>
616
617         *result = os.str();
618 }
619
620 int LuaEntitySAO::punch(v3f dir,
621                 const ToolCapabilities *toolcap,
622                 ServerActiveObject *puncher,
623                 float time_from_last_punch)
624 {
625         if (!m_registered) {
626                 // Delete unknown LuaEntities when punched
627                 m_pending_removal = true;
628                 return 0;
629         }
630
631         ItemStack *punchitem = NULL;
632         ItemStack punchitem_static;
633         if (puncher) {
634                 punchitem_static = puncher->getWieldedItem();
635                 punchitem = &punchitem_static;
636         }
637
638         PunchDamageResult result = getPunchDamage(
639                         m_armor_groups,
640                         toolcap,
641                         punchitem,
642                         time_from_last_punch);
643
644         bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
645                         time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
646
647         if (!damage_handled) {
648                 if (result.did_punch) {
649                         setHP((s32)getHP() - result.damage,
650                                 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
651
652                         if (result.damage > 0) {
653                                 std::string punchername = puncher ? puncher->getDescription() : "nil";
654
655                                 actionstream << getDescription() << " punched by "
656                                                 << punchername << ", damage " << result.damage
657                                                 << " hp, health now " << getHP() << " hp" << std::endl;
658                         }
659
660                         std::string str = gob_cmd_punched(getHP());
661                         // create message and add to list
662                         ActiveObjectMessage aom(getId(), true, str);
663                         m_messages_out.push(aom);
664                 }
665         }
666
667         if (getHP() == 0 && !isGone()) {
668                 m_pending_removal = true;
669                 clearParentAttachment();
670                 clearChildAttachments();
671                 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
672         }
673
674         return result.wear;
675 }
676
677 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
678 {
679         if (!m_registered)
680                 return;
681
682         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
683 }
684
685 void LuaEntitySAO::setPos(const v3f &pos)
686 {
687         if(isAttached())
688                 return;
689         m_base_position = pos;
690         sendPosition(false, true);
691 }
692
693 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
694 {
695         if(isAttached())
696                 return;
697         m_base_position = pos;
698         if(!continuous)
699                 sendPosition(true, true);
700 }
701
702 float LuaEntitySAO::getMinimumSavedMovement()
703 {
704         return 0.1 * BS;
705 }
706
707 std::string LuaEntitySAO::getDescription()
708 {
709         std::ostringstream os(std::ios::binary);
710         os<<"LuaEntitySAO at (";
711         os<<(m_base_position.X/BS)<<",";
712         os<<(m_base_position.Y/BS)<<",";
713         os<<(m_base_position.Z/BS);
714         os<<")";
715         return os.str();
716 }
717
718 void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
719 {
720         m_hp = rangelim(hp, 0, U16_MAX);
721 }
722
723 u16 LuaEntitySAO::getHP() const
724 {
725         return m_hp;
726 }
727
728 void LuaEntitySAO::setVelocity(v3f velocity)
729 {
730         m_velocity = velocity;
731 }
732
733 v3f LuaEntitySAO::getVelocity()
734 {
735         return m_velocity;
736 }
737
738 void LuaEntitySAO::setAcceleration(v3f acceleration)
739 {
740         m_acceleration = acceleration;
741 }
742
743 v3f LuaEntitySAO::getAcceleration()
744 {
745         return m_acceleration;
746 }
747
748 void LuaEntitySAO::setTextureMod(const std::string &mod)
749 {
750         std::string str = gob_cmd_set_texture_mod(mod);
751         m_current_texture_modifier = mod;
752         // create message and add to list
753         ActiveObjectMessage aom(getId(), true, str);
754         m_messages_out.push(aom);
755 }
756
757 std::string LuaEntitySAO::getTextureMod() const
758 {
759         return m_current_texture_modifier;
760 }
761
762 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
763                 bool select_horiz_by_yawpitch)
764 {
765         std::string str = gob_cmd_set_sprite(
766                 p,
767                 num_frames,
768                 framelength,
769                 select_horiz_by_yawpitch
770         );
771         // create message and add to list
772         ActiveObjectMessage aom(getId(), true, str);
773         m_messages_out.push(aom);
774 }
775
776 std::string LuaEntitySAO::getName()
777 {
778         return m_init_name;
779 }
780
781 std::string LuaEntitySAO::getPropertyPacket()
782 {
783         return gob_cmd_set_properties(m_prop);
784 }
785
786 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
787 {
788         // If the object is attached client-side, don't waste bandwidth sending its position to clients
789         if(isAttached())
790                 return;
791
792         m_last_sent_move_precision = m_base_position.getDistanceFrom(
793                         m_last_sent_position);
794         m_last_sent_position_timer = 0;
795         m_last_sent_position = m_base_position;
796         m_last_sent_velocity = m_velocity;
797         //m_last_sent_acceleration = m_acceleration;
798         m_last_sent_rotation = m_rotation;
799
800         float update_interval = m_env->getSendRecommendedInterval();
801
802         std::string str = gob_cmd_update_position(
803                 m_base_position,
804                 m_velocity,
805                 m_acceleration,
806                 m_rotation,
807                 do_interpolate,
808                 is_movement_end,
809                 update_interval
810         );
811         // create message and add to list
812         ActiveObjectMessage aom(getId(), false, str);
813         m_messages_out.push(aom);
814 }
815
816 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
817 {
818         if (m_prop.physical)
819         {
820                 //update collision box
821                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
822                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
823
824                 toset->MinEdge += m_base_position;
825                 toset->MaxEdge += m_base_position;
826
827                 return true;
828         }
829
830         return false;
831 }
832
833 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
834 {
835         if (!m_prop.is_visible || !m_prop.pointable) {
836                 return false;
837         }
838
839         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
840         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
841
842         return true;
843 }
844
845 bool LuaEntitySAO::collideWithObjects() const
846 {
847         return m_prop.collideWithObjects;
848 }
849
850 /*
851         PlayerSAO
852 */
853
854 // No prototype, PlayerSAO does not need to be deserialized
855
856 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
857                 bool is_singleplayer):
858         UnitSAO(env_, v3f(0,0,0)),
859         m_player(player_),
860         m_peer_id(peer_id_),
861         m_is_singleplayer(is_singleplayer)
862 {
863         assert(m_peer_id != 0); // pre-condition
864
865         m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
866         m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
867         m_prop.physical = false;
868         m_prop.weight = 75;
869         m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
870         m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
871         m_prop.pointable = true;
872         // Start of default appearance, this should be overwritten by Lua
873         m_prop.visual = "upright_sprite";
874         m_prop.visual_size = v3f(1, 2, 1);
875         m_prop.textures.clear();
876         m_prop.textures.emplace_back("player.png");
877         m_prop.textures.emplace_back("player_back.png");
878         m_prop.colors.clear();
879         m_prop.colors.emplace_back(255, 255, 255, 255);
880         m_prop.spritediv = v2s16(1,1);
881         m_prop.eye_height = 1.625f;
882         // End of default appearance
883         m_prop.is_visible = true;
884         m_prop.backface_culling = false;
885         m_prop.makes_footstep_sound = true;
886         m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
887         m_hp = m_prop.hp_max;
888         m_breath = m_prop.breath_max;
889         // Disable zoom in survival mode using a value of 0
890         m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
891 }
892
893 PlayerSAO::~PlayerSAO()
894 {
895         if(m_inventory != &m_player->inventory)
896                 delete m_inventory;
897 }
898
899 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
900 {
901         assert(player);
902         m_player = player;
903         m_privs = privs;
904         m_inventory = &m_player->inventory;
905 }
906
907 v3f PlayerSAO::getEyeOffset() const
908 {
909         return v3f(0, BS * m_prop.eye_height, 0);
910 }
911
912 std::string PlayerSAO::getDescription()
913 {
914         return std::string("player ") + m_player->getName();
915 }
916
917 // Called after id has been set and has been inserted in environment
918 void PlayerSAO::addedToEnvironment(u32 dtime_s)
919 {
920         ServerActiveObject::addedToEnvironment(dtime_s);
921         ServerActiveObject::setBasePosition(m_base_position);
922         m_player->setPlayerSAO(this);
923         m_player->setPeerId(m_peer_id);
924         m_last_good_position = m_base_position;
925 }
926
927 // Called before removing from environment
928 void PlayerSAO::removingFromEnvironment()
929 {
930         ServerActiveObject::removingFromEnvironment();
931         if (m_player->getPlayerSAO() == this) {
932                 unlinkPlayerSessionAndSave();
933                 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
934                         m_env->deleteParticleSpawner(attached_particle_spawner, false);
935                 }
936         }
937 }
938
939 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
940 {
941         std::ostringstream os(std::ios::binary);
942
943         // Protocol >= 15
944         writeU8(os, 1); // version
945         os << serializeString(m_player->getName()); // name
946         writeU8(os, 1); // is_player
947         writeS16(os, getId()); // id
948         writeV3F32(os, m_base_position);
949         writeV3F32(os, m_rotation);
950         writeU16(os, getHP());
951
952         std::ostringstream msg_os(std::ios::binary);
953         msg_os << serializeLongString(getPropertyPacket()); // message 1
954         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
955         msg_os << serializeLongString(gob_cmd_update_animation(
956                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
957         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
958                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
959                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
960                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
961         }
962         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
963                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
964         msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
965                         m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
966                         m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
967         // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
968         msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
969         int message_count = 6 + m_bone_position.size();
970         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
971                         ii != m_attachment_child_ids.end(); ++ii) {
972                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
973                         message_count++;
974                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
975                                 obj->getClientInitializationData(protocol_version)));
976                 }
977         }
978
979         writeU8(os, message_count);
980         os.write(msg_os.str().c_str(), msg_os.str().size());
981
982         // return result
983         return os.str();
984 }
985
986 void PlayerSAO::getStaticData(std::string * result) const
987 {
988         FATAL_ERROR("Deprecated function");
989 }
990
991 void PlayerSAO::step(float dtime, bool send_recommended)
992 {
993         if (m_drowning_interval.step(dtime, 2.0f)) {
994                 // Get nose/mouth position, approximate with eye position
995                 v3s16 p = floatToInt(getEyePosition(), BS);
996                 MapNode n = m_env->getMap().getNodeNoEx(p);
997                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
998                 // If node generates drown
999                 if (c.drowning > 0 && m_hp > 0) {
1000                         if (m_breath > 0)
1001                                 setBreath(m_breath - 1);
1002
1003                         // No more breath, damage player
1004                         if (m_breath == 0) {
1005                                 PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING);
1006                                 setHP(m_hp - c.drowning, reason);
1007                                 m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
1008                         }
1009                 }
1010         }
1011
1012         if (m_breathing_interval.step(dtime, 0.5f)) {
1013                 // Get nose/mouth position, approximate with eye position
1014                 v3s16 p = floatToInt(getEyePosition(), BS);
1015                 MapNode n = m_env->getMap().getNodeNoEx(p);
1016                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
1017                 // If player is alive & no drowning, breathe
1018                 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
1019                         setBreath(m_breath + 1);
1020         }
1021
1022         if (m_node_hurt_interval.step(dtime, 1.0f)) {
1023                 u32 damage_per_second = 0;
1024                 // Lowest and highest damage points are 0.1 within collisionbox
1025                 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
1026
1027                 // Sequence of damage points, starting 0.1 above feet and progressing
1028                 // upwards in 1 node intervals, stopping below top damage point.
1029                 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
1030                         v3s16 p = floatToInt(m_base_position +
1031                                 v3f(0.0f, dam_height * BS, 0.0f), BS);
1032                         MapNode n = m_env->getMap().getNodeNoEx(p);
1033                         damage_per_second = std::max(damage_per_second,
1034                                 m_env->getGameDef()->ndef()->get(n).damage_per_second);
1035                 }
1036
1037                 // Top damage point
1038                 v3s16 ptop = floatToInt(m_base_position +
1039                         v3f(0.0f, dam_top * BS, 0.0f), BS);
1040                 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
1041                 damage_per_second = std::max(damage_per_second,
1042                         m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
1043
1044                 if (damage_per_second != 0 && m_hp > 0) {
1045                         s32 newhp = (s32)m_hp - (s32)damage_per_second;
1046                         PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE);
1047                         setHP(newhp, reason);
1048                         m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
1049                 }
1050         }
1051
1052         if (!m_properties_sent) {
1053                 m_properties_sent = true;
1054                 std::string str = getPropertyPacket();
1055                 // create message and add to list
1056                 ActiveObjectMessage aom(getId(), true, str);
1057                 m_messages_out.push(aom);
1058         }
1059
1060         // If attached, check that our parent is still there. If it isn't, detach.
1061         if (m_attachment_parent_id && !isAttached()) {
1062                 m_attachment_parent_id = 0;
1063                 m_attachment_bone = "";
1064                 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
1065                 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
1066                 setBasePosition(m_last_good_position);
1067                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1068         }
1069
1070         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1071
1072         // Set lag pool maximums based on estimated lag
1073         const float LAG_POOL_MIN = 5.0f;
1074         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1075         if(lag_pool_max < LAG_POOL_MIN)
1076                 lag_pool_max = LAG_POOL_MIN;
1077         m_dig_pool.setMax(lag_pool_max);
1078         m_move_pool.setMax(lag_pool_max);
1079
1080         // Increment cheat prevention timers
1081         m_dig_pool.add(dtime);
1082         m_move_pool.add(dtime);
1083         m_time_from_last_teleport += dtime;
1084         m_time_from_last_punch += dtime;
1085         m_nocheat_dig_time += dtime;
1086
1087         // Each frame, parent position is copied if the object is attached,
1088         // otherwise it's calculated normally.
1089         // If the object gets detached this comes into effect automatically from
1090         // the last known origin.
1091         if (isAttached()) {
1092                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1093                 m_last_good_position = pos;
1094                 setBasePosition(pos);
1095         }
1096
1097         if (!send_recommended)
1098                 return;
1099
1100         // If the object is attached client-side, don't waste bandwidth sending its
1101         // position or rotation to clients.
1102         if (m_position_not_sent && !isAttached()) {
1103                 m_position_not_sent = false;
1104                 float update_interval = m_env->getSendRecommendedInterval();
1105                 v3f pos;
1106                 if (isAttached()) // Just in case we ever do send attachment position too
1107                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1108                 else
1109                         pos = m_base_position;
1110
1111                 std::string str = gob_cmd_update_position(
1112                         pos,
1113                         v3f(0.0f, 0.0f, 0.0f),
1114                         v3f(0.0f, 0.0f, 0.0f),
1115                         m_rotation,
1116                         true,
1117                         false,
1118                         update_interval
1119                 );
1120                 // create message and add to list
1121                 ActiveObjectMessage aom(getId(), false, str);
1122                 m_messages_out.push(aom);
1123         }
1124
1125         if (!m_armor_groups_sent) {
1126                 m_armor_groups_sent = true;
1127                 std::string str = gob_cmd_update_armor_groups(
1128                                 m_armor_groups);
1129                 // create message and add to list
1130                 ActiveObjectMessage aom(getId(), true, str);
1131                 m_messages_out.push(aom);
1132         }
1133
1134         if (!m_physics_override_sent) {
1135                 m_physics_override_sent = true;
1136                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1137                                 m_physics_override_jump, m_physics_override_gravity,
1138                                 m_physics_override_sneak, m_physics_override_sneak_glitch,
1139                                 m_physics_override_new_move);
1140                 // create message and add to list
1141                 ActiveObjectMessage aom(getId(), true, str);
1142                 m_messages_out.push(aom);
1143         }
1144
1145         if (!m_animation_sent) {
1146                 m_animation_sent = true;
1147                 std::string str = gob_cmd_update_animation(
1148                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1149                 // create message and add to list
1150                 ActiveObjectMessage aom(getId(), true, str);
1151                 m_messages_out.push(aom);
1152         }
1153
1154         if (!m_bone_position_sent) {
1155                 m_bone_position_sent = true;
1156                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1157                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1158                         std::string str = gob_cmd_update_bone_position((*ii).first,
1159                                         (*ii).second.X, (*ii).second.Y);
1160                         // create message and add to list
1161                         ActiveObjectMessage aom(getId(), true, str);
1162                         m_messages_out.push(aom);
1163                 }
1164         }
1165
1166         if (!m_attachment_sent) {
1167                 m_attachment_sent = true;
1168                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1169                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1170                 // create message and add to list
1171                 ActiveObjectMessage aom(getId(), true, str);
1172                 m_messages_out.push(aom);
1173         }
1174 }
1175
1176 void PlayerSAO::setBasePosition(const v3f &position)
1177 {
1178         if (m_player && position != m_base_position)
1179                 m_player->setDirty(true);
1180
1181         // This needs to be ran for attachments too
1182         ServerActiveObject::setBasePosition(position);
1183
1184         // Updating is not wanted/required for player migration
1185         if (m_env) {
1186                 m_position_not_sent = true;
1187         }
1188 }
1189
1190 void PlayerSAO::setPos(const v3f &pos)
1191 {
1192         if(isAttached())
1193                 return;
1194
1195         setBasePosition(pos);
1196         // Movement caused by this command is always valid
1197         m_last_good_position = pos;
1198         m_move_pool.empty();
1199         m_time_from_last_teleport = 0.0;
1200         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1201 }
1202
1203 void PlayerSAO::moveTo(v3f pos, bool continuous)
1204 {
1205         if(isAttached())
1206                 return;
1207
1208         setBasePosition(pos);
1209         // Movement caused by this command is always valid
1210         m_last_good_position = pos;
1211         m_move_pool.empty();
1212         m_time_from_last_teleport = 0.0;
1213         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1214 }
1215
1216 void PlayerSAO::setPlayerYaw(const float yaw)
1217 {
1218         v3f rotation(0, yaw, 0);
1219         if (m_player && yaw != m_rotation.Y)
1220                 m_player->setDirty(true);
1221
1222         // Set player model yaw, not look view
1223         UnitSAO::setRotation(rotation);
1224 }
1225
1226 void PlayerSAO::setFov(const float fov)
1227 {
1228         if (m_player && fov != m_fov)
1229                 m_player->setDirty(true);
1230
1231         m_fov = fov;
1232 }
1233
1234 void PlayerSAO::setWantedRange(const s16 range)
1235 {
1236         if (m_player && range != m_wanted_range)
1237                 m_player->setDirty(true);
1238
1239         m_wanted_range = range;
1240 }
1241
1242 void PlayerSAO::setPlayerYawAndSend(const float yaw)
1243 {
1244         setPlayerYaw(yaw);
1245         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1246 }
1247
1248 void PlayerSAO::setLookPitch(const float pitch)
1249 {
1250         if (m_player && pitch != m_pitch)
1251                 m_player->setDirty(true);
1252
1253         m_pitch = pitch;
1254 }
1255
1256 void PlayerSAO::setLookPitchAndSend(const float pitch)
1257 {
1258         setLookPitch(pitch);
1259         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1260 }
1261
1262 int PlayerSAO::punch(v3f dir,
1263         const ToolCapabilities *toolcap,
1264         ServerActiveObject *puncher,
1265         float time_from_last_punch)
1266 {
1267         if (!toolcap)
1268                 return 0;
1269
1270         // No effect if PvP disabled
1271         if (!g_settings->getBool("enable_pvp")) {
1272                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1273                         std::string str = gob_cmd_punched(getHP());
1274                         // create message and add to list
1275                         ActiveObjectMessage aom(getId(), true, str);
1276                         m_messages_out.push(aom);
1277                         return 0;
1278                 }
1279         }
1280
1281         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1282                         time_from_last_punch);
1283
1284         std::string punchername = "nil";
1285
1286         if (puncher != 0)
1287                 punchername = puncher->getDescription();
1288
1289         PlayerSAO *playersao = m_player->getPlayerSAO();
1290
1291         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1292                                 puncher, time_from_last_punch, toolcap, dir,
1293                                 hitparams.hp);
1294
1295         if (!damage_handled) {
1296                 setHP((s32)getHP() - (s32)hitparams.hp,
1297                                 PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
1298         } else { // override client prediction
1299                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1300                         std::string str = gob_cmd_punched(getHP());
1301                         // create message and add to list
1302                         ActiveObjectMessage aom(getId(), true, str);
1303                         m_messages_out.push(aom);
1304                 }
1305         }
1306
1307
1308         actionstream << "Player " << m_player->getName() << " punched by "
1309                         << punchername;
1310         if (!damage_handled) {
1311                 actionstream << ", damage " << hitparams.hp << " HP";
1312         } else {
1313                 actionstream << ", damage handled by lua";
1314         }
1315         actionstream << std::endl;
1316
1317         return hitparams.wear;
1318 }
1319
1320 void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
1321 {
1322         s32 oldhp = m_hp;
1323
1324         s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason);
1325         if (hp_change == 0)
1326                 return;
1327
1328         hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max);
1329
1330         if (hp < oldhp && !g_settings->getBool("enable_damage"))
1331                 return;
1332
1333         m_hp = hp;
1334
1335         // Update properties on death
1336         if ((hp == 0) != (oldhp == 0))
1337                 m_properties_sent = false;
1338 }
1339
1340 void PlayerSAO::setBreath(const u16 breath, bool send)
1341 {
1342         if (m_player && breath != m_breath)
1343                 m_player->setDirty(true);
1344
1345         m_breath = MYMIN(breath, m_prop.breath_max);
1346
1347         if (send)
1348                 m_env->getGameDef()->SendPlayerBreath(this);
1349 }
1350
1351 Inventory* PlayerSAO::getInventory()
1352 {
1353         return m_inventory;
1354 }
1355 const Inventory* PlayerSAO::getInventory() const
1356 {
1357         return m_inventory;
1358 }
1359
1360 InventoryLocation PlayerSAO::getInventoryLocation() const
1361 {
1362         InventoryLocation loc;
1363         loc.setPlayer(m_player->getName());
1364         return loc;
1365 }
1366
1367 std::string PlayerSAO::getWieldList() const
1368 {
1369         return "main";
1370 }
1371
1372 ItemStack PlayerSAO::getWieldedItem() const
1373 {
1374         const Inventory *inv = getInventory();
1375         ItemStack ret;
1376         const InventoryList *mlist = inv->getList(getWieldList());
1377         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1378                 ret = mlist->getItem(getWieldIndex());
1379         return ret;
1380 }
1381
1382 ItemStack PlayerSAO::getWieldedItemOrHand() const
1383 {
1384         const Inventory *inv = getInventory();
1385         ItemStack ret;
1386         const InventoryList *mlist = inv->getList(getWieldList());
1387         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1388                 ret = mlist->getItem(getWieldIndex());
1389         if (ret.name.empty()) {
1390                 const InventoryList *hlist = inv->getList("hand");
1391                 if (hlist)
1392                         ret = hlist->getItem(0);
1393         }
1394         return ret;
1395 }
1396
1397 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1398 {
1399         Inventory *inv = getInventory();
1400         if (inv) {
1401                 InventoryList *mlist = inv->getList(getWieldList());
1402                 if (mlist) {
1403                         mlist->changeItem(getWieldIndex(), item);
1404                         return true;
1405                 }
1406         }
1407         return false;
1408 }
1409
1410 int PlayerSAO::getWieldIndex() const
1411 {
1412         return m_wield_index;
1413 }
1414
1415 void PlayerSAO::setWieldIndex(int i)
1416 {
1417         if(i != m_wield_index) {
1418                 m_wield_index = i;
1419         }
1420 }
1421
1422 void PlayerSAO::disconnected()
1423 {
1424         m_peer_id = 0;
1425         m_pending_removal = true;
1426 }
1427
1428 void PlayerSAO::unlinkPlayerSessionAndSave()
1429 {
1430         assert(m_player->getPlayerSAO() == this);
1431         m_player->setPeerId(PEER_ID_INEXISTENT);
1432         m_env->savePlayer(m_player);
1433         m_player->setPlayerSAO(NULL);
1434         m_env->removePlayer(m_player);
1435 }
1436
1437 std::string PlayerSAO::getPropertyPacket()
1438 {
1439         m_prop.is_visible = (true);
1440         return gob_cmd_set_properties(m_prop);
1441 }
1442
1443 bool PlayerSAO::checkMovementCheat()
1444 {
1445         if (isAttached() || m_is_singleplayer ||
1446                         g_settings->getBool("disable_anticheat")) {
1447                 m_last_good_position = m_base_position;
1448                 return false;
1449         }
1450
1451         bool cheated = false;
1452         /*
1453                 Check player movements
1454
1455                 NOTE: Actually the server should handle player physics like the
1456                 client does and compare player's position to what is calculated
1457                 on our side. This is required when eg. players fly due to an
1458                 explosion. Altough a node-based alternative might be possible
1459                 too, and much more lightweight.
1460         */
1461
1462         float player_max_walk = 0; // horizontal movement
1463         float player_max_jump = 0; // vertical upwards movement
1464
1465         if (m_privs.count("fast") != 0)
1466                 player_max_walk = m_player->movement_speed_fast; // Fast speed
1467         else
1468                 player_max_walk = m_player->movement_speed_walk; // Normal speed
1469         player_max_walk *= m_physics_override_speed;
1470         player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
1471         // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
1472         //        until this can be verified correctly, tolerate higher jumping speeds
1473         player_max_jump *= 2.0;
1474
1475         // Don't divide by zero!
1476         if (player_max_walk < 0.0001f)
1477                 player_max_walk = 0.0001f;
1478         if (player_max_jump < 0.0001f)
1479                 player_max_jump = 0.0001f;
1480
1481         v3f diff = (m_base_position - m_last_good_position);
1482         float d_vert = diff.Y;
1483         diff.Y = 0;
1484         float d_horiz = diff.getLength();
1485         float required_time = d_horiz / player_max_walk;
1486
1487         // FIXME: Checking downwards movement is not easily possible currently,
1488         //        the server could calculate speed differences to examine the gravity
1489         if (d_vert > 0) {
1490                 // In certain cases (water, ladders) walking speed is applied vertically
1491                 float s = MYMAX(player_max_jump, player_max_walk);
1492                 required_time = MYMAX(required_time, d_vert / s);
1493         }
1494
1495         if (m_move_pool.grab(required_time)) {
1496                 m_last_good_position = m_base_position;
1497         } else {
1498                 const float LAG_POOL_MIN = 5.0;
1499                 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1500                 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1501                 if (m_time_from_last_teleport > lag_pool_max) {
1502                         actionstream << "Player " << m_player->getName()
1503                                         << " moved too fast; resetting position"
1504                                         << std::endl;
1505                         cheated = true;
1506                 }
1507                 setBasePosition(m_last_good_position);
1508         }
1509         return cheated;
1510 }
1511
1512 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1513 {
1514         //update collision box
1515         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1516         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1517
1518         toset->MinEdge += m_base_position;
1519         toset->MaxEdge += m_base_position;
1520         return true;
1521 }
1522
1523 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1524 {
1525         if (!m_prop.is_visible || !m_prop.pointable) {
1526                 return false;
1527         }
1528
1529         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1530         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
1531
1532         return true;
1533 }
1534
1535 float PlayerSAO::getZoomFOV() const
1536 {
1537         return m_prop.zoom_fov;
1538 }