]> git.lizzy.rs Git - minetest.git/blob - src/content_sao.cpp
Proselytize the network. Use IEEE F32 (#8030)
[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         s16 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 = readS16(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         writeS16(os, getId()); //id
552         writeV3F32(os, m_base_position);
553         writeV3F32(os, m_rotation);
554         writeS16(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         writeS16(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(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(result.damage, 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(s16 hp, const PlayerHPChangeReason &reason)
719 {
720         if (hp < 0)
721                 hp = 0;
722         m_hp = hp;
723 }
724
725 s16 LuaEntitySAO::getHP() const
726 {
727         return m_hp;
728 }
729
730 void LuaEntitySAO::setVelocity(v3f velocity)
731 {
732         m_velocity = velocity;
733 }
734
735 v3f LuaEntitySAO::getVelocity()
736 {
737         return m_velocity;
738 }
739
740 void LuaEntitySAO::setAcceleration(v3f acceleration)
741 {
742         m_acceleration = acceleration;
743 }
744
745 v3f LuaEntitySAO::getAcceleration()
746 {
747         return m_acceleration;
748 }
749
750 void LuaEntitySAO::setTextureMod(const std::string &mod)
751 {
752         std::string str = gob_cmd_set_texture_mod(mod);
753         m_current_texture_modifier = mod;
754         // create message and add to list
755         ActiveObjectMessage aom(getId(), true, str);
756         m_messages_out.push(aom);
757 }
758
759 std::string LuaEntitySAO::getTextureMod() const
760 {
761         return m_current_texture_modifier;
762 }
763
764 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
765                 bool select_horiz_by_yawpitch)
766 {
767         std::string str = gob_cmd_set_sprite(
768                 p,
769                 num_frames,
770                 framelength,
771                 select_horiz_by_yawpitch
772         );
773         // create message and add to list
774         ActiveObjectMessage aom(getId(), true, str);
775         m_messages_out.push(aom);
776 }
777
778 std::string LuaEntitySAO::getName()
779 {
780         return m_init_name;
781 }
782
783 std::string LuaEntitySAO::getPropertyPacket()
784 {
785         return gob_cmd_set_properties(m_prop);
786 }
787
788 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
789 {
790         // If the object is attached client-side, don't waste bandwidth sending its position to clients
791         if(isAttached())
792                 return;
793
794         m_last_sent_move_precision = m_base_position.getDistanceFrom(
795                         m_last_sent_position);
796         m_last_sent_position_timer = 0;
797         m_last_sent_position = m_base_position;
798         m_last_sent_velocity = m_velocity;
799         //m_last_sent_acceleration = m_acceleration;
800         m_last_sent_rotation = m_rotation;
801
802         float update_interval = m_env->getSendRecommendedInterval();
803
804         std::string str = gob_cmd_update_position(
805                 m_base_position,
806                 m_velocity,
807                 m_acceleration,
808                 m_rotation,
809                 do_interpolate,
810                 is_movement_end,
811                 update_interval
812         );
813         // create message and add to list
814         ActiveObjectMessage aom(getId(), false, str);
815         m_messages_out.push(aom);
816 }
817
818 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
819 {
820         if (m_prop.physical)
821         {
822                 //update collision box
823                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
824                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
825
826                 toset->MinEdge += m_base_position;
827                 toset->MaxEdge += m_base_position;
828
829                 return true;
830         }
831
832         return false;
833 }
834
835 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
836 {
837         if (!m_prop.is_visible || !m_prop.pointable) {
838                 return false;
839         }
840
841         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
842         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
843
844         return true;
845 }
846
847 bool LuaEntitySAO::collideWithObjects() const
848 {
849         return m_prop.collideWithObjects;
850 }
851
852 /*
853         PlayerSAO
854 */
855
856 // No prototype, PlayerSAO does not need to be deserialized
857
858 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
859                 bool is_singleplayer):
860         UnitSAO(env_, v3f(0,0,0)),
861         m_player(player_),
862         m_peer_id(peer_id_),
863         m_is_singleplayer(is_singleplayer)
864 {
865         assert(m_peer_id != 0); // pre-condition
866
867         m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
868         m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
869         m_prop.physical = false;
870         m_prop.weight = 75;
871         m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
872         m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
873         m_prop.pointable = true;
874         // Start of default appearance, this should be overwritten by Lua
875         m_prop.visual = "upright_sprite";
876         m_prop.visual_size = v3f(1, 2, 1);
877         m_prop.textures.clear();
878         m_prop.textures.emplace_back("player.png");
879         m_prop.textures.emplace_back("player_back.png");
880         m_prop.colors.clear();
881         m_prop.colors.emplace_back(255, 255, 255, 255);
882         m_prop.spritediv = v2s16(1,1);
883         m_prop.eye_height = 1.625f;
884         // End of default appearance
885         m_prop.is_visible = true;
886         m_prop.backface_culling = false;
887         m_prop.makes_footstep_sound = true;
888         m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
889         m_hp = m_prop.hp_max;
890         m_breath = m_prop.breath_max;
891         // Disable zoom in survival mode using a value of 0
892         m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
893 }
894
895 PlayerSAO::~PlayerSAO()
896 {
897         if(m_inventory != &m_player->inventory)
898                 delete m_inventory;
899 }
900
901 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
902 {
903         assert(player);
904         m_player = player;
905         m_privs = privs;
906         m_inventory = &m_player->inventory;
907 }
908
909 v3f PlayerSAO::getEyeOffset() const
910 {
911         return v3f(0, BS * m_prop.eye_height, 0);
912 }
913
914 std::string PlayerSAO::getDescription()
915 {
916         return std::string("player ") + m_player->getName();
917 }
918
919 // Called after id has been set and has been inserted in environment
920 void PlayerSAO::addedToEnvironment(u32 dtime_s)
921 {
922         ServerActiveObject::addedToEnvironment(dtime_s);
923         ServerActiveObject::setBasePosition(m_base_position);
924         m_player->setPlayerSAO(this);
925         m_player->setPeerId(m_peer_id);
926         m_last_good_position = m_base_position;
927 }
928
929 // Called before removing from environment
930 void PlayerSAO::removingFromEnvironment()
931 {
932         ServerActiveObject::removingFromEnvironment();
933         if (m_player->getPlayerSAO() == this) {
934                 unlinkPlayerSessionAndSave();
935                 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
936                         m_env->deleteParticleSpawner(attached_particle_spawner, false);
937                 }
938         }
939 }
940
941 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
942 {
943         std::ostringstream os(std::ios::binary);
944
945         // Protocol >= 15
946         writeU8(os, 1); // version
947         os << serializeString(m_player->getName()); // name
948         writeU8(os, 1); // is_player
949         writeS16(os, getId()); // id
950         writeV3F32(os, m_base_position);
951         writeV3F32(os, m_rotation);
952         writeS16(os, getHP());
953
954         std::ostringstream msg_os(std::ios::binary);
955         msg_os << serializeLongString(getPropertyPacket()); // message 1
956         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
957         msg_os << serializeLongString(gob_cmd_update_animation(
958                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
959         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
960                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
961                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
962                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
963         }
964         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
965                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
966         msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
967                         m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
968                         m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
969         // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
970         msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
971         int message_count = 6 + m_bone_position.size();
972         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
973                         ii != m_attachment_child_ids.end(); ++ii) {
974                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
975                         message_count++;
976                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
977                                 obj->getClientInitializationData(protocol_version)));
978                 }
979         }
980
981         writeU8(os, message_count);
982         os.write(msg_os.str().c_str(), msg_os.str().size());
983
984         // return result
985         return os.str();
986 }
987
988 void PlayerSAO::getStaticData(std::string * result) const
989 {
990         FATAL_ERROR("Deprecated function");
991 }
992
993 void PlayerSAO::step(float dtime, bool send_recommended)
994 {
995         if (m_drowning_interval.step(dtime, 2.0f)) {
996                 // Get nose/mouth position, approximate with eye position
997                 v3s16 p = floatToInt(getEyePosition(), BS);
998                 MapNode n = m_env->getMap().getNodeNoEx(p);
999                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
1000                 // If node generates drown
1001                 if (c.drowning > 0 && m_hp > 0) {
1002                         if (m_breath > 0)
1003                                 setBreath(m_breath - 1);
1004
1005                         // No more breath, damage player
1006                         if (m_breath == 0) {
1007                                 PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING);
1008                                 setHP(m_hp - c.drowning, reason);
1009                                 m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
1010                         }
1011                 }
1012         }
1013
1014         if (m_breathing_interval.step(dtime, 0.5f)) {
1015                 // Get nose/mouth position, approximate with eye position
1016                 v3s16 p = floatToInt(getEyePosition(), BS);
1017                 MapNode n = m_env->getMap().getNodeNoEx(p);
1018                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
1019                 // If player is alive & no drowning, breathe
1020                 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
1021                         setBreath(m_breath + 1);
1022         }
1023
1024         if (m_node_hurt_interval.step(dtime, 1.0f)) {
1025                 u32 damage_per_second = 0;
1026                 // Lowest and highest damage points are 0.1 within collisionbox
1027                 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
1028
1029                 // Sequence of damage points, starting 0.1 above feet and progressing
1030                 // upwards in 1 node intervals, stopping below top damage point.
1031                 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
1032                         v3s16 p = floatToInt(m_base_position +
1033                                 v3f(0.0f, dam_height * BS, 0.0f), BS);
1034                         MapNode n = m_env->getMap().getNodeNoEx(p);
1035                         damage_per_second = std::max(damage_per_second,
1036                                 m_env->getGameDef()->ndef()->get(n).damage_per_second);
1037                 }
1038
1039                 // Top damage point
1040                 v3s16 ptop = floatToInt(m_base_position +
1041                         v3f(0.0f, dam_top * BS, 0.0f), BS);
1042                 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
1043                 damage_per_second = std::max(damage_per_second,
1044                         m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
1045
1046                 if (damage_per_second != 0 && m_hp > 0) {
1047                         s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
1048                         PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE);
1049                         setHP(newhp, reason);
1050                         m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
1051                 }
1052         }
1053
1054         if (!m_properties_sent) {
1055                 m_properties_sent = true;
1056                 std::string str = getPropertyPacket();
1057                 // create message and add to list
1058                 ActiveObjectMessage aom(getId(), true, str);
1059                 m_messages_out.push(aom);
1060         }
1061
1062         // If attached, check that our parent is still there. If it isn't, detach.
1063         if (m_attachment_parent_id && !isAttached()) {
1064                 m_attachment_parent_id = 0;
1065                 m_attachment_bone = "";
1066                 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
1067                 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
1068                 setBasePosition(m_last_good_position);
1069                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1070         }
1071
1072         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1073
1074         // Set lag pool maximums based on estimated lag
1075         const float LAG_POOL_MIN = 5.0f;
1076         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1077         if(lag_pool_max < LAG_POOL_MIN)
1078                 lag_pool_max = LAG_POOL_MIN;
1079         m_dig_pool.setMax(lag_pool_max);
1080         m_move_pool.setMax(lag_pool_max);
1081
1082         // Increment cheat prevention timers
1083         m_dig_pool.add(dtime);
1084         m_move_pool.add(dtime);
1085         m_time_from_last_teleport += dtime;
1086         m_time_from_last_punch += dtime;
1087         m_nocheat_dig_time += dtime;
1088
1089         // Each frame, parent position is copied if the object is attached,
1090         // otherwise it's calculated normally.
1091         // If the object gets detached this comes into effect automatically from
1092         // the last known origin.
1093         if (isAttached()) {
1094                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1095                 m_last_good_position = pos;
1096                 setBasePosition(pos);
1097         }
1098
1099         if (!send_recommended)
1100                 return;
1101
1102         // If the object is attached client-side, don't waste bandwidth sending its
1103         // position or rotation to clients.
1104         if (m_position_not_sent && !isAttached()) {
1105                 m_position_not_sent = false;
1106                 float update_interval = m_env->getSendRecommendedInterval();
1107                 v3f pos;
1108                 if (isAttached()) // Just in case we ever do send attachment position too
1109                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1110                 else
1111                         pos = m_base_position;
1112
1113                 std::string str = gob_cmd_update_position(
1114                         pos,
1115                         v3f(0.0f, 0.0f, 0.0f),
1116                         v3f(0.0f, 0.0f, 0.0f),
1117                         m_rotation,
1118                         true,
1119                         false,
1120                         update_interval
1121                 );
1122                 // create message and add to list
1123                 ActiveObjectMessage aom(getId(), false, str);
1124                 m_messages_out.push(aom);
1125         }
1126
1127         if (!m_armor_groups_sent) {
1128                 m_armor_groups_sent = true;
1129                 std::string str = gob_cmd_update_armor_groups(
1130                                 m_armor_groups);
1131                 // create message and add to list
1132                 ActiveObjectMessage aom(getId(), true, str);
1133                 m_messages_out.push(aom);
1134         }
1135
1136         if (!m_physics_override_sent) {
1137                 m_physics_override_sent = true;
1138                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1139                                 m_physics_override_jump, m_physics_override_gravity,
1140                                 m_physics_override_sneak, m_physics_override_sneak_glitch,
1141                                 m_physics_override_new_move);
1142                 // create message and add to list
1143                 ActiveObjectMessage aom(getId(), true, str);
1144                 m_messages_out.push(aom);
1145         }
1146
1147         if (!m_animation_sent) {
1148                 m_animation_sent = true;
1149                 std::string str = gob_cmd_update_animation(
1150                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1151                 // create message and add to list
1152                 ActiveObjectMessage aom(getId(), true, str);
1153                 m_messages_out.push(aom);
1154         }
1155
1156         if (!m_bone_position_sent) {
1157                 m_bone_position_sent = true;
1158                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1159                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1160                         std::string str = gob_cmd_update_bone_position((*ii).first,
1161                                         (*ii).second.X, (*ii).second.Y);
1162                         // create message and add to list
1163                         ActiveObjectMessage aom(getId(), true, str);
1164                         m_messages_out.push(aom);
1165                 }
1166         }
1167
1168         if (!m_attachment_sent) {
1169                 m_attachment_sent = true;
1170                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1171                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1172                 // create message and add to list
1173                 ActiveObjectMessage aom(getId(), true, str);
1174                 m_messages_out.push(aom);
1175         }
1176 }
1177
1178 void PlayerSAO::setBasePosition(const v3f &position)
1179 {
1180         if (m_player && position != m_base_position)
1181                 m_player->setDirty(true);
1182
1183         // This needs to be ran for attachments too
1184         ServerActiveObject::setBasePosition(position);
1185
1186         // Updating is not wanted/required for player migration
1187         if (m_env) {
1188                 m_position_not_sent = true;
1189         }
1190 }
1191
1192 void PlayerSAO::setPos(const v3f &pos)
1193 {
1194         if(isAttached())
1195                 return;
1196
1197         setBasePosition(pos);
1198         // Movement caused by this command is always valid
1199         m_last_good_position = pos;
1200         m_move_pool.empty();
1201         m_time_from_last_teleport = 0.0;
1202         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1203 }
1204
1205 void PlayerSAO::moveTo(v3f pos, bool continuous)
1206 {
1207         if(isAttached())
1208                 return;
1209
1210         setBasePosition(pos);
1211         // Movement caused by this command is always valid
1212         m_last_good_position = pos;
1213         m_move_pool.empty();
1214         m_time_from_last_teleport = 0.0;
1215         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1216 }
1217
1218 void PlayerSAO::setPlayerYaw(const float yaw)
1219 {
1220         v3f rotation(0, yaw, 0);
1221         if (m_player && yaw != m_rotation.Y)
1222                 m_player->setDirty(true);
1223
1224         // Set player model yaw, not look view
1225         UnitSAO::setRotation(rotation);
1226 }
1227
1228 void PlayerSAO::setFov(const float fov)
1229 {
1230         if (m_player && fov != m_fov)
1231                 m_player->setDirty(true);
1232
1233         m_fov = fov;
1234 }
1235
1236 void PlayerSAO::setWantedRange(const s16 range)
1237 {
1238         if (m_player && range != m_wanted_range)
1239                 m_player->setDirty(true);
1240
1241         m_wanted_range = range;
1242 }
1243
1244 void PlayerSAO::setPlayerYawAndSend(const float yaw)
1245 {
1246         setPlayerYaw(yaw);
1247         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1248 }
1249
1250 void PlayerSAO::setLookPitch(const float pitch)
1251 {
1252         if (m_player && pitch != m_pitch)
1253                 m_player->setDirty(true);
1254
1255         m_pitch = pitch;
1256 }
1257
1258 void PlayerSAO::setLookPitchAndSend(const float pitch)
1259 {
1260         setLookPitch(pitch);
1261         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1262 }
1263
1264 int PlayerSAO::punch(v3f dir,
1265         const ToolCapabilities *toolcap,
1266         ServerActiveObject *puncher,
1267         float time_from_last_punch)
1268 {
1269         if (!toolcap)
1270                 return 0;
1271
1272         // No effect if PvP disabled
1273         if (!g_settings->getBool("enable_pvp")) {
1274                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1275                         std::string str = gob_cmd_punched(0, getHP());
1276                         // create message and add to list
1277                         ActiveObjectMessage aom(getId(), true, str);
1278                         m_messages_out.push(aom);
1279                         return 0;
1280                 }
1281         }
1282
1283         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1284                         time_from_last_punch);
1285
1286         std::string punchername = "nil";
1287
1288         if (puncher != 0)
1289                 punchername = puncher->getDescription();
1290
1291         PlayerSAO *playersao = m_player->getPlayerSAO();
1292
1293         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1294                                 puncher, time_from_last_punch, toolcap, dir,
1295                                 hitparams.hp);
1296
1297         if (!damage_handled) {
1298                 setHP(getHP() - hitparams.hp,
1299                                 PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
1300         } else { // override client prediction
1301                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1302                         std::string str = gob_cmd_punched(0, getHP());
1303                         // create message and add to list
1304                         ActiveObjectMessage aom(getId(), true, str);
1305                         m_messages_out.push(aom);
1306                 }
1307         }
1308
1309
1310         actionstream << "Player " << m_player->getName() << " punched by "
1311                         << punchername;
1312         if (!damage_handled) {
1313                 actionstream << ", damage " << hitparams.hp << " HP";
1314         } else {
1315                 actionstream << ", damage handled by lua";
1316         }
1317         actionstream << std::endl;
1318
1319         return hitparams.wear;
1320 }
1321
1322 s16 PlayerSAO::readDamage()
1323 {
1324         s16 damage = m_damage;
1325         m_damage = 0;
1326         return damage;
1327 }
1328
1329 void PlayerSAO::setHP(s16 hp, const PlayerHPChangeReason &reason)
1330 {
1331         s16 oldhp = m_hp;
1332
1333         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason);
1334         if (hp_change == 0)
1335                 return;
1336         hp = oldhp + hp_change;
1337
1338         if (hp < 0)
1339                 hp = 0;
1340         else if (hp > m_prop.hp_max)
1341                 hp = m_prop.hp_max;
1342
1343         if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1344                 return;
1345         }
1346
1347         m_hp = hp;
1348
1349         if (oldhp > hp)
1350                 m_damage += (oldhp - hp);
1351
1352         // Update properties on death
1353         if ((hp == 0) != (oldhp == 0))
1354                 m_properties_sent = false;
1355 }
1356
1357 void PlayerSAO::setBreath(const u16 breath, bool send)
1358 {
1359         if (m_player && breath != m_breath)
1360                 m_player->setDirty(true);
1361
1362         m_breath = MYMIN(breath, m_prop.breath_max);
1363
1364         if (send)
1365                 m_env->getGameDef()->SendPlayerBreath(this);
1366 }
1367
1368 Inventory* PlayerSAO::getInventory()
1369 {
1370         return m_inventory;
1371 }
1372 const Inventory* PlayerSAO::getInventory() const
1373 {
1374         return m_inventory;
1375 }
1376
1377 InventoryLocation PlayerSAO::getInventoryLocation() const
1378 {
1379         InventoryLocation loc;
1380         loc.setPlayer(m_player->getName());
1381         return loc;
1382 }
1383
1384 std::string PlayerSAO::getWieldList() const
1385 {
1386         return "main";
1387 }
1388
1389 ItemStack PlayerSAO::getWieldedItem() const
1390 {
1391         const Inventory *inv = getInventory();
1392         ItemStack ret;
1393         const InventoryList *mlist = inv->getList(getWieldList());
1394         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1395                 ret = mlist->getItem(getWieldIndex());
1396         return ret;
1397 }
1398
1399 ItemStack PlayerSAO::getWieldedItemOrHand() const
1400 {
1401         const Inventory *inv = getInventory();
1402         ItemStack ret;
1403         const InventoryList *mlist = inv->getList(getWieldList());
1404         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1405                 ret = mlist->getItem(getWieldIndex());
1406         if (ret.name.empty()) {
1407                 const InventoryList *hlist = inv->getList("hand");
1408                 if (hlist)
1409                         ret = hlist->getItem(0);
1410         }
1411         return ret;
1412 }
1413
1414 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1415 {
1416         Inventory *inv = getInventory();
1417         if (inv) {
1418                 InventoryList *mlist = inv->getList(getWieldList());
1419                 if (mlist) {
1420                         mlist->changeItem(getWieldIndex(), item);
1421                         return true;
1422                 }
1423         }
1424         return false;
1425 }
1426
1427 int PlayerSAO::getWieldIndex() const
1428 {
1429         return m_wield_index;
1430 }
1431
1432 void PlayerSAO::setWieldIndex(int i)
1433 {
1434         if(i != m_wield_index) {
1435                 m_wield_index = i;
1436         }
1437 }
1438
1439 void PlayerSAO::disconnected()
1440 {
1441         m_peer_id = 0;
1442         m_pending_removal = true;
1443 }
1444
1445 void PlayerSAO::unlinkPlayerSessionAndSave()
1446 {
1447         assert(m_player->getPlayerSAO() == this);
1448         m_player->setPeerId(PEER_ID_INEXISTENT);
1449         m_env->savePlayer(m_player);
1450         m_player->setPlayerSAO(NULL);
1451         m_env->removePlayer(m_player);
1452 }
1453
1454 std::string PlayerSAO::getPropertyPacket()
1455 {
1456         m_prop.is_visible = (true);
1457         return gob_cmd_set_properties(m_prop);
1458 }
1459
1460 bool PlayerSAO::checkMovementCheat()
1461 {
1462         if (isAttached() || m_is_singleplayer ||
1463                         g_settings->getBool("disable_anticheat")) {
1464                 m_last_good_position = m_base_position;
1465                 return false;
1466         }
1467
1468         bool cheated = false;
1469         /*
1470                 Check player movements
1471
1472                 NOTE: Actually the server should handle player physics like the
1473                 client does and compare player's position to what is calculated
1474                 on our side. This is required when eg. players fly due to an
1475                 explosion. Altough a node-based alternative might be possible
1476                 too, and much more lightweight.
1477         */
1478
1479         float player_max_walk = 0; // horizontal movement
1480         float player_max_jump = 0; // vertical upwards movement
1481
1482         if (m_privs.count("fast") != 0)
1483                 player_max_walk = m_player->movement_speed_fast; // Fast speed
1484         else
1485                 player_max_walk = m_player->movement_speed_walk; // Normal speed
1486         player_max_walk *= m_physics_override_speed;
1487         player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
1488         // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
1489         //        until this can be verified correctly, tolerate higher jumping speeds
1490         player_max_jump *= 2.0;
1491
1492         // Don't divide by zero!
1493         if (player_max_walk < 0.0001f)
1494                 player_max_walk = 0.0001f;
1495         if (player_max_jump < 0.0001f)
1496                 player_max_jump = 0.0001f;
1497
1498         v3f diff = (m_base_position - m_last_good_position);
1499         float d_vert = diff.Y;
1500         diff.Y = 0;
1501         float d_horiz = diff.getLength();
1502         float required_time = d_horiz / player_max_walk;
1503
1504         // FIXME: Checking downwards movement is not easily possible currently,
1505         //        the server could calculate speed differences to examine the gravity
1506         if (d_vert > 0) {
1507                 // In certain cases (water, ladders) walking speed is applied vertically
1508                 float s = MYMAX(player_max_jump, player_max_walk);
1509                 required_time = MYMAX(required_time, d_vert / s);
1510         }
1511
1512         if (m_move_pool.grab(required_time)) {
1513                 m_last_good_position = m_base_position;
1514         } else {
1515                 const float LAG_POOL_MIN = 5.0;
1516                 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1517                 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1518                 if (m_time_from_last_teleport > lag_pool_max) {
1519                         actionstream << "Player " << m_player->getName()
1520                                         << " moved too fast; resetting position"
1521                                         << std::endl;
1522                         cheated = true;
1523                 }
1524                 setBasePosition(m_last_good_position);
1525         }
1526         return cheated;
1527 }
1528
1529 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1530 {
1531         //update collision box
1532         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1533         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1534
1535         toset->MinEdge += m_base_position;
1536         toset->MaxEdge += m_base_position;
1537         return true;
1538 }
1539
1540 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1541 {
1542         if (!m_prop.is_visible || !m_prop.pointable) {
1543                 return false;
1544         }
1545
1546         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1547         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
1548
1549         return true;
1550 }
1551
1552 float PlayerSAO::getZoomFOV() const
1553 {
1554         return m_prop.zoom_fov;
1555 }