]> git.lizzy.rs Git - minetest.git/blob - src/content_sao.cpp
Fix player rotations (#7941)
[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         if (!data.empty()) {
354                 std::istringstream is(data, std::ios::binary);
355                 // read version
356                 u8 version = readU8(is);
357                 // check if version is supported
358                 if(version == 0){
359                         name = deSerializeString(is);
360                         state = deSerializeLongString(is);
361                 }
362                 else if(version == 1){
363                         name = deSerializeString(is);
364                         state = deSerializeLongString(is);
365                         hp = readS16(is);
366                         velocity = readV3F1000(is);
367                         rotation = readV3F1000(is);
368                 }
369         }
370         // create object
371         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
372                         <<state<<"\")"<<std::endl;
373         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
374         sao->m_hp = hp;
375         sao->m_velocity = velocity;
376         sao->m_rotation = rotation;
377         return sao;
378 }
379
380 void LuaEntitySAO::step(float dtime, bool send_recommended)
381 {
382         if(!m_properties_sent)
383         {
384                 m_properties_sent = true;
385                 std::string str = getPropertyPacket();
386                 // create message and add to list
387                 ActiveObjectMessage aom(getId(), true, str);
388                 m_messages_out.push(aom);
389         }
390
391         // If attached, check that our parent is still there. If it isn't, detach.
392         if(m_attachment_parent_id && !isAttached())
393         {
394                 m_attachment_parent_id = 0;
395                 m_attachment_bone = "";
396                 m_attachment_position = v3f(0,0,0);
397                 m_attachment_rotation = v3f(0,0,0);
398                 sendPosition(false, true);
399         }
400
401         m_last_sent_position_timer += dtime;
402
403         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
404         // If the object gets detached this comes into effect automatically from the last known origin
405         if(isAttached())
406         {
407                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
408                 m_base_position = pos;
409                 m_velocity = v3f(0,0,0);
410                 m_acceleration = v3f(0,0,0);
411         }
412         else
413         {
414                 if(m_prop.physical){
415                         aabb3f box = m_prop.collisionbox;
416                         box.MinEdge *= BS;
417                         box.MaxEdge *= BS;
418                         collisionMoveResult moveresult;
419                         f32 pos_max_d = BS*0.25; // Distance per iteration
420                         v3f p_pos = m_base_position;
421                         v3f p_velocity = m_velocity;
422                         v3f p_acceleration = m_acceleration;
423                         moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
424                                         pos_max_d, box, m_prop.stepheight, dtime,
425                                         &p_pos, &p_velocity, p_acceleration,
426                                         this, m_prop.collideWithObjects);
427
428                         // Apply results
429                         m_base_position = p_pos;
430                         m_velocity = p_velocity;
431                         m_acceleration = p_acceleration;
432                 } else {
433                         m_base_position += dtime * m_velocity + 0.5 * dtime
434                                         * dtime * m_acceleration;
435                         m_velocity += dtime * m_acceleration;
436                 }
437
438                 if (m_prop.automatic_face_movement_dir &&
439                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
440
441                         float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
442                                 + m_prop.automatic_face_movement_dir_offset;
443                         float max_rotation_delta =
444                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
445
446                         m_rotation.Y = wrapDegrees_0_360(m_rotation.Y);
447                         wrappedApproachShortest(m_rotation.Y, target_yaw, max_rotation_delta, 360.f);
448                 }
449         }
450
451         if(m_registered){
452                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
453         }
454
455         if (!send_recommended)
456                 return;
457
458         if(!isAttached())
459         {
460                 // TODO: force send when acceleration changes enough?
461                 float minchange = 0.2*BS;
462                 if(m_last_sent_position_timer > 1.0){
463                         minchange = 0.01*BS;
464                 } else if(m_last_sent_position_timer > 0.2){
465                         minchange = 0.05*BS;
466                 }
467                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
468                 move_d += m_last_sent_move_precision;
469                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
470                 if (move_d > minchange || vel_d > minchange ||
471                                 std::fabs(m_rotation.X - m_last_sent_rotation.X) > 1.0f ||
472                                 std::fabs(m_rotation.Y - m_last_sent_rotation.Y) > 1.0f ||
473                                 std::fabs(m_rotation.Z - m_last_sent_rotation.Z) > 1.0f) {
474
475                         sendPosition(true, false);
476                 }
477         }
478
479         if (!m_armor_groups_sent) {
480                 m_armor_groups_sent = true;
481                 std::string str = gob_cmd_update_armor_groups(
482                                 m_armor_groups);
483                 // create message and add to list
484                 ActiveObjectMessage aom(getId(), true, str);
485                 m_messages_out.push(aom);
486         }
487
488         if (!m_animation_sent) {
489                 m_animation_sent = true;
490                 std::string str = gob_cmd_update_animation(
491                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
492                 // create message and add to list
493                 ActiveObjectMessage aom(getId(), true, str);
494                 m_messages_out.push(aom);
495         }
496
497         if (!m_animation_speed_sent) {
498                 m_animation_speed_sent = true;
499                 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
500                 // create message and add to list
501                 ActiveObjectMessage aom(getId(), true, str);
502                 m_messages_out.push(aom);
503         }
504
505         if (!m_bone_position_sent) {
506                 m_bone_position_sent = true;
507                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
508                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
509                         std::string str = gob_cmd_update_bone_position((*ii).first,
510                                         (*ii).second.X, (*ii).second.Y);
511                         // create message and add to list
512                         ActiveObjectMessage aom(getId(), true, str);
513                         m_messages_out.push(aom);
514                 }
515         }
516
517         if (!m_attachment_sent) {
518                 m_attachment_sent = true;
519                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
520                 // create message and add to list
521                 ActiveObjectMessage aom(getId(), true, str);
522                 m_messages_out.push(aom);
523         }
524 }
525
526 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
527 {
528         std::ostringstream os(std::ios::binary);
529
530         // protocol >= 14
531         writeU8(os, 1); // version
532         os << serializeString(""); // name
533         writeU8(os, 0); // is_player
534         writeS16(os, getId()); //id
535         writeV3F1000(os, m_base_position);
536         writeV3F1000(os, m_rotation);
537         writeS16(os, m_hp);
538
539         std::ostringstream msg_os(std::ios::binary);
540         msg_os << serializeLongString(getPropertyPacket()); // message 1
541         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
542         msg_os << serializeLongString(gob_cmd_update_animation(
543                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
544         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
545                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
546                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
547                                 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
548         }
549         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
550                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
551         int message_count = 4 + m_bone_position.size();
552         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
553                         (ii != m_attachment_child_ids.end()); ++ii) {
554                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
555                         message_count++;
556                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
557                                 obj->getClientInitializationData(protocol_version)));
558                 }
559         }
560
561         msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
562         message_count++;
563
564         writeU8(os, message_count);
565         os.write(msg_os.str().c_str(), msg_os.str().size());
566
567         // return result
568         return os.str();
569 }
570
571 void LuaEntitySAO::getStaticData(std::string *result) const
572 {
573         verbosestream<<FUNCTION_NAME<<std::endl;
574         std::ostringstream os(std::ios::binary);
575         // version
576         writeU8(os, 1);
577         // name
578         os<<serializeString(m_init_name);
579         // state
580         if(m_registered){
581                 std::string state = m_env->getScriptIface()->
582                         luaentity_GetStaticdata(m_id);
583                 os<<serializeLongString(state);
584         } else {
585                 os<<serializeLongString(m_init_state);
586         }
587         // hp
588         writeS16(os, m_hp);
589         // velocity
590         writeV3F1000(os, m_velocity);
591         // rotation
592         writeV3F1000(os, m_rotation);
593
594         *result = os.str();
595 }
596
597 int LuaEntitySAO::punch(v3f dir,
598                 const ToolCapabilities *toolcap,
599                 ServerActiveObject *puncher,
600                 float time_from_last_punch)
601 {
602         if (!m_registered) {
603                 // Delete unknown LuaEntities when punched
604                 m_pending_removal = true;
605                 return 0;
606         }
607
608         ItemStack *punchitem = NULL;
609         ItemStack punchitem_static;
610         if (puncher) {
611                 punchitem_static = puncher->getWieldedItem();
612                 punchitem = &punchitem_static;
613         }
614
615         PunchDamageResult result = getPunchDamage(
616                         m_armor_groups,
617                         toolcap,
618                         punchitem,
619                         time_from_last_punch);
620
621         bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
622                         time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
623
624         if (!damage_handled) {
625                 if (result.did_punch) {
626                         setHP(getHP() - result.damage,
627                                 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
628
629                         if (result.damage > 0) {
630                                 std::string punchername = puncher ? puncher->getDescription() : "nil";
631
632                                 actionstream << getDescription() << " punched by "
633                                                 << punchername << ", damage " << result.damage
634                                                 << " hp, health now " << getHP() << " hp" << std::endl;
635                         }
636
637                         std::string str = gob_cmd_punched(result.damage, getHP());
638                         // create message and add to list
639                         ActiveObjectMessage aom(getId(), true, str);
640                         m_messages_out.push(aom);
641                 }
642         }
643
644         if (getHP() == 0 && !isGone()) {
645                 m_pending_removal = true;
646                 clearParentAttachment();
647                 clearChildAttachments();
648                 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
649         }
650
651         return result.wear;
652 }
653
654 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
655 {
656         if (!m_registered)
657                 return;
658
659         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
660 }
661
662 void LuaEntitySAO::setPos(const v3f &pos)
663 {
664         if(isAttached())
665                 return;
666         m_base_position = pos;
667         sendPosition(false, true);
668 }
669
670 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
671 {
672         if(isAttached())
673                 return;
674         m_base_position = pos;
675         if(!continuous)
676                 sendPosition(true, true);
677 }
678
679 float LuaEntitySAO::getMinimumSavedMovement()
680 {
681         return 0.1 * BS;
682 }
683
684 std::string LuaEntitySAO::getDescription()
685 {
686         std::ostringstream os(std::ios::binary);
687         os<<"LuaEntitySAO at (";
688         os<<(m_base_position.X/BS)<<",";
689         os<<(m_base_position.Y/BS)<<",";
690         os<<(m_base_position.Z/BS);
691         os<<")";
692         return os.str();
693 }
694
695 void LuaEntitySAO::setHP(s16 hp, const PlayerHPChangeReason &reason)
696 {
697         if (hp < 0)
698                 hp = 0;
699         m_hp = hp;
700 }
701
702 s16 LuaEntitySAO::getHP() const
703 {
704         return m_hp;
705 }
706
707 void LuaEntitySAO::setVelocity(v3f velocity)
708 {
709         m_velocity = velocity;
710 }
711
712 v3f LuaEntitySAO::getVelocity()
713 {
714         return m_velocity;
715 }
716
717 void LuaEntitySAO::setAcceleration(v3f acceleration)
718 {
719         m_acceleration = acceleration;
720 }
721
722 v3f LuaEntitySAO::getAcceleration()
723 {
724         return m_acceleration;
725 }
726
727 void LuaEntitySAO::setTextureMod(const std::string &mod)
728 {
729         std::string str = gob_cmd_set_texture_mod(mod);
730         m_current_texture_modifier = mod;
731         // create message and add to list
732         ActiveObjectMessage aom(getId(), true, str);
733         m_messages_out.push(aom);
734 }
735
736 std::string LuaEntitySAO::getTextureMod() const
737 {
738         return m_current_texture_modifier;
739 }
740
741 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
742                 bool select_horiz_by_yawpitch)
743 {
744         std::string str = gob_cmd_set_sprite(
745                 p,
746                 num_frames,
747                 framelength,
748                 select_horiz_by_yawpitch
749         );
750         // create message and add to list
751         ActiveObjectMessage aom(getId(), true, str);
752         m_messages_out.push(aom);
753 }
754
755 std::string LuaEntitySAO::getName()
756 {
757         return m_init_name;
758 }
759
760 std::string LuaEntitySAO::getPropertyPacket()
761 {
762         return gob_cmd_set_properties(m_prop);
763 }
764
765 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
766 {
767         // If the object is attached client-side, don't waste bandwidth sending its position to clients
768         if(isAttached())
769                 return;
770
771         m_last_sent_move_precision = m_base_position.getDistanceFrom(
772                         m_last_sent_position);
773         m_last_sent_position_timer = 0;
774         m_last_sent_position = m_base_position;
775         m_last_sent_velocity = m_velocity;
776         //m_last_sent_acceleration = m_acceleration;
777         m_last_sent_rotation = m_rotation;
778
779         float update_interval = m_env->getSendRecommendedInterval();
780
781         std::string str = gob_cmd_update_position(
782                 m_base_position,
783                 m_velocity,
784                 m_acceleration,
785                 m_rotation,
786                 do_interpolate,
787                 is_movement_end,
788                 update_interval
789         );
790         // create message and add to list
791         ActiveObjectMessage aom(getId(), false, str);
792         m_messages_out.push(aom);
793 }
794
795 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
796 {
797         if (m_prop.physical)
798         {
799                 //update collision box
800                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
801                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
802
803                 toset->MinEdge += m_base_position;
804                 toset->MaxEdge += m_base_position;
805
806                 return true;
807         }
808
809         return false;
810 }
811
812 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
813 {
814         if (!m_prop.is_visible || !m_prop.pointable) {
815                 return false;
816         }
817
818         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
819         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
820
821         return true;
822 }
823
824 bool LuaEntitySAO::collideWithObjects() const
825 {
826         return m_prop.collideWithObjects;
827 }
828
829 /*
830         PlayerSAO
831 */
832
833 // No prototype, PlayerSAO does not need to be deserialized
834
835 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
836                 bool is_singleplayer):
837         UnitSAO(env_, v3f(0,0,0)),
838         m_player(player_),
839         m_peer_id(peer_id_),
840         m_is_singleplayer(is_singleplayer)
841 {
842         assert(m_peer_id != 0); // pre-condition
843
844         m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
845         m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
846         m_prop.physical = false;
847         m_prop.weight = 75;
848         m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
849         m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
850         m_prop.pointable = true;
851         // Start of default appearance, this should be overwritten by Lua
852         m_prop.visual = "upright_sprite";
853         m_prop.visual_size = v2f(1, 2);
854         m_prop.textures.clear();
855         m_prop.textures.emplace_back("player.png");
856         m_prop.textures.emplace_back("player_back.png");
857         m_prop.colors.clear();
858         m_prop.colors.emplace_back(255, 255, 255, 255);
859         m_prop.spritediv = v2s16(1,1);
860         m_prop.eye_height = 1.625f;
861         // End of default appearance
862         m_prop.is_visible = true;
863         m_prop.backface_culling = false;
864         m_prop.makes_footstep_sound = true;
865         m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
866         m_hp = m_prop.hp_max;
867         m_breath = m_prop.breath_max;
868         // Disable zoom in survival mode using a value of 0
869         m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
870 }
871
872 PlayerSAO::~PlayerSAO()
873 {
874         if(m_inventory != &m_player->inventory)
875                 delete m_inventory;
876 }
877
878 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
879 {
880         assert(player);
881         m_player = player;
882         m_privs = privs;
883         m_inventory = &m_player->inventory;
884 }
885
886 v3f PlayerSAO::getEyeOffset() const
887 {
888         return v3f(0, BS * m_prop.eye_height, 0);
889 }
890
891 std::string PlayerSAO::getDescription()
892 {
893         return std::string("player ") + m_player->getName();
894 }
895
896 // Called after id has been set and has been inserted in environment
897 void PlayerSAO::addedToEnvironment(u32 dtime_s)
898 {
899         ServerActiveObject::addedToEnvironment(dtime_s);
900         ServerActiveObject::setBasePosition(m_base_position);
901         m_player->setPlayerSAO(this);
902         m_player->setPeerId(m_peer_id);
903         m_last_good_position = m_base_position;
904 }
905
906 // Called before removing from environment
907 void PlayerSAO::removingFromEnvironment()
908 {
909         ServerActiveObject::removingFromEnvironment();
910         if (m_player->getPlayerSAO() == this) {
911                 unlinkPlayerSessionAndSave();
912                 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
913                         m_env->deleteParticleSpawner(attached_particle_spawner, false);
914                 }
915         }
916 }
917
918 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
919 {
920         std::ostringstream os(std::ios::binary);
921
922         // Protocol >= 15
923         writeU8(os, 1); // version
924         os << serializeString(m_player->getName()); // name
925         writeU8(os, 1); // is_player
926         writeS16(os, getId()); // id
927         writeV3F1000(os, m_base_position);
928         writeV3F1000(os, m_rotation);
929         writeS16(os, getHP());
930
931         std::ostringstream msg_os(std::ios::binary);
932         msg_os << serializeLongString(getPropertyPacket()); // message 1
933         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
934         msg_os << serializeLongString(gob_cmd_update_animation(
935                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
936         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
937                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
938                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
939                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
940         }
941         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
942                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
943         msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
944                         m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
945                         m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
946         // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
947         msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
948         int message_count = 6 + m_bone_position.size();
949         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
950                         ii != m_attachment_child_ids.end(); ++ii) {
951                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
952                         message_count++;
953                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
954                                 obj->getClientInitializationData(protocol_version)));
955                 }
956         }
957
958         writeU8(os, message_count);
959         os.write(msg_os.str().c_str(), msg_os.str().size());
960
961         // return result
962         return os.str();
963 }
964
965 void PlayerSAO::getStaticData(std::string * result) const
966 {
967         FATAL_ERROR("Deprecated function");
968 }
969
970 void PlayerSAO::step(float dtime, bool send_recommended)
971 {
972         if (m_drowning_interval.step(dtime, 2.0f)) {
973                 // Get nose/mouth position, approximate with eye position
974                 v3s16 p = floatToInt(getEyePosition(), BS);
975                 MapNode n = m_env->getMap().getNodeNoEx(p);
976                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
977                 // If node generates drown
978                 if (c.drowning > 0 && m_hp > 0) {
979                         if (m_breath > 0)
980                                 setBreath(m_breath - 1);
981
982                         // No more breath, damage player
983                         if (m_breath == 0) {
984                                 PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING);
985                                 setHP(m_hp - c.drowning, reason);
986                                 m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
987                         }
988                 }
989         }
990
991         if (m_breathing_interval.step(dtime, 0.5f)) {
992                 // Get nose/mouth position, approximate with eye position
993                 v3s16 p = floatToInt(getEyePosition(), BS);
994                 MapNode n = m_env->getMap().getNodeNoEx(p);
995                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
996                 // If player is alive & no drowning, breathe
997                 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
998                         setBreath(m_breath + 1);
999         }
1000
1001         if (m_node_hurt_interval.step(dtime, 1.0f)) {
1002                 u32 damage_per_second = 0;
1003                 // Lowest and highest damage points are 0.1 within collisionbox
1004                 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
1005
1006                 // Sequence of damage points, starting 0.1 above feet and progressing
1007                 // upwards in 1 node intervals, stopping below top damage point.
1008                 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
1009                         v3s16 p = floatToInt(m_base_position +
1010                                 v3f(0.0f, dam_height * BS, 0.0f), BS);
1011                         MapNode n = m_env->getMap().getNodeNoEx(p);
1012                         damage_per_second = std::max(damage_per_second,
1013                                 m_env->getGameDef()->ndef()->get(n).damage_per_second);
1014                 }
1015
1016                 // Top damage point
1017                 v3s16 ptop = floatToInt(m_base_position +
1018                         v3f(0.0f, dam_top * BS, 0.0f), BS);
1019                 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
1020                 damage_per_second = std::max(damage_per_second,
1021                         m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
1022
1023                 if (damage_per_second != 0 && m_hp > 0) {
1024                         s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
1025                         PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE);
1026                         setHP(newhp, reason);
1027                         m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
1028                 }
1029         }
1030
1031         if (!m_properties_sent) {
1032                 m_properties_sent = true;
1033                 std::string str = getPropertyPacket();
1034                 // create message and add to list
1035                 ActiveObjectMessage aom(getId(), true, str);
1036                 m_messages_out.push(aom);
1037         }
1038
1039         // If attached, check that our parent is still there. If it isn't, detach.
1040         if (m_attachment_parent_id && !isAttached()) {
1041                 m_attachment_parent_id = 0;
1042                 m_attachment_bone = "";
1043                 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
1044                 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
1045                 setBasePosition(m_last_good_position);
1046                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1047         }
1048
1049         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1050
1051         // Set lag pool maximums based on estimated lag
1052         const float LAG_POOL_MIN = 5.0f;
1053         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1054         if(lag_pool_max < LAG_POOL_MIN)
1055                 lag_pool_max = LAG_POOL_MIN;
1056         m_dig_pool.setMax(lag_pool_max);
1057         m_move_pool.setMax(lag_pool_max);
1058
1059         // Increment cheat prevention timers
1060         m_dig_pool.add(dtime);
1061         m_move_pool.add(dtime);
1062         m_time_from_last_teleport += dtime;
1063         m_time_from_last_punch += dtime;
1064         m_nocheat_dig_time += dtime;
1065
1066         // Each frame, parent position is copied if the object is attached,
1067         // otherwise it's calculated normally.
1068         // If the object gets detached this comes into effect automatically from
1069         // the last known origin.
1070         if (isAttached()) {
1071                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1072                 m_last_good_position = pos;
1073                 setBasePosition(pos);
1074         }
1075
1076         if (!send_recommended)
1077                 return;
1078
1079         // If the object is attached client-side, don't waste bandwidth sending its
1080         // position or rotation to clients.
1081         if (m_position_not_sent && !isAttached()) {
1082                 m_position_not_sent = false;
1083                 float update_interval = m_env->getSendRecommendedInterval();
1084                 v3f pos;
1085                 if (isAttached()) // Just in case we ever do send attachment position too
1086                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1087                 else
1088                         pos = m_base_position;
1089
1090                 std::string str = gob_cmd_update_position(
1091                         pos,
1092                         v3f(0.0f, 0.0f, 0.0f),
1093                         v3f(0.0f, 0.0f, 0.0f),
1094                         m_rotation,
1095                         true,
1096                         false,
1097                         update_interval
1098                 );
1099                 // create message and add to list
1100                 ActiveObjectMessage aom(getId(), false, str);
1101                 m_messages_out.push(aom);
1102         }
1103
1104         if (!m_armor_groups_sent) {
1105                 m_armor_groups_sent = true;
1106                 std::string str = gob_cmd_update_armor_groups(
1107                                 m_armor_groups);
1108                 // create message and add to list
1109                 ActiveObjectMessage aom(getId(), true, str);
1110                 m_messages_out.push(aom);
1111         }
1112
1113         if (!m_physics_override_sent) {
1114                 m_physics_override_sent = true;
1115                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1116                                 m_physics_override_jump, m_physics_override_gravity,
1117                                 m_physics_override_sneak, m_physics_override_sneak_glitch,
1118                                 m_physics_override_new_move);
1119                 // create message and add to list
1120                 ActiveObjectMessage aom(getId(), true, str);
1121                 m_messages_out.push(aom);
1122         }
1123
1124         if (!m_animation_sent) {
1125                 m_animation_sent = true;
1126                 std::string str = gob_cmd_update_animation(
1127                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1128                 // create message and add to list
1129                 ActiveObjectMessage aom(getId(), true, str);
1130                 m_messages_out.push(aom);
1131         }
1132
1133         if (!m_bone_position_sent) {
1134                 m_bone_position_sent = true;
1135                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1136                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1137                         std::string str = gob_cmd_update_bone_position((*ii).first,
1138                                         (*ii).second.X, (*ii).second.Y);
1139                         // create message and add to list
1140                         ActiveObjectMessage aom(getId(), true, str);
1141                         m_messages_out.push(aom);
1142                 }
1143         }
1144
1145         if (!m_attachment_sent) {
1146                 m_attachment_sent = true;
1147                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1148                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1149                 // create message and add to list
1150                 ActiveObjectMessage aom(getId(), true, str);
1151                 m_messages_out.push(aom);
1152         }
1153 }
1154
1155 void PlayerSAO::setBasePosition(const v3f &position)
1156 {
1157         if (m_player && position != m_base_position)
1158                 m_player->setDirty(true);
1159
1160         // This needs to be ran for attachments too
1161         ServerActiveObject::setBasePosition(position);
1162
1163         // Updating is not wanted/required for player migration
1164         if (m_env) {
1165                 m_position_not_sent = true;
1166         }
1167 }
1168
1169 void PlayerSAO::setPos(const v3f &pos)
1170 {
1171         if(isAttached())
1172                 return;
1173
1174         setBasePosition(pos);
1175         // Movement caused by this command is always valid
1176         m_last_good_position = pos;
1177         m_move_pool.empty();
1178         m_time_from_last_teleport = 0.0;
1179         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1180 }
1181
1182 void PlayerSAO::moveTo(v3f pos, bool continuous)
1183 {
1184         if(isAttached())
1185                 return;
1186
1187         setBasePosition(pos);
1188         // Movement caused by this command is always valid
1189         m_last_good_position = pos;
1190         m_move_pool.empty();
1191         m_time_from_last_teleport = 0.0;
1192         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1193 }
1194
1195 void PlayerSAO::setPlayerYaw(const float yaw)
1196 {
1197         v3f rotation(0, yaw, 0);
1198         if (m_player && yaw != m_rotation.Y)
1199                 m_player->setDirty(true);
1200
1201         // Set player model yaw, not look view
1202         UnitSAO::setRotation(rotation);
1203 }
1204
1205 void PlayerSAO::setFov(const float fov)
1206 {
1207         if (m_player && fov != m_fov)
1208                 m_player->setDirty(true);
1209
1210         m_fov = fov;
1211 }
1212
1213 void PlayerSAO::setWantedRange(const s16 range)
1214 {
1215         if (m_player && range != m_wanted_range)
1216                 m_player->setDirty(true);
1217
1218         m_wanted_range = range;
1219 }
1220
1221 void PlayerSAO::setPlayerYawAndSend(const float yaw)
1222 {
1223         setPlayerYaw(yaw);
1224         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1225 }
1226
1227 void PlayerSAO::setLookPitch(const float pitch)
1228 {
1229         if (m_player && pitch != m_pitch)
1230                 m_player->setDirty(true);
1231
1232         m_pitch = pitch;
1233 }
1234
1235 void PlayerSAO::setLookPitchAndSend(const float pitch)
1236 {
1237         setLookPitch(pitch);
1238         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1239 }
1240
1241 int PlayerSAO::punch(v3f dir,
1242         const ToolCapabilities *toolcap,
1243         ServerActiveObject *puncher,
1244         float time_from_last_punch)
1245 {
1246         if (!toolcap)
1247                 return 0;
1248
1249         // No effect if PvP disabled
1250         if (!g_settings->getBool("enable_pvp")) {
1251                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1252                         std::string str = gob_cmd_punched(0, getHP());
1253                         // create message and add to list
1254                         ActiveObjectMessage aom(getId(), true, str);
1255                         m_messages_out.push(aom);
1256                         return 0;
1257                 }
1258         }
1259
1260         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1261                         time_from_last_punch);
1262
1263         std::string punchername = "nil";
1264
1265         if (puncher != 0)
1266                 punchername = puncher->getDescription();
1267
1268         PlayerSAO *playersao = m_player->getPlayerSAO();
1269
1270         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1271                                 puncher, time_from_last_punch, toolcap, dir,
1272                                 hitparams.hp);
1273
1274         if (!damage_handled) {
1275                 setHP(getHP() - hitparams.hp,
1276                                 PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
1277         } else { // override client prediction
1278                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1279                         std::string str = gob_cmd_punched(0, getHP());
1280                         // create message and add to list
1281                         ActiveObjectMessage aom(getId(), true, str);
1282                         m_messages_out.push(aom);
1283                 }
1284         }
1285
1286
1287         actionstream << "Player " << m_player->getName() << " punched by "
1288                         << punchername;
1289         if (!damage_handled) {
1290                 actionstream << ", damage " << hitparams.hp << " HP";
1291         } else {
1292                 actionstream << ", damage handled by lua";
1293         }
1294         actionstream << std::endl;
1295
1296         return hitparams.wear;
1297 }
1298
1299 s16 PlayerSAO::readDamage()
1300 {
1301         s16 damage = m_damage;
1302         m_damage = 0;
1303         return damage;
1304 }
1305
1306 void PlayerSAO::setHP(s16 hp, const PlayerHPChangeReason &reason)
1307 {
1308         s16 oldhp = m_hp;
1309
1310         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason);
1311         if (hp_change == 0)
1312                 return;
1313         hp = oldhp + hp_change;
1314
1315         if (hp < 0)
1316                 hp = 0;
1317         else if (hp > m_prop.hp_max)
1318                 hp = m_prop.hp_max;
1319
1320         if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1321                 return;
1322         }
1323
1324         m_hp = hp;
1325
1326         if (oldhp > hp)
1327                 m_damage += (oldhp - hp);
1328
1329         // Update properties on death
1330         if ((hp == 0) != (oldhp == 0))
1331                 m_properties_sent = false;
1332 }
1333
1334 void PlayerSAO::setBreath(const u16 breath, bool send)
1335 {
1336         if (m_player && breath != m_breath)
1337                 m_player->setDirty(true);
1338
1339         m_breath = MYMIN(breath, m_prop.breath_max);
1340
1341         if (send)
1342                 m_env->getGameDef()->SendPlayerBreath(this);
1343 }
1344
1345 Inventory* PlayerSAO::getInventory()
1346 {
1347         return m_inventory;
1348 }
1349 const Inventory* PlayerSAO::getInventory() const
1350 {
1351         return m_inventory;
1352 }
1353
1354 InventoryLocation PlayerSAO::getInventoryLocation() const
1355 {
1356         InventoryLocation loc;
1357         loc.setPlayer(m_player->getName());
1358         return loc;
1359 }
1360
1361 std::string PlayerSAO::getWieldList() const
1362 {
1363         return "main";
1364 }
1365
1366 ItemStack PlayerSAO::getWieldedItem() const
1367 {
1368         const Inventory *inv = getInventory();
1369         ItemStack ret;
1370         const InventoryList *mlist = inv->getList(getWieldList());
1371         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1372                 ret = mlist->getItem(getWieldIndex());
1373         return ret;
1374 }
1375
1376 ItemStack PlayerSAO::getWieldedItemOrHand() const
1377 {
1378         const Inventory *inv = getInventory();
1379         ItemStack ret;
1380         const InventoryList *mlist = inv->getList(getWieldList());
1381         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1382                 ret = mlist->getItem(getWieldIndex());
1383         if (ret.name.empty()) {
1384                 const InventoryList *hlist = inv->getList("hand");
1385                 if (hlist)
1386                         ret = hlist->getItem(0);
1387         }
1388         return ret;
1389 }
1390
1391 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1392 {
1393         Inventory *inv = getInventory();
1394         if (inv) {
1395                 InventoryList *mlist = inv->getList(getWieldList());
1396                 if (mlist) {
1397                         mlist->changeItem(getWieldIndex(), item);
1398                         return true;
1399                 }
1400         }
1401         return false;
1402 }
1403
1404 int PlayerSAO::getWieldIndex() const
1405 {
1406         return m_wield_index;
1407 }
1408
1409 void PlayerSAO::setWieldIndex(int i)
1410 {
1411         if(i != m_wield_index) {
1412                 m_wield_index = i;
1413         }
1414 }
1415
1416 void PlayerSAO::disconnected()
1417 {
1418         m_peer_id = 0;
1419         m_pending_removal = true;
1420 }
1421
1422 void PlayerSAO::unlinkPlayerSessionAndSave()
1423 {
1424         assert(m_player->getPlayerSAO() == this);
1425         m_player->setPeerId(PEER_ID_INEXISTENT);
1426         m_env->savePlayer(m_player);
1427         m_player->setPlayerSAO(NULL);
1428         m_env->removePlayer(m_player);
1429 }
1430
1431 std::string PlayerSAO::getPropertyPacket()
1432 {
1433         m_prop.is_visible = (true);
1434         return gob_cmd_set_properties(m_prop);
1435 }
1436
1437 bool PlayerSAO::checkMovementCheat()
1438 {
1439         if (isAttached() || m_is_singleplayer ||
1440                         g_settings->getBool("disable_anticheat")) {
1441                 m_last_good_position = m_base_position;
1442                 return false;
1443         }
1444
1445         bool cheated = false;
1446         /*
1447                 Check player movements
1448
1449                 NOTE: Actually the server should handle player physics like the
1450                 client does and compare player's position to what is calculated
1451                 on our side. This is required when eg. players fly due to an
1452                 explosion. Altough a node-based alternative might be possible
1453                 too, and much more lightweight.
1454         */
1455
1456         float player_max_walk = 0; // horizontal movement
1457         float player_max_jump = 0; // vertical upwards movement
1458
1459         if (m_privs.count("fast") != 0)
1460                 player_max_walk = m_player->movement_speed_fast; // Fast speed
1461         else
1462                 player_max_walk = m_player->movement_speed_walk; // Normal speed
1463         player_max_walk *= m_physics_override_speed;
1464         player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
1465         // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
1466         //        until this can be verified correctly, tolerate higher jumping speeds
1467         player_max_jump *= 2.0;
1468
1469         // Don't divide by zero!
1470         if (player_max_walk < 0.0001f)
1471                 player_max_walk = 0.0001f;
1472         if (player_max_jump < 0.0001f)
1473                 player_max_jump = 0.0001f;
1474
1475         v3f diff = (m_base_position - m_last_good_position);
1476         float d_vert = diff.Y;
1477         diff.Y = 0;
1478         float d_horiz = diff.getLength();
1479         float required_time = d_horiz / player_max_walk;
1480
1481         // FIXME: Checking downwards movement is not easily possible currently,
1482         //        the server could calculate speed differences to examine the gravity
1483         if (d_vert > 0) {
1484                 // In certain cases (water, ladders) walking speed is applied vertically
1485                 float s = MYMAX(player_max_jump, player_max_walk);
1486                 required_time = MYMAX(required_time, d_vert / s);
1487         }
1488
1489         if (m_move_pool.grab(required_time)) {
1490                 m_last_good_position = m_base_position;
1491         } else {
1492                 const float LAG_POOL_MIN = 5.0;
1493                 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1494                 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1495                 if (m_time_from_last_teleport > lag_pool_max) {
1496                         actionstream << "Player " << m_player->getName()
1497                                         << " moved too fast; resetting position"
1498                                         << std::endl;
1499                         cheated = true;
1500                 }
1501                 setBasePosition(m_last_good_position);
1502         }
1503         return cheated;
1504 }
1505
1506 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1507 {
1508         //update collision box
1509         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1510         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1511
1512         toset->MinEdge += m_base_position;
1513         toset->MaxEdge += m_base_position;
1514         return true;
1515 }
1516
1517 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1518 {
1519         if (!m_prop.is_visible || !m_prop.pointable) {
1520                 return false;
1521         }
1522
1523         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1524         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
1525
1526         return true;
1527 }
1528
1529 float PlayerSAO::getZoomFOV() const
1530 {
1531         return m_prop.zoom_fov;
1532 }