Update minetest.conf.example and settings_translation_file.cpp (#8278)
[oweals/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 & not in ignore, breathe
1018                 if (m_breath < m_prop.breath_max &&
1019                                 c.drowning == 0 && n.getContent() != CONTENT_IGNORE && m_hp > 0)
1020                         setBreath(m_breath + 1);
1021         }
1022
1023         if (m_node_hurt_interval.step(dtime, 1.0f)) {
1024                 u32 damage_per_second = 0;
1025                 // Lowest and highest damage points are 0.1 within collisionbox
1026                 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
1027
1028                 // Sequence of damage points, starting 0.1 above feet and progressing
1029                 // upwards in 1 node intervals, stopping below top damage point.
1030                 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
1031                         v3s16 p = floatToInt(m_base_position +
1032                                 v3f(0.0f, dam_height * BS, 0.0f), BS);
1033                         MapNode n = m_env->getMap().getNodeNoEx(p);
1034                         damage_per_second = std::max(damage_per_second,
1035                                 m_env->getGameDef()->ndef()->get(n).damage_per_second);
1036                 }
1037
1038                 // Top damage point
1039                 v3s16 ptop = floatToInt(m_base_position +
1040                         v3f(0.0f, dam_top * BS, 0.0f), BS);
1041                 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
1042                 damage_per_second = std::max(damage_per_second,
1043                         m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
1044
1045                 if (damage_per_second != 0 && m_hp > 0) {
1046                         s32 newhp = (s32)m_hp - (s32)damage_per_second;
1047                         PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE);
1048                         setHP(newhp, reason);
1049                         m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
1050                 }
1051         }
1052
1053         if (!m_properties_sent) {
1054                 m_properties_sent = true;
1055                 std::string str = getPropertyPacket();
1056                 // create message and add to list
1057                 ActiveObjectMessage aom(getId(), true, str);
1058                 m_messages_out.push(aom);
1059         }
1060
1061         // If attached, check that our parent is still there. If it isn't, detach.
1062         if (m_attachment_parent_id && !isAttached()) {
1063                 m_attachment_parent_id = 0;
1064                 m_attachment_bone = "";
1065                 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
1066                 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
1067                 setBasePosition(m_last_good_position);
1068                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1069         }
1070
1071         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1072
1073         // Set lag pool maximums based on estimated lag
1074         const float LAG_POOL_MIN = 5.0f;
1075         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1076         if(lag_pool_max < LAG_POOL_MIN)
1077                 lag_pool_max = LAG_POOL_MIN;
1078         m_dig_pool.setMax(lag_pool_max);
1079         m_move_pool.setMax(lag_pool_max);
1080
1081         // Increment cheat prevention timers
1082         m_dig_pool.add(dtime);
1083         m_move_pool.add(dtime);
1084         m_time_from_last_teleport += dtime;
1085         m_time_from_last_punch += dtime;
1086         m_nocheat_dig_time += dtime;
1087
1088         // Each frame, parent position is copied if the object is attached,
1089         // otherwise it's calculated normally.
1090         // If the object gets detached this comes into effect automatically from
1091         // the last known origin.
1092         if (isAttached()) {
1093                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1094                 m_last_good_position = pos;
1095                 setBasePosition(pos);
1096         }
1097
1098         if (!send_recommended)
1099                 return;
1100
1101         // If the object is attached client-side, don't waste bandwidth sending its
1102         // position or rotation to clients.
1103         if (m_position_not_sent && !isAttached()) {
1104                 m_position_not_sent = false;
1105                 float update_interval = m_env->getSendRecommendedInterval();
1106                 v3f pos;
1107                 if (isAttached()) // Just in case we ever do send attachment position too
1108                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1109                 else
1110                         pos = m_base_position;
1111
1112                 std::string str = gob_cmd_update_position(
1113                         pos,
1114                         v3f(0.0f, 0.0f, 0.0f),
1115                         v3f(0.0f, 0.0f, 0.0f),
1116                         m_rotation,
1117                         true,
1118                         false,
1119                         update_interval
1120                 );
1121                 // create message and add to list
1122                 ActiveObjectMessage aom(getId(), false, str);
1123                 m_messages_out.push(aom);
1124         }
1125
1126         if (!m_armor_groups_sent) {
1127                 m_armor_groups_sent = true;
1128                 std::string str = gob_cmd_update_armor_groups(
1129                                 m_armor_groups);
1130                 // create message and add to list
1131                 ActiveObjectMessage aom(getId(), true, str);
1132                 m_messages_out.push(aom);
1133         }
1134
1135         if (!m_physics_override_sent) {
1136                 m_physics_override_sent = true;
1137                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1138                                 m_physics_override_jump, m_physics_override_gravity,
1139                                 m_physics_override_sneak, m_physics_override_sneak_glitch,
1140                                 m_physics_override_new_move);
1141                 // create message and add to list
1142                 ActiveObjectMessage aom(getId(), true, str);
1143                 m_messages_out.push(aom);
1144         }
1145
1146         if (!m_animation_sent) {
1147                 m_animation_sent = true;
1148                 std::string str = gob_cmd_update_animation(
1149                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1150                 // create message and add to list
1151                 ActiveObjectMessage aom(getId(), true, str);
1152                 m_messages_out.push(aom);
1153         }
1154
1155         if (!m_bone_position_sent) {
1156                 m_bone_position_sent = true;
1157                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1158                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1159                         std::string str = gob_cmd_update_bone_position((*ii).first,
1160                                         (*ii).second.X, (*ii).second.Y);
1161                         // create message and add to list
1162                         ActiveObjectMessage aom(getId(), true, str);
1163                         m_messages_out.push(aom);
1164                 }
1165         }
1166
1167         if (!m_attachment_sent) {
1168                 m_attachment_sent = true;
1169                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1170                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1171                 // create message and add to list
1172                 ActiveObjectMessage aom(getId(), true, str);
1173                 m_messages_out.push(aom);
1174         }
1175 }
1176
1177 void PlayerSAO::setBasePosition(const v3f &position)
1178 {
1179         if (m_player && position != m_base_position)
1180                 m_player->setDirty(true);
1181
1182         // This needs to be ran for attachments too
1183         ServerActiveObject::setBasePosition(position);
1184
1185         // Updating is not wanted/required for player migration
1186         if (m_env) {
1187                 m_position_not_sent = true;
1188         }
1189 }
1190
1191 void PlayerSAO::setPos(const v3f &pos)
1192 {
1193         if(isAttached())
1194                 return;
1195
1196         setBasePosition(pos);
1197         // Movement caused by this command is always valid
1198         m_last_good_position = pos;
1199         m_move_pool.empty();
1200         m_time_from_last_teleport = 0.0;
1201         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1202 }
1203
1204 void PlayerSAO::moveTo(v3f pos, bool continuous)
1205 {
1206         if(isAttached())
1207                 return;
1208
1209         setBasePosition(pos);
1210         // Movement caused by this command is always valid
1211         m_last_good_position = pos;
1212         m_move_pool.empty();
1213         m_time_from_last_teleport = 0.0;
1214         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1215 }
1216
1217 void PlayerSAO::setPlayerYaw(const float yaw)
1218 {
1219         v3f rotation(0, yaw, 0);
1220         if (m_player && yaw != m_rotation.Y)
1221                 m_player->setDirty(true);
1222
1223         // Set player model yaw, not look view
1224         UnitSAO::setRotation(rotation);
1225 }
1226
1227 void PlayerSAO::setFov(const float fov)
1228 {
1229         if (m_player && fov != m_fov)
1230                 m_player->setDirty(true);
1231
1232         m_fov = fov;
1233 }
1234
1235 void PlayerSAO::setWantedRange(const s16 range)
1236 {
1237         if (m_player && range != m_wanted_range)
1238                 m_player->setDirty(true);
1239
1240         m_wanted_range = range;
1241 }
1242
1243 void PlayerSAO::setPlayerYawAndSend(const float yaw)
1244 {
1245         setPlayerYaw(yaw);
1246         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1247 }
1248
1249 void PlayerSAO::setLookPitch(const float pitch)
1250 {
1251         if (m_player && pitch != m_pitch)
1252                 m_player->setDirty(true);
1253
1254         m_pitch = pitch;
1255 }
1256
1257 void PlayerSAO::setLookPitchAndSend(const float pitch)
1258 {
1259         setLookPitch(pitch);
1260         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1261 }
1262
1263 int PlayerSAO::punch(v3f dir,
1264         const ToolCapabilities *toolcap,
1265         ServerActiveObject *puncher,
1266         float time_from_last_punch)
1267 {
1268         if (!toolcap)
1269                 return 0;
1270
1271         // No effect if PvP disabled
1272         if (!g_settings->getBool("enable_pvp")) {
1273                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1274                         std::string str = gob_cmd_punched(getHP());
1275                         // create message and add to list
1276                         ActiveObjectMessage aom(getId(), true, str);
1277                         m_messages_out.push(aom);
1278                         return 0;
1279                 }
1280         }
1281
1282         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1283                         time_from_last_punch);
1284
1285         std::string punchername = "nil";
1286
1287         if (puncher != 0)
1288                 punchername = puncher->getDescription();
1289
1290         PlayerSAO *playersao = m_player->getPlayerSAO();
1291
1292         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1293                                 puncher, time_from_last_punch, toolcap, dir,
1294                                 hitparams.hp);
1295
1296         if (!damage_handled) {
1297                 setHP((s32)getHP() - (s32)hitparams.hp,
1298                                 PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
1299         } else { // override client prediction
1300                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1301                         std::string str = gob_cmd_punched(getHP());
1302                         // create message and add to list
1303                         ActiveObjectMessage aom(getId(), true, str);
1304                         m_messages_out.push(aom);
1305                 }
1306         }
1307
1308
1309         actionstream << "Player " << m_player->getName() << " punched by "
1310                         << punchername;
1311         if (!damage_handled) {
1312                 actionstream << ", damage " << hitparams.hp << " HP";
1313         } else {
1314                 actionstream << ", damage handled by lua";
1315         }
1316         actionstream << std::endl;
1317
1318         return hitparams.wear;
1319 }
1320
1321 void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
1322 {
1323         s32 oldhp = m_hp;
1324
1325         s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason);
1326         if (hp_change == 0)
1327                 return;
1328
1329         hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max);
1330
1331         if (hp < oldhp && !g_settings->getBool("enable_damage"))
1332                 return;
1333
1334         m_hp = hp;
1335
1336         // Update properties on death
1337         if ((hp == 0) != (oldhp == 0))
1338                 m_properties_sent = false;
1339 }
1340
1341 void PlayerSAO::setBreath(const u16 breath, bool send)
1342 {
1343         if (m_player && breath != m_breath)
1344                 m_player->setDirty(true);
1345
1346         m_breath = MYMIN(breath, m_prop.breath_max);
1347
1348         if (send)
1349                 m_env->getGameDef()->SendPlayerBreath(this);
1350 }
1351
1352 Inventory* PlayerSAO::getInventory()
1353 {
1354         return m_inventory;
1355 }
1356 const Inventory* PlayerSAO::getInventory() const
1357 {
1358         return m_inventory;
1359 }
1360
1361 InventoryLocation PlayerSAO::getInventoryLocation() const
1362 {
1363         InventoryLocation loc;
1364         loc.setPlayer(m_player->getName());
1365         return loc;
1366 }
1367
1368 std::string PlayerSAO::getWieldList() const
1369 {
1370         return "main";
1371 }
1372
1373 ItemStack PlayerSAO::getWieldedItem() const
1374 {
1375         const Inventory *inv = getInventory();
1376         ItemStack ret;
1377         const InventoryList *mlist = inv->getList(getWieldList());
1378         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1379                 ret = mlist->getItem(getWieldIndex());
1380         return ret;
1381 }
1382
1383 ItemStack PlayerSAO::getWieldedItemOrHand() const
1384 {
1385         const Inventory *inv = getInventory();
1386         ItemStack ret;
1387         const InventoryList *mlist = inv->getList(getWieldList());
1388         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1389                 ret = mlist->getItem(getWieldIndex());
1390         if (ret.name.empty()) {
1391                 const InventoryList *hlist = inv->getList("hand");
1392                 if (hlist)
1393                         ret = hlist->getItem(0);
1394         }
1395         return ret;
1396 }
1397
1398 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1399 {
1400         Inventory *inv = getInventory();
1401         if (inv) {
1402                 InventoryList *mlist = inv->getList(getWieldList());
1403                 if (mlist) {
1404                         mlist->changeItem(getWieldIndex(), item);
1405                         return true;
1406                 }
1407         }
1408         return false;
1409 }
1410
1411 int PlayerSAO::getWieldIndex() const
1412 {
1413         return m_wield_index;
1414 }
1415
1416 void PlayerSAO::setWieldIndex(int i)
1417 {
1418         if(i != m_wield_index) {
1419                 m_wield_index = i;
1420         }
1421 }
1422
1423 void PlayerSAO::disconnected()
1424 {
1425         m_peer_id = 0;
1426         m_pending_removal = true;
1427 }
1428
1429 void PlayerSAO::unlinkPlayerSessionAndSave()
1430 {
1431         assert(m_player->getPlayerSAO() == this);
1432         m_player->setPeerId(PEER_ID_INEXISTENT);
1433         m_env->savePlayer(m_player);
1434         m_player->setPlayerSAO(NULL);
1435         m_env->removePlayer(m_player);
1436 }
1437
1438 std::string PlayerSAO::getPropertyPacket()
1439 {
1440         m_prop.is_visible = (true);
1441         return gob_cmd_set_properties(m_prop);
1442 }
1443
1444 bool PlayerSAO::checkMovementCheat()
1445 {
1446         if (isAttached() || m_is_singleplayer ||
1447                         g_settings->getBool("disable_anticheat")) {
1448                 m_last_good_position = m_base_position;
1449                 return false;
1450         }
1451
1452         bool cheated = false;
1453         /*
1454                 Check player movements
1455
1456                 NOTE: Actually the server should handle player physics like the
1457                 client does and compare player's position to what is calculated
1458                 on our side. This is required when eg. players fly due to an
1459                 explosion. Altough a node-based alternative might be possible
1460                 too, and much more lightweight.
1461         */
1462
1463         float player_max_walk = 0; // horizontal movement
1464         float player_max_jump = 0; // vertical upwards movement
1465
1466         if (m_privs.count("fast") != 0)
1467                 player_max_walk = m_player->movement_speed_fast; // Fast speed
1468         else
1469                 player_max_walk = m_player->movement_speed_walk; // Normal speed
1470         player_max_walk *= m_physics_override_speed;
1471         player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
1472         // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
1473         //        until this can be verified correctly, tolerate higher jumping speeds
1474         player_max_jump *= 2.0;
1475
1476         // Don't divide by zero!
1477         if (player_max_walk < 0.0001f)
1478                 player_max_walk = 0.0001f;
1479         if (player_max_jump < 0.0001f)
1480                 player_max_jump = 0.0001f;
1481
1482         v3f diff = (m_base_position - m_last_good_position);
1483         float d_vert = diff.Y;
1484         diff.Y = 0;
1485         float d_horiz = diff.getLength();
1486         float required_time = d_horiz / player_max_walk;
1487
1488         // FIXME: Checking downwards movement is not easily possible currently,
1489         //        the server could calculate speed differences to examine the gravity
1490         if (d_vert > 0) {
1491                 // In certain cases (water, ladders) walking speed is applied vertically
1492                 float s = MYMAX(player_max_jump, player_max_walk);
1493                 required_time = MYMAX(required_time, d_vert / s);
1494         }
1495
1496         if (m_move_pool.grab(required_time)) {
1497                 m_last_good_position = m_base_position;
1498         } else {
1499                 const float LAG_POOL_MIN = 5.0;
1500                 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1501                 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1502                 if (m_time_from_last_teleport > lag_pool_max) {
1503                         actionstream << "Player " << m_player->getName()
1504                                         << " moved too fast; resetting position"
1505                                         << std::endl;
1506                         cheated = true;
1507                 }
1508                 setBasePosition(m_last_good_position);
1509         }
1510         return cheated;
1511 }
1512
1513 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1514 {
1515         //update collision box
1516         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1517         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1518
1519         toset->MinEdge += m_base_position;
1520         toset->MaxEdge += m_base_position;
1521         return true;
1522 }
1523
1524 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1525 {
1526         if (!m_prop.is_visible || !m_prop.pointable) {
1527                 return false;
1528         }
1529
1530         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1531         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
1532
1533         return true;
1534 }
1535
1536 float PlayerSAO::getZoomFOV() const
1537 {
1538         return m_prop.zoom_fov;
1539 }