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