3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "collision.h"
23 #include "environment.h"
24 #include "tool.h" // For ToolCapabilities
27 #include "remoteplayer.h"
29 #include "scripting_server.h"
30 #include "genericobject.h"
32 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
38 class TestSAO : public ServerActiveObject
41 TestSAO(ServerEnvironment *env, v3f pos):
42 ServerActiveObject(env, pos),
46 ServerActiveObject::registerType(getType(), create);
48 ActiveObjectType getType() const
49 { return ACTIVEOBJECT_TYPE_TEST; }
51 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
52 const std::string &data)
54 return new TestSAO(env, pos);
57 void step(float dtime, bool send_recommended)
66 m_base_position.Y += dtime * BS * 2;
67 if(m_base_position.Y > 8*BS)
68 m_base_position.Y = 2*BS;
70 if (!send_recommended)
80 data += itos(0); // 0 = position
82 data += itos(m_base_position.X);
84 data += itos(m_base_position.Y);
86 data += itos(m_base_position.Z);
88 ActiveObjectMessage aom(getId(), false, data);
89 m_messages_out.push(aom);
93 bool getCollisionBox(aabb3f *toset) const { return false; }
95 virtual bool getSelectionBox(aabb3f *toset) const { return false; }
97 bool collideWithObjects() const { return false; }
104 // Prototype (registers item for deserialization)
105 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
111 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
112 ServerActiveObject(env, pos)
114 // Initialize something to armor groups
115 m_armor_groups["fleshy"] = 100;
118 bool UnitSAO::isAttached() const
120 if (!m_attachment_parent_id)
122 // Check if the parent still exists
123 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
129 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
131 m_armor_groups = armor_groups;
132 m_armor_groups_sent = false;
135 const ItemGroupList &UnitSAO::getArmorGroups()
137 return m_armor_groups;
140 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
142 // store these so they can be updated to clients
143 m_animation_range = frame_range;
144 m_animation_speed = frame_speed;
145 m_animation_blend = frame_blend;
146 m_animation_loop = frame_loop;
147 m_animation_sent = false;
150 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
152 *frame_range = m_animation_range;
153 *frame_speed = m_animation_speed;
154 *frame_blend = m_animation_blend;
155 *frame_loop = m_animation_loop;
158 void UnitSAO::setAnimationSpeed(float frame_speed)
160 m_animation_speed = frame_speed;
161 m_animation_speed_sent = false;
164 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
166 // store these so they can be updated to clients
167 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
168 m_bone_position_sent = false;
171 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
173 *position = m_bone_position[bone].X;
174 *rotation = m_bone_position[bone].Y;
177 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
179 // Attachments need to be handled on both the server and client.
180 // If we just attach on the server, we can only copy the position of the parent. Attachments
181 // are still sent to clients at an interval so players might see them lagging, plus we can't
182 // read and attach to skeletal bones.
183 // If we just attach on the client, the server still sees the child at its original location.
184 // This breaks some things so we also give the server the most accurate representation
185 // even if players only see the client changes.
187 m_attachment_parent_id = parent_id;
188 m_attachment_bone = bone;
189 m_attachment_position = position;
190 m_attachment_rotation = rotation;
191 m_attachment_sent = false;
194 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
197 *parent_id = m_attachment_parent_id;
198 *bone = m_attachment_bone;
199 *position = m_attachment_position;
200 *rotation = m_attachment_rotation;
203 void UnitSAO::addAttachmentChild(int child_id)
205 m_attachment_child_ids.insert(child_id);
208 void UnitSAO::removeAttachmentChild(int child_id)
210 m_attachment_child_ids.erase(child_id);
213 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
215 return m_attachment_child_ids;
218 ObjectProperties* UnitSAO::accessObjectProperties()
223 void UnitSAO::notifyObjectPropertiesModified()
225 m_properties_sent = false;
232 // Prototype (registers item for deserialization)
233 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
235 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
236 const std::string &name, const std::string &state):
241 // Only register type if no environment supplied
243 ServerActiveObject::registerType(getType(), create);
248 LuaEntitySAO::~LuaEntitySAO()
251 m_env->getScriptIface()->luaentity_Remove(m_id);
254 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
255 m_env->deleteParticleSpawner(attached_particle_spawner, false);
259 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
261 ServerActiveObject::addedToEnvironment(dtime_s);
263 // Create entity from name
264 m_registered = m_env->getScriptIface()->
265 luaentity_Add(m_id, m_init_name.c_str());
269 m_env->getScriptIface()->
270 luaentity_GetProperties(m_id, &m_prop);
271 // Initialize HP from properties
272 m_hp = m_prop.hp_max;
273 // Activate entity, supplying serialized state
274 m_env->getScriptIface()->
275 luaentity_Activate(m_id, m_init_state, dtime_s);
277 m_prop.infotext = m_init_name;
281 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
282 const std::string &data)
290 std::istringstream is(data, std::ios::binary);
292 u8 version = readU8(is);
293 // check if version is supported
295 name = deSerializeString(is);
296 state = deSerializeLongString(is);
298 else if(version == 1){
299 name = deSerializeString(is);
300 state = deSerializeLongString(is);
302 velocity = readV3F1000(is);
307 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
308 <<state<<"\")"<<std::endl;
309 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
311 sao->m_velocity = velocity;
316 void LuaEntitySAO::step(float dtime, bool send_recommended)
318 if(!m_properties_sent)
320 m_properties_sent = true;
321 std::string str = getPropertyPacket();
322 // create message and add to list
323 ActiveObjectMessage aom(getId(), true, str);
324 m_messages_out.push(aom);
327 // If attached, check that our parent is still there. If it isn't, detach.
328 if(m_attachment_parent_id && !isAttached())
330 m_attachment_parent_id = 0;
331 m_attachment_bone = "";
332 m_attachment_position = v3f(0,0,0);
333 m_attachment_rotation = v3f(0,0,0);
334 sendPosition(false, true);
337 m_last_sent_position_timer += dtime;
339 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
340 // If the object gets detached this comes into effect automatically from the last known origin
343 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
344 m_base_position = pos;
345 m_velocity = v3f(0,0,0);
346 m_acceleration = v3f(0,0,0);
351 aabb3f box = m_prop.collisionbox;
354 collisionMoveResult moveresult;
355 f32 pos_max_d = BS*0.25; // Distance per iteration
356 v3f p_pos = m_base_position;
357 v3f p_velocity = m_velocity;
358 v3f p_acceleration = m_acceleration;
359 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
360 pos_max_d, box, m_prop.stepheight, dtime,
361 &p_pos, &p_velocity, p_acceleration,
362 this, m_prop.collideWithObjects);
365 m_base_position = p_pos;
366 m_velocity = p_velocity;
367 m_acceleration = p_acceleration;
369 m_base_position += dtime * m_velocity + 0.5 * dtime
370 * dtime * m_acceleration;
371 m_velocity += dtime * m_acceleration;
374 if((m_prop.automatic_face_movement_dir) &&
375 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
377 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
378 + m_prop.automatic_face_movement_dir_offset;
379 float max_rotation_delta =
380 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
382 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
383 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
385 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
393 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
396 // Remove LuaEntity beyond terrain edges
398 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
400 if (!m_pending_deactivation &&
401 map->saoPositionOverLimit(m_base_position)) {
402 infostream << "Remove SAO " << m_id << "(" << m_init_name
403 << "), outside of limits" << std::endl;
404 m_pending_deactivation = true;
410 if (!send_recommended)
415 // TODO: force send when acceleration changes enough?
416 float minchange = 0.2*BS;
417 if(m_last_sent_position_timer > 1.0){
419 } else if(m_last_sent_position_timer > 0.2){
422 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
423 move_d += m_last_sent_move_precision;
424 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
425 if(move_d > minchange || vel_d > minchange ||
426 fabs(m_yaw - m_last_sent_yaw) > 1.0){
427 sendPosition(true, false);
431 if (!m_armor_groups_sent) {
432 m_armor_groups_sent = true;
433 std::string str = gob_cmd_update_armor_groups(
435 // create message and add to list
436 ActiveObjectMessage aom(getId(), true, str);
437 m_messages_out.push(aom);
440 if (!m_animation_sent) {
441 m_animation_sent = true;
442 std::string str = gob_cmd_update_animation(
443 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
444 // create message and add to list
445 ActiveObjectMessage aom(getId(), true, str);
446 m_messages_out.push(aom);
449 if (!m_animation_speed_sent) {
450 m_animation_speed_sent = true;
451 std::string str = gob_cmd_update_animation_speed(m_animation_speed);
452 // create message and add to list
453 ActiveObjectMessage aom(getId(), true, str);
454 m_messages_out.push(aom);
457 if (!m_bone_position_sent) {
458 m_bone_position_sent = true;
459 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
460 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
461 std::string str = gob_cmd_update_bone_position((*ii).first,
462 (*ii).second.X, (*ii).second.Y);
463 // create message and add to list
464 ActiveObjectMessage aom(getId(), true, str);
465 m_messages_out.push(aom);
469 if (!m_attachment_sent) {
470 m_attachment_sent = true;
471 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
472 // create message and add to list
473 ActiveObjectMessage aom(getId(), true, str);
474 m_messages_out.push(aom);
478 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
480 std::ostringstream os(std::ios::binary);
483 writeU8(os, 1); // version
484 os << serializeString(""); // name
485 writeU8(os, 0); // is_player
486 writeS16(os, getId()); //id
487 writeV3F1000(os, m_base_position);
488 writeF1000(os, m_yaw);
491 std::ostringstream msg_os(std::ios::binary);
492 msg_os << serializeLongString(getPropertyPacket()); // message 1
493 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
494 msg_os << serializeLongString(gob_cmd_update_animation(
495 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
496 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
497 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
498 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
499 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
501 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
502 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
503 int message_count = 4 + m_bone_position.size();
504 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
505 (ii != m_attachment_child_ids.end()); ++ii) {
506 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
508 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
509 obj->getClientInitializationData(protocol_version)));
513 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
516 writeU8(os, message_count);
517 os.write(msg_os.str().c_str(), msg_os.str().size());
523 void LuaEntitySAO::getStaticData(std::string *result) const
525 verbosestream<<FUNCTION_NAME<<std::endl;
526 std::ostringstream os(std::ios::binary);
530 os<<serializeString(m_init_name);
533 std::string state = m_env->getScriptIface()->
534 luaentity_GetStaticdata(m_id);
535 os<<serializeLongString(state);
537 os<<serializeLongString(m_init_state);
542 writeV3F1000(os, m_velocity);
544 writeF1000(os, m_yaw);
548 int LuaEntitySAO::punch(v3f dir,
549 const ToolCapabilities *toolcap,
550 ServerActiveObject *puncher,
551 float time_from_last_punch)
554 // Delete unknown LuaEntities when punched
559 // It's best that attachments cannot be punched
563 ItemStack *punchitem = NULL;
564 ItemStack punchitem_static;
566 punchitem_static = puncher->getWieldedItem();
567 punchitem = &punchitem_static;
570 PunchDamageResult result = getPunchDamage(
574 time_from_last_punch);
576 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
577 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
579 if (!damage_handled) {
580 if (result.did_punch) {
581 setHP(getHP() - result.damage);
583 if (result.damage > 0) {
584 std::string punchername = puncher ? puncher->getDescription() : "nil";
586 actionstream << getDescription() << " punched by "
587 << punchername << ", damage " << result.damage
588 << " hp, health now " << getHP() << " hp" << std::endl;
591 std::string str = gob_cmd_punched(result.damage, getHP());
592 // create message and add to list
593 ActiveObjectMessage aom(getId(), true, str);
594 m_messages_out.push(aom);
600 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
608 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
612 // It's best that attachments cannot be clicked
615 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
618 void LuaEntitySAO::setPos(const v3f &pos)
622 m_base_position = pos;
623 sendPosition(false, true);
626 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
630 m_base_position = pos;
632 sendPosition(true, true);
635 float LuaEntitySAO::getMinimumSavedMovement()
640 std::string LuaEntitySAO::getDescription()
642 std::ostringstream os(std::ios::binary);
643 os<<"LuaEntitySAO at (";
644 os<<(m_base_position.X/BS)<<",";
645 os<<(m_base_position.Y/BS)<<",";
646 os<<(m_base_position.Z/BS);
651 void LuaEntitySAO::setHP(s16 hp)
657 s16 LuaEntitySAO::getHP() const
662 void LuaEntitySAO::setVelocity(v3f velocity)
664 m_velocity = velocity;
667 v3f LuaEntitySAO::getVelocity()
672 void LuaEntitySAO::setAcceleration(v3f acceleration)
674 m_acceleration = acceleration;
677 v3f LuaEntitySAO::getAcceleration()
679 return m_acceleration;
682 void LuaEntitySAO::setTextureMod(const std::string &mod)
684 std::string str = gob_cmd_set_texture_mod(mod);
685 m_current_texture_modifier = mod;
686 // create message and add to list
687 ActiveObjectMessage aom(getId(), true, str);
688 m_messages_out.push(aom);
691 std::string LuaEntitySAO::getTextureMod() const
693 return m_current_texture_modifier;
696 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
697 bool select_horiz_by_yawpitch)
699 std::string str = gob_cmd_set_sprite(
703 select_horiz_by_yawpitch
705 // create message and add to list
706 ActiveObjectMessage aom(getId(), true, str);
707 m_messages_out.push(aom);
710 std::string LuaEntitySAO::getName()
715 std::string LuaEntitySAO::getPropertyPacket()
717 return gob_cmd_set_properties(m_prop);
720 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
722 // If the object is attached client-side, don't waste bandwidth sending its position to clients
726 m_last_sent_move_precision = m_base_position.getDistanceFrom(
727 m_last_sent_position);
728 m_last_sent_position_timer = 0;
729 m_last_sent_yaw = m_yaw;
730 m_last_sent_position = m_base_position;
731 m_last_sent_velocity = m_velocity;
732 //m_last_sent_acceleration = m_acceleration;
734 float update_interval = m_env->getSendRecommendedInterval();
736 std::string str = gob_cmd_update_position(
745 // create message and add to list
746 ActiveObjectMessage aom(getId(), false, str);
747 m_messages_out.push(aom);
750 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
754 //update collision box
755 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
756 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
758 toset->MinEdge += m_base_position;
759 toset->MaxEdge += m_base_position;
767 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
769 if (!m_prop.is_visible || !m_prop.pointable) {
773 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
774 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
779 bool LuaEntitySAO::collideWithObjects() const
781 return m_prop.collideWithObjects;
788 // No prototype, PlayerSAO does not need to be deserialized
790 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
791 bool is_singleplayer):
792 UnitSAO(env_, v3f(0,0,0)),
795 m_is_singleplayer(is_singleplayer)
797 assert(m_peer_id != 0); // pre-condition
799 m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
800 m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
801 m_prop.physical = false;
803 m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
804 m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
805 m_prop.pointable = true;
806 // start of default appearance, this should be overwritten by LUA
807 m_prop.visual = "upright_sprite";
808 m_prop.visual_size = v2f(1, 2);
809 m_prop.textures.clear();
810 m_prop.textures.emplace_back("player.png");
811 m_prop.textures.emplace_back("player_back.png");
812 m_prop.colors.clear();
813 m_prop.colors.emplace_back(255, 255, 255, 255);
814 m_prop.spritediv = v2s16(1,1);
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_prop.can_zoom = true;
820 m_hp = m_prop.hp_max;
821 m_breath = m_prop.breath_max;
824 PlayerSAO::~PlayerSAO()
826 if(m_inventory != &m_player->inventory)
830 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
835 m_inventory = &m_player->inventory;
838 v3f PlayerSAO::getEyeOffset() const
840 return v3f(0, BS * 1.625f, 0);
843 std::string PlayerSAO::getDescription()
845 return std::string("player ") + m_player->getName();
848 // Called after id has been set and has been inserted in environment
849 void PlayerSAO::addedToEnvironment(u32 dtime_s)
851 ServerActiveObject::addedToEnvironment(dtime_s);
852 ServerActiveObject::setBasePosition(m_base_position);
853 m_player->setPlayerSAO(this);
854 m_player->peer_id = m_peer_id;
855 m_last_good_position = m_base_position;
858 // Called before removing from environment
859 void PlayerSAO::removingFromEnvironment()
861 ServerActiveObject::removingFromEnvironment();
862 if (m_player->getPlayerSAO() == this) {
863 unlinkPlayerSessionAndSave();
864 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
865 m_env->deleteParticleSpawner(attached_particle_spawner, false);
870 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
872 std::ostringstream os(std::ios::binary);
875 writeU8(os, 1); // version
876 os << serializeString(m_player->getName()); // name
877 writeU8(os, 1); // is_player
878 writeS16(os, getId()); //id
879 writeV3F1000(os, m_base_position);
880 writeF1000(os, m_yaw);
881 writeS16(os, getHP());
883 std::ostringstream msg_os(std::ios::binary);
884 msg_os << serializeLongString(getPropertyPacket()); // message 1
885 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
886 msg_os << serializeLongString(gob_cmd_update_animation(
887 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
888 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
889 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
890 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
891 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
893 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
894 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
895 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
896 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
897 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
898 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
899 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
900 int message_count = 6 + m_bone_position.size();
901 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
902 ii != m_attachment_child_ids.end(); ++ii) {
903 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
905 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
906 obj->getClientInitializationData(protocol_version)));
910 writeU8(os, message_count);
911 os.write(msg_os.str().c_str(), msg_os.str().size());
917 void PlayerSAO::getStaticData(std::string * result) const
919 FATAL_ERROR("Deprecated function");
922 void PlayerSAO::step(float dtime, bool send_recommended)
924 if (m_drowning_interval.step(dtime, 2.0)) {
926 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
927 MapNode n = m_env->getMap().getNodeNoEx(p);
928 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
929 // If node generates drown
930 if (c.drowning > 0 && m_hp > 0) {
932 setBreath(m_breath - 1);
934 // No more breath, damage player
936 setHP(m_hp - c.drowning);
937 m_env->getGameDef()->SendPlayerHPOrDie(this);
942 if (m_breathing_interval.step(dtime, 0.5)) {
944 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
945 MapNode n = m_env->getMap().getNodeNoEx(p);
946 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
947 // If player is alive & no drowning, breath
948 if (m_hp > 0 && m_breath < m_prop.breath_max && c.drowning == 0)
949 setBreath(m_breath + 1);
952 if (m_node_hurt_interval.step(dtime, 1.0)) {
953 // Feet, middle and head
954 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
955 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
956 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
957 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
958 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
959 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
961 u32 damage_per_second = 0;
962 damage_per_second = MYMAX(damage_per_second,
963 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
964 damage_per_second = MYMAX(damage_per_second,
965 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
966 damage_per_second = MYMAX(damage_per_second,
967 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
969 if (damage_per_second != 0 && m_hp > 0) {
970 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
972 m_env->getGameDef()->SendPlayerHPOrDie(this);
976 if (!m_properties_sent) {
977 m_properties_sent = true;
978 std::string str = getPropertyPacket();
979 // create message and add to list
980 ActiveObjectMessage aom(getId(), true, str);
981 m_messages_out.push(aom);
984 // If attached, check that our parent is still there. If it isn't, detach.
985 if(m_attachment_parent_id && !isAttached())
987 m_attachment_parent_id = 0;
988 m_attachment_bone = "";
989 m_attachment_position = v3f(0,0,0);
990 m_attachment_rotation = v3f(0,0,0);
991 setBasePosition(m_last_good_position);
992 m_env->getGameDef()->SendMovePlayer(m_peer_id);
995 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
997 // Set lag pool maximums based on estimated lag
998 const float LAG_POOL_MIN = 5.0;
999 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1000 if(lag_pool_max < LAG_POOL_MIN)
1001 lag_pool_max = LAG_POOL_MIN;
1002 m_dig_pool.setMax(lag_pool_max);
1003 m_move_pool.setMax(lag_pool_max);
1005 // Increment cheat prevention timers
1006 m_dig_pool.add(dtime);
1007 m_move_pool.add(dtime);
1008 m_time_from_last_teleport += dtime;
1009 m_time_from_last_punch += dtime;
1010 m_nocheat_dig_time += dtime;
1012 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1013 // If the object gets detached this comes into effect automatically from the last known origin
1015 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1016 m_last_good_position = pos;
1017 setBasePosition(pos);
1020 if (!send_recommended)
1023 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1024 if(m_position_not_sent && !isAttached())
1026 m_position_not_sent = false;
1027 float update_interval = m_env->getSendRecommendedInterval();
1029 if(isAttached()) // Just in case we ever do send attachment position too
1030 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1032 pos = m_base_position;
1033 std::string str = gob_cmd_update_position(
1042 // create message and add to list
1043 ActiveObjectMessage aom(getId(), false, str);
1044 m_messages_out.push(aom);
1047 if (!m_armor_groups_sent) {
1048 m_armor_groups_sent = true;
1049 std::string str = gob_cmd_update_armor_groups(
1051 // create message and add to list
1052 ActiveObjectMessage aom(getId(), true, str);
1053 m_messages_out.push(aom);
1056 if (!m_physics_override_sent) {
1057 m_physics_override_sent = true;
1058 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1059 m_physics_override_jump, m_physics_override_gravity,
1060 m_physics_override_sneak, m_physics_override_sneak_glitch,
1061 m_physics_override_new_move);
1062 // create message and add to list
1063 ActiveObjectMessage aom(getId(), true, str);
1064 m_messages_out.push(aom);
1067 if (!m_animation_sent) {
1068 m_animation_sent = true;
1069 std::string str = gob_cmd_update_animation(
1070 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1071 // create message and add to list
1072 ActiveObjectMessage aom(getId(), true, str);
1073 m_messages_out.push(aom);
1076 if (!m_bone_position_sent) {
1077 m_bone_position_sent = true;
1078 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1079 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1080 std::string str = gob_cmd_update_bone_position((*ii).first,
1081 (*ii).second.X, (*ii).second.Y);
1082 // create message and add to list
1083 ActiveObjectMessage aom(getId(), true, str);
1084 m_messages_out.push(aom);
1088 if (!m_attachment_sent){
1089 m_attachment_sent = true;
1090 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1091 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1092 // create message and add to list
1093 ActiveObjectMessage aom(getId(), true, str);
1094 m_messages_out.push(aom);
1098 void PlayerSAO::setBasePosition(const v3f &position)
1100 if (m_player && position != m_base_position)
1101 m_player->setDirty(true);
1103 // This needs to be ran for attachments too
1104 ServerActiveObject::setBasePosition(position);
1105 m_position_not_sent = true;
1108 void PlayerSAO::setPos(const v3f &pos)
1113 setBasePosition(pos);
1114 // Movement caused by this command is always valid
1115 m_last_good_position = pos;
1116 m_move_pool.empty();
1117 m_time_from_last_teleport = 0.0;
1118 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1121 void PlayerSAO::moveTo(v3f pos, bool continuous)
1126 setBasePosition(pos);
1127 // Movement caused by this command is always valid
1128 m_last_good_position = pos;
1129 m_move_pool.empty();
1130 m_time_from_last_teleport = 0.0;
1131 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1134 void PlayerSAO::setYaw(const float yaw)
1136 if (m_player && yaw != m_yaw)
1137 m_player->setDirty(true);
1139 UnitSAO::setYaw(yaw);
1142 void PlayerSAO::setFov(const float fov)
1144 if (m_player && fov != m_fov)
1145 m_player->setDirty(true);
1150 void PlayerSAO::setWantedRange(const s16 range)
1152 if (m_player && range != m_wanted_range)
1153 m_player->setDirty(true);
1155 m_wanted_range = range;
1158 void PlayerSAO::setYawAndSend(const float yaw)
1161 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1164 void PlayerSAO::setPitch(const float pitch)
1166 if (m_player && pitch != m_pitch)
1167 m_player->setDirty(true);
1172 void PlayerSAO::setPitchAndSend(const float pitch)
1175 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1178 int PlayerSAO::punch(v3f dir,
1179 const ToolCapabilities *toolcap,
1180 ServerActiveObject *puncher,
1181 float time_from_last_punch)
1183 // It's best that attachments cannot be punched
1190 // No effect if PvP disabled
1191 if (!g_settings->getBool("enable_pvp")) {
1192 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1193 std::string str = gob_cmd_punched(0, getHP());
1194 // create message and add to list
1195 ActiveObjectMessage aom(getId(), true, str);
1196 m_messages_out.push(aom);
1201 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1202 time_from_last_punch);
1204 std::string punchername = "nil";
1207 punchername = puncher->getDescription();
1209 PlayerSAO *playersao = m_player->getPlayerSAO();
1211 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1212 puncher, time_from_last_punch, toolcap, dir,
1215 if (!damage_handled) {
1216 setHP(getHP() - hitparams.hp);
1217 } else { // override client prediction
1218 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1219 std::string str = gob_cmd_punched(0, getHP());
1220 // create message and add to list
1221 ActiveObjectMessage aom(getId(), true, str);
1222 m_messages_out.push(aom);
1227 actionstream << "Player " << m_player->getName() << " punched by "
1229 if (!damage_handled) {
1230 actionstream << ", damage " << hitparams.hp << " HP";
1232 actionstream << ", damage handled by lua";
1234 actionstream << std::endl;
1236 return hitparams.wear;
1239 s16 PlayerSAO::readDamage()
1241 s16 damage = m_damage;
1246 void PlayerSAO::setHP(s16 hp)
1250 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1253 hp = oldhp + hp_change;
1257 else if (hp > m_prop.hp_max)
1260 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1267 m_damage += (oldhp - hp);
1269 // Update properties on death
1270 if ((hp == 0) != (oldhp == 0))
1271 m_properties_sent = false;
1274 void PlayerSAO::setBreath(const u16 breath, bool send)
1276 if (m_player && breath != m_breath)
1277 m_player->setDirty(true);
1279 m_breath = MYMIN(breath, m_prop.breath_max);
1282 m_env->getGameDef()->SendPlayerBreath(this);
1285 Inventory* PlayerSAO::getInventory()
1289 const Inventory* PlayerSAO::getInventory() const
1294 InventoryLocation PlayerSAO::getInventoryLocation() const
1296 InventoryLocation loc;
1297 loc.setPlayer(m_player->getName());
1301 std::string PlayerSAO::getWieldList() const
1306 ItemStack PlayerSAO::getWieldedItem() const
1308 const Inventory *inv = getInventory();
1310 const InventoryList *mlist = inv->getList(getWieldList());
1311 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1312 ret = mlist->getItem(getWieldIndex());
1316 ItemStack PlayerSAO::getWieldedItemOrHand() const
1318 const Inventory *inv = getInventory();
1320 const InventoryList *mlist = inv->getList(getWieldList());
1321 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1322 ret = mlist->getItem(getWieldIndex());
1323 if (ret.name.empty()) {
1324 const InventoryList *hlist = inv->getList("hand");
1326 ret = hlist->getItem(0);
1331 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1333 Inventory *inv = getInventory();
1335 InventoryList *mlist = inv->getList(getWieldList());
1337 mlist->changeItem(getWieldIndex(), item);
1344 int PlayerSAO::getWieldIndex() const
1346 return m_wield_index;
1349 void PlayerSAO::setWieldIndex(int i)
1351 if(i != m_wield_index) {
1356 // Erase the peer id and make the object for removal
1357 void PlayerSAO::disconnected()
1363 void PlayerSAO::unlinkPlayerSessionAndSave()
1365 assert(m_player->getPlayerSAO() == this);
1366 m_player->peer_id = 0;
1367 m_env->savePlayer(m_player);
1368 m_player->setPlayerSAO(NULL);
1369 m_env->removePlayer(m_player);
1372 std::string PlayerSAO::getPropertyPacket()
1374 m_prop.is_visible = (true);
1375 return gob_cmd_set_properties(m_prop);
1378 bool PlayerSAO::checkMovementCheat()
1380 if (isAttached() || m_is_singleplayer ||
1381 g_settings->getBool("disable_anticheat")) {
1382 m_last_good_position = m_base_position;
1386 bool cheated = false;
1388 Check player movements
1390 NOTE: Actually the server should handle player physics like the
1391 client does and compare player's position to what is calculated
1392 on our side. This is required when eg. players fly due to an
1393 explosion. Altough a node-based alternative might be possible
1394 too, and much more lightweight.
1397 float player_max_speed = 0;
1399 if (m_privs.count("fast") != 0) {
1401 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1404 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1406 // Tolerance. The lag pool does this a bit.
1407 //player_max_speed *= 2.5;
1409 v3f diff = (m_base_position - m_last_good_position);
1410 float d_vert = diff.Y;
1412 float d_horiz = diff.getLength();
1413 float required_time = d_horiz / player_max_speed;
1415 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1416 required_time = d_vert / player_max_speed; // Moving upwards
1418 if (m_move_pool.grab(required_time)) {
1419 m_last_good_position = m_base_position;
1421 const float LAG_POOL_MIN = 5.0;
1422 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1423 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1424 if (m_time_from_last_teleport > lag_pool_max) {
1425 actionstream << "Player " << m_player->getName()
1426 << " moved too fast; resetting position"
1430 setBasePosition(m_last_good_position);
1435 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1437 //update collision box
1438 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1439 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1441 toset->MinEdge += m_base_position;
1442 toset->MaxEdge += m_base_position;
1446 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1448 if (!m_prop.is_visible || !m_prop.pointable) {
1452 toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
1453 toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;