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)
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_deactivation &&
401 map->saoPositionOverLimit(m_base_position)) {
402 infostream << "Remove SAO " << m_id << "(" << m_init_name
403 << "), outside of limits" << std::endl;
404 m_pending_deactivation = true;
410 if (!send_recommended)
415 // TODO: force send when acceleration changes enough?
416 float minchange = 0.2*BS;
417 if(m_last_sent_position_timer > 1.0){
419 } else if(m_last_sent_position_timer > 0.2){
422 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
423 move_d += m_last_sent_move_precision;
424 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
425 if(move_d > minchange || vel_d > minchange ||
426 fabs(m_yaw - m_last_sent_yaw) > 1.0){
427 sendPosition(true, false);
431 if (!m_armor_groups_sent) {
432 m_armor_groups_sent = true;
433 std::string str = gob_cmd_update_armor_groups(
435 // create message and add to list
436 ActiveObjectMessage aom(getId(), true, str);
437 m_messages_out.push(aom);
440 if (!m_animation_sent) {
441 m_animation_sent = true;
442 std::string str = gob_cmd_update_animation(
443 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
444 // create message and add to list
445 ActiveObjectMessage aom(getId(), true, str);
446 m_messages_out.push(aom);
449 if (!m_animation_speed_sent) {
450 m_animation_speed_sent = true;
451 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
452 // create message and add to list
453 ActiveObjectMessage aom(getId(), true, str);
454 m_messages_out.push(aom);
457 if (!m_bone_position_sent) {
458 m_bone_position_sent = true;
459 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
460 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
461 std::string str = gob_cmd_update_bone_position((*ii).first,
462 (*ii).second.X, (*ii).second.Y);
463 // create message and add to list
464 ActiveObjectMessage aom(getId(), true, str);
465 m_messages_out.push(aom);
469 if (!m_attachment_sent) {
470 m_attachment_sent = true;
471 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
472 // create message and add to list
473 ActiveObjectMessage aom(getId(), true, str);
474 m_messages_out.push(aom);
478 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
480 std::ostringstream os(std::ios::binary);
483 writeU8(os, 1); // version
484 os << serializeString(""); // name
485 writeU8(os, 0); // is_player
486 writeS16(os, getId()); //id
487 writeV3F1000(os, m_base_position);
488 writeF1000(os, m_yaw);
491 std::ostringstream msg_os(std::ios::binary);
492 msg_os << serializeLongString(getPropertyPacket()); // message 1
493 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
494 msg_os << serializeLongString(gob_cmd_update_animation(
495 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
496 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
497 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
498 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
499 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
501 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
502 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
503 int message_count = 4 + m_bone_position.size();
504 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
505 (ii != m_attachment_child_ids.end()); ++ii) {
506 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
508 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
509 obj->getClientInitializationData(protocol_version)));
513 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
516 writeU8(os, message_count);
517 os.write(msg_os.str().c_str(), msg_os.str().size());
523 void LuaEntitySAO::getStaticData(std::string *result) const
525 verbosestream<<FUNCTION_NAME<<std::endl;
526 std::ostringstream os(std::ios::binary);
530 os<<serializeString(m_init_name);
533 std::string state = m_env->getScriptIface()->
534 luaentity_GetStaticdata(m_id);
535 os<<serializeLongString(state);
537 os<<serializeLongString(m_init_state);
542 writeV3F1000(os, m_velocity);
544 writeF1000(os, m_yaw);
548 int LuaEntitySAO::punch(v3f dir,
549 const ToolCapabilities *toolcap,
550 ServerActiveObject *puncher,
551 float time_from_last_punch)
554 // Delete unknown LuaEntities when punched
559 // It's best that attachments cannot be punched
563 ItemStack *punchitem = NULL;
564 ItemStack punchitem_static;
566 punchitem_static = puncher->getWieldedItem();
567 punchitem = &punchitem_static;
570 PunchDamageResult result = getPunchDamage(
574 time_from_last_punch);
576 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
577 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
579 if (!damage_handled) {
580 if (result.did_punch) {
581 setHP(getHP() - result.damage);
583 if (result.damage > 0) {
584 std::string punchername = puncher ? puncher->getDescription() : "nil";
586 actionstream << getDescription() << " punched by "
587 << punchername << ", damage " << result.damage
588 << " hp, health now " << getHP() << " hp" << std::endl;
591 std::string str = gob_cmd_punched(result.damage, getHP());
592 // create message and add to list
593 ActiveObjectMessage aom(getId(), true, str);
594 m_messages_out.push(aom);
600 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
608 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
612 // It's best that attachments cannot be clicked
615 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
618 void LuaEntitySAO::setPos(const v3f &pos)
622 m_base_position = pos;
623 sendPosition(false, true);
626 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
630 m_base_position = pos;
632 sendPosition(true, true);
635 float LuaEntitySAO::getMinimumSavedMovement()
640 std::string LuaEntitySAO::getDescription()
642 std::ostringstream os(std::ios::binary);
643 os<<"LuaEntitySAO at (";
644 os<<(m_base_position.X/BS)<<",";
645 os<<(m_base_position.Y/BS)<<",";
646 os<<(m_base_position.Z/BS);
651 void LuaEntitySAO::setHP(s16 hp)
657 s16 LuaEntitySAO::getHP() const
662 void LuaEntitySAO::setVelocity(v3f velocity)
664 m_velocity = velocity;
667 v3f LuaEntitySAO::getVelocity()
672 void LuaEntitySAO::setAcceleration(v3f acceleration)
674 m_acceleration = acceleration;
677 v3f LuaEntitySAO::getAcceleration()
679 return m_acceleration;
682 void LuaEntitySAO::setTextureMod(const std::string &mod)
684 std::string str = gob_cmd_set_texture_mod(mod);
685 m_current_texture_modifier = mod;
686 // create message and add to list
687 ActiveObjectMessage aom(getId(), true, str);
688 m_messages_out.push(aom);
691 std::string LuaEntitySAO::getTextureMod() const
693 return m_current_texture_modifier;
696 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
697 bool select_horiz_by_yawpitch)
699 std::string str = gob_cmd_set_sprite(
703 select_horiz_by_yawpitch
705 // create message and add to list
706 ActiveObjectMessage aom(getId(), true, str);
707 m_messages_out.push(aom);
710 std::string LuaEntitySAO::getName()
715 std::string LuaEntitySAO::getPropertyPacket()
717 return gob_cmd_set_properties(m_prop);
720 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
722 // If the object is attached client-side, don't waste bandwidth sending its position to clients
726 m_last_sent_move_precision = m_base_position.getDistanceFrom(
727 m_last_sent_position);
728 m_last_sent_position_timer = 0;
729 m_last_sent_yaw = m_yaw;
730 m_last_sent_position = m_base_position;
731 m_last_sent_velocity = m_velocity;
732 //m_last_sent_acceleration = m_acceleration;
734 float update_interval = m_env->getSendRecommendedInterval();
736 std::string str = gob_cmd_update_position(
745 // create message and add to list
746 ActiveObjectMessage aom(getId(), false, str);
747 m_messages_out.push(aom);
750 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
754 //update collision box
755 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
756 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
758 toset->MinEdge += m_base_position;
759 toset->MaxEdge += m_base_position;
767 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
769 if (!m_prop.is_visible || !m_prop.pointable) {
773 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
774 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
779 bool LuaEntitySAO::collideWithObjects() const
781 return m_prop.collideWithObjects;
788 // No prototype, PlayerSAO does not need to be deserialized
790 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
791 bool is_singleplayer):
792 UnitSAO(env_, v3f(0,0,0)),
795 m_is_singleplayer(is_singleplayer)
797 assert(m_peer_id != 0); // pre-condition
799 m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
800 m_prop.physical = false;
802 m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
803 m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
804 m_prop.pointable = true;
805 // start of default appearance, this should be overwritten by LUA
806 m_prop.visual = "upright_sprite";
807 m_prop.visual_size = v2f(1, 2);
808 m_prop.textures.clear();
809 m_prop.textures.emplace_back("player.png");
810 m_prop.textures.emplace_back("player_back.png");
811 m_prop.colors.clear();
812 m_prop.colors.emplace_back(255, 255, 255, 255);
813 m_prop.spritediv = v2s16(1,1);
814 // end of default appearance
815 m_prop.is_visible = true;
816 m_prop.makes_footstep_sound = true;
817 m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
818 m_prop.can_zoom = true;
819 m_hp = m_prop.hp_max;
822 PlayerSAO::~PlayerSAO()
824 if(m_inventory != &m_player->inventory)
828 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
833 m_inventory = &m_player->inventory;
836 v3f PlayerSAO::getEyeOffset() const
838 return v3f(0, BS * 1.625f, 0);
841 std::string PlayerSAO::getDescription()
843 return std::string("player ") + m_player->getName();
846 // Called after id has been set and has been inserted in environment
847 void PlayerSAO::addedToEnvironment(u32 dtime_s)
849 ServerActiveObject::addedToEnvironment(dtime_s);
850 ServerActiveObject::setBasePosition(m_base_position);
851 m_player->setPlayerSAO(this);
852 m_player->peer_id = m_peer_id;
853 m_last_good_position = m_base_position;
856 // Called before removing from environment
857 void PlayerSAO::removingFromEnvironment()
859 ServerActiveObject::removingFromEnvironment();
860 if (m_player->getPlayerSAO() == this) {
861 unlinkPlayerSessionAndSave();
862 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
863 m_env->deleteParticleSpawner(attached_particle_spawner, false);
868 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
870 std::ostringstream os(std::ios::binary);
873 writeU8(os, 1); // version
874 os << serializeString(m_player->getName()); // name
875 writeU8(os, 1); // is_player
876 writeS16(os, getId()); //id
877 writeV3F1000(os, m_base_position);
878 writeF1000(os, m_yaw);
879 writeS16(os, getHP());
881 std::ostringstream msg_os(std::ios::binary);
882 msg_os << serializeLongString(getPropertyPacket()); // message 1
883 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
884 msg_os << serializeLongString(gob_cmd_update_animation(
885 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
886 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
887 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
888 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
889 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
891 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
892 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
893 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
894 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
895 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
896 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
897 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
898 int message_count = 6 + m_bone_position.size();
899 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
900 ii != m_attachment_child_ids.end(); ++ii) {
901 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
903 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
904 obj->getClientInitializationData(protocol_version)));
908 writeU8(os, message_count);
909 os.write(msg_os.str().c_str(), msg_os.str().size());
915 void PlayerSAO::getStaticData(std::string * result) const
917 FATAL_ERROR("Deprecated function");
920 void PlayerSAO::step(float dtime, bool send_recommended)
922 if (m_drowning_interval.step(dtime, 2.0)) {
924 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
925 MapNode n = m_env->getMap().getNodeNoEx(p);
926 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
927 // If node generates drown
928 if (c.drowning > 0 && m_hp > 0) {
930 setBreath(m_breath - 1);
932 // No more breath, damage player
934 setHP(m_hp - c.drowning);
935 m_env->getGameDef()->SendPlayerHPOrDie(this);
940 if (m_breathing_interval.step(dtime, 0.5)) {
942 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
943 MapNode n = m_env->getMap().getNodeNoEx(p);
944 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
945 // If player is alive & no drowning, breath
946 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
947 setBreath(m_breath + 1);
950 if (m_node_hurt_interval.step(dtime, 1.0)) {
951 // Feet, middle and head
952 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
953 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
954 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
955 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
956 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
957 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
959 u32 damage_per_second = 0;
960 damage_per_second = MYMAX(damage_per_second,
961 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
962 damage_per_second = MYMAX(damage_per_second,
963 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
964 damage_per_second = MYMAX(damage_per_second,
965 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
967 if (damage_per_second != 0 && m_hp > 0) {
968 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
970 m_env->getGameDef()->SendPlayerHPOrDie(this);
974 if (!m_properties_sent) {
975 m_properties_sent = true;
976 std::string str = getPropertyPacket();
977 // create message and add to list
978 ActiveObjectMessage aom(getId(), true, str);
979 m_messages_out.push(aom);
982 // If attached, check that our parent is still there. If it isn't, detach.
983 if(m_attachment_parent_id && !isAttached())
985 m_attachment_parent_id = 0;
986 m_attachment_bone = "";
987 m_attachment_position = v3f(0,0,0);
988 m_attachment_rotation = v3f(0,0,0);
989 setBasePosition(m_last_good_position);
990 m_env->getGameDef()->SendMovePlayer(m_peer_id);
993 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
995 // Set lag pool maximums based on estimated lag
996 const float LAG_POOL_MIN = 5.0;
997 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
998 if(lag_pool_max < LAG_POOL_MIN)
999 lag_pool_max = LAG_POOL_MIN;
1000 m_dig_pool.setMax(lag_pool_max);
1001 m_move_pool.setMax(lag_pool_max);
1003 // Increment cheat prevention timers
1004 m_dig_pool.add(dtime);
1005 m_move_pool.add(dtime);
1006 m_time_from_last_teleport += dtime;
1007 m_time_from_last_punch += dtime;
1008 m_nocheat_dig_time += dtime;
1010 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1011 // If the object gets detached this comes into effect automatically from the last known origin
1013 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1014 m_last_good_position = pos;
1015 setBasePosition(pos);
1018 if (!send_recommended)
1021 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1022 if(m_position_not_sent && !isAttached())
1024 m_position_not_sent = false;
1025 float update_interval = m_env->getSendRecommendedInterval();
1027 if(isAttached()) // Just in case we ever do send attachment position too
1028 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1030 pos = m_base_position;
1031 std::string str = gob_cmd_update_position(
1040 // create message and add to list
1041 ActiveObjectMessage aom(getId(), false, str);
1042 m_messages_out.push(aom);
1045 if (!m_armor_groups_sent) {
1046 m_armor_groups_sent = true;
1047 std::string str = gob_cmd_update_armor_groups(
1049 // create message and add to list
1050 ActiveObjectMessage aom(getId(), true, str);
1051 m_messages_out.push(aom);
1054 if (!m_physics_override_sent) {
1055 m_physics_override_sent = true;
1056 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1057 m_physics_override_jump, m_physics_override_gravity,
1058 m_physics_override_sneak, m_physics_override_sneak_glitch,
1059 m_physics_override_new_move);
1060 // create message and add to list
1061 ActiveObjectMessage aom(getId(), true, str);
1062 m_messages_out.push(aom);
1065 if (!m_animation_sent) {
1066 m_animation_sent = true;
1067 std::string str = gob_cmd_update_animation(
1068 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1069 // create message and add to list
1070 ActiveObjectMessage aom(getId(), true, str);
1071 m_messages_out.push(aom);
1074 if (!m_bone_position_sent) {
1075 m_bone_position_sent = true;
1076 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1077 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1078 std::string str = gob_cmd_update_bone_position((*ii).first,
1079 (*ii).second.X, (*ii).second.Y);
1080 // create message and add to list
1081 ActiveObjectMessage aom(getId(), true, str);
1082 m_messages_out.push(aom);
1086 if (!m_attachment_sent){
1087 m_attachment_sent = true;
1088 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1089 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1090 // create message and add to list
1091 ActiveObjectMessage aom(getId(), true, str);
1092 m_messages_out.push(aom);
1096 void PlayerSAO::setBasePosition(const v3f &position)
1098 if (m_player && position != m_base_position)
1099 m_player->setDirty(true);
1101 // This needs to be ran for attachments too
1102 ServerActiveObject::setBasePosition(position);
1103 m_position_not_sent = true;
1106 void PlayerSAO::setPos(const v3f &pos)
1111 setBasePosition(pos);
1112 // Movement caused by this command is always valid
1113 m_last_good_position = pos;
1114 m_move_pool.empty();
1115 m_time_from_last_teleport = 0.0;
1116 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1119 void PlayerSAO::moveTo(v3f pos, bool continuous)
1124 setBasePosition(pos);
1125 // Movement caused by this command is always valid
1126 m_last_good_position = pos;
1127 m_move_pool.empty();
1128 m_time_from_last_teleport = 0.0;
1129 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1132 void PlayerSAO::setYaw(const float yaw)
1134 if (m_player && yaw != m_yaw)
1135 m_player->setDirty(true);
1137 UnitSAO::setYaw(yaw);
1140 void PlayerSAO::setFov(const float fov)
1142 if (m_player && fov != m_fov)
1143 m_player->setDirty(true);
1148 void PlayerSAO::setWantedRange(const s16 range)
1150 if (m_player && range != m_wanted_range)
1151 m_player->setDirty(true);
1153 m_wanted_range = range;
1156 void PlayerSAO::setYawAndSend(const float yaw)
1159 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1162 void PlayerSAO::setPitch(const float pitch)
1164 if (m_player && pitch != m_pitch)
1165 m_player->setDirty(true);
1170 void PlayerSAO::setPitchAndSend(const float pitch)
1173 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1176 int PlayerSAO::punch(v3f dir,
1177 const ToolCapabilities *toolcap,
1178 ServerActiveObject *puncher,
1179 float time_from_last_punch)
1181 // It's best that attachments cannot be punched
1188 // No effect if PvP disabled
1189 if (!g_settings->getBool("enable_pvp")) {
1190 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1191 std::string str = gob_cmd_punched(0, getHP());
1192 // create message and add to list
1193 ActiveObjectMessage aom(getId(), true, str);
1194 m_messages_out.push(aom);
1199 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1200 time_from_last_punch);
1202 std::string punchername = "nil";
1205 punchername = puncher->getDescription();
1207 PlayerSAO *playersao = m_player->getPlayerSAO();
1209 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1210 puncher, time_from_last_punch, toolcap, dir,
1213 if (!damage_handled) {
1214 setHP(getHP() - hitparams.hp);
1215 } else { // override client prediction
1216 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1217 std::string str = gob_cmd_punched(0, getHP());
1218 // create message and add to list
1219 ActiveObjectMessage aom(getId(), true, str);
1220 m_messages_out.push(aom);
1225 actionstream << "Player " << m_player->getName() << " punched by "
1227 if (!damage_handled) {
1228 actionstream << ", damage " << hitparams.hp << " HP";
1230 actionstream << ", damage handled by lua";
1232 actionstream << std::endl;
1234 return hitparams.wear;
1237 s16 PlayerSAO::readDamage()
1239 s16 damage = m_damage;
1244 void PlayerSAO::setHP(s16 hp)
1248 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1251 hp = oldhp + hp_change;
1255 else if (hp > m_prop.hp_max)
1258 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1265 m_damage += (oldhp - hp);
1267 // Update properties on death
1268 if ((hp == 0) != (oldhp == 0))
1269 m_properties_sent = false;
1272 void PlayerSAO::setBreath(const u16 breath, bool send)
1274 if (m_player && breath != m_breath)
1275 m_player->setDirty(true);
1277 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1280 m_env->getGameDef()->SendPlayerBreath(this);
1283 Inventory* PlayerSAO::getInventory()
1287 const Inventory* PlayerSAO::getInventory() const
1292 InventoryLocation PlayerSAO::getInventoryLocation() const
1294 InventoryLocation loc;
1295 loc.setPlayer(m_player->getName());
1299 std::string PlayerSAO::getWieldList() const
1304 ItemStack PlayerSAO::getWieldedItem() const
1306 const Inventory *inv = getInventory();
1308 const InventoryList *mlist = inv->getList(getWieldList());
1309 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1310 ret = mlist->getItem(getWieldIndex());
1314 ItemStack PlayerSAO::getWieldedItemOrHand() const
1316 const Inventory *inv = getInventory();
1318 const InventoryList *mlist = inv->getList(getWieldList());
1319 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1320 ret = mlist->getItem(getWieldIndex());
1321 if (ret.name.empty()) {
1322 const InventoryList *hlist = inv->getList("hand");
1324 ret = hlist->getItem(0);
1329 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1331 Inventory *inv = getInventory();
1333 InventoryList *mlist = inv->getList(getWieldList());
1335 mlist->changeItem(getWieldIndex(), item);
1342 int PlayerSAO::getWieldIndex() const
1344 return m_wield_index;
1347 void PlayerSAO::setWieldIndex(int i)
1349 if(i != m_wield_index) {
1354 // Erase the peer id and make the object for removal
1355 void PlayerSAO::disconnected()
1361 void PlayerSAO::unlinkPlayerSessionAndSave()
1363 assert(m_player->getPlayerSAO() == this);
1364 m_player->peer_id = 0;
1365 m_env->savePlayer(m_player);
1366 m_player->setPlayerSAO(NULL);
1367 m_env->removePlayer(m_player);
1370 std::string PlayerSAO::getPropertyPacket()
1372 m_prop.is_visible = (true);
1373 return gob_cmd_set_properties(m_prop);
1376 bool PlayerSAO::checkMovementCheat()
1378 if (isAttached() || m_is_singleplayer ||
1379 g_settings->getBool("disable_anticheat")) {
1380 m_last_good_position = m_base_position;
1384 bool cheated = false;
1386 Check player movements
1388 NOTE: Actually the server should handle player physics like the
1389 client does and compare player's position to what is calculated
1390 on our side. This is required when eg. players fly due to an
1391 explosion. Altough a node-based alternative might be possible
1392 too, and much more lightweight.
1395 float player_max_speed = 0;
1397 if (m_privs.count("fast") != 0) {
1399 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1402 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1404 // Tolerance. The lag pool does this a bit.
1405 //player_max_speed *= 2.5;
1407 v3f diff = (m_base_position - m_last_good_position);
1408 float d_vert = diff.Y;
1410 float d_horiz = diff.getLength();
1411 float required_time = d_horiz / player_max_speed;
1413 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1414 required_time = d_vert / player_max_speed; // Moving upwards
1416 if (m_move_pool.grab(required_time)) {
1417 m_last_good_position = m_base_position;
1419 const float LAG_POOL_MIN = 5.0;
1420 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1421 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1422 if (m_time_from_last_teleport > lag_pool_max) {
1423 actionstream << "Player " << m_player->getName()
1424 << " moved too fast; resetting position"
1428 setBasePosition(m_last_good_position);
1433 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1435 //update collision box
1436 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1437 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1439 toset->MinEdge += m_base_position;
1440 toset->MaxEdge += m_base_position;
1444 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1446 if (!m_prop.is_visible || !m_prop.pointable) {
1450 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1451 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;