]> git.lizzy.rs Git - minetest.git/blob - src/content_sao.cpp
Stratum ore: Add option for a constant thickness stratum
[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 <algorithm>
32
33 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
34
35 /*
36         TestSAO
37 */
38
39 class TestSAO : public ServerActiveObject
40 {
41 public:
42         TestSAO(ServerEnvironment *env, v3f pos):
43                 ServerActiveObject(env, pos),
44                 m_timer1(0),
45                 m_age(0)
46         {
47                 ServerActiveObject::registerType(getType(), create);
48         }
49         ActiveObjectType getType() const
50         { return ACTIVEOBJECT_TYPE_TEST; }
51
52         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
53                         const std::string &data)
54         {
55                 return new TestSAO(env, pos);
56         }
57
58         void step(float dtime, bool send_recommended)
59         {
60                 m_age += dtime;
61                 if(m_age > 10)
62                 {
63                         m_pending_removal = true;
64                         return;
65                 }
66
67                 m_base_position.Y += dtime * BS * 2;
68                 if(m_base_position.Y > 8*BS)
69                         m_base_position.Y = 2*BS;
70
71                 if (!send_recommended)
72                         return;
73
74                 m_timer1 -= dtime;
75                 if(m_timer1 < 0.0)
76                 {
77                         m_timer1 += 0.125;
78
79                         std::string data;
80
81                         data += itos(0); // 0 = position
82                         data += " ";
83                         data += itos(m_base_position.X);
84                         data += " ";
85                         data += itos(m_base_position.Y);
86                         data += " ";
87                         data += itos(m_base_position.Z);
88
89                         ActiveObjectMessage aom(getId(), false, data);
90                         m_messages_out.push(aom);
91                 }
92         }
93
94         bool getCollisionBox(aabb3f *toset) const { return false; }
95
96         virtual bool getSelectionBox(aabb3f *toset) const { return false; }
97
98         bool collideWithObjects() const { return false; }
99
100 private:
101         float m_timer1;
102         float m_age;
103 };
104
105 // Prototype (registers item for deserialization)
106 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
107
108 /*
109         UnitSAO
110  */
111
112 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
113         ServerActiveObject(env, pos)
114 {
115         // Initialize something to armor groups
116         m_armor_groups["fleshy"] = 100;
117 }
118
119 bool UnitSAO::isAttached() const
120 {
121         if (!m_attachment_parent_id)
122                 return false;
123         // Check if the parent still exists
124         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
125         if (obj)
126                 return true;
127         return false;
128 }
129
130 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
131 {
132         m_armor_groups = armor_groups;
133         m_armor_groups_sent = false;
134 }
135
136 const ItemGroupList &UnitSAO::getArmorGroups()
137 {
138         return m_armor_groups;
139 }
140
141 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
142 {
143         // store these so they can be updated to clients
144         m_animation_range = frame_range;
145         m_animation_speed = frame_speed;
146         m_animation_blend = frame_blend;
147         m_animation_loop = frame_loop;
148         m_animation_sent = false;
149 }
150
151 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
152 {
153         *frame_range = m_animation_range;
154         *frame_speed = m_animation_speed;
155         *frame_blend = m_animation_blend;
156         *frame_loop = m_animation_loop;
157 }
158
159 void UnitSAO::setAnimationSpeed(float frame_speed)
160 {
161         m_animation_speed = frame_speed;
162         m_animation_speed_sent = false;
163 }
164
165 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
166 {
167         // store these so they can be updated to clients
168         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
169         m_bone_position_sent = false;
170 }
171
172 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
173 {
174         *position = m_bone_position[bone].X;
175         *rotation = m_bone_position[bone].Y;
176 }
177
178 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
179 {
180         // Attachments need to be handled on both the server and client.
181         // If we just attach on the server, we can only copy the position of the parent. Attachments
182         // are still sent to clients at an interval so players might see them lagging, plus we can't
183         // read and attach to skeletal bones.
184         // If we just attach on the client, the server still sees the child at its original location.
185         // This breaks some things so we also give the server the most accurate representation
186         // even if players only see the client changes.
187
188         m_attachment_parent_id = parent_id;
189         m_attachment_bone = bone;
190         m_attachment_position = position;
191         m_attachment_rotation = rotation;
192         m_attachment_sent = false;
193 }
194
195 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
196         v3f *rotation)
197 {
198         *parent_id = m_attachment_parent_id;
199         *bone = m_attachment_bone;
200         *position = m_attachment_position;
201         *rotation = m_attachment_rotation;
202 }
203
204 void UnitSAO::addAttachmentChild(int child_id)
205 {
206         m_attachment_child_ids.insert(child_id);
207 }
208
209 void UnitSAO::removeAttachmentChild(int child_id)
210 {
211         m_attachment_child_ids.erase(child_id);
212 }
213
214 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
215 {
216         return m_attachment_child_ids;
217 }
218
219 ObjectProperties* UnitSAO::accessObjectProperties()
220 {
221         return &m_prop;
222 }
223
224 void UnitSAO::notifyObjectPropertiesModified()
225 {
226         m_properties_sent = false;
227 }
228
229 /*
230         LuaEntitySAO
231 */
232
233 // Prototype (registers item for deserialization)
234 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
235
236 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
237                 const std::string &name, const std::string &state):
238         UnitSAO(env, pos),
239         m_init_name(name),
240         m_init_state(state)
241 {
242         // Only register type if no environment supplied
243         if(env == NULL){
244                 ServerActiveObject::registerType(getType(), create);
245                 return;
246         }
247 }
248
249 LuaEntitySAO::~LuaEntitySAO()
250 {
251         if(m_registered){
252                 m_env->getScriptIface()->luaentity_Remove(m_id);
253         }
254
255         for (u32 attached_particle_spawner : m_attached_particle_spawners) {
256                 m_env->deleteParticleSpawner(attached_particle_spawner, false);
257         }
258 }
259
260 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
261 {
262         ServerActiveObject::addedToEnvironment(dtime_s);
263
264         // Create entity from name
265         m_registered = m_env->getScriptIface()->
266                 luaentity_Add(m_id, m_init_name.c_str());
267
268         if(m_registered){
269                 // Get properties
270                 m_env->getScriptIface()->
271                         luaentity_GetProperties(m_id, &m_prop);
272                 // Initialize HP from properties
273                 m_hp = m_prop.hp_max;
274                 // Activate entity, supplying serialized state
275                 m_env->getScriptIface()->
276                         luaentity_Activate(m_id, m_init_state, dtime_s);
277         } else {
278                 m_prop.infotext = m_init_name;
279         }
280 }
281
282 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
283                 const std::string &data)
284 {
285         std::string name;
286         std::string state;
287         s16 hp = 1;
288         v3f velocity;
289         float yaw = 0;
290         if (!data.empty()) {
291                 std::istringstream is(data, std::ios::binary);
292                 // read version
293                 u8 version = readU8(is);
294                 // check if version is supported
295                 if(version == 0){
296                         name = deSerializeString(is);
297                         state = deSerializeLongString(is);
298                 }
299                 else if(version == 1){
300                         name = deSerializeString(is);
301                         state = deSerializeLongString(is);
302                         hp = readS16(is);
303                         velocity = readV3F1000(is);
304                         yaw = readF1000(is);
305                 }
306         }
307         // create object
308         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
309                         <<state<<"\")"<<std::endl;
310         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
311         sao->m_hp = hp;
312         sao->m_velocity = velocity;
313         sao->m_yaw = yaw;
314         return sao;
315 }
316
317 void LuaEntitySAO::step(float dtime, bool send_recommended)
318 {
319         if(!m_properties_sent)
320         {
321                 m_properties_sent = true;
322                 std::string str = getPropertyPacket();
323                 // create message and add to list
324                 ActiveObjectMessage aom(getId(), true, str);
325                 m_messages_out.push(aom);
326         }
327
328         // If attached, check that our parent is still there. If it isn't, detach.
329         if(m_attachment_parent_id && !isAttached())
330         {
331                 m_attachment_parent_id = 0;
332                 m_attachment_bone = "";
333                 m_attachment_position = v3f(0,0,0);
334                 m_attachment_rotation = v3f(0,0,0);
335                 sendPosition(false, true);
336         }
337
338         m_last_sent_position_timer += dtime;
339
340         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
341         // If the object gets detached this comes into effect automatically from the last known origin
342         if(isAttached())
343         {
344                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
345                 m_base_position = pos;
346                 m_velocity = v3f(0,0,0);
347                 m_acceleration = v3f(0,0,0);
348         }
349         else
350         {
351                 if(m_prop.physical){
352                         aabb3f box = m_prop.collisionbox;
353                         box.MinEdge *= BS;
354                         box.MaxEdge *= BS;
355                         collisionMoveResult moveresult;
356                         f32 pos_max_d = BS*0.25; // Distance per iteration
357                         v3f p_pos = m_base_position;
358                         v3f p_velocity = m_velocity;
359                         v3f p_acceleration = m_acceleration;
360                         moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
361                                         pos_max_d, box, m_prop.stepheight, dtime,
362                                         &p_pos, &p_velocity, p_acceleration,
363                                         this, m_prop.collideWithObjects);
364
365                         // Apply results
366                         m_base_position = p_pos;
367                         m_velocity = p_velocity;
368                         m_acceleration = p_acceleration;
369                 } else {
370                         m_base_position += dtime * m_velocity + 0.5 * dtime
371                                         * dtime * m_acceleration;
372                         m_velocity += dtime * m_acceleration;
373                 }
374
375                 if((m_prop.automatic_face_movement_dir) &&
376                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
377                 {
378                         float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
379                                         + m_prop.automatic_face_movement_dir_offset;
380                         float max_rotation_delta =
381                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
382
383                         if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
384                                 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
385
386                                 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
387                         } else {
388                                 m_yaw = optimal_yaw;
389                         }
390                 }
391         }
392
393         if(m_registered){
394                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
395         }
396
397         // Remove LuaEntity beyond terrain edges
398         {
399                 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
400                 assert(map);
401                 if (!m_pending_removal &&
402                                 map->saoPositionOverLimit(m_base_position)) {
403                         infostream << "Removing SAO " << m_id << "(" << m_init_name
404                                 << "), outside of limits" << std::endl;
405                         m_pending_removal = true;
406                         return;
407                 }
408         }
409
410         if (!send_recommended)
411                 return;
412
413         if(!isAttached())
414         {
415                 // TODO: force send when acceleration changes enough?
416                 float minchange = 0.2*BS;
417                 if(m_last_sent_position_timer > 1.0){
418                         minchange = 0.01*BS;
419                 } else if(m_last_sent_position_timer > 0.2){
420                         minchange = 0.05*BS;
421                 }
422                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
423                 move_d += m_last_sent_move_precision;
424                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
425                 if(move_d > minchange || vel_d > minchange ||
426                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
427                         sendPosition(true, false);
428                 }
429         }
430
431         if (!m_armor_groups_sent) {
432                 m_armor_groups_sent = true;
433                 std::string str = gob_cmd_update_armor_groups(
434                                 m_armor_groups);
435                 // create message and add to list
436                 ActiveObjectMessage aom(getId(), true, str);
437                 m_messages_out.push(aom);
438         }
439
440         if (!m_animation_sent) {
441                 m_animation_sent = true;
442                 std::string str = gob_cmd_update_animation(
443                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
444                 // create message and add to list
445                 ActiveObjectMessage aom(getId(), true, str);
446                 m_messages_out.push(aom);
447         }
448
449         if (!m_animation_speed_sent) {
450                 m_animation_speed_sent = true;
451                 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
452                 // create message and add to list
453                 ActiveObjectMessage aom(getId(), true, str);
454                 m_messages_out.push(aom);
455         }
456
457         if (!m_bone_position_sent) {
458                 m_bone_position_sent = true;
459                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
460                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
461                         std::string str = gob_cmd_update_bone_position((*ii).first,
462                                         (*ii).second.X, (*ii).second.Y);
463                         // create message and add to list
464                         ActiveObjectMessage aom(getId(), true, str);
465                         m_messages_out.push(aom);
466                 }
467         }
468
469         if (!m_attachment_sent) {
470                 m_attachment_sent = true;
471                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
472                 // create message and add to list
473                 ActiveObjectMessage aom(getId(), true, str);
474                 m_messages_out.push(aom);
475         }
476 }
477
478 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
479 {
480         std::ostringstream os(std::ios::binary);
481
482         // protocol >= 14
483         writeU8(os, 1); // version
484         os << serializeString(""); // name
485         writeU8(os, 0); // is_player
486         writeS16(os, getId()); //id
487         writeV3F1000(os, m_base_position);
488         writeF1000(os, m_yaw);
489         writeS16(os, m_hp);
490
491         std::ostringstream msg_os(std::ios::binary);
492         msg_os << serializeLongString(getPropertyPacket()); // message 1
493         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
494         msg_os << serializeLongString(gob_cmd_update_animation(
495                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
496         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
497                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
498                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
499                                 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
500         }
501         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
502                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
503         int message_count = 4 + m_bone_position.size();
504         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
505                         (ii != m_attachment_child_ids.end()); ++ii) {
506                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
507                         message_count++;
508                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
509                                 obj->getClientInitializationData(protocol_version)));
510                 }
511         }
512
513         msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
514         message_count++;
515
516         writeU8(os, message_count);
517         os.write(msg_os.str().c_str(), msg_os.str().size());
518
519         // return result
520         return os.str();
521 }
522
523 void LuaEntitySAO::getStaticData(std::string *result) const
524 {
525         verbosestream<<FUNCTION_NAME<<std::endl;
526         std::ostringstream os(std::ios::binary);
527         // version
528         writeU8(os, 1);
529         // name
530         os<<serializeString(m_init_name);
531         // state
532         if(m_registered){
533                 std::string state = m_env->getScriptIface()->
534                         luaentity_GetStaticdata(m_id);
535                 os<<serializeLongString(state);
536         } else {
537                 os<<serializeLongString(m_init_state);
538         }
539         // hp
540         writeS16(os, m_hp);
541         // velocity
542         writeV3F1000(os, m_velocity);
543         // yaw
544         writeF1000(os, m_yaw);
545         *result = os.str();
546 }
547
548 int LuaEntitySAO::punch(v3f dir,
549                 const ToolCapabilities *toolcap,
550                 ServerActiveObject *puncher,
551                 float time_from_last_punch)
552 {
553         if (!m_registered) {
554                 // Delete unknown LuaEntities when punched
555                 m_pending_removal = true;
556                 return 0;
557         }
558
559         // It's best that attachments cannot be punched
560         if (isAttached())
561                 return 0;
562
563         ItemStack *punchitem = NULL;
564         ItemStack punchitem_static;
565         if (puncher) {
566                 punchitem_static = puncher->getWieldedItem();
567                 punchitem = &punchitem_static;
568         }
569
570         PunchDamageResult result = getPunchDamage(
571                         m_armor_groups,
572                         toolcap,
573                         punchitem,
574                         time_from_last_punch);
575
576         bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
577                         time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
578
579         if (!damage_handled) {
580                 if (result.did_punch) {
581                         setHP(getHP() - result.damage);
582
583                         if (result.damage > 0) {
584                                 std::string punchername = puncher ? puncher->getDescription() : "nil";
585
586                                 actionstream << getDescription() << " punched by "
587                                                 << punchername << ", damage " << result.damage
588                                                 << " hp, health now " << getHP() << " hp" << std::endl;
589                         }
590
591                         std::string str = gob_cmd_punched(result.damage, getHP());
592                         // create message and add to list
593                         ActiveObjectMessage aom(getId(), true, str);
594                         m_messages_out.push(aom);
595                 }
596         }
597
598         if (getHP() == 0) {
599                 m_pending_removal = true;
600                 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
601         }
602
603         return result.wear;
604 }
605
606 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
607 {
608         if (!m_registered)
609                 return;
610         // It's best that attachments cannot be clicked
611         if (isAttached())
612                 return;
613         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
614 }
615
616 void LuaEntitySAO::setPos(const v3f &pos)
617 {
618         if(isAttached())
619                 return;
620         m_base_position = pos;
621         sendPosition(false, true);
622 }
623
624 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
625 {
626         if(isAttached())
627                 return;
628         m_base_position = pos;
629         if(!continuous)
630                 sendPosition(true, true);
631 }
632
633 float LuaEntitySAO::getMinimumSavedMovement()
634 {
635         return 0.1 * BS;
636 }
637
638 std::string LuaEntitySAO::getDescription()
639 {
640         std::ostringstream os(std::ios::binary);
641         os<<"LuaEntitySAO at (";
642         os<<(m_base_position.X/BS)<<",";
643         os<<(m_base_position.Y/BS)<<",";
644         os<<(m_base_position.Z/BS);
645         os<<")";
646         return os.str();
647 }
648
649 void LuaEntitySAO::setHP(s16 hp)
650 {
651         if(hp < 0) hp = 0;
652         m_hp = hp;
653 }
654
655 s16 LuaEntitySAO::getHP() const
656 {
657         return m_hp;
658 }
659
660 void LuaEntitySAO::setVelocity(v3f velocity)
661 {
662         m_velocity = velocity;
663 }
664
665 v3f LuaEntitySAO::getVelocity()
666 {
667         return m_velocity;
668 }
669
670 void LuaEntitySAO::setAcceleration(v3f acceleration)
671 {
672         m_acceleration = acceleration;
673 }
674
675 v3f LuaEntitySAO::getAcceleration()
676 {
677         return m_acceleration;
678 }
679
680 void LuaEntitySAO::setTextureMod(const std::string &mod)
681 {
682         std::string str = gob_cmd_set_texture_mod(mod);
683         m_current_texture_modifier = mod;
684         // create message and add to list
685         ActiveObjectMessage aom(getId(), true, str);
686         m_messages_out.push(aom);
687 }
688
689 std::string LuaEntitySAO::getTextureMod() const
690 {
691         return m_current_texture_modifier;
692 }
693
694 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
695                 bool select_horiz_by_yawpitch)
696 {
697         std::string str = gob_cmd_set_sprite(
698                 p,
699                 num_frames,
700                 framelength,
701                 select_horiz_by_yawpitch
702         );
703         // create message and add to list
704         ActiveObjectMessage aom(getId(), true, str);
705         m_messages_out.push(aom);
706 }
707
708 std::string LuaEntitySAO::getName()
709 {
710         return m_init_name;
711 }
712
713 std::string LuaEntitySAO::getPropertyPacket()
714 {
715         return gob_cmd_set_properties(m_prop);
716 }
717
718 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
719 {
720         // If the object is attached client-side, don't waste bandwidth sending its position to clients
721         if(isAttached())
722                 return;
723
724         m_last_sent_move_precision = m_base_position.getDistanceFrom(
725                         m_last_sent_position);
726         m_last_sent_position_timer = 0;
727         m_last_sent_yaw = m_yaw;
728         m_last_sent_position = m_base_position;
729         m_last_sent_velocity = m_velocity;
730         //m_last_sent_acceleration = m_acceleration;
731
732         float update_interval = m_env->getSendRecommendedInterval();
733
734         std::string str = gob_cmd_update_position(
735                 m_base_position,
736                 m_velocity,
737                 m_acceleration,
738                 m_yaw,
739                 do_interpolate,
740                 is_movement_end,
741                 update_interval
742         );
743         // create message and add to list
744         ActiveObjectMessage aom(getId(), false, str);
745         m_messages_out.push(aom);
746 }
747
748 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
749 {
750         if (m_prop.physical)
751         {
752                 //update collision box
753                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
754                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
755
756                 toset->MinEdge += m_base_position;
757                 toset->MaxEdge += m_base_position;
758
759                 return true;
760         }
761
762         return false;
763 }
764
765 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
766 {
767         if (!m_prop.is_visible || !m_prop.pointable) {
768                 return false;
769         }
770
771         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
772         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
773
774         return true;
775 }
776
777 bool LuaEntitySAO::collideWithObjects() const
778 {
779         return m_prop.collideWithObjects;
780 }
781
782 /*
783         PlayerSAO
784 */
785
786 // No prototype, PlayerSAO does not need to be deserialized
787
788 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
789                 bool is_singleplayer):
790         UnitSAO(env_, v3f(0,0,0)),
791         m_player(player_),
792         m_peer_id(peer_id_),
793         m_is_singleplayer(is_singleplayer)
794 {
795         assert(m_peer_id != 0); // pre-condition
796
797         m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
798         m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
799         m_prop.physical = false;
800         m_prop.weight = 75;
801         m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
802         m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
803         m_prop.pointable = true;
804         // start of default appearance, this should be overwritten by LUA
805         m_prop.visual = "upright_sprite";
806         m_prop.visual_size = v2f(1, 2);
807         m_prop.textures.clear();
808         m_prop.textures.emplace_back("player.png");
809         m_prop.textures.emplace_back("player_back.png");
810         m_prop.colors.clear();
811         m_prop.colors.emplace_back(255, 255, 255, 255);
812         m_prop.spritediv = v2s16(1,1);
813         m_prop.eye_height = 1.625f;
814         // end of default appearance
815         m_prop.is_visible = true;
816         m_prop.makes_footstep_sound = true;
817         m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
818         m_prop.can_zoom = true;
819         m_hp = m_prop.hp_max;
820         m_breath = m_prop.breath_max;
821 }
822
823 PlayerSAO::~PlayerSAO()
824 {
825         if(m_inventory != &m_player->inventory)
826                 delete m_inventory;
827 }
828
829 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
830 {
831         assert(player);
832         m_player = player;
833         m_privs = privs;
834         m_inventory = &m_player->inventory;
835 }
836
837 v3f PlayerSAO::getEyeOffset() const
838 {
839         return v3f(0, BS * m_prop.eye_height, 0);
840 }
841
842 std::string PlayerSAO::getDescription()
843 {
844         return std::string("player ") + m_player->getName();
845 }
846
847 // Called after id has been set and has been inserted in environment
848 void PlayerSAO::addedToEnvironment(u32 dtime_s)
849 {
850         ServerActiveObject::addedToEnvironment(dtime_s);
851         ServerActiveObject::setBasePosition(m_base_position);
852         m_player->setPlayerSAO(this);
853         m_player->setPeerId(m_peer_id);
854         m_last_good_position = m_base_position;
855 }
856
857 // Called before removing from environment
858 void PlayerSAO::removingFromEnvironment()
859 {
860         ServerActiveObject::removingFromEnvironment();
861         if (m_player->getPlayerSAO() == this) {
862                 unlinkPlayerSessionAndSave();
863                 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
864                         m_env->deleteParticleSpawner(attached_particle_spawner, false);
865                 }
866         }
867 }
868
869 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
870 {
871         std::ostringstream os(std::ios::binary);
872
873         // Protocol >= 15
874         writeU8(os, 1); // version
875         os << serializeString(m_player->getName()); // name
876         writeU8(os, 1); // is_player
877         writeS16(os, getId()); //id
878         writeV3F1000(os, m_base_position);
879         writeF1000(os, m_yaw);
880         writeS16(os, getHP());
881
882         std::ostringstream msg_os(std::ios::binary);
883         msg_os << serializeLongString(getPropertyPacket()); // message 1
884         msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
885         msg_os << serializeLongString(gob_cmd_update_animation(
886                 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
887         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
888                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
889                 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
890                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
891         }
892         msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
893                 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
894         msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
895                         m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
896                         m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
897         // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
898         msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
899         int message_count = 6 + m_bone_position.size();
900         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
901                         ii != m_attachment_child_ids.end(); ++ii) {
902                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
903                         message_count++;
904                         msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
905                                 obj->getClientInitializationData(protocol_version)));
906                 }
907         }
908
909         writeU8(os, message_count);
910         os.write(msg_os.str().c_str(), msg_os.str().size());
911
912         // return result
913         return os.str();
914 }
915
916 void PlayerSAO::getStaticData(std::string * result) const
917 {
918         FATAL_ERROR("Deprecated function");
919 }
920
921 void PlayerSAO::step(float dtime, bool send_recommended)
922 {
923         if (m_drowning_interval.step(dtime, 2.0f)) {
924                 // Get nose/mouth position, approximate with eye position
925                 v3s16 p = floatToInt(getEyePosition(), BS);
926                 MapNode n = m_env->getMap().getNodeNoEx(p);
927                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
928                 // If node generates drown
929                 if (c.drowning > 0 && m_hp > 0) {
930                         if (m_breath > 0)
931                                 setBreath(m_breath - 1);
932
933                         // No more breath, damage player
934                         if (m_breath == 0) {
935                                 setHP(m_hp - c.drowning);
936                                 m_env->getGameDef()->SendPlayerHPOrDie(this);
937                         }
938                 }
939         }
940
941         if (m_breathing_interval.step(dtime, 0.5f)) {
942                 // Get nose/mouth position, approximate with eye position
943                 v3s16 p = floatToInt(getEyePosition(), BS);
944                 MapNode n = m_env->getMap().getNodeNoEx(p);
945                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
946                 // If player is alive & no drowning, breathe
947                 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
948                         setBreath(m_breath + 1);
949         }
950
951         if (m_node_hurt_interval.step(dtime, 1.0f)) {
952                 u32 damage_per_second = 0;
953                 // Lowest and highest damage points are 0.1 within collisionbox
954                 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
955
956                 // Sequence of damage points, starting 0.1 above feet and progressing
957                 // upwards in 1 node intervals, stopping below top damage point.
958                 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
959                         v3s16 p = floatToInt(m_base_position +
960                                 v3f(0.0f, dam_height * BS, 0.0f), BS);
961                         MapNode n = m_env->getMap().getNodeNoEx(p);
962                         damage_per_second = std::max(damage_per_second,
963                                 m_env->getGameDef()->ndef()->get(n).damage_per_second);
964                 }
965
966                 // Top damage point
967                 v3s16 ptop = floatToInt(m_base_position +
968                         v3f(0.0f, dam_top * BS, 0.0f), BS);
969                 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
970                 damage_per_second = std::max(damage_per_second,
971                         m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
972
973                 if (damage_per_second != 0 && m_hp > 0) {
974                         s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
975                         setHP(newhp);
976                         m_env->getGameDef()->SendPlayerHPOrDie(this);
977                 }
978         }
979
980         if (!m_properties_sent) {
981                 m_properties_sent = true;
982                 std::string str = getPropertyPacket();
983                 // create message and add to list
984                 ActiveObjectMessage aom(getId(), true, str);
985                 m_messages_out.push(aom);
986         }
987
988         // If attached, check that our parent is still there. If it isn't, detach.
989         if (m_attachment_parent_id && !isAttached()) {
990                 m_attachment_parent_id = 0;
991                 m_attachment_bone = "";
992                 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
993                 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
994                 setBasePosition(m_last_good_position);
995                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
996         }
997
998         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
999
1000         // Set lag pool maximums based on estimated lag
1001         const float LAG_POOL_MIN = 5.0f;
1002         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1003         if(lag_pool_max < LAG_POOL_MIN)
1004                 lag_pool_max = LAG_POOL_MIN;
1005         m_dig_pool.setMax(lag_pool_max);
1006         m_move_pool.setMax(lag_pool_max);
1007
1008         // Increment cheat prevention timers
1009         m_dig_pool.add(dtime);
1010         m_move_pool.add(dtime);
1011         m_time_from_last_teleport += dtime;
1012         m_time_from_last_punch += dtime;
1013         m_nocheat_dig_time += dtime;
1014
1015         // Each frame, parent position is copied if the object is attached,
1016         // otherwise it's calculated normally.
1017         // If the object gets detached this comes into effect automatically from
1018         // the last known origin.
1019         if (isAttached()) {
1020                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1021                 m_last_good_position = pos;
1022                 setBasePosition(pos);
1023         }
1024
1025         if (!send_recommended)
1026                 return;
1027
1028         // If the object is attached client-side, don't waste bandwidth sending its
1029         // position to clients.
1030         if (m_position_not_sent && !isAttached()) {
1031                 m_position_not_sent = false;
1032                 float update_interval = m_env->getSendRecommendedInterval();
1033                 v3f pos;
1034                 if (isAttached()) // Just in case we ever do send attachment position too
1035                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1036                 else
1037                         pos = m_base_position;
1038
1039                 std::string str = gob_cmd_update_position(
1040                         pos,
1041                         v3f(0.0f, 0.0f, 0.0f),
1042                         v3f(0.0f, 0.0f, 0.0f),
1043                         m_yaw,
1044                         true,
1045                         false,
1046                         update_interval
1047                 );
1048                 // create message and add to list
1049                 ActiveObjectMessage aom(getId(), false, str);
1050                 m_messages_out.push(aom);
1051         }
1052
1053         if (!m_armor_groups_sent) {
1054                 m_armor_groups_sent = true;
1055                 std::string str = gob_cmd_update_armor_groups(
1056                                 m_armor_groups);
1057                 // create message and add to list
1058                 ActiveObjectMessage aom(getId(), true, str);
1059                 m_messages_out.push(aom);
1060         }
1061
1062         if (!m_physics_override_sent) {
1063                 m_physics_override_sent = true;
1064                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1065                                 m_physics_override_jump, m_physics_override_gravity,
1066                                 m_physics_override_sneak, m_physics_override_sneak_glitch,
1067                                 m_physics_override_new_move);
1068                 // create message and add to list
1069                 ActiveObjectMessage aom(getId(), true, str);
1070                 m_messages_out.push(aom);
1071         }
1072
1073         if (!m_animation_sent) {
1074                 m_animation_sent = true;
1075                 std::string str = gob_cmd_update_animation(
1076                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1077                 // create message and add to list
1078                 ActiveObjectMessage aom(getId(), true, str);
1079                 m_messages_out.push(aom);
1080         }
1081
1082         if (!m_bone_position_sent) {
1083                 m_bone_position_sent = true;
1084                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1085                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1086                         std::string str = gob_cmd_update_bone_position((*ii).first,
1087                                         (*ii).second.X, (*ii).second.Y);
1088                         // create message and add to list
1089                         ActiveObjectMessage aom(getId(), true, str);
1090                         m_messages_out.push(aom);
1091                 }
1092         }
1093
1094         if (!m_attachment_sent) {
1095                 m_attachment_sent = true;
1096                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1097                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1098                 // create message and add to list
1099                 ActiveObjectMessage aom(getId(), true, str);
1100                 m_messages_out.push(aom);
1101         }
1102 }
1103
1104 void PlayerSAO::setBasePosition(const v3f &position)
1105 {
1106         if (m_player && position != m_base_position)
1107                 m_player->setDirty(true);
1108
1109         // This needs to be ran for attachments too
1110         ServerActiveObject::setBasePosition(position);
1111         m_position_not_sent = true;
1112 }
1113
1114 void PlayerSAO::setPos(const v3f &pos)
1115 {
1116         if(isAttached())
1117                 return;
1118
1119         setBasePosition(pos);
1120         // Movement caused by this command is always valid
1121         m_last_good_position = pos;
1122         m_move_pool.empty();
1123         m_time_from_last_teleport = 0.0;
1124         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1125 }
1126
1127 void PlayerSAO::moveTo(v3f pos, bool continuous)
1128 {
1129         if(isAttached())
1130                 return;
1131
1132         setBasePosition(pos);
1133         // Movement caused by this command is always valid
1134         m_last_good_position = pos;
1135         m_move_pool.empty();
1136         m_time_from_last_teleport = 0.0;
1137         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1138 }
1139
1140 void PlayerSAO::setYaw(const float yaw)
1141 {
1142         if (m_player && yaw != m_yaw)
1143                 m_player->setDirty(true);
1144
1145         UnitSAO::setYaw(yaw);
1146 }
1147
1148 void PlayerSAO::setFov(const float fov)
1149 {
1150         if (m_player && fov != m_fov)
1151                 m_player->setDirty(true);
1152
1153         m_fov = fov;
1154 }
1155
1156 void PlayerSAO::setWantedRange(const s16 range)
1157 {
1158         if (m_player && range != m_wanted_range)
1159                 m_player->setDirty(true);
1160
1161         m_wanted_range = range;
1162 }
1163
1164 void PlayerSAO::setYawAndSend(const float yaw)
1165 {
1166         setYaw(yaw);
1167         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1168 }
1169
1170 void PlayerSAO::setPitch(const float pitch)
1171 {
1172         if (m_player && pitch != m_pitch)
1173                 m_player->setDirty(true);
1174
1175         m_pitch = pitch;
1176 }
1177
1178 void PlayerSAO::setPitchAndSend(const float pitch)
1179 {
1180         setPitch(pitch);
1181         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1182 }
1183
1184 int PlayerSAO::punch(v3f dir,
1185         const ToolCapabilities *toolcap,
1186         ServerActiveObject *puncher,
1187         float time_from_last_punch)
1188 {
1189         // It's best that attachments cannot be punched
1190         if (isAttached())
1191                 return 0;
1192
1193         if (!toolcap)
1194                 return 0;
1195
1196         // No effect if PvP disabled
1197         if (!g_settings->getBool("enable_pvp")) {
1198                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1199                         std::string str = gob_cmd_punched(0, getHP());
1200                         // create message and add to list
1201                         ActiveObjectMessage aom(getId(), true, str);
1202                         m_messages_out.push(aom);
1203                         return 0;
1204                 }
1205         }
1206
1207         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1208                         time_from_last_punch);
1209
1210         std::string punchername = "nil";
1211
1212         if (puncher != 0)
1213                 punchername = puncher->getDescription();
1214
1215         PlayerSAO *playersao = m_player->getPlayerSAO();
1216
1217         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1218                                 puncher, time_from_last_punch, toolcap, dir,
1219                                 hitparams.hp);
1220
1221         if (!damage_handled) {
1222                 setHP(getHP() - hitparams.hp);
1223         } else { // override client prediction
1224                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1225                         std::string str = gob_cmd_punched(0, getHP());
1226                         // create message and add to list
1227                         ActiveObjectMessage aom(getId(), true, str);
1228                         m_messages_out.push(aom);
1229                 }
1230         }
1231
1232
1233         actionstream << "Player " << m_player->getName() << " punched by "
1234                         << punchername;
1235         if (!damage_handled) {
1236                 actionstream << ", damage " << hitparams.hp << " HP";
1237         } else {
1238                 actionstream << ", damage handled by lua";
1239         }
1240         actionstream << std::endl;
1241
1242         return hitparams.wear;
1243 }
1244
1245 s16 PlayerSAO::readDamage()
1246 {
1247         s16 damage = m_damage;
1248         m_damage = 0;
1249         return damage;
1250 }
1251
1252 void PlayerSAO::setHP(s16 hp)
1253 {
1254         s16 oldhp = m_hp;
1255
1256         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1257         if (hp_change == 0)
1258                 return;
1259         hp = oldhp + hp_change;
1260
1261         if (hp < 0)
1262                 hp = 0;
1263         else if (hp > m_prop.hp_max)
1264                 hp = m_prop.hp_max;
1265
1266         if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1267                 return;
1268         }
1269
1270         m_hp = hp;
1271
1272         if (oldhp > hp)
1273                 m_damage += (oldhp - hp);
1274
1275         // Update properties on death
1276         if ((hp == 0) != (oldhp == 0))
1277                 m_properties_sent = false;
1278 }
1279
1280 void PlayerSAO::setBreath(const u16 breath, bool send)
1281 {
1282         if (m_player && breath != m_breath)
1283                 m_player->setDirty(true);
1284
1285         m_breath = MYMIN(breath, m_prop.breath_max);
1286
1287         if (send)
1288                 m_env->getGameDef()->SendPlayerBreath(this);
1289 }
1290
1291 Inventory* PlayerSAO::getInventory()
1292 {
1293         return m_inventory;
1294 }
1295 const Inventory* PlayerSAO::getInventory() const
1296 {
1297         return m_inventory;
1298 }
1299
1300 InventoryLocation PlayerSAO::getInventoryLocation() const
1301 {
1302         InventoryLocation loc;
1303         loc.setPlayer(m_player->getName());
1304         return loc;
1305 }
1306
1307 std::string PlayerSAO::getWieldList() const
1308 {
1309         return "main";
1310 }
1311
1312 ItemStack PlayerSAO::getWieldedItem() const
1313 {
1314         const Inventory *inv = getInventory();
1315         ItemStack ret;
1316         const InventoryList *mlist = inv->getList(getWieldList());
1317         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1318                 ret = mlist->getItem(getWieldIndex());
1319         return ret;
1320 }
1321
1322 ItemStack PlayerSAO::getWieldedItemOrHand() const
1323 {
1324         const Inventory *inv = getInventory();
1325         ItemStack ret;
1326         const InventoryList *mlist = inv->getList(getWieldList());
1327         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1328                 ret = mlist->getItem(getWieldIndex());
1329         if (ret.name.empty()) {
1330                 const InventoryList *hlist = inv->getList("hand");
1331                 if (hlist)
1332                         ret = hlist->getItem(0);
1333         }
1334         return ret;
1335 }
1336
1337 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1338 {
1339         Inventory *inv = getInventory();
1340         if (inv) {
1341                 InventoryList *mlist = inv->getList(getWieldList());
1342                 if (mlist) {
1343                         mlist->changeItem(getWieldIndex(), item);
1344                         return true;
1345                 }
1346         }
1347         return false;
1348 }
1349
1350 int PlayerSAO::getWieldIndex() const
1351 {
1352         return m_wield_index;
1353 }
1354
1355 void PlayerSAO::setWieldIndex(int i)
1356 {
1357         if(i != m_wield_index) {
1358                 m_wield_index = i;
1359         }
1360 }
1361
1362 void PlayerSAO::disconnected()
1363 {
1364         m_peer_id = 0;
1365         m_pending_removal = true;
1366 }
1367
1368 void PlayerSAO::unlinkPlayerSessionAndSave()
1369 {
1370         assert(m_player->getPlayerSAO() == this);
1371         m_player->setPeerId(PEER_ID_INEXISTENT);
1372         m_env->savePlayer(m_player);
1373         m_player->setPlayerSAO(NULL);
1374         m_env->removePlayer(m_player);
1375 }
1376
1377 std::string PlayerSAO::getPropertyPacket()
1378 {
1379         m_prop.is_visible = (true);
1380         return gob_cmd_set_properties(m_prop);
1381 }
1382
1383 bool PlayerSAO::checkMovementCheat()
1384 {
1385         if (isAttached() || m_is_singleplayer ||
1386                         g_settings->getBool("disable_anticheat")) {
1387                 m_last_good_position = m_base_position;
1388                 return false;
1389         }
1390
1391         bool cheated = false;
1392         /*
1393                 Check player movements
1394
1395                 NOTE: Actually the server should handle player physics like the
1396                 client does and compare player's position to what is calculated
1397                 on our side. This is required when eg. players fly due to an
1398                 explosion. Altough a node-based alternative might be possible
1399                 too, and much more lightweight.
1400         */
1401
1402         float player_max_speed = 0;
1403
1404         if (m_privs.count("fast") != 0) {
1405                 // Fast speed
1406                 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1407         } else {
1408                 // Normal speed
1409                 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1410         }
1411         // Tolerance. The lag pool does this a bit.
1412         //player_max_speed *= 2.5;
1413
1414         v3f diff = (m_base_position - m_last_good_position);
1415         float d_vert = diff.Y;
1416         diff.Y = 0;
1417         float d_horiz = diff.getLength();
1418         float required_time = d_horiz / player_max_speed;
1419
1420         if (d_vert > 0 && d_vert / player_max_speed > required_time)
1421                 required_time = d_vert / player_max_speed; // Moving upwards
1422
1423         if (m_move_pool.grab(required_time)) {
1424                 m_last_good_position = m_base_position;
1425         } else {
1426                 const float LAG_POOL_MIN = 5.0;
1427                 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1428                 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1429                 if (m_time_from_last_teleport > lag_pool_max) {
1430                         actionstream << "Player " << m_player->getName()
1431                                         << " moved too fast; resetting position"
1432                                         << std::endl;
1433                         cheated = true;
1434                 }
1435                 setBasePosition(m_last_good_position);
1436         }
1437         return cheated;
1438 }
1439
1440 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1441 {
1442         //update collision box
1443         toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1444         toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1445
1446         toset->MinEdge += m_base_position;
1447         toset->MaxEdge += m_base_position;
1448         return true;
1449 }
1450
1451 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1452 {
1453         if (!m_prop.is_visible || !m_prop.pointable) {
1454                 return false;
1455         }
1456
1457         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1458         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
1459
1460         return true;
1461 }