Revert 6587 - Optimize entity-entity collision (#7539)
[oweals/minetest.git] / src / content_sao.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "collision.h"
23 #include "environment.h"
24 #include "tool.h" // For ToolCapabilities
25 #include "gamedef.h"
26 #include "nodedef.h"
27 #include "remoteplayer.h"
28 #include "server.h"
29 #include "scripting_server.h"
30 #include "genericobject.h"
31 #include "settings.h"
32 #include <algorithm>
33 #include <cmath>
34
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
36
37 /*
38         TestSAO
39 */
40
41 class TestSAO : public ServerActiveObject
42 {
43 public:
44         TestSAO(ServerEnvironment *env, v3f pos):
45                 ServerActiveObject(env, pos),
46                 m_timer1(0),
47                 m_age(0)
48         {
49                 ServerActiveObject::registerType(getType(), create);
50         }
51         ActiveObjectType getType() const
52         { return ACTIVEOBJECT_TYPE_TEST; }
53
54         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55                         const std::string &data)
56         {
57                 return new TestSAO(env, pos);
58         }
59
60         void step(float dtime, bool send_recommended)
61         {
62                 m_age += dtime;
63                 if(m_age > 10)
64                 {
65                         m_pending_removal = true;
66                         return;
67                 }
68
69                 m_base_position.Y += dtime * BS * 2;
70                 if(m_base_position.Y > 8*BS)
71                         m_base_position.Y = 2*BS;
72
73                 if (!send_recommended)
74                         return;
75
76                 m_timer1 -= dtime;
77                 if(m_timer1 < 0.0)
78                 {
79                         m_timer1 += 0.125;
80
81                         std::string data;
82
83                         data += itos(0); // 0 = position
84                         data += " ";
85                         data += itos(m_base_position.X);
86                         data += " ";
87                         data += itos(m_base_position.Y);
88                         data += " ";
89                         data += itos(m_base_position.Z);
90
91                         ActiveObjectMessage aom(getId(), false, data);
92                         m_messages_out.push(aom);
93                 }
94         }
95
96         bool getCollisionBox(aabb3f *toset) const { return false; }
97
98         virtual bool getSelectionBox(aabb3f *toset) const { return false; }
99
100         bool collideWithObjects() const { return false; }
101
102 private:
103         float m_timer1;
104         float m_age;
105 };
106
107 // Prototype (registers item for deserialization)
108 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
109
110 /*
111         UnitSAO
112  */
113
114 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
115         ServerActiveObject(env, pos)
116 {
117         // Initialize something to armor groups
118         m_armor_groups["fleshy"] = 100;
119 }
120
121 bool UnitSAO::isAttached() const
122 {
123         if (!m_attachment_parent_id)
124                 return false;
125         // Check if the parent still exists
126         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
127         if (obj)
128                 return true;
129         return false;
130 }
131
132 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
133 {
134         m_armor_groups = armor_groups;
135         m_armor_groups_sent = false;
136 }
137
138 const ItemGroupList &UnitSAO::getArmorGroups()
139 {
140         return m_armor_groups;
141 }
142
143 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
144 {
145         // store these so they can be updated to clients
146         m_animation_range = frame_range;
147         m_animation_speed = frame_speed;
148         m_animation_blend = frame_blend;
149         m_animation_loop = frame_loop;
150         m_animation_sent = false;
151 }
152
153 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
154 {
155         *frame_range = m_animation_range;
156         *frame_speed = m_animation_speed;
157         *frame_blend = m_animation_blend;
158         *frame_loop = m_animation_loop;
159 }
160
161 void UnitSAO::setAnimationSpeed(float frame_speed)
162 {
163         m_animation_speed = frame_speed;
164         m_animation_speed_sent = false;
165 }
166
167 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
168 {
169         // store these so they can be updated to clients
170         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
171         m_bone_position_sent = false;
172 }
173
174 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
175 {
176         *position = m_bone_position[bone].X;
177         *rotation = m_bone_position[bone].Y;
178 }
179
180 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
181 {
182         // Attachments need to be handled on both the server and client.
183         // If we just attach on the server, we can only copy the position of the parent. Attachments
184         // are still sent to clients at an interval so players might see them lagging, plus we can't
185         // read and attach to skeletal bones.
186         // If we just attach on the client, the server still sees the child at its original location.
187         // This breaks some things so we also give the server the most accurate representation
188         // even if players only see the client changes.
189
190         int old_parent = m_attachment_parent_id;
191         m_attachment_parent_id = parent_id;
192         m_attachment_bone = bone;
193         m_attachment_position = position;
194         m_attachment_rotation = rotation;
195         m_attachment_sent = false;
196
197         if (parent_id != old_parent) {
198                 onDetach(old_parent);
199                 onAttach(parent_id);
200         }
201 }
202
203 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
204         v3f *rotation)
205 {
206         *parent_id = m_attachment_parent_id;
207         *bone = m_attachment_bone;
208         *position = m_attachment_position;
209         *rotation = m_attachment_rotation;
210 }
211
212 void UnitSAO::clearChildAttachments()
213 {
214         for (int child_id : m_attachment_child_ids) {
215                 // Child can be NULL if it was deleted earlier
216                 if (ServerActiveObject *child = m_env->getActiveObject(child_id))
217                         child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
218         }
219         m_attachment_child_ids.clear();
220 }
221
222 void UnitSAO::clearParentAttachment()
223 {
224         ServerActiveObject *parent = nullptr;
225         if (m_attachment_parent_id) {
226                 parent = m_env->getActiveObject(m_attachment_parent_id);
227                 setAttachment(0, "", m_attachment_position, m_attachment_rotation);
228         } else {
229                 setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
230         }
231         // Do it
232         if (parent)
233                 parent->removeAttachmentChild(m_id);
234 }
235
236 void UnitSAO::addAttachmentChild(int child_id)
237 {
238         m_attachment_child_ids.insert(child_id);
239 }
240
241 void UnitSAO::removeAttachmentChild(int child_id)
242 {
243         m_attachment_child_ids.erase(child_id);
244 }
245
246 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
247 {
248         return m_attachment_child_ids;
249 }
250
251 void UnitSAO::onAttach(int parent_id)
252 {
253         if (!parent_id)
254                 return;
255
256         ServerActiveObject *parent = m_env->getActiveObject(parent_id);
257
258         if (!parent || parent->isGone())
259                 return; // Do not try to notify soon gone parent
260
261         if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) {
262                 // Call parent's on_attach field
263                 m_env->getScriptIface()->luaentity_on_attach_child(parent_id, this);
264         }
265 }
266
267 void UnitSAO::onDetach(int parent_id)
268 {
269         if (!parent_id)
270                 return;
271
272         ServerActiveObject *parent = m_env->getActiveObject(parent_id);
273         if (getType() == ACTIVEOBJECT_TYPE_LUAENTITY)
274                 m_env->getScriptIface()->luaentity_on_detach(m_id, parent);
275
276         if (!parent || parent->isGone())
277                 return; // Do not try to notify soon gone parent
278
279         if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY)
280                 m_env->getScriptIface()->luaentity_on_detach_child(parent_id, this);
281 }
282
283 ObjectProperties* UnitSAO::accessObjectProperties()
284 {
285         return &m_prop;
286 }
287
288 void UnitSAO::notifyObjectPropertiesModified()
289 {
290         m_properties_sent = false;
291 }
292
293 /*
294         LuaEntitySAO
295 */
296
297 // Prototype (registers item for deserialization)
298 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
299
300 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
301                 const std::string &name, const std::string &state):
302         UnitSAO(env, pos),
303         m_init_name(name),
304         m_init_state(state)
305 {
306         // Only register type if no environment supplied
307         if(env == NULL){
308                 ServerActiveObject::registerType(getType(), create);
309                 return;
310         }
311 }
312
313 LuaEntitySAO::~LuaEntitySAO()
314 {
315         if(m_registered){
316                 m_env->getScriptIface()->luaentity_Remove(m_id);
317         }
318
319         for (u32 attached_particle_spawner : m_attached_particle_spawners) {
320                 m_env->deleteParticleSpawner(attached_particle_spawner, false);
321         }
322 }
323
324 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
325 {
326         ServerActiveObject::addedToEnvironment(dtime_s);
327
328         // Create entity from name
329         m_registered = m_env->getScriptIface()->
330                 luaentity_Add(m_id, m_init_name.c_str());
331
332         if(m_registered){
333                 // Get properties
334                 m_env->getScriptIface()->
335                         luaentity_GetProperties(m_id, &m_prop);
336                 // Initialize HP from properties
337                 m_hp = m_prop.hp_max;
338                 // Activate entity, supplying serialized state
339                 m_env->getScriptIface()->
340                         luaentity_Activate(m_id, m_init_state, dtime_s);
341         } else {
342                 m_prop.infotext = m_init_name;
343         }
344 }
345
346 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
347                 const std::string &data)
348 {
349         std::string name;
350         std::string state;
351         s16 hp = 1;
352         v3f velocity;
353         float yaw = 0;
354         if (!data.empty()) {
355                 std::istringstream is(data, std::ios::binary);
356                 // read version
357                 u8 version = readU8(is);
358                 // check if version is supported
359                 if(version == 0){
360                         name = deSerializeString(is);
361                         state = deSerializeLongString(is);
362                 }
363                 else if(version == 1){
364                         name = deSerializeString(is);
365                         state = deSerializeLongString(is);
366                         hp = readS16(is);
367                         velocity = readV3F1000(is);
368                         yaw = readF1000(is);
369                 }
370         }
371         // create object
372         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
373                         <<state<<"\")"<<std::endl;
374         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
375         sao->m_hp = hp;
376         sao->m_velocity = velocity;
377         sao->m_yaw = yaw;
378         return sao;
379 }
380
381 void LuaEntitySAO::step(float dtime, bool send_recommended)
382 {
383         if(!m_properties_sent)
384         {
385                 m_properties_sent = true;
386                 std::string str = getPropertyPacket();
387                 // create message and add to list
388                 ActiveObjectMessage aom(getId(), true, str);
389                 m_messages_out.push(aom);
390         }
391
392         // If attached, check that our parent is still there. If it isn't, detach.
393         if(m_attachment_parent_id && !isAttached())
394         {
395                 m_attachment_parent_id = 0;
396                 m_attachment_bone = "";
397                 m_attachment_position = v3f(0,0,0);
398                 m_attachment_rotation = v3f(0,0,0);
399                 sendPosition(false, true);
400         }
401
402         m_last_sent_position_timer += dtime;
403
404         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
405         // If the object gets detached this comes into effect automatically from the last known origin
406         if(isAttached())
407         {
408                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
409                 m_base_position = pos;
410                 m_velocity = v3f(0,0,0);
411                 m_acceleration = v3f(0,0,0);
412         }
413         else
414         {
415                 if(m_prop.physical){
416                         aabb3f box = m_prop.collisionbox;
417                         box.MinEdge *= BS;
418                         box.MaxEdge *= BS;
419                         collisionMoveResult moveresult;
420                         f32 pos_max_d = BS*0.25; // Distance per iteration
421                         v3f p_pos = m_base_position;
422                         v3f p_velocity = m_velocity;
423                         v3f p_acceleration = m_acceleration;
424                         moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
425                                         pos_max_d, box, m_prop.stepheight, dtime,
426                                         &p_pos, &p_velocity, p_acceleration,
427                                         this, m_prop.collideWithObjects);
428
429                         // Apply results
430                         m_base_position = p_pos;
431                         m_velocity = p_velocity;
432                         m_acceleration = p_acceleration;
433                 } else {
434                         m_base_position += dtime * m_velocity + 0.5 * dtime
435                                         * dtime * m_acceleration;
436                         m_velocity += dtime * m_acceleration;
437                 }
438
439                 if (m_prop.automatic_face_movement_dir &&
440                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
441
442                         float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
443                                 + m_prop.automatic_face_movement_dir_offset;
444                         float max_rotation_delta =
445                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
446                         float delta = wrapDegrees_0_360(target_yaw - m_yaw);
447
448                         if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
449                                 m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
450                                 m_yaw = wrapDegrees_0_360(m_yaw);
451                         } else {
452                                 m_yaw = target_yaw;
453                         }
454                 }
455         }
456
457         if(m_registered){
458                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
459         }
460
461         if (!send_recommended)
462                 return;
463
464         if(!isAttached())
465         {
466                 // TODO: force send when acceleration changes enough?
467                 float minchange = 0.2*BS;
468                 if(m_last_sent_position_timer > 1.0){
469                         minchange = 0.01*BS;
470                 } else if(m_last_sent_position_timer > 0.2){
471                         minchange = 0.05*BS;
472                 }
473                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
474                 move_d += m_last_sent_move_precision;
475                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
476                 if (move_d > minchange || vel_d > minchange ||
477                                 std::fabs(m_yaw - m_last_sent_yaw) > 1.0) {
478                         sendPosition(true, false);
479                 }
480         }
481
482         if (!m_armor_groups_sent) {
483                 m_armor_groups_sent = true;
484                 std::string str = gob_cmd_update_armor_groups(
485                                 m_armor_groups);
486                 // create message and add to list
487                 ActiveObjectMessage aom(getId(), true, str);
488                 m_messages_out.push(aom);
489         }
490
491         if (!m_animation_sent) {
492                 m_animation_sent = true;
493                 std::string str = gob_cmd_update_animation(
494                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
495                 // create message and add to list
496                 ActiveObjectMessage aom(getId(), true, str);
497                 m_messages_out.push(aom);
498         }
499
500         if (!m_animation_speed_sent) {
501                 m_animation_speed_sent = true;
502                 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
503                 // create message and add to list
504                 ActiveObjectMessage aom(getId(), true, str);
505                 m_messages_out.push(aom);
506         }
507
508         if (!m_bone_position_sent) {
509                 m_bone_position_sent = true;
510                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
511                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
512                         std::string str = gob_cmd_update_bone_position((*ii).first,
513                                         (*ii).second.X, (*ii).second.Y);
514                         // create message and add to list
515                         ActiveObjectMessage aom(getId(), true, str);
516                         m_messages_out.push(aom);
517                 }
518         }
519
520         if (!m_attachment_sent) {
521                 m_attachment_sent = true;
522                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
523                 // create message and add to list
524                 ActiveObjectMessage aom(getId(), true, str);
525                 m_messages_out.push(aom);
526         }
527 }
528
529 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
530 {
531         std::ostringstream os(std::ios::binary);
532
533         // protocol >= 14
534         writeU8(os, 1); // version
535         os << serializeString(""); // name
536         writeU8(os, 0); // is_player
537         writeS16(os, getId()); //id
538         writeV3F1000(os, m_base_position);
539         writeF1000(os, m_yaw);
540         writeS16(os, m_hp);
541
542         std::ostringstream msg_os(std::ios::binary);
543         msg_os << serializeLongString(getPropertyPacket()); // message 1
544         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
545         msg_os << serializeLongString(gob_cmd_update_animation(
546                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
547         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
548                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
549                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
550                                 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
551         }
552         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
553                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
554         int message_count = 4 + m_bone_position.size();
555         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
556                         (ii != m_attachment_child_ids.end()); ++ii) {
557                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
558                         message_count++;
559                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
560                                 obj->getClientInitializationData(protocol_version)));
561                 }
562         }
563
564         msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
565         message_count++;
566
567         writeU8(os, message_count);
568         os.write(msg_os.str().c_str(), msg_os.str().size());
569
570         // return result
571         return os.str();
572 }
573
574 void LuaEntitySAO::getStaticData(std::string *result) const
575 {
576         verbosestream<<FUNCTION_NAME<<std::endl;
577         std::ostringstream os(std::ios::binary);
578         // version
579         writeU8(os, 1);
580         // name
581         os<<serializeString(m_init_name);
582         // state
583         if(m_registered){
584                 std::string state = m_env->getScriptIface()->
585                         luaentity_GetStaticdata(m_id);
586                 os<<serializeLongString(state);
587         } else {
588                 os<<serializeLongString(m_init_state);
589         }
590         // hp
591         writeS16(os, m_hp);
592         // velocity
593         writeV3F1000(os, m_velocity);
594         // yaw
595         writeF1000(os, m_yaw);
596         *result = os.str();
597 }
598
599 int LuaEntitySAO::punch(v3f dir,
600                 const ToolCapabilities *toolcap,
601                 ServerActiveObject *puncher,
602                 float time_from_last_punch)
603 {
604         if (!m_registered) {
605                 // Delete unknown LuaEntities when punched
606                 m_pending_removal = true;
607                 return 0;
608         }
609
610         ItemStack *punchitem = NULL;
611         ItemStack punchitem_static;
612         if (puncher) {
613                 punchitem_static = puncher->getWieldedItem();
614                 punchitem = &punchitem_static;
615         }
616
617         PunchDamageResult result = getPunchDamage(
618                         m_armor_groups,
619                         toolcap,
620                         punchitem,
621                         time_from_last_punch);
622
623         bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
624                         time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
625
626         if (!damage_handled) {
627                 if (result.did_punch) {
628                         setHP(getHP() - result.damage,
629                                 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
630
631                         if (result.damage > 0) {
632                                 std::string punchername = puncher ? puncher->getDescription() : "nil";
633
634                                 actionstream << getDescription() << " punched by "
635                                                 << punchername << ", damage " << result.damage
636                                                 << " hp, health now " << getHP() << " hp" << std::endl;
637                         }
638
639                         std::string str = gob_cmd_punched(result.damage, getHP());
640                         // create message and add to list
641                         ActiveObjectMessage aom(getId(), true, str);
642                         m_messages_out.push(aom);
643                 }
644         }
645
646         if (getHP() == 0 && !isGone()) {
647                 m_pending_removal = true;
648                 clearParentAttachment();
649                 clearChildAttachments();
650                 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
651         }
652
653         return result.wear;
654 }
655
656 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
657 {
658         if (!m_registered)
659                 return;
660
661         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
662 }
663
664 void LuaEntitySAO::setPos(const v3f &pos)
665 {
666         if(isAttached())
667                 return;
668         m_base_position = pos;
669         sendPosition(false, true);
670 }
671
672 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
673 {
674         if(isAttached())
675                 return;
676         m_base_position = pos;
677         if(!continuous)
678                 sendPosition(true, true);
679 }
680
681 float LuaEntitySAO::getMinimumSavedMovement()
682 {
683         return 0.1 * BS;
684 }
685
686 std::string LuaEntitySAO::getDescription()
687 {
688         std::ostringstream os(std::ios::binary);
689         os<<"LuaEntitySAO at (";
690         os<<(m_base_position.X/BS)<<",";
691         os<<(m_base_position.Y/BS)<<",";
692         os<<(m_base_position.Z/BS);
693         os<<")";
694         return os.str();
695 }
696
697 void LuaEntitySAO::setHP(s16 hp, const PlayerHPChangeReason &reason)
698 {
699         if (hp < 0)
700                 hp = 0;
701         m_hp = hp;
702 }
703
704 s16 LuaEntitySAO::getHP() const
705 {
706         return m_hp;
707 }
708
709 void LuaEntitySAO::setVelocity(v3f velocity)
710 {
711         m_velocity = velocity;
712 }
713
714 v3f LuaEntitySAO::getVelocity()
715 {
716         return m_velocity;
717 }
718
719 void LuaEntitySAO::setAcceleration(v3f acceleration)
720 {
721         m_acceleration = acceleration;
722 }
723
724 v3f LuaEntitySAO::getAcceleration()
725 {
726         return m_acceleration;
727 }
728
729 void LuaEntitySAO::setTextureMod(const std::string &mod)
730 {
731         std::string str = gob_cmd_set_texture_mod(mod);
732         m_current_texture_modifier = mod;
733         // create message and add to list
734         ActiveObjectMessage aom(getId(), true, str);
735         m_messages_out.push(aom);
736 }
737
738 std::string LuaEntitySAO::getTextureMod() const
739 {
740         return m_current_texture_modifier;
741 }
742
743 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
744                 bool select_horiz_by_yawpitch)
745 {
746         std::string str = gob_cmd_set_sprite(
747                 p,
748                 num_frames,
749                 framelength,
750                 select_horiz_by_yawpitch
751         );
752         // create message and add to list
753         ActiveObjectMessage aom(getId(), true, str);
754         m_messages_out.push(aom);
755 }
756
757 std::string LuaEntitySAO::getName()
758 {
759         return m_init_name;
760 }
761
762 std::string LuaEntitySAO::getPropertyPacket()
763 {
764         return gob_cmd_set_properties(m_prop);
765 }
766
767 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
768 {
769         // If the object is attached client-side, don't waste bandwidth sending its position to clients
770         if(isAttached())
771                 return;
772
773         m_last_sent_move_precision = m_base_position.getDistanceFrom(
774                         m_last_sent_position);
775         m_last_sent_position_timer = 0;
776         m_last_sent_yaw = m_yaw;
777         m_last_sent_position = m_base_position;
778         m_last_sent_velocity = m_velocity;
779         //m_last_sent_acceleration = m_acceleration;
780
781         float update_interval = m_env->getSendRecommendedInterval();
782
783         std::string str = gob_cmd_update_position(
784                 m_base_position,
785                 m_velocity,
786                 m_acceleration,
787                 m_yaw,
788                 do_interpolate,
789                 is_movement_end,
790                 update_interval
791         );
792         // create message and add to list
793         ActiveObjectMessage aom(getId(), false, str);
794         m_messages_out.push(aom);
795 }
796
797 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
798 {
799         if (m_prop.physical)
800         {
801                 //update collision box
802                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
803                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
804
805                 toset->MinEdge += m_base_position;
806                 toset->MaxEdge += m_base_position;
807
808                 return true;
809         }
810
811         return false;
812 }
813
814 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
815 {
816         if (!m_prop.is_visible || !m_prop.pointable) {
817                 return false;
818         }
819
820         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
821         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
822
823         return true;
824 }
825
826 bool LuaEntitySAO::collideWithObjects() const
827 {
828         return m_prop.collideWithObjects;
829 }
830
831 /*
832         PlayerSAO
833 */
834
835 // No prototype, PlayerSAO does not need to be deserialized
836
837 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
838                 bool is_singleplayer):
839         UnitSAO(env_, v3f(0,0,0)),
840         m_player(player_),
841         m_peer_id(peer_id_),
842         m_is_singleplayer(is_singleplayer)
843 {
844         assert(m_peer_id != 0); // pre-condition
845
846         m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
847         m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
848         m_prop.physical = false;
849         m_prop.weight = 75;
850         m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
851         m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
852         m_prop.pointable = true;
853         // Start of default appearance, this should be overwritten by Lua
854         m_prop.visual = "upright_sprite";
855         m_prop.visual_size = v2f(1, 2);
856         m_prop.textures.clear();
857         m_prop.textures.emplace_back("player.png");
858         m_prop.textures.emplace_back("player_back.png");
859         m_prop.colors.clear();
860         m_prop.colors.emplace_back(255, 255, 255, 255);
861         m_prop.spritediv = v2s16(1,1);
862         m_prop.eye_height = 1.625f;
863         // End of default appearance
864         m_prop.is_visible = true;
865         m_prop.backface_culling = false;
866         m_prop.makes_footstep_sound = true;
867         m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
868         m_hp = m_prop.hp_max;
869         m_breath = m_prop.breath_max;
870         // Disable zoom in survival mode using a value of 0
871         m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
872 }
873
874 PlayerSAO::~PlayerSAO()
875 {
876         if(m_inventory != &m_player->inventory)
877                 delete m_inventory;
878 }
879
880 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
881 {
882         assert(player);
883         m_player = player;
884         m_privs = privs;
885         m_inventory = &m_player->inventory;
886 }
887
888 v3f PlayerSAO::getEyeOffset() const
889 {
890         return v3f(0, BS * m_prop.eye_height, 0);
891 }
892
893 std::string PlayerSAO::getDescription()
894 {
895         return std::string("player ") + m_player->getName();
896 }
897
898 // Called after id has been set and has been inserted in environment
899 void PlayerSAO::addedToEnvironment(u32 dtime_s)
900 {
901         ServerActiveObject::addedToEnvironment(dtime_s);
902         ServerActiveObject::setBasePosition(m_base_position);
903         m_player->setPlayerSAO(this);
904         m_player->setPeerId(m_peer_id);
905         m_last_good_position = m_base_position;
906 }
907
908 // Called before removing from environment
909 void PlayerSAO::removingFromEnvironment()
910 {
911         ServerActiveObject::removingFromEnvironment();
912         if (m_player->getPlayerSAO() == this) {
913                 unlinkPlayerSessionAndSave();
914                 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
915                         m_env->deleteParticleSpawner(attached_particle_spawner, false);
916                 }
917         }
918 }
919
920 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
921 {
922         std::ostringstream os(std::ios::binary);
923
924         // Protocol >= 15
925         writeU8(os, 1); // version
926         os << serializeString(m_player->getName()); // name
927         writeU8(os, 1); // is_player
928         writeS16(os, getId()); //id
929         writeV3F1000(os, m_base_position);
930         writeF1000(os, m_yaw);
931         writeS16(os, getHP());
932
933         std::ostringstream msg_os(std::ios::binary);
934         msg_os << serializeLongString(getPropertyPacket()); // message 1
935         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
936         msg_os << serializeLongString(gob_cmd_update_animation(
937                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
938         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
939                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
940                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
941                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
942         }
943         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
944                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
945         msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
946                         m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
947                         m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
948         // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
949         msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
950         int message_count = 6 + m_bone_position.size();
951         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
952                         ii != m_attachment_child_ids.end(); ++ii) {
953                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
954                         message_count++;
955                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
956                                 obj->getClientInitializationData(protocol_version)));
957                 }
958         }
959
960         writeU8(os, message_count);
961         os.write(msg_os.str().c_str(), msg_os.str().size());
962
963         // return result
964         return os.str();
965 }
966
967 void PlayerSAO::getStaticData(std::string * result) const
968 {
969         FATAL_ERROR("Deprecated function");
970 }
971
972 void PlayerSAO::step(float dtime, bool send_recommended)
973 {
974         if (m_drowning_interval.step(dtime, 2.0f)) {
975                 // Get nose/mouth position, approximate with eye position
976                 v3s16 p = floatToInt(getEyePosition(), BS);
977                 MapNode n = m_env->getMap().getNodeNoEx(p);
978                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
979                 // If node generates drown
980                 if (c.drowning > 0 && m_hp > 0) {
981                         if (m_breath > 0)
982                                 setBreath(m_breath - 1);
983
984                         // No more breath, damage player
985                         if (m_breath == 0) {
986                                 PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING);
987                                 setHP(m_hp - c.drowning, reason);
988                                 m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
989                         }
990                 }
991         }
992
993         if (m_breathing_interval.step(dtime, 0.5f)) {
994                 // Get nose/mouth position, approximate with eye position
995                 v3s16 p = floatToInt(getEyePosition(), BS);
996                 MapNode n = m_env->getMap().getNodeNoEx(p);
997                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
998                 // If player is alive & no drowning, breathe
999                 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
1000                         setBreath(m_breath + 1);
1001         }
1002
1003         if (m_node_hurt_interval.step(dtime, 1.0f)) {
1004                 u32 damage_per_second = 0;
1005                 // Lowest and highest damage points are 0.1 within collisionbox
1006                 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
1007
1008                 // Sequence of damage points, starting 0.1 above feet and progressing
1009                 // upwards in 1 node intervals, stopping below top damage point.
1010                 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
1011                         v3s16 p = floatToInt(m_base_position +
1012                                 v3f(0.0f, dam_height * BS, 0.0f), BS);
1013                         MapNode n = m_env->getMap().getNodeNoEx(p);
1014                         damage_per_second = std::max(damage_per_second,
1015                                 m_env->getGameDef()->ndef()->get(n).damage_per_second);
1016                 }
1017
1018                 // Top damage point
1019                 v3s16 ptop = floatToInt(m_base_position +
1020                         v3f(0.0f, dam_top * BS, 0.0f), BS);
1021                 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
1022                 damage_per_second = std::max(damage_per_second,
1023                         m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
1024
1025                 if (damage_per_second != 0 && m_hp > 0) {
1026                         s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
1027                         PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE);
1028                         setHP(newhp, reason);
1029                         m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
1030                 }
1031         }
1032
1033         if (!m_properties_sent) {
1034                 m_properties_sent = true;
1035                 std::string str = getPropertyPacket();
1036                 // create message and add to list
1037                 ActiveObjectMessage aom(getId(), true, str);
1038                 m_messages_out.push(aom);
1039         }
1040
1041         // If attached, check that our parent is still there. If it isn't, detach.
1042         if (m_attachment_parent_id && !isAttached()) {
1043                 m_attachment_parent_id = 0;
1044                 m_attachment_bone = "";
1045                 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
1046                 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
1047                 setBasePosition(m_last_good_position);
1048                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1049         }
1050
1051         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1052
1053         // Set lag pool maximums based on estimated lag
1054         const float LAG_POOL_MIN = 5.0f;
1055         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1056         if(lag_pool_max < LAG_POOL_MIN)
1057                 lag_pool_max = LAG_POOL_MIN;
1058         m_dig_pool.setMax(lag_pool_max);
1059         m_move_pool.setMax(lag_pool_max);
1060
1061         // Increment cheat prevention timers
1062         m_dig_pool.add(dtime);
1063         m_move_pool.add(dtime);
1064         m_time_from_last_teleport += dtime;
1065         m_time_from_last_punch += dtime;
1066         m_nocheat_dig_time += dtime;
1067
1068         // Each frame, parent position is copied if the object is attached,
1069         // otherwise it's calculated normally.
1070         // If the object gets detached this comes into effect automatically from
1071         // the last known origin.
1072         if (isAttached()) {
1073                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1074                 m_last_good_position = pos;
1075                 setBasePosition(pos);
1076         }
1077
1078         if (!send_recommended)
1079                 return;
1080
1081         // If the object is attached client-side, don't waste bandwidth sending its
1082         // position to clients.
1083         if (m_position_not_sent && !isAttached()) {
1084                 m_position_not_sent = false;
1085                 float update_interval = m_env->getSendRecommendedInterval();
1086                 v3f pos;
1087                 if (isAttached()) // Just in case we ever do send attachment position too
1088                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1089                 else
1090                         pos = m_base_position;
1091
1092                 std::string str = gob_cmd_update_position(
1093                         pos,
1094                         v3f(0.0f, 0.0f, 0.0f),
1095                         v3f(0.0f, 0.0f, 0.0f),
1096                         m_yaw,
1097                         true,
1098                         false,
1099                         update_interval
1100                 );
1101                 // create message and add to list
1102                 ActiveObjectMessage aom(getId(), false, str);
1103                 m_messages_out.push(aom);
1104         }
1105
1106         if (!m_armor_groups_sent) {
1107                 m_armor_groups_sent = true;
1108                 std::string str = gob_cmd_update_armor_groups(
1109                                 m_armor_groups);
1110                 // create message and add to list
1111                 ActiveObjectMessage aom(getId(), true, str);
1112                 m_messages_out.push(aom);
1113         }
1114
1115         if (!m_physics_override_sent) {
1116                 m_physics_override_sent = true;
1117                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1118                                 m_physics_override_jump, m_physics_override_gravity,
1119                                 m_physics_override_sneak, m_physics_override_sneak_glitch,
1120                                 m_physics_override_new_move);
1121                 // create message and add to list
1122                 ActiveObjectMessage aom(getId(), true, str);
1123                 m_messages_out.push(aom);
1124         }
1125
1126         if (!m_animation_sent) {
1127                 m_animation_sent = true;
1128                 std::string str = gob_cmd_update_animation(
1129                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1130                 // create message and add to list
1131                 ActiveObjectMessage aom(getId(), true, str);
1132                 m_messages_out.push(aom);
1133         }
1134
1135         if (!m_bone_position_sent) {
1136                 m_bone_position_sent = true;
1137                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1138                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1139                         std::string str = gob_cmd_update_bone_position((*ii).first,
1140                                         (*ii).second.X, (*ii).second.Y);
1141                         // create message and add to list
1142                         ActiveObjectMessage aom(getId(), true, str);
1143                         m_messages_out.push(aom);
1144                 }
1145         }
1146
1147         if (!m_attachment_sent) {
1148                 m_attachment_sent = true;
1149                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1150                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1151                 // create message and add to list
1152                 ActiveObjectMessage aom(getId(), true, str);
1153                 m_messages_out.push(aom);
1154         }
1155 }
1156
1157 void PlayerSAO::setBasePosition(const v3f &position)
1158 {
1159         if (m_player && position != m_base_position)
1160                 m_player->setDirty(true);
1161
1162         // This needs to be ran for attachments too
1163         ServerActiveObject::setBasePosition(position);
1164
1165         // Updating is not wanted/required for player migration
1166         if (m_env) {
1167                 m_position_not_sent = true;
1168         }
1169 }
1170
1171 void PlayerSAO::setPos(const v3f &pos)
1172 {
1173         if(isAttached())
1174                 return;
1175
1176         setBasePosition(pos);
1177         // Movement caused by this command is always valid
1178         m_last_good_position = pos;
1179         m_move_pool.empty();
1180         m_time_from_last_teleport = 0.0;
1181         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1182 }
1183
1184 void PlayerSAO::moveTo(v3f pos, bool continuous)
1185 {
1186         if(isAttached())
1187                 return;
1188
1189         setBasePosition(pos);
1190         // Movement caused by this command is always valid
1191         m_last_good_position = pos;
1192         m_move_pool.empty();
1193         m_time_from_last_teleport = 0.0;
1194         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1195 }
1196
1197 void PlayerSAO::setYaw(const float yaw)
1198 {
1199         if (m_player && yaw != m_yaw)
1200                 m_player->setDirty(true);
1201
1202         UnitSAO::setYaw(yaw);
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::setYawAndSend(const float yaw)
1222 {
1223         setYaw(yaw);
1224         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1225 }
1226
1227 void PlayerSAO::setPitch(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::setPitchAndSend(const float pitch)
1236 {
1237         setPitch(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 }