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