3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "collision.h"
23 #include "environment.h"
24 #include "tool.h" // For ToolCapabilities
27 #include "remoteplayer.h"
29 #include "scripting_game.h"
30 #include "genericobject.h"
32 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
38 class TestSAO : public ServerActiveObject
41 TestSAO(ServerEnvironment *env, v3f pos):
42 ServerActiveObject(env, pos),
46 ServerActiveObject::registerType(getType(), create);
48 ActiveObjectType getType() const
49 { return ACTIVEOBJECT_TYPE_TEST; }
51 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
52 const std::string &data)
54 return new TestSAO(env, pos);
57 void step(float dtime, bool send_recommended)
66 m_base_position.Y += dtime * BS * 2;
67 if(m_base_position.Y > 8*BS)
68 m_base_position.Y = 2*BS;
70 if(send_recommended == false)
80 data += itos(0); // 0 = position
82 data += itos(m_base_position.X);
84 data += itos(m_base_position.Y);
86 data += itos(m_base_position.Z);
88 ActiveObjectMessage aom(getId(), false, data);
89 m_messages_out.push(aom);
93 bool getCollisionBox(aabb3f *toset) const { return false; }
94 bool collideWithObjects() const { return false; }
101 // Prototype (registers item for deserialization)
102 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
108 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
109 ServerActiveObject(env, pos),
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)
123 // Initialize something to armor groups
124 m_armor_groups["fleshy"] = 100;
127 bool UnitSAO::isAttached() const
129 if (!m_attachment_parent_id)
131 // Check if the parent still exists
132 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
138 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
140 m_armor_groups = armor_groups;
141 m_armor_groups_sent = false;
144 const ItemGroupList &UnitSAO::getArmorGroups()
146 return m_armor_groups;
149 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
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;
159 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
161 *frame_range = m_animation_range;
162 *frame_speed = m_animation_speed;
163 *frame_blend = m_animation_blend;
164 *frame_loop = m_animation_loop;
167 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
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;
174 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
176 *position = m_bone_position[bone].X;
177 *rotation = m_bone_position[bone].Y;
180 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
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.
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;
197 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
200 *parent_id = m_attachment_parent_id;
201 *bone = m_attachment_bone;
202 *position = m_attachment_position;
203 *rotation = m_attachment_rotation;
206 void UnitSAO::addAttachmentChild(int child_id)
208 m_attachment_child_ids.insert(child_id);
211 void UnitSAO::removeAttachmentChild(int child_id)
213 m_attachment_child_ids.erase(child_id);
216 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
218 return m_attachment_child_ids;
221 ObjectProperties* UnitSAO::accessObjectProperties()
226 void UnitSAO::notifyObjectPropertiesModified()
228 m_properties_sent = false;
235 // Prototype (registers item for deserialization)
236 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
238 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
239 const std::string &name, const std::string &state):
245 m_acceleration(0,0,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("")
253 // Only register type if no environment supplied
255 ServerActiveObject::registerType(getType(), create);
260 LuaEntitySAO::~LuaEntitySAO()
263 m_env->getScriptIface()->luaentity_Remove(m_id);
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);
272 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
274 ServerActiveObject::addedToEnvironment(dtime_s);
276 // Create entity from name
277 m_registered = m_env->getScriptIface()->
278 luaentity_Add(m_id, m_init_name.c_str());
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);
290 m_prop.infotext = m_init_name;
294 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
295 const std::string &data)
303 std::istringstream is(data, std::ios::binary);
305 u8 version = readU8(is);
306 // check if version is supported
308 name = deSerializeString(is);
309 state = deSerializeLongString(is);
311 else if(version == 1){
312 name = deSerializeString(is);
313 state = deSerializeLongString(is);
315 velocity = readV3F1000(is);
320 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
321 <<state<<"\")"<<std::endl;
322 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
324 sao->m_velocity = velocity;
329 void LuaEntitySAO::step(float dtime, bool send_recommended)
331 if(!m_properties_sent)
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);
340 // If attached, check that our parent is still there. If it isn't, detach.
341 if(m_attachment_parent_id && !isAttached())
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);
350 m_last_sent_position_timer += dtime;
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
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);
364 aabb3f box = m_prop.collisionbox;
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);
378 m_base_position = p_pos;
379 m_velocity = p_velocity;
380 m_acceleration = p_acceleration;
382 m_base_position += dtime * m_velocity + 0.5 * dtime
383 * dtime * m_acceleration;
384 m_velocity += dtime * m_acceleration;
387 if((m_prop.automatic_face_movement_dir) &&
388 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
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;
395 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
396 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
398 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
406 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
409 if(send_recommended == false)
414 // TODO: force send when acceleration changes enough?
415 float minchange = 0.2*BS;
416 if(m_last_sent_position_timer > 1.0){
418 } else if(m_last_sent_position_timer > 0.2){
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);
430 if(m_armor_groups_sent == false){
431 m_armor_groups_sent = true;
432 std::string str = gob_cmd_update_armor_groups(
434 // create message and add to list
435 ActiveObjectMessage aom(getId(), true, str);
436 m_messages_out.push(aom);
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);
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);
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);
469 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
471 std::ostringstream os(std::ios::binary);
473 if(protocol_version >= 14)
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);
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
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)) {
500 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
501 obj->getClientInitializationData(protocol_version)));
505 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
508 writeU8(os, message_count);
509 os.write(msg_os.str().c_str(), msg_os.str().size());
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);
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
528 void LuaEntitySAO::getStaticData(std::string *result) const
530 verbosestream<<FUNCTION_NAME<<std::endl;
531 std::ostringstream os(std::ios::binary);
535 os<<serializeString(m_init_name);
538 std::string state = m_env->getScriptIface()->
539 luaentity_GetStaticdata(m_id);
540 os<<serializeLongString(state);
542 os<<serializeLongString(m_init_state);
547 writeV3F1000(os, m_velocity);
549 writeF1000(os, m_yaw);
553 int LuaEntitySAO::punch(v3f dir,
554 const ToolCapabilities *toolcap,
555 ServerActiveObject *puncher,
556 float time_from_last_punch)
559 // Delete unknown LuaEntities when punched
564 // It's best that attachments cannot be punched
568 ItemStack *punchitem = NULL;
569 ItemStack punchitem_static;
571 punchitem_static = puncher->getWieldedItem();
572 punchitem = &punchitem_static;
575 PunchDamageResult result = getPunchDamage(
579 time_from_last_punch);
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);
584 if (!damage_handled) {
585 if (result.did_punch) {
586 setHP(getHP() - result.damage);
588 if (result.damage > 0) {
589 std::string punchername = puncher ? puncher->getDescription() : "nil";
591 actionstream << getDescription() << " punched by "
592 << punchername << ", damage " << result.damage
593 << " hp, health now " << getHP() << " hp" << std::endl;
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);
611 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
615 // It's best that attachments cannot be clicked
618 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
621 void LuaEntitySAO::setPos(const v3f &pos)
625 m_base_position = pos;
626 sendPosition(false, true);
629 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
633 m_base_position = pos;
635 sendPosition(true, true);
638 float LuaEntitySAO::getMinimumSavedMovement()
643 std::string LuaEntitySAO::getDescription()
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);
654 void LuaEntitySAO::setHP(s16 hp)
660 s16 LuaEntitySAO::getHP() const
665 void LuaEntitySAO::setVelocity(v3f velocity)
667 m_velocity = velocity;
670 v3f LuaEntitySAO::getVelocity()
675 void LuaEntitySAO::setAcceleration(v3f acceleration)
677 m_acceleration = acceleration;
680 v3f LuaEntitySAO::getAcceleration()
682 return m_acceleration;
685 void LuaEntitySAO::setTextureMod(const std::string &mod)
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);
694 std::string LuaEntitySAO::getTextureMod() const
696 return m_current_texture_modifier;
699 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
700 bool select_horiz_by_yawpitch)
702 std::string str = gob_cmd_set_sprite(
706 select_horiz_by_yawpitch
708 // create message and add to list
709 ActiveObjectMessage aom(getId(), true, str);
710 m_messages_out.push(aom);
713 std::string LuaEntitySAO::getName()
718 std::string LuaEntitySAO::getPropertyPacket()
720 return gob_cmd_set_properties(m_prop);
723 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
725 // If the object is attached client-side, don't waste bandwidth sending its position to clients
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;
737 float update_interval = m_env->getSendRecommendedInterval();
739 std::string str = gob_cmd_update_position(
748 // create message and add to list
749 ActiveObjectMessage aom(getId(), false, str);
750 m_messages_out.push(aom);
753 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
757 //update collision box
758 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
759 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
761 toset->MinEdge += m_base_position;
762 toset->MaxEdge += m_base_position;
770 bool LuaEntitySAO::collideWithObjects() const
772 return m_prop.collideWithObjects;
779 // No prototype, PlayerSAO does not need to be deserialized
781 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
782 UnitSAO(env_, v3f(0,0,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),
793 m_position_not_sent(false),
794 m_is_singleplayer(is_singleplayer),
795 m_breath(PLAYER_MAX_BREATH),
799 m_extended_attributes_modified(false),
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)
808 assert(m_peer_id != 0); // pre-condition
810 m_prop.hp_max = PLAYER_MAX_HP;
811 m_prop.physical = false;
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;
829 PlayerSAO::~PlayerSAO()
831 if(m_inventory != &m_player->inventory)
835 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
840 m_inventory = &m_player->inventory;
843 v3f PlayerSAO::getEyeOffset() const
845 return v3f(0, BS * 1.625f, 0);
848 std::string PlayerSAO::getDescription()
850 return std::string("player ") + m_player->getName();
853 // Called after id has been set and has been inserted in environment
854 void PlayerSAO::addedToEnvironment(u32 dtime_s)
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;
863 // Called before removing from environment
864 void PlayerSAO::removingFromEnvironment()
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);
876 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
878 std::ostringstream os(std::ios::binary);
880 if(protocol_version >= 15)
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());
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
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)) {
912 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
913 obj->getClientInitializationData(protocol_version)));
917 writeU8(os, message_count);
918 os.write(msg_os.str().c_str(), msg_os.str().size());
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
937 void PlayerSAO::getStaticData(std::string *result) const
939 FATAL_ERROR("Deprecated function");
942 void PlayerSAO::step(float dtime, bool send_recommended)
944 if (m_drowning_interval.step(dtime, 2.0)) {
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);
954 // No more breath, damage player
956 setHP(m_hp - c.drowning);
957 m_env->getGameDef()->SendPlayerHPOrDie(this);
962 if (m_breathing_interval.step(dtime, 0.5)) {
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);
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);
980 // If attached, check that our parent is still there. If it isn't, detach.
981 if(m_attachment_parent_id && !isAttached())
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);
991 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
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);
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;
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
1011 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1012 m_last_good_position = pos;
1013 setBasePosition(pos);
1016 if (!send_recommended)
1019 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1020 if(m_position_not_sent && !isAttached())
1022 m_position_not_sent = false;
1023 float update_interval = m_env->getSendRecommendedInterval();
1025 if(isAttached()) // Just in case we ever do send attachment position too
1026 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1028 pos = m_base_position + v3f(0,BS*1,0);
1029 std::string str = gob_cmd_update_position(
1038 // create message and add to list
1039 ActiveObjectMessage aom(getId(), false, str);
1040 m_messages_out.push(aom);
1043 if (!m_armor_groups_sent) {
1044 m_armor_groups_sent = true;
1045 std::string str = gob_cmd_update_armor_groups(
1047 // create message and add to list
1048 ActiveObjectMessage aom(getId(), true, str);
1049 m_messages_out.push(aom);
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);
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);
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);
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);
1093 void PlayerSAO::setBasePosition(const v3f &position)
1095 if (m_player && position != m_base_position)
1096 m_player->setDirty(true);
1098 // This needs to be ran for attachments too
1099 ServerActiveObject::setBasePosition(position);
1100 m_position_not_sent = true;
1103 void PlayerSAO::setPos(const v3f &pos)
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);
1116 void PlayerSAO::moveTo(v3f pos, bool continuous)
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);
1129 void PlayerSAO::setYaw(const float yaw)
1131 if (m_player && yaw != m_yaw)
1132 m_player->setDirty(true);
1134 UnitSAO::setYaw(yaw);
1137 void PlayerSAO::setFov(const float fov)
1139 if (m_player && fov != m_fov)
1140 m_player->setDirty(true);
1145 void PlayerSAO::setWantedRange(const s16 range)
1147 if (m_player && range != m_wanted_range)
1148 m_player->setDirty(true);
1150 m_wanted_range = range;
1153 void PlayerSAO::setYawAndSend(const float yaw)
1156 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1159 void PlayerSAO::setPitch(const float pitch)
1161 if (m_player && pitch != m_pitch)
1162 m_player->setDirty(true);
1167 void PlayerSAO::setPitchAndSend(const float pitch)
1170 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1173 int PlayerSAO::punch(v3f dir,
1174 const ToolCapabilities *toolcap,
1175 ServerActiveObject *puncher,
1176 float time_from_last_punch)
1178 // It's best that attachments cannot be punched
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);
1196 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1197 time_from_last_punch);
1199 std::string punchername = "nil";
1202 punchername = puncher->getDescription();
1204 PlayerSAO *playersao = m_player->getPlayerSAO();
1206 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1207 puncher, time_from_last_punch, toolcap, dir,
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);
1222 actionstream << "Player " << m_player->getName() << " punched by "
1224 if (!damage_handled) {
1225 actionstream << ", damage " << hitparams.hp << " HP";
1227 actionstream << ", damage handled by lua";
1229 actionstream << std::endl;
1231 return hitparams.wear;
1234 s16 PlayerSAO::readDamage()
1236 s16 damage = m_damage;
1241 void PlayerSAO::setHP(s16 hp)
1245 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1248 hp = oldhp + hp_change;
1252 else if (hp > PLAYER_MAX_HP)
1255 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1262 m_damage += (oldhp - hp);
1264 // Update properties on death
1265 if ((hp == 0) != (oldhp == 0))
1266 m_properties_sent = false;
1269 void PlayerSAO::setBreath(const u16 breath, bool send)
1271 if (m_player && breath != m_breath)
1272 m_player->setDirty(true);
1274 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1277 m_env->getGameDef()->SendPlayerBreath(this);
1280 Inventory* PlayerSAO::getInventory()
1284 const Inventory* PlayerSAO::getInventory() const
1289 InventoryLocation PlayerSAO::getInventoryLocation() const
1291 InventoryLocation loc;
1292 loc.setPlayer(m_player->getName());
1296 std::string PlayerSAO::getWieldList() const
1301 ItemStack PlayerSAO::getWieldedItem() const
1303 const Inventory *inv = getInventory();
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");
1311 ret = hlist->getItem(0);
1316 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1318 Inventory *inv = getInventory();
1320 InventoryList *mlist = inv->getList(getWieldList());
1322 ItemStack olditem = mlist->getItem(getWieldIndex());
1323 if (olditem.name.empty()) {
1324 InventoryList *hlist = inv->getList("hand");
1326 hlist->changeItem(0, item);
1330 mlist->changeItem(getWieldIndex(), item);
1337 int PlayerSAO::getWieldIndex() const
1339 return m_wield_index;
1342 void PlayerSAO::setWieldIndex(int i)
1344 if(i != m_wield_index) {
1349 // Erase the peer id and make the object for removal
1350 void PlayerSAO::disconnected()
1356 void PlayerSAO::unlinkPlayerSessionAndSave()
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);
1365 std::string PlayerSAO::getPropertyPacket()
1367 m_prop.is_visible = (true);
1368 return gob_cmd_set_properties(m_prop);
1371 bool PlayerSAO::checkMovementCheat()
1373 if (isAttached() || m_is_singleplayer ||
1374 g_settings->getBool("disable_anticheat")) {
1375 m_last_good_position = m_base_position;
1379 bool cheated = false;
1381 Check player movements
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.
1390 float player_max_speed = 0;
1392 if (m_privs.count("fast") != 0) {
1394 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1397 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1399 // Tolerance. The lag pool does this a bit.
1400 //player_max_speed *= 2.5;
1402 v3f diff = (m_base_position - m_last_good_position);
1403 float d_vert = diff.Y;
1405 float d_horiz = diff.getLength();
1406 float required_time = d_horiz / player_max_speed;
1408 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1409 required_time = d_vert / player_max_speed; // Moving upwards
1411 if (m_move_pool.grab(required_time)) {
1412 m_last_good_position = m_base_position;
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"
1423 setBasePosition(m_last_good_position);
1428 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
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;