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"
33 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
39 class TestSAO : public ServerActiveObject
42 TestSAO(ServerEnvironment *env, v3f pos):
43 ServerActiveObject(env, pos),
47 ServerActiveObject::registerType(getType(), create);
49 ActiveObjectType getType() const
50 { return ACTIVEOBJECT_TYPE_TEST; }
52 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
53 const std::string &data)
55 return new TestSAO(env, pos);
58 void step(float dtime, bool send_recommended)
63 m_pending_removal = true;
67 m_base_position.Y += dtime * BS * 2;
68 if(m_base_position.Y > 8*BS)
69 m_base_position.Y = 2*BS;
71 if (!send_recommended)
81 data += itos(0); // 0 = position
83 data += itos(m_base_position.X);
85 data += itos(m_base_position.Y);
87 data += itos(m_base_position.Z);
89 ActiveObjectMessage aom(getId(), false, data);
90 m_messages_out.push(aom);
94 bool getCollisionBox(aabb3f *toset) const { return false; }
96 virtual bool getSelectionBox(aabb3f *toset) const { return false; }
98 bool collideWithObjects() const { return false; }
105 // Prototype (registers item for deserialization)
106 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
112 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
113 ServerActiveObject(env, pos)
115 // Initialize something to armor groups
116 m_armor_groups["fleshy"] = 100;
119 bool UnitSAO::isAttached() const
121 if (!m_attachment_parent_id)
123 // Check if the parent still exists
124 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
130 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
132 m_armor_groups = armor_groups;
133 m_armor_groups_sent = false;
136 const ItemGroupList &UnitSAO::getArmorGroups()
138 return m_armor_groups;
141 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
143 // store these so they can be updated to clients
144 m_animation_range = frame_range;
145 m_animation_speed = frame_speed;
146 m_animation_blend = frame_blend;
147 m_animation_loop = frame_loop;
148 m_animation_sent = false;
151 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
153 *frame_range = m_animation_range;
154 *frame_speed = m_animation_speed;
155 *frame_blend = m_animation_blend;
156 *frame_loop = m_animation_loop;
159 void UnitSAO::setAnimationSpeed(float frame_speed)
161 m_animation_speed = frame_speed;
162 m_animation_speed_sent = false;
165 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
167 // store these so they can be updated to clients
168 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
169 m_bone_position_sent = false;
172 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
174 *position = m_bone_position[bone].X;
175 *rotation = m_bone_position[bone].Y;
178 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
180 // Attachments need to be handled on both the server and client.
181 // If we just attach on the server, we can only copy the position of the parent. Attachments
182 // are still sent to clients at an interval so players might see them lagging, plus we can't
183 // read and attach to skeletal bones.
184 // If we just attach on the client, the server still sees the child at its original location.
185 // This breaks some things so we also give the server the most accurate representation
186 // even if players only see the client changes.
188 m_attachment_parent_id = parent_id;
189 m_attachment_bone = bone;
190 m_attachment_position = position;
191 m_attachment_rotation = rotation;
192 m_attachment_sent = false;
195 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
198 *parent_id = m_attachment_parent_id;
199 *bone = m_attachment_bone;
200 *position = m_attachment_position;
201 *rotation = m_attachment_rotation;
204 void UnitSAO::addAttachmentChild(int child_id)
206 m_attachment_child_ids.insert(child_id);
209 void UnitSAO::removeAttachmentChild(int child_id)
211 m_attachment_child_ids.erase(child_id);
214 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
216 return m_attachment_child_ids;
219 ObjectProperties* UnitSAO::accessObjectProperties()
224 void UnitSAO::notifyObjectPropertiesModified()
226 m_properties_sent = false;
233 // Prototype (registers item for deserialization)
234 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
236 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
237 const std::string &name, const std::string &state):
242 // Only register type if no environment supplied
244 ServerActiveObject::registerType(getType(), create);
249 LuaEntitySAO::~LuaEntitySAO()
252 m_env->getScriptIface()->luaentity_Remove(m_id);
255 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
256 m_env->deleteParticleSpawner(attached_particle_spawner, false);
260 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
262 ServerActiveObject::addedToEnvironment(dtime_s);
264 // Create entity from name
265 m_registered = m_env->getScriptIface()->
266 luaentity_Add(m_id, m_init_name.c_str());
270 m_env->getScriptIface()->
271 luaentity_GetProperties(m_id, &m_prop);
272 // Initialize HP from properties
273 m_hp = m_prop.hp_max;
274 // Activate entity, supplying serialized state
275 m_env->getScriptIface()->
276 luaentity_Activate(m_id, m_init_state, dtime_s);
278 m_prop.infotext = m_init_name;
282 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
283 const std::string &data)
291 std::istringstream is(data, std::ios::binary);
293 u8 version = readU8(is);
294 // check if version is supported
296 name = deSerializeString(is);
297 state = deSerializeLongString(is);
299 else if(version == 1){
300 name = deSerializeString(is);
301 state = deSerializeLongString(is);
303 velocity = readV3F1000(is);
308 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
309 <<state<<"\")"<<std::endl;
310 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
312 sao->m_velocity = velocity;
317 void LuaEntitySAO::step(float dtime, bool send_recommended)
319 if(!m_properties_sent)
321 m_properties_sent = true;
322 std::string str = getPropertyPacket();
323 // create message and add to list
324 ActiveObjectMessage aom(getId(), true, str);
325 m_messages_out.push(aom);
328 // If attached, check that our parent is still there. If it isn't, detach.
329 if(m_attachment_parent_id && !isAttached())
331 m_attachment_parent_id = 0;
332 m_attachment_bone = "";
333 m_attachment_position = v3f(0,0,0);
334 m_attachment_rotation = v3f(0,0,0);
335 sendPosition(false, true);
338 m_last_sent_position_timer += dtime;
340 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
341 // If the object gets detached this comes into effect automatically from the last known origin
344 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
345 m_base_position = pos;
346 m_velocity = v3f(0,0,0);
347 m_acceleration = v3f(0,0,0);
352 aabb3f box = m_prop.collisionbox;
355 collisionMoveResult moveresult;
356 f32 pos_max_d = BS*0.25; // Distance per iteration
357 v3f p_pos = m_base_position;
358 v3f p_velocity = m_velocity;
359 v3f p_acceleration = m_acceleration;
360 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
361 pos_max_d, box, m_prop.stepheight, dtime,
362 &p_pos, &p_velocity, p_acceleration,
363 this, m_prop.collideWithObjects);
366 m_base_position = p_pos;
367 m_velocity = p_velocity;
368 m_acceleration = p_acceleration;
370 m_base_position += dtime * m_velocity + 0.5 * dtime
371 * dtime * m_acceleration;
372 m_velocity += dtime * m_acceleration;
375 if((m_prop.automatic_face_movement_dir) &&
376 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
378 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
379 + m_prop.automatic_face_movement_dir_offset;
380 float max_rotation_delta =
381 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
383 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
384 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
386 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
394 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
397 // Remove LuaEntity beyond terrain edges
399 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
401 if (!m_pending_removal &&
402 map->saoPositionOverLimit(m_base_position)) {
403 infostream << "Removing SAO " << m_id << "(" << m_init_name
404 << "), outside of limits" << std::endl;
405 m_pending_removal = 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
555 m_pending_removal = true;
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);
599 m_pending_removal = true;
600 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
606 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
610 // It's best that attachments cannot be clicked
613 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
616 void LuaEntitySAO::setPos(const v3f &pos)
620 m_base_position = pos;
621 sendPosition(false, true);
624 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
628 m_base_position = pos;
630 sendPosition(true, true);
633 float LuaEntitySAO::getMinimumSavedMovement()
638 std::string LuaEntitySAO::getDescription()
640 std::ostringstream os(std::ios::binary);
641 os<<"LuaEntitySAO at (";
642 os<<(m_base_position.X/BS)<<",";
643 os<<(m_base_position.Y/BS)<<",";
644 os<<(m_base_position.Z/BS);
649 void LuaEntitySAO::setHP(s16 hp)
655 s16 LuaEntitySAO::getHP() const
660 void LuaEntitySAO::setVelocity(v3f velocity)
662 m_velocity = velocity;
665 v3f LuaEntitySAO::getVelocity()
670 void LuaEntitySAO::setAcceleration(v3f acceleration)
672 m_acceleration = acceleration;
675 v3f LuaEntitySAO::getAcceleration()
677 return m_acceleration;
680 void LuaEntitySAO::setTextureMod(const std::string &mod)
682 std::string str = gob_cmd_set_texture_mod(mod);
683 m_current_texture_modifier = mod;
684 // create message and add to list
685 ActiveObjectMessage aom(getId(), true, str);
686 m_messages_out.push(aom);
689 std::string LuaEntitySAO::getTextureMod() const
691 return m_current_texture_modifier;
694 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
695 bool select_horiz_by_yawpitch)
697 std::string str = gob_cmd_set_sprite(
701 select_horiz_by_yawpitch
703 // create message and add to list
704 ActiveObjectMessage aom(getId(), true, str);
705 m_messages_out.push(aom);
708 std::string LuaEntitySAO::getName()
713 std::string LuaEntitySAO::getPropertyPacket()
715 return gob_cmd_set_properties(m_prop);
718 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
720 // If the object is attached client-side, don't waste bandwidth sending its position to clients
724 m_last_sent_move_precision = m_base_position.getDistanceFrom(
725 m_last_sent_position);
726 m_last_sent_position_timer = 0;
727 m_last_sent_yaw = m_yaw;
728 m_last_sent_position = m_base_position;
729 m_last_sent_velocity = m_velocity;
730 //m_last_sent_acceleration = m_acceleration;
732 float update_interval = m_env->getSendRecommendedInterval();
734 std::string str = gob_cmd_update_position(
743 // create message and add to list
744 ActiveObjectMessage aom(getId(), false, str);
745 m_messages_out.push(aom);
748 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
752 //update collision box
753 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
754 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
756 toset->MinEdge += m_base_position;
757 toset->MaxEdge += m_base_position;
765 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
767 if (!m_prop.is_visible || !m_prop.pointable) {
771 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
772 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
777 bool LuaEntitySAO::collideWithObjects() const
779 return m_prop.collideWithObjects;
786 // No prototype, PlayerSAO does not need to be deserialized
788 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
789 bool is_singleplayer):
790 UnitSAO(env_, v3f(0,0,0)),
793 m_is_singleplayer(is_singleplayer)
795 assert(m_peer_id != 0); // pre-condition
797 m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
798 m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
799 m_prop.physical = false;
801 m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
802 m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
803 m_prop.pointable = true;
804 // start of default appearance, this should be overwritten by LUA
805 m_prop.visual = "upright_sprite";
806 m_prop.visual_size = v2f(1, 2);
807 m_prop.textures.clear();
808 m_prop.textures.emplace_back("player.png");
809 m_prop.textures.emplace_back("player_back.png");
810 m_prop.colors.clear();
811 m_prop.colors.emplace_back(255, 255, 255, 255);
812 m_prop.spritediv = v2s16(1,1);
813 m_prop.eye_height = 1.625f;
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;
820 m_breath = m_prop.breath_max;
823 PlayerSAO::~PlayerSAO()
825 if(m_inventory != &m_player->inventory)
829 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
834 m_inventory = &m_player->inventory;
837 v3f PlayerSAO::getEyeOffset() const
839 return v3f(0, BS * m_prop.eye_height, 0);
842 std::string PlayerSAO::getDescription()
844 return std::string("player ") + m_player->getName();
847 // Called after id has been set and has been inserted in environment
848 void PlayerSAO::addedToEnvironment(u32 dtime_s)
850 ServerActiveObject::addedToEnvironment(dtime_s);
851 ServerActiveObject::setBasePosition(m_base_position);
852 m_player->setPlayerSAO(this);
853 m_player->setPeerId(m_peer_id);
854 m_last_good_position = m_base_position;
857 // Called before removing from environment
858 void PlayerSAO::removingFromEnvironment()
860 ServerActiveObject::removingFromEnvironment();
861 if (m_player->getPlayerSAO() == this) {
862 unlinkPlayerSessionAndSave();
863 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
864 m_env->deleteParticleSpawner(attached_particle_spawner, false);
869 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
871 std::ostringstream os(std::ios::binary);
874 writeU8(os, 1); // version
875 os << serializeString(m_player->getName()); // name
876 writeU8(os, 1); // is_player
877 writeS16(os, getId()); //id
878 writeV3F1000(os, m_base_position);
879 writeF1000(os, m_yaw);
880 writeS16(os, getHP());
882 std::ostringstream msg_os(std::ios::binary);
883 msg_os << serializeLongString(getPropertyPacket()); // message 1
884 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
885 msg_os << serializeLongString(gob_cmd_update_animation(
886 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
887 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
888 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
889 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
890 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
892 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
893 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
894 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
895 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
896 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
897 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
898 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
899 int message_count = 6 + m_bone_position.size();
900 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
901 ii != m_attachment_child_ids.end(); ++ii) {
902 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
904 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
905 obj->getClientInitializationData(protocol_version)));
909 writeU8(os, message_count);
910 os.write(msg_os.str().c_str(), msg_os.str().size());
916 void PlayerSAO::getStaticData(std::string * result) const
918 FATAL_ERROR("Deprecated function");
921 void PlayerSAO::step(float dtime, bool send_recommended)
923 if (m_drowning_interval.step(dtime, 2.0f)) {
924 // Get nose/mouth position, approximate with eye position
925 v3s16 p = floatToInt(getEyePosition(), BS);
926 MapNode n = m_env->getMap().getNodeNoEx(p);
927 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
928 // If node generates drown
929 if (c.drowning > 0 && m_hp > 0) {
931 setBreath(m_breath - 1);
933 // No more breath, damage player
935 setHP(m_hp - c.drowning);
936 m_env->getGameDef()->SendPlayerHPOrDie(this);
941 if (m_breathing_interval.step(dtime, 0.5f)) {
942 // Get nose/mouth position, approximate with eye position
943 v3s16 p = floatToInt(getEyePosition(), BS);
944 MapNode n = m_env->getMap().getNodeNoEx(p);
945 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
946 // If player is alive & no drowning, breathe
947 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
948 setBreath(m_breath + 1);
951 if (m_node_hurt_interval.step(dtime, 1.0f)) {
952 u32 damage_per_second = 0;
953 // Lowest and highest damage points are 0.1 within collisionbox
954 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
956 // Sequence of damage points, starting 0.1 above feet and progressing
957 // upwards in 1 node intervals, stopping below top damage point.
958 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
959 v3s16 p = floatToInt(m_base_position +
960 v3f(0.0f, dam_height * BS, 0.0f), BS);
961 MapNode n = m_env->getMap().getNodeNoEx(p);
962 damage_per_second = std::max(damage_per_second,
963 m_env->getGameDef()->ndef()->get(n).damage_per_second);
967 v3s16 ptop = floatToInt(m_base_position +
968 v3f(0.0f, dam_top * BS, 0.0f), BS);
969 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
970 damage_per_second = std::max(damage_per_second,
971 m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
973 if (damage_per_second != 0 && m_hp > 0) {
974 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
976 m_env->getGameDef()->SendPlayerHPOrDie(this);
980 if (!m_properties_sent) {
981 m_properties_sent = true;
982 std::string str = getPropertyPacket();
983 // create message and add to list
984 ActiveObjectMessage aom(getId(), true, str);
985 m_messages_out.push(aom);
988 // If attached, check that our parent is still there. If it isn't, detach.
989 if (m_attachment_parent_id && !isAttached()) {
990 m_attachment_parent_id = 0;
991 m_attachment_bone = "";
992 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
993 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
994 setBasePosition(m_last_good_position);
995 m_env->getGameDef()->SendMovePlayer(m_peer_id);
998 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1000 // Set lag pool maximums based on estimated lag
1001 const float LAG_POOL_MIN = 5.0f;
1002 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1003 if(lag_pool_max < LAG_POOL_MIN)
1004 lag_pool_max = LAG_POOL_MIN;
1005 m_dig_pool.setMax(lag_pool_max);
1006 m_move_pool.setMax(lag_pool_max);
1008 // Increment cheat prevention timers
1009 m_dig_pool.add(dtime);
1010 m_move_pool.add(dtime);
1011 m_time_from_last_teleport += dtime;
1012 m_time_from_last_punch += dtime;
1013 m_nocheat_dig_time += dtime;
1015 // Each frame, parent position is copied if the object is attached,
1016 // otherwise it's calculated normally.
1017 // If the object gets detached this comes into effect automatically from
1018 // the last known origin.
1020 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1021 m_last_good_position = pos;
1022 setBasePosition(pos);
1025 if (!send_recommended)
1028 // If the object is attached client-side, don't waste bandwidth sending its
1029 // position to clients.
1030 if (m_position_not_sent && !isAttached()) {
1031 m_position_not_sent = false;
1032 float update_interval = m_env->getSendRecommendedInterval();
1034 if (isAttached()) // Just in case we ever do send attachment position too
1035 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1037 pos = m_base_position;
1039 std::string str = gob_cmd_update_position(
1041 v3f(0.0f, 0.0f, 0.0f),
1042 v3f(0.0f, 0.0f, 0.0f),
1048 // create message and add to list
1049 ActiveObjectMessage aom(getId(), false, str);
1050 m_messages_out.push(aom);
1053 if (!m_armor_groups_sent) {
1054 m_armor_groups_sent = true;
1055 std::string str = gob_cmd_update_armor_groups(
1057 // create message and add to list
1058 ActiveObjectMessage aom(getId(), true, str);
1059 m_messages_out.push(aom);
1062 if (!m_physics_override_sent) {
1063 m_physics_override_sent = true;
1064 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1065 m_physics_override_jump, m_physics_override_gravity,
1066 m_physics_override_sneak, m_physics_override_sneak_glitch,
1067 m_physics_override_new_move);
1068 // create message and add to list
1069 ActiveObjectMessage aom(getId(), true, str);
1070 m_messages_out.push(aom);
1073 if (!m_animation_sent) {
1074 m_animation_sent = true;
1075 std::string str = gob_cmd_update_animation(
1076 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1077 // create message and add to list
1078 ActiveObjectMessage aom(getId(), true, str);
1079 m_messages_out.push(aom);
1082 if (!m_bone_position_sent) {
1083 m_bone_position_sent = true;
1084 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1085 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1086 std::string str = gob_cmd_update_bone_position((*ii).first,
1087 (*ii).second.X, (*ii).second.Y);
1088 // create message and add to list
1089 ActiveObjectMessage aom(getId(), true, str);
1090 m_messages_out.push(aom);
1094 if (!m_attachment_sent) {
1095 m_attachment_sent = true;
1096 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1097 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1098 // create message and add to list
1099 ActiveObjectMessage aom(getId(), true, str);
1100 m_messages_out.push(aom);
1104 void PlayerSAO::setBasePosition(const v3f &position)
1106 if (m_player && position != m_base_position)
1107 m_player->setDirty(true);
1109 // This needs to be ran for attachments too
1110 ServerActiveObject::setBasePosition(position);
1111 m_position_not_sent = true;
1114 void PlayerSAO::setPos(const v3f &pos)
1119 setBasePosition(pos);
1120 // Movement caused by this command is always valid
1121 m_last_good_position = pos;
1122 m_move_pool.empty();
1123 m_time_from_last_teleport = 0.0;
1124 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1127 void PlayerSAO::moveTo(v3f pos, bool continuous)
1132 setBasePosition(pos);
1133 // Movement caused by this command is always valid
1134 m_last_good_position = pos;
1135 m_move_pool.empty();
1136 m_time_from_last_teleport = 0.0;
1137 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1140 void PlayerSAO::setYaw(const float yaw)
1142 if (m_player && yaw != m_yaw)
1143 m_player->setDirty(true);
1145 UnitSAO::setYaw(yaw);
1148 void PlayerSAO::setFov(const float fov)
1150 if (m_player && fov != m_fov)
1151 m_player->setDirty(true);
1156 void PlayerSAO::setWantedRange(const s16 range)
1158 if (m_player && range != m_wanted_range)
1159 m_player->setDirty(true);
1161 m_wanted_range = range;
1164 void PlayerSAO::setYawAndSend(const float yaw)
1167 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1170 void PlayerSAO::setPitch(const float pitch)
1172 if (m_player && pitch != m_pitch)
1173 m_player->setDirty(true);
1178 void PlayerSAO::setPitchAndSend(const float pitch)
1181 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1184 int PlayerSAO::punch(v3f dir,
1185 const ToolCapabilities *toolcap,
1186 ServerActiveObject *puncher,
1187 float time_from_last_punch)
1189 // It's best that attachments cannot be punched
1196 // No effect if PvP disabled
1197 if (!g_settings->getBool("enable_pvp")) {
1198 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1199 std::string str = gob_cmd_punched(0, getHP());
1200 // create message and add to list
1201 ActiveObjectMessage aom(getId(), true, str);
1202 m_messages_out.push(aom);
1207 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1208 time_from_last_punch);
1210 std::string punchername = "nil";
1213 punchername = puncher->getDescription();
1215 PlayerSAO *playersao = m_player->getPlayerSAO();
1217 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1218 puncher, time_from_last_punch, toolcap, dir,
1221 if (!damage_handled) {
1222 setHP(getHP() - hitparams.hp);
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)
1256 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
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_speed = 0;
1404 if (m_privs.count("fast") != 0) {
1406 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1409 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1411 // Tolerance. The lag pool does this a bit.
1412 //player_max_speed *= 2.5;
1414 v3f diff = (m_base_position - m_last_good_position);
1415 float d_vert = diff.Y;
1417 float d_horiz = diff.getLength();
1418 float required_time = d_horiz / player_max_speed;
1420 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1421 required_time = d_vert / player_max_speed; // Moving upwards
1423 if (m_move_pool.grab(required_time)) {
1424 m_last_good_position = m_base_position;
1426 const float LAG_POOL_MIN = 5.0;
1427 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1428 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1429 if (m_time_from_last_teleport > lag_pool_max) {
1430 actionstream << "Player " << m_player->getName()
1431 << " moved too fast; resetting position"
1435 setBasePosition(m_last_good_position);
1440 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1442 //update collision box
1443 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1444 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1446 toset->MinEdge += m_base_position;
1447 toset->MaxEdge += m_base_position;
1451 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1453 if (!m_prop.is_visible || !m_prop.pointable) {
1457 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1458 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;