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_, session_t 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 m_prop.eye_height = 1.625f;
813 // end of default appearance
814 m_prop.is_visible = true;
815 m_prop.makes_footstep_sound = true;
816 m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
817 m_prop.can_zoom = true;
818 m_hp = m_prop.hp_max;
819 m_breath = m_prop.breath_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 * m_prop.eye_height, 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->setPeerId(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 < m_prop.breath_max && 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, m_prop.breath_max);
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 void PlayerSAO::disconnected()
1357 m_pending_removal = true;
1360 void PlayerSAO::unlinkPlayerSessionAndSave()
1362 assert(m_player->getPlayerSAO() == this);
1363 m_player->setPeerId(PEER_ID_INEXISTENT);
1364 m_env->savePlayer(m_player);
1365 m_player->setPlayerSAO(NULL);
1366 m_env->removePlayer(m_player);
1369 std::string PlayerSAO::getPropertyPacket()
1371 m_prop.is_visible = (true);
1372 return gob_cmd_set_properties(m_prop);
1375 bool PlayerSAO::checkMovementCheat()
1377 if (isAttached() || m_is_singleplayer ||
1378 g_settings->getBool("disable_anticheat")) {
1379 m_last_good_position = m_base_position;
1383 bool cheated = false;
1385 Check player movements
1387 NOTE: Actually the server should handle player physics like the
1388 client does and compare player's position to what is calculated
1389 on our side. This is required when eg. players fly due to an
1390 explosion. Altough a node-based alternative might be possible
1391 too, and much more lightweight.
1394 float player_max_speed = 0;
1396 if (m_privs.count("fast") != 0) {
1398 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1401 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1403 // Tolerance. The lag pool does this a bit.
1404 //player_max_speed *= 2.5;
1406 v3f diff = (m_base_position - m_last_good_position);
1407 float d_vert = diff.Y;
1409 float d_horiz = diff.getLength();
1410 float required_time = d_horiz / player_max_speed;
1412 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1413 required_time = d_vert / player_max_speed; // Moving upwards
1415 if (m_move_pool.grab(required_time)) {
1416 m_last_good_position = m_base_position;
1418 const float LAG_POOL_MIN = 5.0;
1419 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1420 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1421 if (m_time_from_last_teleport > lag_pool_max) {
1422 actionstream << "Player " << m_player->getName()
1423 << " moved too fast; resetting position"
1427 setBasePosition(m_last_good_position);
1432 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1434 //update collision box
1435 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1436 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1438 toset->MinEdge += m_base_position;
1439 toset->MaxEdge += m_base_position;
1443 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1445 if (!m_prop.is_visible || !m_prop.pointable) {
1449 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1450 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;