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"
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
41 class TestSAO : public ServerActiveObject
44 TestSAO(ServerEnvironment *env, v3f pos):
45 ServerActiveObject(env, pos),
49 ServerActiveObject::registerType(getType(), create);
51 ActiveObjectType getType() const
52 { return ACTIVEOBJECT_TYPE_TEST; }
54 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55 const std::string &data)
57 return new TestSAO(env, pos);
60 void step(float dtime, bool send_recommended)
65 m_pending_removal = true;
69 m_base_position.Y += dtime * BS * 2;
70 if(m_base_position.Y > 8*BS)
71 m_base_position.Y = 2*BS;
73 if (!send_recommended)
83 data += itos(0); // 0 = position
85 data += itos(m_base_position.X);
87 data += itos(m_base_position.Y);
89 data += itos(m_base_position.Z);
91 ActiveObjectMessage aom(getId(), false, data);
92 m_messages_out.push(aom);
96 bool getCollisionBox(aabb3f *toset) const { return false; }
98 virtual bool getSelectionBox(aabb3f *toset) const { return false; }
100 bool collideWithObjects() const { return false; }
107 // Prototype (registers item for deserialization)
108 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
114 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
115 ServerActiveObject(env, pos)
117 // Initialize something to armor groups
118 m_armor_groups["fleshy"] = 100;
121 bool UnitSAO::isAttached() const
123 if (!m_attachment_parent_id)
125 // Check if the parent still exists
126 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
132 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
134 m_armor_groups = armor_groups;
135 m_armor_groups_sent = false;
138 const ItemGroupList &UnitSAO::getArmorGroups()
140 return m_armor_groups;
143 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
145 // store these so they can be updated to clients
146 m_animation_range = frame_range;
147 m_animation_speed = frame_speed;
148 m_animation_blend = frame_blend;
149 m_animation_loop = frame_loop;
150 m_animation_sent = false;
153 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
155 *frame_range = m_animation_range;
156 *frame_speed = m_animation_speed;
157 *frame_blend = m_animation_blend;
158 *frame_loop = m_animation_loop;
161 void UnitSAO::setAnimationSpeed(float frame_speed)
163 m_animation_speed = frame_speed;
164 m_animation_speed_sent = false;
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 std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
218 return m_attachment_child_ids;
221 ObjectProperties* UnitSAO::accessObjectProperties()
226 void UnitSAO::notifyObjectPropertiesModified()
228 m_env->updateActiveObject(this);
229 m_properties_sent = false;
236 // Prototype (registers item for deserialization)
237 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
239 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
240 const std::string &name, const std::string &state):
245 // Only register type if no environment supplied
247 ServerActiveObject::registerType(getType(), create);
252 LuaEntitySAO::~LuaEntitySAO()
255 m_env->getScriptIface()->luaentity_Remove(m_id);
258 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
259 m_env->deleteParticleSpawner(attached_particle_spawner, false);
263 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
265 ServerActiveObject::addedToEnvironment(dtime_s);
267 // Create entity from name
268 m_registered = m_env->getScriptIface()->
269 luaentity_Add(m_id, m_init_name.c_str());
273 m_env->getScriptIface()->
274 luaentity_GetProperties(m_id, &m_prop);
275 // Initialize HP from properties
276 m_hp = m_prop.hp_max;
277 // Activate entity, supplying serialized state
278 m_env->getScriptIface()->
279 luaentity_Activate(m_id, m_init_state, dtime_s);
281 m_prop.infotext = m_init_name;
285 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
286 const std::string &data)
294 std::istringstream is(data, std::ios::binary);
296 u8 version = readU8(is);
297 // check if version is supported
299 name = deSerializeString(is);
300 state = deSerializeLongString(is);
302 else if(version == 1){
303 name = deSerializeString(is);
304 state = deSerializeLongString(is);
306 velocity = readV3F1000(is);
311 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
312 <<state<<"\")"<<std::endl;
313 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
315 sao->m_velocity = velocity;
320 void LuaEntitySAO::step(float dtime, bool send_recommended)
322 if(!m_properties_sent)
324 m_properties_sent = true;
325 std::string str = getPropertyPacket();
326 // create message and add to list
327 ActiveObjectMessage aom(getId(), true, str);
328 m_messages_out.push(aom);
331 // If attached, check that our parent is still there. If it isn't, detach.
332 if(m_attachment_parent_id && !isAttached())
334 m_attachment_parent_id = 0;
335 m_attachment_bone = "";
336 m_attachment_position = v3f(0,0,0);
337 m_attachment_rotation = v3f(0,0,0);
338 sendPosition(false, true);
341 m_last_sent_position_timer += dtime;
343 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
344 // If the object gets detached this comes into effect automatically from the last known origin
347 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
348 m_base_position = pos;
349 m_velocity = v3f(0,0,0);
350 m_acceleration = v3f(0,0,0);
355 aabb3f box = m_prop.collisionbox;
358 collisionMoveResult moveresult;
359 f32 pos_max_d = BS*0.25; // Distance per iteration
360 v3f p_pos = m_base_position;
361 v3f p_velocity = m_velocity;
362 v3f p_acceleration = m_acceleration;
363 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
364 pos_max_d, box, m_prop.stepheight, dtime,
365 &p_pos, &p_velocity, p_acceleration,
366 this, m_prop.collideWithObjects);
369 m_base_position = p_pos;
370 m_velocity = p_velocity;
371 m_acceleration = p_acceleration;
373 m_base_position += dtime * m_velocity + 0.5 * dtime
374 * dtime * m_acceleration;
375 m_velocity += dtime * m_acceleration;
378 if (m_prop.automatic_face_movement_dir &&
379 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
381 float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
382 + m_prop.automatic_face_movement_dir_offset;
383 float max_rotation_delta =
384 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
385 float delta = wrapDegrees_0_360(target_yaw - m_yaw);
387 if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
388 m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
389 m_yaw = wrapDegrees_0_360(m_yaw);
397 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
400 if (!send_recommended)
405 // TODO: force send when acceleration changes enough?
406 float minchange = 0.2*BS;
407 if(m_last_sent_position_timer > 1.0){
409 } else if(m_last_sent_position_timer > 0.2){
412 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
413 move_d += m_last_sent_move_precision;
414 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
415 if (move_d > minchange || vel_d > minchange ||
416 std::fabs(m_yaw - m_last_sent_yaw) > 1.0) {
417 sendPosition(true, false);
421 if (!m_armor_groups_sent) {
422 m_armor_groups_sent = true;
423 std::string str = gob_cmd_update_armor_groups(
425 // create message and add to list
426 ActiveObjectMessage aom(getId(), true, str);
427 m_messages_out.push(aom);
430 if (!m_animation_sent) {
431 m_animation_sent = true;
432 std::string str = gob_cmd_update_animation(
433 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
434 // create message and add to list
435 ActiveObjectMessage aom(getId(), true, str);
436 m_messages_out.push(aom);
439 if (!m_animation_speed_sent) {
440 m_animation_speed_sent = true;
441 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
442 // create message and add to list
443 ActiveObjectMessage aom(getId(), true, str);
444 m_messages_out.push(aom);
447 if (!m_bone_position_sent) {
448 m_bone_position_sent = true;
449 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
450 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
451 std::string str = gob_cmd_update_bone_position((*ii).first,
452 (*ii).second.X, (*ii).second.Y);
453 // create message and add to list
454 ActiveObjectMessage aom(getId(), true, str);
455 m_messages_out.push(aom);
459 if (!m_attachment_sent) {
460 m_attachment_sent = true;
461 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
462 // create message and add to list
463 ActiveObjectMessage aom(getId(), true, str);
464 m_messages_out.push(aom);
468 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
470 std::ostringstream os(std::ios::binary);
473 writeU8(os, 1); // version
474 os << serializeString(""); // name
475 writeU8(os, 0); // is_player
476 writeS16(os, getId()); //id
477 writeV3F1000(os, m_base_position);
478 writeF1000(os, m_yaw);
481 std::ostringstream msg_os(std::ios::binary);
482 msg_os << serializeLongString(getPropertyPacket()); // message 1
483 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
484 msg_os << serializeLongString(gob_cmd_update_animation(
485 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
486 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
487 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
488 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
489 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
491 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
492 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
493 int message_count = 4 + m_bone_position.size();
494 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
495 (ii != m_attachment_child_ids.end()); ++ii) {
496 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
498 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
499 obj->getClientInitializationData(protocol_version)));
503 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
506 writeU8(os, message_count);
507 os.write(msg_os.str().c_str(), msg_os.str().size());
513 void LuaEntitySAO::getStaticData(std::string *result) const
515 verbosestream<<FUNCTION_NAME<<std::endl;
516 std::ostringstream os(std::ios::binary);
520 os<<serializeString(m_init_name);
523 std::string state = m_env->getScriptIface()->
524 luaentity_GetStaticdata(m_id);
525 os<<serializeLongString(state);
527 os<<serializeLongString(m_init_state);
532 writeV3F1000(os, m_velocity);
534 writeF1000(os, m_yaw);
538 int LuaEntitySAO::punch(v3f dir,
539 const ToolCapabilities *toolcap,
540 ServerActiveObject *puncher,
541 float time_from_last_punch)
544 // Delete unknown LuaEntities when punched
545 m_pending_removal = true;
549 // It's best that attachments cannot be punched
553 ItemStack *punchitem = NULL;
554 ItemStack punchitem_static;
556 punchitem_static = puncher->getWieldedItem();
557 punchitem = &punchitem_static;
560 PunchDamageResult result = getPunchDamage(
564 time_from_last_punch);
566 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
567 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
569 if (!damage_handled) {
570 if (result.did_punch) {
571 setHP(getHP() - result.damage,
572 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
574 if (result.damage > 0) {
575 std::string punchername = puncher ? puncher->getDescription() : "nil";
577 actionstream << getDescription() << " punched by "
578 << punchername << ", damage " << result.damage
579 << " hp, health now " << getHP() << " hp" << std::endl;
582 std::string str = gob_cmd_punched(result.damage, getHP());
583 // create message and add to list
584 ActiveObjectMessage aom(getId(), true, str);
585 m_messages_out.push(aom);
590 m_pending_removal = true;
591 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
597 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
601 // It's best that attachments cannot be clicked
604 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
607 void LuaEntitySAO::setPos(const v3f &pos)
611 m_base_position = pos;
612 m_env->updateActiveObject(this);
613 sendPosition(false, true);
616 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
620 m_base_position = pos;
621 m_env->updateActiveObject(this);
623 sendPosition(true, true);
626 float LuaEntitySAO::getMinimumSavedMovement()
631 std::string LuaEntitySAO::getDescription()
633 std::ostringstream os(std::ios::binary);
634 os<<"LuaEntitySAO at (";
635 os<<(m_base_position.X/BS)<<",";
636 os<<(m_base_position.Y/BS)<<",";
637 os<<(m_base_position.Z/BS);
642 void LuaEntitySAO::setHP(s16 hp, const PlayerHPChangeReason &reason)
649 s16 LuaEntitySAO::getHP() const
654 void LuaEntitySAO::setVelocity(v3f velocity)
656 m_velocity = velocity;
659 v3f LuaEntitySAO::getVelocity()
664 void LuaEntitySAO::setAcceleration(v3f acceleration)
666 m_acceleration = acceleration;
669 v3f LuaEntitySAO::getAcceleration()
671 return m_acceleration;
674 void LuaEntitySAO::setTextureMod(const std::string &mod)
676 std::string str = gob_cmd_set_texture_mod(mod);
677 m_current_texture_modifier = mod;
678 // create message and add to list
679 ActiveObjectMessage aom(getId(), true, str);
680 m_messages_out.push(aom);
683 std::string LuaEntitySAO::getTextureMod() const
685 return m_current_texture_modifier;
688 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
689 bool select_horiz_by_yawpitch)
691 std::string str = gob_cmd_set_sprite(
695 select_horiz_by_yawpitch
697 // create message and add to list
698 ActiveObjectMessage aom(getId(), true, str);
699 m_messages_out.push(aom);
702 std::string LuaEntitySAO::getName()
707 std::string LuaEntitySAO::getPropertyPacket()
709 return gob_cmd_set_properties(m_prop);
712 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
714 // If the object is attached client-side, don't waste bandwidth sending its position to clients
718 m_last_sent_move_precision = m_base_position.getDistanceFrom(
719 m_last_sent_position);
720 m_last_sent_position_timer = 0;
721 m_last_sent_yaw = m_yaw;
722 m_last_sent_position = m_base_position;
723 m_last_sent_velocity = m_velocity;
724 //m_last_sent_acceleration = m_acceleration;
726 float update_interval = m_env->getSendRecommendedInterval();
728 std::string str = gob_cmd_update_position(
737 // create message and add to list
738 ActiveObjectMessage aom(getId(), false, str);
739 m_messages_out.push(aom);
742 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
746 //update collision box
747 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
748 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
750 toset->MinEdge += m_base_position;
751 toset->MaxEdge += m_base_position;
759 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
761 if (!m_prop.is_visible || !m_prop.pointable) {
765 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
766 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
771 bool LuaEntitySAO::collideWithObjects() const
773 return m_prop.collideWithObjects;
780 // No prototype, PlayerSAO does not need to be deserialized
782 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
783 bool is_singleplayer):
784 UnitSAO(env_, v3f(0,0,0)),
787 m_is_singleplayer(is_singleplayer)
789 assert(m_peer_id != 0); // pre-condition
791 m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
792 m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
793 m_prop.physical = false;
795 m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
796 m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
797 m_prop.pointable = true;
798 // Start of default appearance, this should be overwritten by Lua
799 m_prop.visual = "upright_sprite";
800 m_prop.visual_size = v2f(1, 2);
801 m_prop.textures.clear();
802 m_prop.textures.emplace_back("player.png");
803 m_prop.textures.emplace_back("player_back.png");
804 m_prop.colors.clear();
805 m_prop.colors.emplace_back(255, 255, 255, 255);
806 m_prop.spritediv = v2s16(1,1);
807 m_prop.eye_height = 1.625f;
808 // End of default appearance
809 m_prop.is_visible = true;
810 m_prop.backface_culling = false;
811 m_prop.makes_footstep_sound = true;
812 m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
813 m_hp = m_prop.hp_max;
814 m_breath = m_prop.breath_max;
815 // Disable zoom in survival mode using a value of 0
816 m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
819 PlayerSAO::~PlayerSAO()
821 if(m_inventory != &m_player->inventory)
825 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
830 m_inventory = &m_player->inventory;
833 v3f PlayerSAO::getEyeOffset() const
835 return v3f(0, BS * m_prop.eye_height, 0);
838 std::string PlayerSAO::getDescription()
840 return std::string("player ") + m_player->getName();
843 // Called after id has been set and has been inserted in environment
844 void PlayerSAO::addedToEnvironment(u32 dtime_s)
846 ServerActiveObject::addedToEnvironment(dtime_s);
847 ServerActiveObject::setBasePosition(m_base_position);
848 m_player->setPlayerSAO(this);
849 m_player->setPeerId(m_peer_id);
850 m_last_good_position = m_base_position;
853 // Called before removing from environment
854 void PlayerSAO::removingFromEnvironment()
856 ServerActiveObject::removingFromEnvironment();
857 if (m_player->getPlayerSAO() == this) {
858 unlinkPlayerSessionAndSave();
859 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
860 m_env->deleteParticleSpawner(attached_particle_spawner, false);
865 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
867 std::ostringstream os(std::ios::binary);
870 writeU8(os, 1); // version
871 os << serializeString(m_player->getName()); // name
872 writeU8(os, 1); // is_player
873 writeS16(os, getId()); //id
874 writeV3F1000(os, m_base_position);
875 writeF1000(os, m_yaw);
876 writeS16(os, getHP());
878 std::ostringstream msg_os(std::ios::binary);
879 msg_os << serializeLongString(getPropertyPacket()); // message 1
880 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
881 msg_os << serializeLongString(gob_cmd_update_animation(
882 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
883 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
884 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
885 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
886 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
888 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
889 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
890 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
891 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
892 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
893 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
894 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
895 int message_count = 6 + m_bone_position.size();
896 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
897 ii != m_attachment_child_ids.end(); ++ii) {
898 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
900 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
901 obj->getClientInitializationData(protocol_version)));
905 writeU8(os, message_count);
906 os.write(msg_os.str().c_str(), msg_os.str().size());
912 void PlayerSAO::getStaticData(std::string * result) const
914 FATAL_ERROR("Deprecated function");
917 void PlayerSAO::step(float dtime, bool send_recommended)
919 if (m_drowning_interval.step(dtime, 2.0f)) {
920 // Get nose/mouth position, approximate with eye position
921 v3s16 p = floatToInt(getEyePosition(), BS);
922 MapNode n = m_env->getMap().getNodeNoEx(p);
923 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
924 // If node generates drown
925 if (c.drowning > 0 && m_hp > 0) {
927 setBreath(m_breath - 1);
929 // No more breath, damage player
931 PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING);
932 setHP(m_hp - c.drowning, reason);
933 m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
938 if (m_breathing_interval.step(dtime, 0.5f)) {
939 // Get nose/mouth position, approximate with eye position
940 v3s16 p = floatToInt(getEyePosition(), BS);
941 MapNode n = m_env->getMap().getNodeNoEx(p);
942 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
943 // If player is alive & no drowning, breathe
944 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
945 setBreath(m_breath + 1);
948 if (m_node_hurt_interval.step(dtime, 1.0f)) {
949 u32 damage_per_second = 0;
950 // Lowest and highest damage points are 0.1 within collisionbox
951 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
953 // Sequence of damage points, starting 0.1 above feet and progressing
954 // upwards in 1 node intervals, stopping below top damage point.
955 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
956 v3s16 p = floatToInt(m_base_position +
957 v3f(0.0f, dam_height * BS, 0.0f), BS);
958 MapNode n = m_env->getMap().getNodeNoEx(p);
959 damage_per_second = std::max(damage_per_second,
960 m_env->getGameDef()->ndef()->get(n).damage_per_second);
964 v3s16 ptop = floatToInt(m_base_position +
965 v3f(0.0f, dam_top * BS, 0.0f), BS);
966 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
967 damage_per_second = std::max(damage_per_second,
968 m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
970 if (damage_per_second != 0 && m_hp > 0) {
971 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
972 PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE);
973 setHP(newhp, reason);
974 m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
978 if (!m_properties_sent) {
979 m_properties_sent = true;
980 std::string str = getPropertyPacket();
981 // create message and add to list
982 ActiveObjectMessage aom(getId(), true, str);
983 m_messages_out.push(aom);
986 // If attached, check that our parent is still there. If it isn't, detach.
987 if (m_attachment_parent_id && !isAttached()) {
988 m_attachment_parent_id = 0;
989 m_attachment_bone = "";
990 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
991 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
992 setBasePosition(m_last_good_position);
993 m_env->getGameDef()->SendMovePlayer(m_peer_id);
996 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
998 // Set lag pool maximums based on estimated lag
999 const float LAG_POOL_MIN = 5.0f;
1000 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1001 if(lag_pool_max < LAG_POOL_MIN)
1002 lag_pool_max = LAG_POOL_MIN;
1003 m_dig_pool.setMax(lag_pool_max);
1004 m_move_pool.setMax(lag_pool_max);
1006 // Increment cheat prevention timers
1007 m_dig_pool.add(dtime);
1008 m_move_pool.add(dtime);
1009 m_time_from_last_teleport += dtime;
1010 m_time_from_last_punch += dtime;
1011 m_nocheat_dig_time += dtime;
1013 // Each frame, parent position is copied if the object is attached,
1014 // otherwise it's calculated normally.
1015 // If the object gets detached this comes into effect automatically from
1016 // the last known origin.
1018 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1019 m_last_good_position = pos;
1020 setBasePosition(pos);
1023 if (!send_recommended)
1026 // If the object is attached client-side, don't waste bandwidth sending its
1027 // position to clients.
1028 if (m_position_not_sent && !isAttached()) {
1029 m_position_not_sent = false;
1030 float update_interval = m_env->getSendRecommendedInterval();
1032 if (isAttached()) // Just in case we ever do send attachment position too
1033 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1035 pos = m_base_position;
1037 std::string str = gob_cmd_update_position(
1039 v3f(0.0f, 0.0f, 0.0f),
1040 v3f(0.0f, 0.0f, 0.0f),
1046 // create message and add to list
1047 ActiveObjectMessage aom(getId(), false, str);
1048 m_messages_out.push(aom);
1051 if (!m_armor_groups_sent) {
1052 m_armor_groups_sent = true;
1053 std::string str = gob_cmd_update_armor_groups(
1055 // create message and add to list
1056 ActiveObjectMessage aom(getId(), true, str);
1057 m_messages_out.push(aom);
1060 if (!m_physics_override_sent) {
1061 m_physics_override_sent = true;
1062 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1063 m_physics_override_jump, m_physics_override_gravity,
1064 m_physics_override_sneak, m_physics_override_sneak_glitch,
1065 m_physics_override_new_move);
1066 // create message and add to list
1067 ActiveObjectMessage aom(getId(), true, str);
1068 m_messages_out.push(aom);
1071 if (!m_animation_sent) {
1072 m_animation_sent = true;
1073 std::string str = gob_cmd_update_animation(
1074 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1075 // create message and add to list
1076 ActiveObjectMessage aom(getId(), true, str);
1077 m_messages_out.push(aom);
1080 if (!m_bone_position_sent) {
1081 m_bone_position_sent = true;
1082 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1083 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1084 std::string str = gob_cmd_update_bone_position((*ii).first,
1085 (*ii).second.X, (*ii).second.Y);
1086 // create message and add to list
1087 ActiveObjectMessage aom(getId(), true, str);
1088 m_messages_out.push(aom);
1092 if (!m_attachment_sent) {
1093 m_attachment_sent = true;
1094 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1095 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1096 // create message and add to list
1097 ActiveObjectMessage aom(getId(), true, str);
1098 m_messages_out.push(aom);
1102 void PlayerSAO::setBasePosition(const v3f &position)
1104 if (m_player && position != m_base_position)
1105 m_player->setDirty(true);
1107 // This needs to be ran for attachments too
1108 ServerActiveObject::setBasePosition(position);
1109 m_env->updateActiveObject(this);
1110 m_position_not_sent = true;
1113 void PlayerSAO::setPos(const v3f &pos)
1118 setBasePosition(pos);
1119 // Movement caused by this command is always valid
1120 m_last_good_position = pos;
1121 m_move_pool.empty();
1122 m_time_from_last_teleport = 0.0;
1123 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1126 void PlayerSAO::moveTo(v3f pos, bool continuous)
1131 setBasePosition(pos);
1132 // Movement caused by this command is always valid
1133 m_last_good_position = pos;
1134 m_move_pool.empty();
1135 m_time_from_last_teleport = 0.0;
1136 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1139 void PlayerSAO::setYaw(const float yaw)
1141 if (m_player && yaw != m_yaw)
1142 m_player->setDirty(true);
1144 UnitSAO::setYaw(yaw);
1147 void PlayerSAO::setFov(const float fov)
1149 if (m_player && fov != m_fov)
1150 m_player->setDirty(true);
1155 void PlayerSAO::setWantedRange(const s16 range)
1157 if (m_player && range != m_wanted_range)
1158 m_player->setDirty(true);
1160 m_wanted_range = range;
1163 void PlayerSAO::setYawAndSend(const float yaw)
1166 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1169 void PlayerSAO::setPitch(const float pitch)
1171 if (m_player && pitch != m_pitch)
1172 m_player->setDirty(true);
1177 void PlayerSAO::setPitchAndSend(const float pitch)
1180 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1183 int PlayerSAO::punch(v3f dir,
1184 const ToolCapabilities *toolcap,
1185 ServerActiveObject *puncher,
1186 float time_from_last_punch)
1188 // It's best that attachments cannot be punched
1195 // No effect if PvP disabled
1196 if (!g_settings->getBool("enable_pvp")) {
1197 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1198 std::string str = gob_cmd_punched(0, getHP());
1199 // create message and add to list
1200 ActiveObjectMessage aom(getId(), true, str);
1201 m_messages_out.push(aom);
1206 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1207 time_from_last_punch);
1209 std::string punchername = "nil";
1212 punchername = puncher->getDescription();
1214 PlayerSAO *playersao = m_player->getPlayerSAO();
1216 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1217 puncher, time_from_last_punch, toolcap, dir,
1220 if (!damage_handled) {
1221 setHP(getHP() - hitparams.hp,
1222 PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
1223 } else { // override client prediction
1224 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1225 std::string str = gob_cmd_punched(0, getHP());
1226 // create message and add to list
1227 ActiveObjectMessage aom(getId(), true, str);
1228 m_messages_out.push(aom);
1233 actionstream << "Player " << m_player->getName() << " punched by "
1235 if (!damage_handled) {
1236 actionstream << ", damage " << hitparams.hp << " HP";
1238 actionstream << ", damage handled by lua";
1240 actionstream << std::endl;
1242 return hitparams.wear;
1245 s16 PlayerSAO::readDamage()
1247 s16 damage = m_damage;
1252 void PlayerSAO::setHP(s16 hp, const PlayerHPChangeReason &reason)
1256 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason);
1259 hp = oldhp + hp_change;
1263 else if (hp > m_prop.hp_max)
1266 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1273 m_damage += (oldhp - hp);
1275 // Update properties on death
1276 if ((hp == 0) != (oldhp == 0))
1277 m_properties_sent = false;
1280 void PlayerSAO::setBreath(const u16 breath, bool send)
1282 if (m_player && breath != m_breath)
1283 m_player->setDirty(true);
1285 m_breath = MYMIN(breath, m_prop.breath_max);
1288 m_env->getGameDef()->SendPlayerBreath(this);
1291 Inventory* PlayerSAO::getInventory()
1295 const Inventory* PlayerSAO::getInventory() const
1300 InventoryLocation PlayerSAO::getInventoryLocation() const
1302 InventoryLocation loc;
1303 loc.setPlayer(m_player->getName());
1307 std::string PlayerSAO::getWieldList() const
1312 ItemStack PlayerSAO::getWieldedItem() const
1314 const Inventory *inv = getInventory();
1316 const InventoryList *mlist = inv->getList(getWieldList());
1317 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1318 ret = mlist->getItem(getWieldIndex());
1322 ItemStack PlayerSAO::getWieldedItemOrHand() const
1324 const Inventory *inv = getInventory();
1326 const InventoryList *mlist = inv->getList(getWieldList());
1327 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1328 ret = mlist->getItem(getWieldIndex());
1329 if (ret.name.empty()) {
1330 const InventoryList *hlist = inv->getList("hand");
1332 ret = hlist->getItem(0);
1337 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1339 Inventory *inv = getInventory();
1341 InventoryList *mlist = inv->getList(getWieldList());
1343 mlist->changeItem(getWieldIndex(), item);
1350 int PlayerSAO::getWieldIndex() const
1352 return m_wield_index;
1355 void PlayerSAO::setWieldIndex(int i)
1357 if(i != m_wield_index) {
1362 void PlayerSAO::disconnected()
1365 m_pending_removal = true;
1368 void PlayerSAO::unlinkPlayerSessionAndSave()
1370 assert(m_player->getPlayerSAO() == this);
1371 m_player->setPeerId(PEER_ID_INEXISTENT);
1372 m_env->savePlayer(m_player);
1373 m_player->setPlayerSAO(NULL);
1374 m_env->removePlayer(m_player);
1377 std::string PlayerSAO::getPropertyPacket()
1379 m_prop.is_visible = (true);
1380 return gob_cmd_set_properties(m_prop);
1383 bool PlayerSAO::checkMovementCheat()
1385 if (isAttached() || m_is_singleplayer ||
1386 g_settings->getBool("disable_anticheat")) {
1387 m_last_good_position = m_base_position;
1391 bool cheated = false;
1393 Check player movements
1395 NOTE: Actually the server should handle player physics like the
1396 client does and compare player's position to what is calculated
1397 on our side. This is required when eg. players fly due to an
1398 explosion. Altough a node-based alternative might be possible
1399 too, and much more lightweight.
1402 float player_max_walk = 0; // horizontal movement
1403 float player_max_jump = 0; // vertical upwards movement
1405 if (m_privs.count("fast") != 0)
1406 player_max_walk = m_player->movement_speed_fast; // Fast speed
1408 player_max_walk = m_player->movement_speed_walk; // Normal speed
1409 player_max_walk *= m_physics_override_speed;
1410 player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
1411 // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
1412 // until this can be verified correctly, tolerate higher jumping speeds
1413 player_max_jump *= 2.0;
1415 // Don't divide by zero!
1416 if (player_max_walk < 0.0001f)
1417 player_max_walk = 0.0001f;
1418 if (player_max_jump < 0.0001f)
1419 player_max_jump = 0.0001f;
1421 v3f diff = (m_base_position - m_last_good_position);
1422 float d_vert = diff.Y;
1424 float d_horiz = diff.getLength();
1425 float required_time = d_horiz / player_max_walk;
1427 // FIXME: Checking downwards movement is not easily possible currently,
1428 // the server could calculate speed differences to examine the gravity
1430 // In certain cases (water, ladders) walking speed is applied vertically
1431 float s = MYMAX(player_max_jump, player_max_walk);
1432 required_time = MYMAX(required_time, d_vert / s);
1435 if (m_move_pool.grab(required_time)) {
1436 m_last_good_position = m_base_position;
1438 const float LAG_POOL_MIN = 5.0;
1439 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1440 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1441 if (m_time_from_last_teleport > lag_pool_max) {
1442 actionstream << "Player " << m_player->getName()
1443 << " moved too fast; resetting position"
1447 setBasePosition(m_last_good_position);
1452 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1454 //update collision box
1455 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1456 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1458 toset->MinEdge += m_base_position;
1459 toset->MaxEdge += m_base_position;
1463 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1465 if (!m_prop.is_visible || !m_prop.pointable) {
1469 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1470 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;