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