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"
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)
62 m_pending_removal = true;
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)
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; }
95 virtual bool getSelectionBox(aabb3f *toset) const { return false; }
97 bool collideWithObjects() const { return false; }
104 // Prototype (registers item for deserialization)
105 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
111 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
112 ServerActiveObject(env, pos)
114 // Initialize something to armor groups
115 m_armor_groups["fleshy"] = 100;
118 bool UnitSAO::isAttached() const
120 if (!m_attachment_parent_id)
122 // Check if the parent still exists
123 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
129 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
131 m_armor_groups = armor_groups;
132 m_armor_groups_sent = false;
135 const ItemGroupList &UnitSAO::getArmorGroups()
137 return m_armor_groups;
140 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
142 // store these so they can be updated to clients
143 m_animation_range = frame_range;
144 m_animation_speed = frame_speed;
145 m_animation_blend = frame_blend;
146 m_animation_loop = frame_loop;
147 m_animation_sent = false;
150 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
152 *frame_range = m_animation_range;
153 *frame_speed = m_animation_speed;
154 *frame_blend = m_animation_blend;
155 *frame_loop = m_animation_loop;
158 void UnitSAO::setAnimationSpeed(float frame_speed)
160 m_animation_speed = frame_speed;
161 m_animation_speed_sent = false;
164 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
166 // store these so they can be updated to clients
167 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
168 m_bone_position_sent = false;
171 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
173 *position = m_bone_position[bone].X;
174 *rotation = m_bone_position[bone].Y;
177 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
179 // Attachments need to be handled on both the server and client.
180 // If we just attach on the server, we can only copy the position of the parent. Attachments
181 // are still sent to clients at an interval so players might see them lagging, plus we can't
182 // read and attach to skeletal bones.
183 // If we just attach on the client, the server still sees the child at its original location.
184 // This breaks some things so we also give the server the most accurate representation
185 // even if players only see the client changes.
187 m_attachment_parent_id = parent_id;
188 m_attachment_bone = bone;
189 m_attachment_position = position;
190 m_attachment_rotation = rotation;
191 m_attachment_sent = false;
194 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
197 *parent_id = m_attachment_parent_id;
198 *bone = m_attachment_bone;
199 *position = m_attachment_position;
200 *rotation = m_attachment_rotation;
203 void UnitSAO::addAttachmentChild(int child_id)
205 m_attachment_child_ids.insert(child_id);
208 void UnitSAO::removeAttachmentChild(int child_id)
210 m_attachment_child_ids.erase(child_id);
213 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
215 return m_attachment_child_ids;
218 ObjectProperties* UnitSAO::accessObjectProperties()
223 void UnitSAO::notifyObjectPropertiesModified()
225 m_properties_sent = false;
232 // Prototype (registers item for deserialization)
233 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
235 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
236 const std::string &name, const std::string &state):
241 // Only register type if no environment supplied
243 ServerActiveObject::registerType(getType(), create);
248 LuaEntitySAO::~LuaEntitySAO()
251 m_env->getScriptIface()->luaentity_Remove(m_id);
254 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
255 m_env->deleteParticleSpawner(attached_particle_spawner, false);
259 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
261 ServerActiveObject::addedToEnvironment(dtime_s);
263 // Create entity from name
264 m_registered = m_env->getScriptIface()->
265 luaentity_Add(m_id, m_init_name.c_str());
269 m_env->getScriptIface()->
270 luaentity_GetProperties(m_id, &m_prop);
271 // Initialize HP from properties
272 m_hp = m_prop.hp_max;
273 // Activate entity, supplying serialized state
274 m_env->getScriptIface()->
275 luaentity_Activate(m_id, m_init_state, dtime_s);
277 m_prop.infotext = m_init_name;
281 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
282 const std::string &data)
290 std::istringstream is(data, std::ios::binary);
292 u8 version = readU8(is);
293 // check if version is supported
295 name = deSerializeString(is);
296 state = deSerializeLongString(is);
298 else if(version == 1){
299 name = deSerializeString(is);
300 state = deSerializeLongString(is);
302 velocity = readV3F1000(is);
307 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
308 <<state<<"\")"<<std::endl;
309 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
311 sao->m_velocity = velocity;
316 void LuaEntitySAO::step(float dtime, bool send_recommended)
318 if(!m_properties_sent)
320 m_properties_sent = true;
321 std::string str = getPropertyPacket();
322 // create message and add to list
323 ActiveObjectMessage aom(getId(), true, str);
324 m_messages_out.push(aom);
327 // If attached, check that our parent is still there. If it isn't, detach.
328 if(m_attachment_parent_id && !isAttached())
330 m_attachment_parent_id = 0;
331 m_attachment_bone = "";
332 m_attachment_position = v3f(0,0,0);
333 m_attachment_rotation = v3f(0,0,0);
334 sendPosition(false, true);
337 m_last_sent_position_timer += dtime;
339 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
340 // If the object gets detached this comes into effect automatically from the last known origin
343 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
344 m_base_position = pos;
345 m_velocity = v3f(0,0,0);
346 m_acceleration = v3f(0,0,0);
351 aabb3f box = m_prop.collisionbox;
354 collisionMoveResult moveresult;
355 f32 pos_max_d = BS*0.25; // Distance per iteration
356 v3f p_pos = m_base_position;
357 v3f p_velocity = m_velocity;
358 v3f p_acceleration = m_acceleration;
359 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
360 pos_max_d, box, m_prop.stepheight, dtime,
361 &p_pos, &p_velocity, p_acceleration,
362 this, m_prop.collideWithObjects);
365 m_base_position = p_pos;
366 m_velocity = p_velocity;
367 m_acceleration = p_acceleration;
369 m_base_position += dtime * m_velocity + 0.5 * dtime
370 * dtime * m_acceleration;
371 m_velocity += dtime * m_acceleration;
374 if((m_prop.automatic_face_movement_dir) &&
375 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
377 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
378 + m_prop.automatic_face_movement_dir_offset;
379 float max_rotation_delta =
380 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
382 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
383 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
385 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
393 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
396 // Remove LuaEntity beyond terrain edges
398 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
400 if (!m_pending_removal &&
401 map->saoPositionOverLimit(m_base_position)) {
402 infostream << "Removing SAO " << m_id << "(" << m_init_name
403 << "), outside of limits" << std::endl;
404 m_pending_removal = true;
409 if (!send_recommended)
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) {
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) {
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_animation_speed_sent) {
449 m_animation_speed_sent = true;
450 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
451 // create message and add to list
452 ActiveObjectMessage aom(getId(), true, str);
453 m_messages_out.push(aom);
456 if (!m_bone_position_sent) {
457 m_bone_position_sent = true;
458 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
459 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
460 std::string str = gob_cmd_update_bone_position((*ii).first,
461 (*ii).second.X, (*ii).second.Y);
462 // create message and add to list
463 ActiveObjectMessage aom(getId(), true, str);
464 m_messages_out.push(aom);
468 if (!m_attachment_sent) {
469 m_attachment_sent = true;
470 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
471 // create message and add to list
472 ActiveObjectMessage aom(getId(), true, str);
473 m_messages_out.push(aom);
477 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
479 std::ostringstream os(std::ios::binary);
482 writeU8(os, 1); // version
483 os << serializeString(""); // name
484 writeU8(os, 0); // is_player
485 writeS16(os, getId()); //id
486 writeV3F1000(os, m_base_position);
487 writeF1000(os, m_yaw);
490 std::ostringstream msg_os(std::ios::binary);
491 msg_os << serializeLongString(getPropertyPacket()); // message 1
492 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
493 msg_os << serializeLongString(gob_cmd_update_animation(
494 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
495 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
496 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
497 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
498 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
500 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
501 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
502 int message_count = 4 + m_bone_position.size();
503 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
504 (ii != m_attachment_child_ids.end()); ++ii) {
505 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
507 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
508 obj->getClientInitializationData(protocol_version)));
512 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
515 writeU8(os, message_count);
516 os.write(msg_os.str().c_str(), msg_os.str().size());
522 void LuaEntitySAO::getStaticData(std::string *result) const
524 verbosestream<<FUNCTION_NAME<<std::endl;
525 std::ostringstream os(std::ios::binary);
529 os<<serializeString(m_init_name);
532 std::string state = m_env->getScriptIface()->
533 luaentity_GetStaticdata(m_id);
534 os<<serializeLongString(state);
536 os<<serializeLongString(m_init_state);
541 writeV3F1000(os, m_velocity);
543 writeF1000(os, m_yaw);
547 int LuaEntitySAO::punch(v3f dir,
548 const ToolCapabilities *toolcap,
549 ServerActiveObject *puncher,
550 float time_from_last_punch)
553 // Delete unknown LuaEntities when punched
554 m_pending_removal = true;
558 // It's best that attachments cannot be punched
562 ItemStack *punchitem = NULL;
563 ItemStack punchitem_static;
565 punchitem_static = puncher->getWieldedItem();
566 punchitem = &punchitem_static;
569 PunchDamageResult result = getPunchDamage(
573 time_from_last_punch);
575 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
576 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
578 if (!damage_handled) {
579 if (result.did_punch) {
580 setHP(getHP() - result.damage);
582 if (result.damage > 0) {
583 std::string punchername = puncher ? puncher->getDescription() : "nil";
585 actionstream << getDescription() << " punched by "
586 << punchername << ", damage " << result.damage
587 << " hp, health now " << getHP() << " hp" << std::endl;
590 std::string str = gob_cmd_punched(result.damage, getHP());
591 // create message and add to list
592 ActiveObjectMessage aom(getId(), true, str);
593 m_messages_out.push(aom);
598 m_pending_removal = true;
599 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
605 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
609 // It's best that attachments cannot be clicked
612 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
615 void LuaEntitySAO::setPos(const v3f &pos)
619 m_base_position = pos;
620 sendPosition(false, true);
623 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
627 m_base_position = pos;
629 sendPosition(true, true);
632 float LuaEntitySAO::getMinimumSavedMovement()
637 std::string LuaEntitySAO::getDescription()
639 std::ostringstream os(std::ios::binary);
640 os<<"LuaEntitySAO at (";
641 os<<(m_base_position.X/BS)<<",";
642 os<<(m_base_position.Y/BS)<<",";
643 os<<(m_base_position.Z/BS);
648 void LuaEntitySAO::setHP(s16 hp)
654 s16 LuaEntitySAO::getHP() const
659 void LuaEntitySAO::setVelocity(v3f velocity)
661 m_velocity = velocity;
664 v3f LuaEntitySAO::getVelocity()
669 void LuaEntitySAO::setAcceleration(v3f acceleration)
671 m_acceleration = acceleration;
674 v3f LuaEntitySAO::getAcceleration()
676 return m_acceleration;
679 void LuaEntitySAO::setTextureMod(const std::string &mod)
681 std::string str = gob_cmd_set_texture_mod(mod);
682 m_current_texture_modifier = mod;
683 // create message and add to list
684 ActiveObjectMessage aom(getId(), true, str);
685 m_messages_out.push(aom);
688 std::string LuaEntitySAO::getTextureMod() const
690 return m_current_texture_modifier;
693 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
694 bool select_horiz_by_yawpitch)
696 std::string str = gob_cmd_set_sprite(
700 select_horiz_by_yawpitch
702 // create message and add to list
703 ActiveObjectMessage aom(getId(), true, str);
704 m_messages_out.push(aom);
707 std::string LuaEntitySAO::getName()
712 std::string LuaEntitySAO::getPropertyPacket()
714 return gob_cmd_set_properties(m_prop);
717 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
719 // If the object is attached client-side, don't waste bandwidth sending its position to clients
723 m_last_sent_move_precision = m_base_position.getDistanceFrom(
724 m_last_sent_position);
725 m_last_sent_position_timer = 0;
726 m_last_sent_yaw = m_yaw;
727 m_last_sent_position = m_base_position;
728 m_last_sent_velocity = m_velocity;
729 //m_last_sent_acceleration = m_acceleration;
731 float update_interval = m_env->getSendRecommendedInterval();
733 std::string str = gob_cmd_update_position(
742 // create message and add to list
743 ActiveObjectMessage aom(getId(), false, str);
744 m_messages_out.push(aom);
747 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
751 //update collision box
752 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
753 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
755 toset->MinEdge += m_base_position;
756 toset->MaxEdge += m_base_position;
764 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
766 if (!m_prop.is_visible || !m_prop.pointable) {
770 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
771 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
776 bool LuaEntitySAO::collideWithObjects() const
778 return m_prop.collideWithObjects;
785 // No prototype, PlayerSAO does not need to be deserialized
787 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
788 bool is_singleplayer):
789 UnitSAO(env_, v3f(0,0,0)),
792 m_is_singleplayer(is_singleplayer)
794 assert(m_peer_id != 0); // pre-condition
796 m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
797 m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
798 m_prop.physical = false;
800 m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
801 m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
802 m_prop.pointable = true;
803 // start of default appearance, this should be overwritten by LUA
804 m_prop.visual = "upright_sprite";
805 m_prop.visual_size = v2f(1, 2);
806 m_prop.textures.clear();
807 m_prop.textures.emplace_back("player.png");
808 m_prop.textures.emplace_back("player_back.png");
809 m_prop.colors.clear();
810 m_prop.colors.emplace_back(255, 255, 255, 255);
811 m_prop.spritediv = v2s16(1,1);
812 // end of default appearance
813 m_prop.is_visible = true;
814 m_prop.makes_footstep_sound = true;
815 m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
816 m_prop.can_zoom = true;
817 m_hp = m_prop.hp_max;
818 m_breath = m_prop.breath_max;
821 PlayerSAO::~PlayerSAO()
823 if(m_inventory != &m_player->inventory)
827 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
832 m_inventory = &m_player->inventory;
835 v3f PlayerSAO::getEyeOffset() const
837 return v3f(0, BS * 1.625f, 0);
840 std::string PlayerSAO::getDescription()
842 return std::string("player ") + m_player->getName();
845 // Called after id has been set and has been inserted in environment
846 void PlayerSAO::addedToEnvironment(u32 dtime_s)
848 ServerActiveObject::addedToEnvironment(dtime_s);
849 ServerActiveObject::setBasePosition(m_base_position);
850 m_player->setPlayerSAO(this);
851 m_player->peer_id = m_peer_id;
852 m_last_good_position = m_base_position;
855 // Called before removing from environment
856 void PlayerSAO::removingFromEnvironment()
858 ServerActiveObject::removingFromEnvironment();
859 if (m_player->getPlayerSAO() == this) {
860 unlinkPlayerSessionAndSave();
861 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
862 m_env->deleteParticleSpawner(attached_particle_spawner, false);
867 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
869 std::ostringstream os(std::ios::binary);
872 writeU8(os, 1); // version
873 os << serializeString(m_player->getName()); // name
874 writeU8(os, 1); // is_player
875 writeS16(os, getId()); //id
876 writeV3F1000(os, m_base_position);
877 writeF1000(os, m_yaw);
878 writeS16(os, getHP());
880 std::ostringstream msg_os(std::ios::binary);
881 msg_os << serializeLongString(getPropertyPacket()); // message 1
882 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
883 msg_os << serializeLongString(gob_cmd_update_animation(
884 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
885 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
886 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
887 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
888 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
890 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
891 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
892 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
893 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
894 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
895 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
896 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
897 int message_count = 6 + m_bone_position.size();
898 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
899 ii != m_attachment_child_ids.end(); ++ii) {
900 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
902 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
903 obj->getClientInitializationData(protocol_version)));
907 writeU8(os, message_count);
908 os.write(msg_os.str().c_str(), msg_os.str().size());
914 void PlayerSAO::getStaticData(std::string * result) const
916 FATAL_ERROR("Deprecated function");
919 void PlayerSAO::step(float dtime, bool send_recommended)
921 if (m_drowning_interval.step(dtime, 2.0)) {
923 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
924 MapNode n = m_env->getMap().getNodeNoEx(p);
925 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
926 // If node generates drown
927 if (c.drowning > 0 && m_hp > 0) {
929 setBreath(m_breath - 1);
931 // No more breath, damage player
933 setHP(m_hp - c.drowning);
934 m_env->getGameDef()->SendPlayerHPOrDie(this);
939 if (m_breathing_interval.step(dtime, 0.5)) {
941 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
942 MapNode n = m_env->getMap().getNodeNoEx(p);
943 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
944 // If player is alive & no drowning, breath
945 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
946 setBreath(m_breath + 1);
949 if (m_node_hurt_interval.step(dtime, 1.0)) {
950 // Feet, middle and head
951 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
952 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
953 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
954 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
955 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
956 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
958 u32 damage_per_second = 0;
959 damage_per_second = MYMAX(damage_per_second,
960 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
961 damage_per_second = MYMAX(damage_per_second,
962 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
963 damage_per_second = MYMAX(damage_per_second,
964 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
966 if (damage_per_second != 0 && m_hp > 0) {
967 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
969 m_env->getGameDef()->SendPlayerHPOrDie(this);
973 if (!m_properties_sent) {
974 m_properties_sent = true;
975 std::string str = getPropertyPacket();
976 // create message and add to list
977 ActiveObjectMessage aom(getId(), true, str);
978 m_messages_out.push(aom);
981 // If attached, check that our parent is still there. If it isn't, detach.
982 if(m_attachment_parent_id && !isAttached())
984 m_attachment_parent_id = 0;
985 m_attachment_bone = "";
986 m_attachment_position = v3f(0,0,0);
987 m_attachment_rotation = v3f(0,0,0);
988 setBasePosition(m_last_good_position);
989 m_env->getGameDef()->SendMovePlayer(m_peer_id);
992 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
994 // Set lag pool maximums based on estimated lag
995 const float LAG_POOL_MIN = 5.0;
996 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
997 if(lag_pool_max < LAG_POOL_MIN)
998 lag_pool_max = LAG_POOL_MIN;
999 m_dig_pool.setMax(lag_pool_max);
1000 m_move_pool.setMax(lag_pool_max);
1002 // Increment cheat prevention timers
1003 m_dig_pool.add(dtime);
1004 m_move_pool.add(dtime);
1005 m_time_from_last_teleport += dtime;
1006 m_time_from_last_punch += dtime;
1007 m_nocheat_dig_time += dtime;
1009 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1010 // If the object gets detached this comes into effect automatically from the last known origin
1012 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1013 m_last_good_position = pos;
1014 setBasePosition(pos);
1017 if (!send_recommended)
1020 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1021 if(m_position_not_sent && !isAttached())
1023 m_position_not_sent = false;
1024 float update_interval = m_env->getSendRecommendedInterval();
1026 if(isAttached()) // Just in case we ever do send attachment position too
1027 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1029 pos = m_base_position;
1030 std::string str = gob_cmd_update_position(
1039 // create message and add to list
1040 ActiveObjectMessage aom(getId(), false, str);
1041 m_messages_out.push(aom);
1044 if (!m_armor_groups_sent) {
1045 m_armor_groups_sent = true;
1046 std::string str = gob_cmd_update_armor_groups(
1048 // create message and add to list
1049 ActiveObjectMessage aom(getId(), true, str);
1050 m_messages_out.push(aom);
1053 if (!m_physics_override_sent) {
1054 m_physics_override_sent = true;
1055 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1056 m_physics_override_jump, m_physics_override_gravity,
1057 m_physics_override_sneak, m_physics_override_sneak_glitch,
1058 m_physics_override_new_move);
1059 // create message and add to list
1060 ActiveObjectMessage aom(getId(), true, str);
1061 m_messages_out.push(aom);
1064 if (!m_animation_sent) {
1065 m_animation_sent = true;
1066 std::string str = gob_cmd_update_animation(
1067 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1068 // create message and add to list
1069 ActiveObjectMessage aom(getId(), true, str);
1070 m_messages_out.push(aom);
1073 if (!m_bone_position_sent) {
1074 m_bone_position_sent = true;
1075 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1076 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1077 std::string str = gob_cmd_update_bone_position((*ii).first,
1078 (*ii).second.X, (*ii).second.Y);
1079 // create message and add to list
1080 ActiveObjectMessage aom(getId(), true, str);
1081 m_messages_out.push(aom);
1085 if (!m_attachment_sent){
1086 m_attachment_sent = true;
1087 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1088 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1089 // create message and add to list
1090 ActiveObjectMessage aom(getId(), true, str);
1091 m_messages_out.push(aom);
1095 void PlayerSAO::setBasePosition(const v3f &position)
1097 if (m_player && position != m_base_position)
1098 m_player->setDirty(true);
1100 // This needs to be ran for attachments too
1101 ServerActiveObject::setBasePosition(position);
1102 m_position_not_sent = true;
1105 void PlayerSAO::setPos(const v3f &pos)
1110 setBasePosition(pos);
1111 // Movement caused by this command is always valid
1112 m_last_good_position = pos;
1113 m_move_pool.empty();
1114 m_time_from_last_teleport = 0.0;
1115 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1118 void PlayerSAO::moveTo(v3f pos, bool continuous)
1123 setBasePosition(pos);
1124 // Movement caused by this command is always valid
1125 m_last_good_position = pos;
1126 m_move_pool.empty();
1127 m_time_from_last_teleport = 0.0;
1128 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1131 void PlayerSAO::setYaw(const float yaw)
1133 if (m_player && yaw != m_yaw)
1134 m_player->setDirty(true);
1136 UnitSAO::setYaw(yaw);
1139 void PlayerSAO::setFov(const float fov)
1141 if (m_player && fov != m_fov)
1142 m_player->setDirty(true);
1147 void PlayerSAO::setWantedRange(const s16 range)
1149 if (m_player && range != m_wanted_range)
1150 m_player->setDirty(true);
1152 m_wanted_range = range;
1155 void PlayerSAO::setYawAndSend(const float yaw)
1158 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1161 void PlayerSAO::setPitch(const float pitch)
1163 if (m_player && pitch != m_pitch)
1164 m_player->setDirty(true);
1169 void PlayerSAO::setPitchAndSend(const float pitch)
1172 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1175 int PlayerSAO::punch(v3f dir,
1176 const ToolCapabilities *toolcap,
1177 ServerActiveObject *puncher,
1178 float time_from_last_punch)
1180 // It's best that attachments cannot be punched
1187 // No effect if PvP disabled
1188 if (!g_settings->getBool("enable_pvp")) {
1189 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1190 std::string str = gob_cmd_punched(0, getHP());
1191 // create message and add to list
1192 ActiveObjectMessage aom(getId(), true, str);
1193 m_messages_out.push(aom);
1198 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1199 time_from_last_punch);
1201 std::string punchername = "nil";
1204 punchername = puncher->getDescription();
1206 PlayerSAO *playersao = m_player->getPlayerSAO();
1208 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1209 puncher, time_from_last_punch, toolcap, dir,
1212 if (!damage_handled) {
1213 setHP(getHP() - hitparams.hp);
1214 } else { // override client prediction
1215 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1216 std::string str = gob_cmd_punched(0, getHP());
1217 // create message and add to list
1218 ActiveObjectMessage aom(getId(), true, str);
1219 m_messages_out.push(aom);
1224 actionstream << "Player " << m_player->getName() << " punched by "
1226 if (!damage_handled) {
1227 actionstream << ", damage " << hitparams.hp << " HP";
1229 actionstream << ", damage handled by lua";
1231 actionstream << std::endl;
1233 return hitparams.wear;
1236 s16 PlayerSAO::readDamage()
1238 s16 damage = m_damage;
1243 void PlayerSAO::setHP(s16 hp)
1247 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1250 hp = oldhp + hp_change;
1254 else if (hp > m_prop.hp_max)
1257 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1264 m_damage += (oldhp - hp);
1266 // Update properties on death
1267 if ((hp == 0) != (oldhp == 0))
1268 m_properties_sent = false;
1271 void PlayerSAO::setBreath(const u16 breath, bool send)
1273 if (m_player && breath != m_breath)
1274 m_player->setDirty(true);
1276 m_breath = MYMIN(breath, m_prop.breath_max);
1279 m_env->getGameDef()->SendPlayerBreath(this);
1282 Inventory* PlayerSAO::getInventory()
1286 const Inventory* PlayerSAO::getInventory() const
1291 InventoryLocation PlayerSAO::getInventoryLocation() const
1293 InventoryLocation loc;
1294 loc.setPlayer(m_player->getName());
1298 std::string PlayerSAO::getWieldList() const
1303 ItemStack PlayerSAO::getWieldedItem() const
1305 const Inventory *inv = getInventory();
1307 const InventoryList *mlist = inv->getList(getWieldList());
1308 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1309 ret = mlist->getItem(getWieldIndex());
1313 ItemStack PlayerSAO::getWieldedItemOrHand() const
1315 const Inventory *inv = getInventory();
1317 const InventoryList *mlist = inv->getList(getWieldList());
1318 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1319 ret = mlist->getItem(getWieldIndex());
1320 if (ret.name.empty()) {
1321 const InventoryList *hlist = inv->getList("hand");
1323 ret = hlist->getItem(0);
1328 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1330 Inventory *inv = getInventory();
1332 InventoryList *mlist = inv->getList(getWieldList());
1334 mlist->changeItem(getWieldIndex(), item);
1341 int PlayerSAO::getWieldIndex() const
1343 return m_wield_index;
1346 void PlayerSAO::setWieldIndex(int i)
1348 if(i != m_wield_index) {
1353 void PlayerSAO::disconnected()
1356 m_pending_removal = true;
1359 void PlayerSAO::unlinkPlayerSessionAndSave()
1361 assert(m_player->getPlayerSAO() == this);
1362 m_player->peer_id = 0;
1363 m_env->savePlayer(m_player);
1364 m_player->setPlayerSAO(NULL);
1365 m_env->removePlayer(m_player);
1368 std::string PlayerSAO::getPropertyPacket()
1370 m_prop.is_visible = (true);
1371 return gob_cmd_set_properties(m_prop);
1374 bool PlayerSAO::checkMovementCheat()
1376 if (isAttached() || m_is_singleplayer ||
1377 g_settings->getBool("disable_anticheat")) {
1378 m_last_good_position = m_base_position;
1382 bool cheated = false;
1384 Check player movements
1386 NOTE: Actually the server should handle player physics like the
1387 client does and compare player's position to what is calculated
1388 on our side. This is required when eg. players fly due to an
1389 explosion. Altough a node-based alternative might be possible
1390 too, and much more lightweight.
1393 float player_max_speed = 0;
1395 if (m_privs.count("fast") != 0) {
1397 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1400 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1402 // Tolerance. The lag pool does this a bit.
1403 //player_max_speed *= 2.5;
1405 v3f diff = (m_base_position - m_last_good_position);
1406 float d_vert = diff.Y;
1408 float d_horiz = diff.getLength();
1409 float required_time = d_horiz / player_max_speed;
1411 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1412 required_time = d_vert / player_max_speed; // Moving upwards
1414 if (m_move_pool.grab(required_time)) {
1415 m_last_good_position = m_base_position;
1417 const float LAG_POOL_MIN = 5.0;
1418 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1419 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1420 if (m_time_from_last_teleport > lag_pool_max) {
1421 actionstream << "Player " << m_player->getName()
1422 << " moved too fast; resetting position"
1426 setBasePosition(m_last_good_position);
1431 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1433 //update collision box
1434 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1435 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1437 toset->MinEdge += m_base_position;
1438 toset->MaxEdge += m_base_position;
1442 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1444 if (!m_prop.is_visible || !m_prop.pointable) {
1448 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1449 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;