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"
34 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
40 class TestSAO : public ServerActiveObject
43 TestSAO(ServerEnvironment *env, v3f pos):
44 ServerActiveObject(env, pos),
48 ServerActiveObject::registerType(getType(), create);
50 ActiveObjectType getType() const
51 { return ACTIVEOBJECT_TYPE_TEST; }
53 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
54 const std::string &data)
56 return new TestSAO(env, pos);
59 void step(float dtime, bool send_recommended)
64 m_pending_removal = true;
68 m_base_position.Y += dtime * BS * 2;
69 if(m_base_position.Y > 8*BS)
70 m_base_position.Y = 2*BS;
72 if (!send_recommended)
82 data += itos(0); // 0 = position
84 data += itos(m_base_position.X);
86 data += itos(m_base_position.Y);
88 data += itos(m_base_position.Z);
90 ActiveObjectMessage aom(getId(), false, data);
91 m_messages_out.push(aom);
95 bool getCollisionBox(aabb3f *toset) const { return false; }
97 virtual bool getSelectionBox(aabb3f *toset) const { return false; }
99 bool collideWithObjects() const { return false; }
106 // Prototype (registers item for deserialization)
107 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
113 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
114 ServerActiveObject(env, pos)
116 // Initialize something to armor groups
117 m_armor_groups["fleshy"] = 100;
120 bool UnitSAO::isAttached() const
122 if (!m_attachment_parent_id)
124 // Check if the parent still exists
125 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
131 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
133 m_armor_groups = armor_groups;
134 m_armor_groups_sent = false;
137 const ItemGroupList &UnitSAO::getArmorGroups()
139 return m_armor_groups;
142 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
144 // store these so they can be updated to clients
145 m_animation_range = frame_range;
146 m_animation_speed = frame_speed;
147 m_animation_blend = frame_blend;
148 m_animation_loop = frame_loop;
149 m_animation_sent = false;
152 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
154 *frame_range = m_animation_range;
155 *frame_speed = m_animation_speed;
156 *frame_blend = m_animation_blend;
157 *frame_loop = m_animation_loop;
160 void UnitSAO::setAnimationSpeed(float frame_speed)
162 m_animation_speed = frame_speed;
163 m_animation_speed_sent = false;
166 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
168 // store these so they can be updated to clients
169 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
170 m_bone_position_sent = false;
173 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
175 *position = m_bone_position[bone].X;
176 *rotation = m_bone_position[bone].Y;
179 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
181 // Attachments need to be handled on both the server and client.
182 // If we just attach on the server, we can only copy the position of the parent. Attachments
183 // are still sent to clients at an interval so players might see them lagging, plus we can't
184 // read and attach to skeletal bones.
185 // If we just attach on the client, the server still sees the child at its original location.
186 // This breaks some things so we also give the server the most accurate representation
187 // even if players only see the client changes.
189 m_attachment_parent_id = parent_id;
190 m_attachment_bone = bone;
191 m_attachment_position = position;
192 m_attachment_rotation = rotation;
193 m_attachment_sent = false;
196 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
199 *parent_id = m_attachment_parent_id;
200 *bone = m_attachment_bone;
201 *position = m_attachment_position;
202 *rotation = m_attachment_rotation;
205 void UnitSAO::addAttachmentChild(int child_id)
207 m_attachment_child_ids.insert(child_id);
210 void UnitSAO::removeAttachmentChild(int child_id)
212 m_attachment_child_ids.erase(child_id);
215 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
217 return m_attachment_child_ids;
220 ObjectProperties* UnitSAO::accessObjectProperties()
225 void UnitSAO::notifyObjectPropertiesModified()
227 m_properties_sent = false;
234 // Prototype (registers item for deserialization)
235 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
237 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
238 const std::string &name, const std::string &state):
243 // Only register type if no environment supplied
245 ServerActiveObject::registerType(getType(), create);
250 LuaEntitySAO::~LuaEntitySAO()
253 m_env->getScriptIface()->luaentity_Remove(m_id);
256 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
257 m_env->deleteParticleSpawner(attached_particle_spawner, false);
261 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
263 ServerActiveObject::addedToEnvironment(dtime_s);
265 // Create entity from name
266 m_registered = m_env->getScriptIface()->
267 luaentity_Add(m_id, m_init_name.c_str());
271 m_env->getScriptIface()->
272 luaentity_GetProperties(m_id, &m_prop);
273 // Initialize HP from properties
274 m_hp = m_prop.hp_max;
275 // Activate entity, supplying serialized state
276 m_env->getScriptIface()->
277 luaentity_Activate(m_id, m_init_state, dtime_s);
279 m_prop.infotext = m_init_name;
283 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
284 const std::string &data)
292 std::istringstream is(data, std::ios::binary);
294 u8 version = readU8(is);
295 // check if version is supported
297 name = deSerializeString(is);
298 state = deSerializeLongString(is);
300 else if(version == 1){
301 name = deSerializeString(is);
302 state = deSerializeLongString(is);
304 velocity = readV3F1000(is);
309 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
310 <<state<<"\")"<<std::endl;
311 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
313 sao->m_velocity = velocity;
318 void LuaEntitySAO::step(float dtime, bool send_recommended)
320 if(!m_properties_sent)
322 m_properties_sent = true;
323 std::string str = getPropertyPacket();
324 // create message and add to list
325 ActiveObjectMessage aom(getId(), true, str);
326 m_messages_out.push(aom);
329 // If attached, check that our parent is still there. If it isn't, detach.
330 if(m_attachment_parent_id && !isAttached())
332 m_attachment_parent_id = 0;
333 m_attachment_bone = "";
334 m_attachment_position = v3f(0,0,0);
335 m_attachment_rotation = v3f(0,0,0);
336 sendPosition(false, true);
339 m_last_sent_position_timer += dtime;
341 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
342 // If the object gets detached this comes into effect automatically from the last known origin
345 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
346 m_base_position = pos;
347 m_velocity = v3f(0,0,0);
348 m_acceleration = v3f(0,0,0);
353 aabb3f box = m_prop.collisionbox;
356 collisionMoveResult moveresult;
357 f32 pos_max_d = BS*0.25; // Distance per iteration
358 v3f p_pos = m_base_position;
359 v3f p_velocity = m_velocity;
360 v3f p_acceleration = m_acceleration;
361 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
362 pos_max_d, box, m_prop.stepheight, dtime,
363 &p_pos, &p_velocity, p_acceleration,
364 this, m_prop.collideWithObjects);
367 m_base_position = p_pos;
368 m_velocity = p_velocity;
369 m_acceleration = p_acceleration;
371 m_base_position += dtime * m_velocity + 0.5 * dtime
372 * dtime * m_acceleration;
373 m_velocity += dtime * m_acceleration;
376 if (m_prop.automatic_face_movement_dir &&
377 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
379 float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
380 + m_prop.automatic_face_movement_dir_offset;
381 float max_rotation_delta =
382 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
383 float delta = wrapDegrees_0_360(target_yaw - m_yaw);
385 if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
386 m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
387 m_yaw = wrapDegrees_0_360(m_yaw);
395 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
398 // Remove LuaEntity beyond terrain edges
400 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
402 if (!m_pending_removal &&
403 map->saoPositionOverLimit(m_base_position)) {
404 infostream << "Removing SAO " << m_id << "(" << m_init_name
405 << "), outside of limits" << std::endl;
406 m_pending_removal = true;
411 if (!send_recommended)
416 // TODO: force send when acceleration changes enough?
417 float minchange = 0.2*BS;
418 if(m_last_sent_position_timer > 1.0){
420 } else if(m_last_sent_position_timer > 0.2){
423 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
424 move_d += m_last_sent_move_precision;
425 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
426 if(move_d > minchange || vel_d > minchange ||
427 fabs(m_yaw - m_last_sent_yaw) > 1.0){
428 sendPosition(true, false);
432 if (!m_armor_groups_sent) {
433 m_armor_groups_sent = true;
434 std::string str = gob_cmd_update_armor_groups(
436 // create message and add to list
437 ActiveObjectMessage aom(getId(), true, str);
438 m_messages_out.push(aom);
441 if (!m_animation_sent) {
442 m_animation_sent = true;
443 std::string str = gob_cmd_update_animation(
444 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
445 // create message and add to list
446 ActiveObjectMessage aom(getId(), true, str);
447 m_messages_out.push(aom);
450 if (!m_animation_speed_sent) {
451 m_animation_speed_sent = true;
452 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
453 // create message and add to list
454 ActiveObjectMessage aom(getId(), true, str);
455 m_messages_out.push(aom);
458 if (!m_bone_position_sent) {
459 m_bone_position_sent = true;
460 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
461 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
462 std::string str = gob_cmd_update_bone_position((*ii).first,
463 (*ii).second.X, (*ii).second.Y);
464 // create message and add to list
465 ActiveObjectMessage aom(getId(), true, str);
466 m_messages_out.push(aom);
470 if (!m_attachment_sent) {
471 m_attachment_sent = true;
472 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
473 // create message and add to list
474 ActiveObjectMessage aom(getId(), true, str);
475 m_messages_out.push(aom);
479 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
481 std::ostringstream os(std::ios::binary);
484 writeU8(os, 1); // version
485 os << serializeString(""); // name
486 writeU8(os, 0); // is_player
487 writeS16(os, getId()); //id
488 writeV3F1000(os, m_base_position);
489 writeF1000(os, m_yaw);
492 std::ostringstream msg_os(std::ios::binary);
493 msg_os << serializeLongString(getPropertyPacket()); // message 1
494 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
495 msg_os << serializeLongString(gob_cmd_update_animation(
496 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
497 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
498 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
499 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
500 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
502 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
503 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
504 int message_count = 4 + m_bone_position.size();
505 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
506 (ii != m_attachment_child_ids.end()); ++ii) {
507 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
509 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
510 obj->getClientInitializationData(protocol_version)));
514 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
517 writeU8(os, message_count);
518 os.write(msg_os.str().c_str(), msg_os.str().size());
524 void LuaEntitySAO::getStaticData(std::string *result) const
526 verbosestream<<FUNCTION_NAME<<std::endl;
527 std::ostringstream os(std::ios::binary);
531 os<<serializeString(m_init_name);
534 std::string state = m_env->getScriptIface()->
535 luaentity_GetStaticdata(m_id);
536 os<<serializeLongString(state);
538 os<<serializeLongString(m_init_state);
543 writeV3F1000(os, m_velocity);
545 writeF1000(os, m_yaw);
549 int LuaEntitySAO::punch(v3f dir,
550 const ToolCapabilities *toolcap,
551 ServerActiveObject *puncher,
552 float time_from_last_punch)
555 // Delete unknown LuaEntities when punched
556 m_pending_removal = true;
560 // It's best that attachments cannot be punched
564 ItemStack *punchitem = NULL;
565 ItemStack punchitem_static;
567 punchitem_static = puncher->getWieldedItem();
568 punchitem = &punchitem_static;
571 PunchDamageResult result = getPunchDamage(
575 time_from_last_punch);
577 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
578 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
580 if (!damage_handled) {
581 if (result.did_punch) {
582 setHP(getHP() - result.damage);
584 if (result.damage > 0) {
585 std::string punchername = puncher ? puncher->getDescription() : "nil";
587 actionstream << getDescription() << " punched by "
588 << punchername << ", damage " << result.damage
589 << " hp, health now " << getHP() << " hp" << std::endl;
592 std::string str = gob_cmd_punched(result.damage, getHP());
593 // create message and add to list
594 ActiveObjectMessage aom(getId(), true, str);
595 m_messages_out.push(aom);
600 m_pending_removal = true;
601 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
607 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
611 // It's best that attachments cannot be clicked
614 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
617 void LuaEntitySAO::setPos(const v3f &pos)
621 m_base_position = pos;
622 sendPosition(false, true);
625 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
629 m_base_position = pos;
631 sendPosition(true, true);
634 float LuaEntitySAO::getMinimumSavedMovement()
639 std::string LuaEntitySAO::getDescription()
641 std::ostringstream os(std::ios::binary);
642 os<<"LuaEntitySAO at (";
643 os<<(m_base_position.X/BS)<<",";
644 os<<(m_base_position.Y/BS)<<",";
645 os<<(m_base_position.Z/BS);
650 void LuaEntitySAO::setHP(s16 hp)
656 s16 LuaEntitySAO::getHP() const
661 void LuaEntitySAO::setVelocity(v3f velocity)
663 m_velocity = velocity;
666 v3f LuaEntitySAO::getVelocity()
671 void LuaEntitySAO::setAcceleration(v3f acceleration)
673 m_acceleration = acceleration;
676 v3f LuaEntitySAO::getAcceleration()
678 return m_acceleration;
681 void LuaEntitySAO::setTextureMod(const std::string &mod)
683 std::string str = gob_cmd_set_texture_mod(mod);
684 m_current_texture_modifier = mod;
685 // create message and add to list
686 ActiveObjectMessage aom(getId(), true, str);
687 m_messages_out.push(aom);
690 std::string LuaEntitySAO::getTextureMod() const
692 return m_current_texture_modifier;
695 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
696 bool select_horiz_by_yawpitch)
698 std::string str = gob_cmd_set_sprite(
702 select_horiz_by_yawpitch
704 // create message and add to list
705 ActiveObjectMessage aom(getId(), true, str);
706 m_messages_out.push(aom);
709 std::string LuaEntitySAO::getName()
714 std::string LuaEntitySAO::getPropertyPacket()
716 return gob_cmd_set_properties(m_prop);
719 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
721 // If the object is attached client-side, don't waste bandwidth sending its position to clients
725 m_last_sent_move_precision = m_base_position.getDistanceFrom(
726 m_last_sent_position);
727 m_last_sent_position_timer = 0;
728 m_last_sent_yaw = m_yaw;
729 m_last_sent_position = m_base_position;
730 m_last_sent_velocity = m_velocity;
731 //m_last_sent_acceleration = m_acceleration;
733 float update_interval = m_env->getSendRecommendedInterval();
735 std::string str = gob_cmd_update_position(
744 // create message and add to list
745 ActiveObjectMessage aom(getId(), false, str);
746 m_messages_out.push(aom);
749 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
753 //update collision box
754 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
755 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
757 toset->MinEdge += m_base_position;
758 toset->MaxEdge += m_base_position;
766 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
768 if (!m_prop.is_visible || !m_prop.pointable) {
772 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
773 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
778 bool LuaEntitySAO::collideWithObjects() const
780 return m_prop.collideWithObjects;
787 // No prototype, PlayerSAO does not need to be deserialized
789 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
790 bool is_singleplayer):
791 UnitSAO(env_, v3f(0,0,0)),
794 m_is_singleplayer(is_singleplayer)
796 assert(m_peer_id != 0); // pre-condition
798 m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
799 m_prop.breath_max = PLAYER_MAX_BREATH_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 m_prop.eye_height = 1.625f;
815 // End of default appearance
816 m_prop.is_visible = true;
817 m_prop.makes_footstep_sound = true;
818 m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
819 m_hp = m_prop.hp_max;
820 m_breath = m_prop.breath_max;
821 // Disable zoom in survival mode using a value of 0
822 m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
825 PlayerSAO::~PlayerSAO()
827 if(m_inventory != &m_player->inventory)
831 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
836 m_inventory = &m_player->inventory;
839 v3f PlayerSAO::getEyeOffset() const
841 return v3f(0, BS * m_prop.eye_height, 0);
844 std::string PlayerSAO::getDescription()
846 return std::string("player ") + m_player->getName();
849 // Called after id has been set and has been inserted in environment
850 void PlayerSAO::addedToEnvironment(u32 dtime_s)
852 ServerActiveObject::addedToEnvironment(dtime_s);
853 ServerActiveObject::setBasePosition(m_base_position);
854 m_player->setPlayerSAO(this);
855 m_player->setPeerId(m_peer_id);
856 m_last_good_position = m_base_position;
859 // Called before removing from environment
860 void PlayerSAO::removingFromEnvironment()
862 ServerActiveObject::removingFromEnvironment();
863 if (m_player->getPlayerSAO() == this) {
864 unlinkPlayerSessionAndSave();
865 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
866 m_env->deleteParticleSpawner(attached_particle_spawner, false);
871 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
873 std::ostringstream os(std::ios::binary);
876 writeU8(os, 1); // version
877 os << serializeString(m_player->getName()); // name
878 writeU8(os, 1); // is_player
879 writeS16(os, getId()); //id
880 writeV3F1000(os, m_base_position);
881 writeF1000(os, m_yaw);
882 writeS16(os, getHP());
884 std::ostringstream msg_os(std::ios::binary);
885 msg_os << serializeLongString(getPropertyPacket()); // message 1
886 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
887 msg_os << serializeLongString(gob_cmd_update_animation(
888 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
889 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
890 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
891 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
892 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
894 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
895 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
896 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
897 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
898 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
899 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
900 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
901 int message_count = 6 + m_bone_position.size();
902 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
903 ii != m_attachment_child_ids.end(); ++ii) {
904 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
906 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
907 obj->getClientInitializationData(protocol_version)));
911 writeU8(os, message_count);
912 os.write(msg_os.str().c_str(), msg_os.str().size());
918 void PlayerSAO::getStaticData(std::string * result) const
920 FATAL_ERROR("Deprecated function");
923 void PlayerSAO::step(float dtime, bool send_recommended)
925 if (m_drowning_interval.step(dtime, 2.0f)) {
926 // Get nose/mouth position, approximate with eye position
927 v3s16 p = floatToInt(getEyePosition(), BS);
928 MapNode n = m_env->getMap().getNodeNoEx(p);
929 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
930 // If node generates drown
931 if (c.drowning > 0 && m_hp > 0) {
933 setBreath(m_breath - 1);
935 // No more breath, damage player
937 setHP(m_hp - c.drowning);
938 m_env->getGameDef()->SendPlayerHPOrDie(this);
943 if (m_breathing_interval.step(dtime, 0.5f)) {
944 // Get nose/mouth position, approximate with eye position
945 v3s16 p = floatToInt(getEyePosition(), BS);
946 MapNode n = m_env->getMap().getNodeNoEx(p);
947 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
948 // If player is alive & no drowning, breathe
949 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
950 setBreath(m_breath + 1);
953 if (m_node_hurt_interval.step(dtime, 1.0f)) {
954 u32 damage_per_second = 0;
955 // Lowest and highest damage points are 0.1 within collisionbox
956 float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
958 // Sequence of damage points, starting 0.1 above feet and progressing
959 // upwards in 1 node intervals, stopping below top damage point.
960 for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
961 v3s16 p = floatToInt(m_base_position +
962 v3f(0.0f, dam_height * BS, 0.0f), BS);
963 MapNode n = m_env->getMap().getNodeNoEx(p);
964 damage_per_second = std::max(damage_per_second,
965 m_env->getGameDef()->ndef()->get(n).damage_per_second);
969 v3s16 ptop = floatToInt(m_base_position +
970 v3f(0.0f, dam_top * BS, 0.0f), BS);
971 MapNode ntop = m_env->getMap().getNodeNoEx(ptop);
972 damage_per_second = std::max(damage_per_second,
973 m_env->getGameDef()->ndef()->get(ntop).damage_per_second);
975 if (damage_per_second != 0 && m_hp > 0) {
976 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
978 m_env->getGameDef()->SendPlayerHPOrDie(this);
982 if (!m_properties_sent) {
983 m_properties_sent = true;
984 std::string str = getPropertyPacket();
985 // create message and add to list
986 ActiveObjectMessage aom(getId(), true, str);
987 m_messages_out.push(aom);
990 // If attached, check that our parent is still there. If it isn't, detach.
991 if (m_attachment_parent_id && !isAttached()) {
992 m_attachment_parent_id = 0;
993 m_attachment_bone = "";
994 m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
995 m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
996 setBasePosition(m_last_good_position);
997 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1000 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1002 // Set lag pool maximums based on estimated lag
1003 const float LAG_POOL_MIN = 5.0f;
1004 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
1005 if(lag_pool_max < LAG_POOL_MIN)
1006 lag_pool_max = LAG_POOL_MIN;
1007 m_dig_pool.setMax(lag_pool_max);
1008 m_move_pool.setMax(lag_pool_max);
1010 // Increment cheat prevention timers
1011 m_dig_pool.add(dtime);
1012 m_move_pool.add(dtime);
1013 m_time_from_last_teleport += dtime;
1014 m_time_from_last_punch += dtime;
1015 m_nocheat_dig_time += dtime;
1017 // Each frame, parent position is copied if the object is attached,
1018 // otherwise it's calculated normally.
1019 // If the object gets detached this comes into effect automatically from
1020 // the last known origin.
1022 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1023 m_last_good_position = pos;
1024 setBasePosition(pos);
1027 if (!send_recommended)
1030 // If the object is attached client-side, don't waste bandwidth sending its
1031 // position to clients.
1032 if (m_position_not_sent && !isAttached()) {
1033 m_position_not_sent = false;
1034 float update_interval = m_env->getSendRecommendedInterval();
1036 if (isAttached()) // Just in case we ever do send attachment position too
1037 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1039 pos = m_base_position;
1041 std::string str = gob_cmd_update_position(
1043 v3f(0.0f, 0.0f, 0.0f),
1044 v3f(0.0f, 0.0f, 0.0f),
1050 // create message and add to list
1051 ActiveObjectMessage aom(getId(), false, str);
1052 m_messages_out.push(aom);
1055 if (!m_armor_groups_sent) {
1056 m_armor_groups_sent = true;
1057 std::string str = gob_cmd_update_armor_groups(
1059 // create message and add to list
1060 ActiveObjectMessage aom(getId(), true, str);
1061 m_messages_out.push(aom);
1064 if (!m_physics_override_sent) {
1065 m_physics_override_sent = true;
1066 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1067 m_physics_override_jump, m_physics_override_gravity,
1068 m_physics_override_sneak, m_physics_override_sneak_glitch,
1069 m_physics_override_new_move);
1070 // create message and add to list
1071 ActiveObjectMessage aom(getId(), true, str);
1072 m_messages_out.push(aom);
1075 if (!m_animation_sent) {
1076 m_animation_sent = true;
1077 std::string str = gob_cmd_update_animation(
1078 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1079 // create message and add to list
1080 ActiveObjectMessage aom(getId(), true, str);
1081 m_messages_out.push(aom);
1084 if (!m_bone_position_sent) {
1085 m_bone_position_sent = true;
1086 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1087 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1088 std::string str = gob_cmd_update_bone_position((*ii).first,
1089 (*ii).second.X, (*ii).second.Y);
1090 // create message and add to list
1091 ActiveObjectMessage aom(getId(), true, str);
1092 m_messages_out.push(aom);
1096 if (!m_attachment_sent) {
1097 m_attachment_sent = true;
1098 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1099 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1100 // create message and add to list
1101 ActiveObjectMessage aom(getId(), true, str);
1102 m_messages_out.push(aom);
1106 void PlayerSAO::setBasePosition(const v3f &position)
1108 if (m_player && position != m_base_position)
1109 m_player->setDirty(true);
1111 // This needs to be ran for attachments too
1112 ServerActiveObject::setBasePosition(position);
1113 m_position_not_sent = true;
1116 void PlayerSAO::setPos(const v3f &pos)
1121 setBasePosition(pos);
1122 // Movement caused by this command is always valid
1123 m_last_good_position = pos;
1124 m_move_pool.empty();
1125 m_time_from_last_teleport = 0.0;
1126 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1129 void PlayerSAO::moveTo(v3f pos, bool continuous)
1134 setBasePosition(pos);
1135 // Movement caused by this command is always valid
1136 m_last_good_position = pos;
1137 m_move_pool.empty();
1138 m_time_from_last_teleport = 0.0;
1139 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1142 void PlayerSAO::setYaw(const float yaw)
1144 if (m_player && yaw != m_yaw)
1145 m_player->setDirty(true);
1147 UnitSAO::setYaw(yaw);
1150 void PlayerSAO::setFov(const float fov)
1152 if (m_player && fov != m_fov)
1153 m_player->setDirty(true);
1158 void PlayerSAO::setWantedRange(const s16 range)
1160 if (m_player && range != m_wanted_range)
1161 m_player->setDirty(true);
1163 m_wanted_range = range;
1166 void PlayerSAO::setYawAndSend(const float yaw)
1169 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1172 void PlayerSAO::setPitch(const float pitch)
1174 if (m_player && pitch != m_pitch)
1175 m_player->setDirty(true);
1180 void PlayerSAO::setPitchAndSend(const float pitch)
1183 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1186 int PlayerSAO::punch(v3f dir,
1187 const ToolCapabilities *toolcap,
1188 ServerActiveObject *puncher,
1189 float time_from_last_punch)
1191 // It's best that attachments cannot be punched
1198 // No effect if PvP disabled
1199 if (!g_settings->getBool("enable_pvp")) {
1200 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1201 std::string str = gob_cmd_punched(0, getHP());
1202 // create message and add to list
1203 ActiveObjectMessage aom(getId(), true, str);
1204 m_messages_out.push(aom);
1209 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1210 time_from_last_punch);
1212 std::string punchername = "nil";
1215 punchername = puncher->getDescription();
1217 PlayerSAO *playersao = m_player->getPlayerSAO();
1219 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1220 puncher, time_from_last_punch, toolcap, dir,
1223 if (!damage_handled) {
1224 setHP(getHP() - hitparams.hp);
1225 } else { // override client prediction
1226 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1227 std::string str = gob_cmd_punched(0, getHP());
1228 // create message and add to list
1229 ActiveObjectMessage aom(getId(), true, str);
1230 m_messages_out.push(aom);
1235 actionstream << "Player " << m_player->getName() << " punched by "
1237 if (!damage_handled) {
1238 actionstream << ", damage " << hitparams.hp << " HP";
1240 actionstream << ", damage handled by lua";
1242 actionstream << std::endl;
1244 return hitparams.wear;
1247 s16 PlayerSAO::readDamage()
1249 s16 damage = m_damage;
1254 void PlayerSAO::setHP(s16 hp)
1258 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1261 hp = oldhp + hp_change;
1265 else if (hp > m_prop.hp_max)
1268 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1275 m_damage += (oldhp - hp);
1277 // Update properties on death
1278 if ((hp == 0) != (oldhp == 0))
1279 m_properties_sent = false;
1282 void PlayerSAO::setBreath(const u16 breath, bool send)
1284 if (m_player && breath != m_breath)
1285 m_player->setDirty(true);
1287 m_breath = MYMIN(breath, m_prop.breath_max);
1290 m_env->getGameDef()->SendPlayerBreath(this);
1293 Inventory* PlayerSAO::getInventory()
1297 const Inventory* PlayerSAO::getInventory() const
1302 InventoryLocation PlayerSAO::getInventoryLocation() const
1304 InventoryLocation loc;
1305 loc.setPlayer(m_player->getName());
1309 std::string PlayerSAO::getWieldList() const
1314 ItemStack PlayerSAO::getWieldedItem() 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());
1324 ItemStack PlayerSAO::getWieldedItemOrHand() const
1326 const Inventory *inv = getInventory();
1328 const InventoryList *mlist = inv->getList(getWieldList());
1329 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1330 ret = mlist->getItem(getWieldIndex());
1331 if (ret.name.empty()) {
1332 const InventoryList *hlist = inv->getList("hand");
1334 ret = hlist->getItem(0);
1339 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1341 Inventory *inv = getInventory();
1343 InventoryList *mlist = inv->getList(getWieldList());
1345 mlist->changeItem(getWieldIndex(), item);
1352 int PlayerSAO::getWieldIndex() const
1354 return m_wield_index;
1357 void PlayerSAO::setWieldIndex(int i)
1359 if(i != m_wield_index) {
1364 void PlayerSAO::disconnected()
1367 m_pending_removal = true;
1370 void PlayerSAO::unlinkPlayerSessionAndSave()
1372 assert(m_player->getPlayerSAO() == this);
1373 m_player->setPeerId(PEER_ID_INEXISTENT);
1374 m_env->savePlayer(m_player);
1375 m_player->setPlayerSAO(NULL);
1376 m_env->removePlayer(m_player);
1379 std::string PlayerSAO::getPropertyPacket()
1381 m_prop.is_visible = (true);
1382 return gob_cmd_set_properties(m_prop);
1385 bool PlayerSAO::checkMovementCheat()
1387 if (isAttached() || m_is_singleplayer ||
1388 g_settings->getBool("disable_anticheat")) {
1389 m_last_good_position = m_base_position;
1393 bool cheated = false;
1395 Check player movements
1397 NOTE: Actually the server should handle player physics like the
1398 client does and compare player's position to what is calculated
1399 on our side. This is required when eg. players fly due to an
1400 explosion. Altough a node-based alternative might be possible
1401 too, and much more lightweight.
1404 float player_max_speed = 0;
1406 if (m_privs.count("fast") != 0) {
1408 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1411 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1413 // Tolerance. The lag pool does this a bit.
1414 //player_max_speed *= 2.5;
1416 v3f diff = (m_base_position - m_last_good_position);
1417 float d_vert = diff.Y;
1419 float d_horiz = diff.getLength();
1420 float required_time = d_horiz / player_max_speed;
1422 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1423 required_time = d_vert / player_max_speed; // Moving upwards
1425 if (m_move_pool.grab(required_time)) {
1426 m_last_good_position = m_base_position;
1428 const float LAG_POOL_MIN = 5.0;
1429 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1430 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1431 if (m_time_from_last_teleport > lag_pool_max) {
1432 actionstream << "Player " << m_player->getName()
1433 << " moved too fast; resetting position"
1437 setBasePosition(m_last_good_position);
1442 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1444 //update collision box
1445 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1446 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1448 toset->MinEdge += m_base_position;
1449 toset->MaxEdge += m_base_position;
1453 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1455 if (!m_prop.is_visible || !m_prop.pointable) {
1459 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1460 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;