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_server.h"
30 #include "genericobject.h"
34 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
40 class TestSAO : public ServerActiveObject
43 TestSAO(ServerEnvironment *env, v3f pos):
44 ServerActiveObject(env, pos),
48 ServerActiveObject::registerType(getType(), create);
50 ActiveObjectType getType() const
51 { return ACTIVEOBJECT_TYPE_TEST; }
53 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
54 const std::string &data)
56 return new TestSAO(env, pos);
59 void step(float dtime, bool send_recommended)
64 m_pending_removal = true;
68 m_base_position.Y += dtime * BS * 2;
69 if(m_base_position.Y > 8*BS)
70 m_base_position.Y = 2*BS;
72 if (!send_recommended)
82 data += itos(0); // 0 = position
84 data += itos(m_base_position.X);
86 data += itos(m_base_position.Y);
88 data += itos(m_base_position.Z);
90 ActiveObjectMessage aom(getId(), false, data);
91 m_messages_out.push(aom);
95 bool getCollisionBox(aabb3f *toset) const { return false; }
97 virtual bool getSelectionBox(aabb3f *toset) const { return false; }
99 bool collideWithObjects() const { return false; }
106 // Prototype (registers item for deserialization)
107 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
113 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
114 ServerActiveObject(env, pos)
116 // Initialize something to armor groups
117 m_armor_groups["fleshy"] = 100;
120 bool UnitSAO::isAttached() const
122 if (!m_attachment_parent_id)
124 // Check if the parent still exists
125 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
131 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
133 m_armor_groups = armor_groups;
134 m_armor_groups_sent = false;
137 const ItemGroupList &UnitSAO::getArmorGroups()
139 return m_armor_groups;
142 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
144 // store these so they can be updated to clients
145 m_animation_range = frame_range;
146 m_animation_speed = frame_speed;
147 m_animation_blend = frame_blend;
148 m_animation_loop = frame_loop;
149 m_animation_sent = false;
152 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
154 *frame_range = m_animation_range;
155 *frame_speed = m_animation_speed;
156 *frame_blend = m_animation_blend;
157 *frame_loop = m_animation_loop;
160 void UnitSAO::setAnimationSpeed(float frame_speed)
162 m_animation_speed = frame_speed;
163 m_animation_speed_sent = false;
166 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
168 // store these so they can be updated to clients
169 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
170 m_bone_position_sent = false;
173 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
175 *position = m_bone_position[bone].X;
176 *rotation = m_bone_position[bone].Y;
179 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
181 // Attachments need to be handled on both the server and client.
182 // If we just attach on the server, we can only copy the position of the parent. Attachments
183 // are still sent to clients at an interval so players might see them lagging, plus we can't
184 // read and attach to skeletal bones.
185 // If we just attach on the client, the server still sees the child at its original location.
186 // This breaks some things so we also give the server the most accurate representation
187 // even if players only see the client changes.
189 m_attachment_parent_id = parent_id;
190 m_attachment_bone = bone;
191 m_attachment_position = position;
192 m_attachment_rotation = rotation;
193 m_attachment_sent = false;
196 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
199 *parent_id = m_attachment_parent_id;
200 *bone = m_attachment_bone;
201 *position = m_attachment_position;
202 *rotation = m_attachment_rotation;
205 void UnitSAO::addAttachmentChild(int child_id)
207 m_attachment_child_ids.insert(child_id);
210 void UnitSAO::removeAttachmentChild(int child_id)
212 m_attachment_child_ids.erase(child_id);
215 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
217 return m_attachment_child_ids;
220 ObjectProperties* UnitSAO::accessObjectProperties()
225 void UnitSAO::notifyObjectPropertiesModified()
227 m_properties_sent = false;
234 // Prototype (registers item for deserialization)
235 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
237 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
238 const std::string &name, const std::string &state):
243 // Only register type if no environment supplied
245 ServerActiveObject::registerType(getType(), create);
250 LuaEntitySAO::~LuaEntitySAO()
253 m_env->getScriptIface()->luaentity_Remove(m_id);
256 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
257 m_env->deleteParticleSpawner(attached_particle_spawner, false);
261 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
263 ServerActiveObject::addedToEnvironment(dtime_s);
265 // Create entity from name
266 m_registered = m_env->getScriptIface()->
267 luaentity_Add(m_id, m_init_name.c_str());
271 m_env->getScriptIface()->
272 luaentity_GetProperties(m_id, &m_prop);
273 // Initialize HP from properties
274 m_hp = m_prop.hp_max;
275 // Activate entity, supplying serialized state
276 m_env->getScriptIface()->
277 luaentity_Activate(m_id, m_init_state, dtime_s);
279 m_prop.infotext = m_init_name;
283 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
284 const std::string &data)
292 std::istringstream is(data, std::ios::binary);
294 u8 version = readU8(is);
295 // check if version is supported
297 name = deSerializeString(is);
298 state = deSerializeLongString(is);
300 else if(version == 1){
301 name = deSerializeString(is);
302 state = deSerializeLongString(is);
304 velocity = readV3F1000(is);
309 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
310 <<state<<"\")"<<std::endl;
311 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
313 sao->m_velocity = velocity;
318 void LuaEntitySAO::step(float dtime, bool send_recommended)
320 if(!m_properties_sent)
322 m_properties_sent = true;
323 std::string str = getPropertyPacket();
324 // create message and add to list
325 ActiveObjectMessage aom(getId(), true, str);
326 m_messages_out.push(aom);
329 // If attached, check that our parent is still there. If it isn't, detach.
330 if(m_attachment_parent_id && !isAttached())
332 m_attachment_parent_id = 0;
333 m_attachment_bone = "";
334 m_attachment_position = v3f(0,0,0);
335 m_attachment_rotation = v3f(0,0,0);
336 sendPosition(false, true);
339 m_last_sent_position_timer += dtime;
341 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
342 // If the object gets detached this comes into effect automatically from the last known origin
345 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
346 m_base_position = pos;
347 m_velocity = v3f(0,0,0);
348 m_acceleration = v3f(0,0,0);
353 aabb3f box = m_prop.collisionbox;
356 collisionMoveResult moveresult;
357 f32 pos_max_d = BS*0.25; // Distance per iteration
358 v3f p_pos = m_base_position;
359 v3f p_velocity = m_velocity;
360 v3f p_acceleration = m_acceleration;
361 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
362 pos_max_d, box, m_prop.stepheight, dtime,
363 &p_pos, &p_velocity, p_acceleration,
364 this, m_prop.collideWithObjects);
367 m_base_position = p_pos;
368 m_velocity = p_velocity;
369 m_acceleration = p_acceleration;
371 m_base_position += dtime * m_velocity + 0.5 * dtime
372 * dtime * m_acceleration;
373 m_velocity += dtime * m_acceleration;
376 if (m_prop.automatic_face_movement_dir &&
377 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
379 float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
380 + m_prop.automatic_face_movement_dir_offset;
381 float max_rotation_delta =
382 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
383 float delta = wrapDegrees_0_360(target_yaw - m_yaw);
385 if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
386 m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
387 m_yaw = wrapDegrees_0_360(m_yaw);
395 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
398 if (!send_recommended)
403 // TODO: force send when acceleration changes enough?
404 float minchange = 0.2*BS;
405 if(m_last_sent_position_timer > 1.0){
407 } else if(m_last_sent_position_timer > 0.2){
410 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
411 move_d += m_last_sent_move_precision;
412 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
413 if(move_d > minchange || vel_d > minchange ||
414 fabs(m_yaw - m_last_sent_yaw) > 1.0){
415 sendPosition(true, false);
419 if (!m_armor_groups_sent) {
420 m_armor_groups_sent = true;
421 std::string str = gob_cmd_update_armor_groups(
423 // create message and add to list
424 ActiveObjectMessage aom(getId(), true, str);
425 m_messages_out.push(aom);
428 if (!m_animation_sent) {
429 m_animation_sent = true;
430 std::string str = gob_cmd_update_animation(
431 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
432 // create message and add to list
433 ActiveObjectMessage aom(getId(), true, str);
434 m_messages_out.push(aom);
437 if (!m_animation_speed_sent) {
438 m_animation_speed_sent = true;
439 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
440 // create message and add to list
441 ActiveObjectMessage aom(getId(), true, str);
442 m_messages_out.push(aom);
445 if (!m_bone_position_sent) {
446 m_bone_position_sent = true;
447 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
448 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
449 std::string str = gob_cmd_update_bone_position((*ii).first,
450 (*ii).second.X, (*ii).second.Y);
451 // create message and add to list
452 ActiveObjectMessage aom(getId(), true, str);
453 m_messages_out.push(aom);
457 if (!m_attachment_sent) {
458 m_attachment_sent = true;
459 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
460 // create message and add to list
461 ActiveObjectMessage aom(getId(), true, str);
462 m_messages_out.push(aom);
466 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
468 std::ostringstream os(std::ios::binary);
471 writeU8(os, 1); // version
472 os << serializeString(""); // name
473 writeU8(os, 0); // is_player
474 writeS16(os, getId()); //id
475 writeV3F1000(os, m_base_position);
476 writeF1000(os, m_yaw);
479 std::ostringstream msg_os(std::ios::binary);
480 msg_os << serializeLongString(getPropertyPacket()); // message 1
481 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
482 msg_os << serializeLongString(gob_cmd_update_animation(
483 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
484 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
485 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
486 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
487 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
489 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
490 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
491 int message_count = 4 + m_bone_position.size();
492 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
493 (ii != m_attachment_child_ids.end()); ++ii) {
494 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
496 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
497 obj->getClientInitializationData(protocol_version)));
501 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
504 writeU8(os, message_count);
505 os.write(msg_os.str().c_str(), msg_os.str().size());
511 void LuaEntitySAO::getStaticData(std::string *result) const
513 verbosestream<<FUNCTION_NAME<<std::endl;
514 std::ostringstream os(std::ios::binary);
518 os<<serializeString(m_init_name);
521 std::string state = m_env->getScriptIface()->
522 luaentity_GetStaticdata(m_id);
523 os<<serializeLongString(state);
525 os<<serializeLongString(m_init_state);
530 writeV3F1000(os, m_velocity);
532 writeF1000(os, m_yaw);
536 int LuaEntitySAO::punch(v3f dir,
537 const ToolCapabilities *toolcap,
538 ServerActiveObject *puncher,
539 float time_from_last_punch)
542 // Delete unknown LuaEntities when punched
543 m_pending_removal = true;
547 // It's best that attachments cannot be punched
551 ItemStack *punchitem = NULL;
552 ItemStack punchitem_static;
554 punchitem_static = puncher->getWieldedItem();
555 punchitem = &punchitem_static;
558 PunchDamageResult result = getPunchDamage(
562 time_from_last_punch);
564 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
565 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
567 if (!damage_handled) {
568 if (result.did_punch) {
569 setHP(getHP() - result.damage);
571 if (result.damage > 0) {
572 std::string punchername = puncher ? puncher->getDescription() : "nil";
574 actionstream << getDescription() << " punched by "
575 << punchername << ", damage " << result.damage
576 << " hp, health now " << getHP() << " hp" << std::endl;
579 std::string str = gob_cmd_punched(result.damage, getHP());
580 // create message and add to list
581 ActiveObjectMessage aom(getId(), true, str);
582 m_messages_out.push(aom);
587 m_pending_removal = true;
588 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
594 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
598 // It's best that attachments cannot be clicked
601 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
604 void LuaEntitySAO::setPos(const v3f &pos)
608 m_base_position = pos;
609 sendPosition(false, true);
612 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
616 m_base_position = pos;
618 sendPosition(true, true);
621 float LuaEntitySAO::getMinimumSavedMovement()
626 std::string LuaEntitySAO::getDescription()
628 std::ostringstream os(std::ios::binary);
629 os<<"LuaEntitySAO at (";
630 os<<(m_base_position.X/BS)<<",";
631 os<<(m_base_position.Y/BS)<<",";
632 os<<(m_base_position.Z/BS);
637 void LuaEntitySAO::setHP(s16 hp)
643 s16 LuaEntitySAO::getHP() const
648 void LuaEntitySAO::setVelocity(v3f velocity)
650 m_velocity = velocity;
653 v3f LuaEntitySAO::getVelocity()
658 void LuaEntitySAO::setAcceleration(v3f acceleration)
660 m_acceleration = acceleration;
663 v3f LuaEntitySAO::getAcceleration()
665 return m_acceleration;
668 void LuaEntitySAO::setTextureMod(const std::string &mod)
670 std::string str = gob_cmd_set_texture_mod(mod);
671 m_current_texture_modifier = mod;
672 // create message and add to list
673 ActiveObjectMessage aom(getId(), true, str);
674 m_messages_out.push(aom);
677 std::string LuaEntitySAO::getTextureMod() const
679 return m_current_texture_modifier;
682 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
683 bool select_horiz_by_yawpitch)
685 std::string str = gob_cmd_set_sprite(
689 select_horiz_by_yawpitch
691 // create message and add to list
692 ActiveObjectMessage aom(getId(), true, str);
693 m_messages_out.push(aom);
696 std::string LuaEntitySAO::getName()
701 std::string LuaEntitySAO::getPropertyPacket()
703 return gob_cmd_set_properties(m_prop);
706 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
708 // If the object is attached client-side, don't waste bandwidth sending its position to clients
712 m_last_sent_move_precision = m_base_position.getDistanceFrom(
713 m_last_sent_position);
714 m_last_sent_position_timer = 0;
715 m_last_sent_yaw = m_yaw;
716 m_last_sent_position = m_base_position;
717 m_last_sent_velocity = m_velocity;
718 //m_last_sent_acceleration = m_acceleration;
720 float update_interval = m_env->getSendRecommendedInterval();
722 std::string str = gob_cmd_update_position(
731 // create message and add to list
732 ActiveObjectMessage aom(getId(), false, str);
733 m_messages_out.push(aom);
736 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
740 //update collision box
741 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
742 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
744 toset->MinEdge += m_base_position;
745 toset->MaxEdge += m_base_position;
753 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
755 if (!m_prop.is_visible || !m_prop.pointable) {
759 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
760 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
765 bool LuaEntitySAO::collideWithObjects() const
767 return m_prop.collideWithObjects;
774 // No prototype, PlayerSAO does not need to be deserialized
776 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
777 bool is_singleplayer):
778 UnitSAO(env_, v3f(0,0,0)),
781 m_is_singleplayer(is_singleplayer)
783 assert(m_peer_id != 0); // pre-condition
785 m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
786 m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
787 m_prop.physical = false;
789 m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
790 m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
791 m_prop.pointable = true;
792 // Start of default appearance, this should be overwritten by Lua
793 m_prop.visual = "upright_sprite";
794 m_prop.visual_size = v2f(1, 2);
795 m_prop.textures.clear();
796 m_prop.textures.emplace_back("player.png");
797 m_prop.textures.emplace_back("player_back.png");
798 m_prop.colors.clear();
799 m_prop.colors.emplace_back(255, 255, 255, 255);
800 m_prop.spritediv = v2s16(1,1);
801 m_prop.eye_height = 1.625f;
802 // End of default appearance
803 m_prop.is_visible = true;
804 m_prop.makes_footstep_sound = true;
805 m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
806 m_hp = m_prop.hp_max;
807 m_breath = m_prop.breath_max;
808 // Disable zoom in survival mode using a value of 0
809 m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
812 PlayerSAO::~PlayerSAO()
814 if(m_inventory != &m_player->inventory)
818 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
823 m_inventory = &m_player->inventory;
826 v3f PlayerSAO::getEyeOffset() const
828 return v3f(0, BS * m_prop.eye_height, 0);
831 std::string PlayerSAO::getDescription()
833 return std::string("player ") + m_player->getName();
836 // Called after id has been set and has been inserted in environment
837 void PlayerSAO::addedToEnvironment(u32 dtime_s)
839 ServerActiveObject::addedToEnvironment(dtime_s);
840 ServerActiveObject::setBasePosition(m_base_position);
841 m_player->setPlayerSAO(this);
842 m_player->setPeerId(m_peer_id);
843 m_last_good_position = m_base_position;
846 // Called before removing from environment
847 void PlayerSAO::removingFromEnvironment()
849 ServerActiveObject::removingFromEnvironment();
850 if (m_player->getPlayerSAO() == this) {
851 unlinkPlayerSessionAndSave();
852 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
853 m_env->deleteParticleSpawner(attached_particle_spawner, false);
858 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
860 std::ostringstream os(std::ios::binary);
863 writeU8(os, 1); // version
864 os << serializeString(m_player->getName()); // name
865 writeU8(os, 1); // is_player
866 writeS16(os, getId()); //id
867 writeV3F1000(os, m_base_position);
868 writeF1000(os, m_yaw);
869 writeS16(os, getHP());
871 std::ostringstream msg_os(std::ios::binary);
872 msg_os << serializeLongString(getPropertyPacket()); // message 1
873 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
874 msg_os << serializeLongString(gob_cmd_update_animation(
875 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
876 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
877 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
878 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
879 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
881 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
882 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
883 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
884 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
885 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
886 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
887 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
888 int message_count = 6 + m_bone_position.size();
889 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
890 ii != m_attachment_child_ids.end(); ++ii) {
891 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
893 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
894 obj->getClientInitializationData(protocol_version)));
898 writeU8(os, message_count);
899 os.write(msg_os.str().c_str(), msg_os.str().size());
905 void PlayerSAO::getStaticData(std::string * result) const
907 FATAL_ERROR("Deprecated function");
910 void PlayerSAO::step(float dtime, bool send_recommended)
912 if (m_drowning_interval.step(dtime, 2.0f)) {
913 // Get nose/mouth position, approximate with eye position
914 v3s16 p = floatToInt(getEyePosition(), BS);
915 MapNode n = m_env->getMap().getNodeNoEx(p);
916 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
917 // If node generates drown
918 if (c.drowning > 0 && m_hp > 0) {
920 setBreath(m_breath - 1);
922 // No more breath, damage player
924 setHP(m_hp - c.drowning);
925 m_env->getGameDef()->SendPlayerHPOrDie(this);
930 if (m_breathing_interval.step(dtime, 0.5f)) {
931 // Get nose/mouth position, approximate with eye position
932 v3s16 p = floatToInt(getEyePosition(), BS);
933 MapNode n = m_env->getMap().getNodeNoEx(p);
934 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
935 // If player is alive & no drowning, breathe
936 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
937 setBreath(m_breath + 1);
940 if (m_node_hurt_interval.step(dtime, 1.0f)) {
941 u32 damage_per_second = 0;
942 // Lowest and highest damage points are 0.1 within collisionbox
943 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
945 // Sequence of damage points, starting 0.1 above feet and progressing
946 // upwards in 1 node intervals, stopping below top damage point.
947 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
948 v3s16 p = floatToInt(m_base_position +
949 v3f(0.0f, dam_height * BS, 0.0f), BS);
950 MapNode n = m_env->getMap().getNodeNoEx(p);
951 damage_per_second = std::max(damage_per_second,
952 m_env->getGameDef()->ndef()->get(n).damage_per_second);
956 v3s16 ptop = floatToInt(m_base_position +
957 v3f(0.0f, dam_top * BS, 0.0f), BS);
958 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
959 damage_per_second = std::max(damage_per_second,
960 m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
962 if (damage_per_second != 0 && m_hp > 0) {
963 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
965 m_env->getGameDef()->SendPlayerHPOrDie(this);
969 if (!m_properties_sent) {
970 m_properties_sent = true;
971 std::string str = getPropertyPacket();
972 // create message and add to list
973 ActiveObjectMessage aom(getId(), true, str);
974 m_messages_out.push(aom);
977 // If attached, check that our parent is still there. If it isn't, detach.
978 if (m_attachment_parent_id && !isAttached()) {
979 m_attachment_parent_id = 0;
980 m_attachment_bone = "";
981 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
982 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
983 setBasePosition(m_last_good_position);
984 m_env->getGameDef()->SendMovePlayer(m_peer_id);
987 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
989 // Set lag pool maximums based on estimated lag
990 const float LAG_POOL_MIN = 5.0f;
991 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
992 if(lag_pool_max < LAG_POOL_MIN)
993 lag_pool_max = LAG_POOL_MIN;
994 m_dig_pool.setMax(lag_pool_max);
995 m_move_pool.setMax(lag_pool_max);
997 // Increment cheat prevention timers
998 m_dig_pool.add(dtime);
999 m_move_pool.add(dtime);
1000 m_time_from_last_teleport += dtime;
1001 m_time_from_last_punch += dtime;
1002 m_nocheat_dig_time += dtime;
1004 // Each frame, parent position is copied if the object is attached,
1005 // otherwise it's calculated normally.
1006 // If the object gets detached this comes into effect automatically from
1007 // the last known origin.
1009 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1010 m_last_good_position = pos;
1011 setBasePosition(pos);
1014 if (!send_recommended)
1017 // If the object is attached client-side, don't waste bandwidth sending its
1018 // position to clients.
1019 if (m_position_not_sent && !isAttached()) {
1020 m_position_not_sent = false;
1021 float update_interval = m_env->getSendRecommendedInterval();
1023 if (isAttached()) // Just in case we ever do send attachment position too
1024 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1026 pos = m_base_position;
1028 std::string str = gob_cmd_update_position(
1030 v3f(0.0f, 0.0f, 0.0f),
1031 v3f(0.0f, 0.0f, 0.0f),
1037 // create message and add to list
1038 ActiveObjectMessage aom(getId(), false, str);
1039 m_messages_out.push(aom);
1042 if (!m_armor_groups_sent) {
1043 m_armor_groups_sent = true;
1044 std::string str = gob_cmd_update_armor_groups(
1046 // create message and add to list
1047 ActiveObjectMessage aom(getId(), true, str);
1048 m_messages_out.push(aom);
1051 if (!m_physics_override_sent) {
1052 m_physics_override_sent = true;
1053 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1054 m_physics_override_jump, m_physics_override_gravity,
1055 m_physics_override_sneak, m_physics_override_sneak_glitch,
1056 m_physics_override_new_move);
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 (std::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")) {
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 > m_prop.hp_max)
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, m_prop.breath_max);
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());
1311 ItemStack PlayerSAO::getWieldedItemOrHand() const
1313 const Inventory *inv = getInventory();
1315 const InventoryList *mlist = inv->getList(getWieldList());
1316 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1317 ret = mlist->getItem(getWieldIndex());
1318 if (ret.name.empty()) {
1319 const InventoryList *hlist = inv->getList("hand");
1321 ret = hlist->getItem(0);
1326 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1328 Inventory *inv = getInventory();
1330 InventoryList *mlist = inv->getList(getWieldList());
1332 mlist->changeItem(getWieldIndex(), item);
1339 int PlayerSAO::getWieldIndex() const
1341 return m_wield_index;
1344 void PlayerSAO::setWieldIndex(int i)
1346 if(i != m_wield_index) {
1351 void PlayerSAO::disconnected()
1354 m_pending_removal = true;
1357 void PlayerSAO::unlinkPlayerSessionAndSave()
1359 assert(m_player->getPlayerSAO() == this);
1360 m_player->setPeerId(PEER_ID_INEXISTENT);
1361 m_env->savePlayer(m_player);
1362 m_player->setPlayerSAO(NULL);
1363 m_env->removePlayer(m_player);
1366 std::string PlayerSAO::getPropertyPacket()
1368 m_prop.is_visible = (true);
1369 return gob_cmd_set_properties(m_prop);
1372 bool PlayerSAO::checkMovementCheat()
1374 if (isAttached() || m_is_singleplayer ||
1375 g_settings->getBool("disable_anticheat")) {
1376 m_last_good_position = m_base_position;
1380 bool cheated = false;
1382 Check player movements
1384 NOTE: Actually the server should handle player physics like the
1385 client does and compare player's position to what is calculated
1386 on our side. This is required when eg. players fly due to an
1387 explosion. Altough a node-based alternative might be possible
1388 too, and much more lightweight.
1391 float player_max_walk = 0; // horizontal movement
1392 float player_max_jump = 0; // vertical upwards movement
1394 if (m_privs.count("fast") != 0)
1395 player_max_walk = m_player->movement_speed_fast; // Fast speed
1397 player_max_walk = m_player->movement_speed_walk; // Normal speed
1398 player_max_walk *= m_physics_override_speed;
1399 player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
1400 // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
1401 // until this can be verified correctly, tolerate higher jumping speeds
1402 player_max_jump *= 2.0;
1404 // Don't divide by zero!
1405 if (player_max_walk < 0.0001f)
1406 player_max_walk = 0.0001f;
1407 if (player_max_jump < 0.0001f)
1408 player_max_jump = 0.0001f;
1410 v3f diff = (m_base_position - m_last_good_position);
1411 float d_vert = diff.Y;
1413 float d_horiz = diff.getLength();
1414 float required_time = d_horiz / player_max_walk;
1416 // FIXME: Checking downwards movement is not easily possible currently,
1417 // the server could calculate speed differences to examine the gravity
1419 // In certain cases (water, ladders) walking speed is applied vertically
1420 float s = MYMAX(player_max_jump, player_max_walk);
1421 required_time = MYMAX(required_time, d_vert / s);
1424 if (m_move_pool.grab(required_time)) {
1425 m_last_good_position = m_base_position;
1427 const float LAG_POOL_MIN = 5.0;
1428 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1429 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1430 if (m_time_from_last_teleport > lag_pool_max) {
1431 actionstream << "Player " << m_player->getName()
1432 << " moved too fast; resetting position"
1436 setBasePosition(m_last_good_position);
1441 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1443 //update collision box
1444 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1445 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1447 toset->MinEdge += m_base_position;
1448 toset->MaxEdge += m_base_position;
1452 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1454 if (!m_prop.is_visible || !m_prop.pointable) {
1458 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1459 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;