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::setBonePosition(const std::string &bone, v3f position, v3f rotation)
160 // store these so they can be updated to clients
161 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
162 m_bone_position_sent = false;
165 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
167 *position = m_bone_position[bone].X;
168 *rotation = m_bone_position[bone].Y;
171 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
173 // Attachments need to be handled on both the server and client.
174 // If we just attach on the server, we can only copy the position of the parent. Attachments
175 // are still sent to clients at an interval so players might see them lagging, plus we can't
176 // read and attach to skeletal bones.
177 // If we just attach on the client, the server still sees the child at its original location.
178 // This breaks some things so we also give the server the most accurate representation
179 // even if players only see the client changes.
181 m_attachment_parent_id = parent_id;
182 m_attachment_bone = bone;
183 m_attachment_position = position;
184 m_attachment_rotation = rotation;
185 m_attachment_sent = false;
188 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
191 *parent_id = m_attachment_parent_id;
192 *bone = m_attachment_bone;
193 *position = m_attachment_position;
194 *rotation = m_attachment_rotation;
197 void UnitSAO::addAttachmentChild(int child_id)
199 m_attachment_child_ids.insert(child_id);
202 void UnitSAO::removeAttachmentChild(int child_id)
204 m_attachment_child_ids.erase(child_id);
207 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
209 return m_attachment_child_ids;
212 ObjectProperties* UnitSAO::accessObjectProperties()
217 void UnitSAO::notifyObjectPropertiesModified()
219 m_properties_sent = false;
226 // Prototype (registers item for deserialization)
227 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
229 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
230 const std::string &name, const std::string &state):
235 // Only register type if no environment supplied
237 ServerActiveObject::registerType(getType(), create);
242 LuaEntitySAO::~LuaEntitySAO()
245 m_env->getScriptIface()->luaentity_Remove(m_id);
248 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
249 m_env->deleteParticleSpawner(attached_particle_spawner, false);
253 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
255 ServerActiveObject::addedToEnvironment(dtime_s);
257 // Create entity from name
258 m_registered = m_env->getScriptIface()->
259 luaentity_Add(m_id, m_init_name.c_str());
263 m_env->getScriptIface()->
264 luaentity_GetProperties(m_id, &m_prop);
265 // Initialize HP from properties
266 m_hp = m_prop.hp_max;
267 // Activate entity, supplying serialized state
268 m_env->getScriptIface()->
269 luaentity_Activate(m_id, m_init_state, dtime_s);
271 m_prop.infotext = m_init_name;
275 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
276 const std::string &data)
284 std::istringstream is(data, std::ios::binary);
286 u8 version = readU8(is);
287 // check if version is supported
289 name = deSerializeString(is);
290 state = deSerializeLongString(is);
292 else if(version == 1){
293 name = deSerializeString(is);
294 state = deSerializeLongString(is);
296 velocity = readV3F1000(is);
301 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
302 <<state<<"\")"<<std::endl;
303 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
305 sao->m_velocity = velocity;
310 void LuaEntitySAO::step(float dtime, bool send_recommended)
312 if(!m_properties_sent)
314 m_properties_sent = true;
315 std::string str = getPropertyPacket();
316 // create message and add to list
317 ActiveObjectMessage aom(getId(), true, str);
318 m_messages_out.push(aom);
321 // If attached, check that our parent is still there. If it isn't, detach.
322 if(m_attachment_parent_id && !isAttached())
324 m_attachment_parent_id = 0;
325 m_attachment_bone = "";
326 m_attachment_position = v3f(0,0,0);
327 m_attachment_rotation = v3f(0,0,0);
328 sendPosition(false, true);
331 m_last_sent_position_timer += dtime;
333 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
334 // If the object gets detached this comes into effect automatically from the last known origin
337 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
338 m_base_position = pos;
339 m_velocity = v3f(0,0,0);
340 m_acceleration = v3f(0,0,0);
345 aabb3f box = m_prop.collisionbox;
348 collisionMoveResult moveresult;
349 f32 pos_max_d = BS*0.25; // Distance per iteration
350 v3f p_pos = m_base_position;
351 v3f p_velocity = m_velocity;
352 v3f p_acceleration = m_acceleration;
353 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
354 pos_max_d, box, m_prop.stepheight, dtime,
355 &p_pos, &p_velocity, p_acceleration,
356 this, m_prop.collideWithObjects);
359 m_base_position = p_pos;
360 m_velocity = p_velocity;
361 m_acceleration = p_acceleration;
363 m_base_position += dtime * m_velocity + 0.5 * dtime
364 * dtime * m_acceleration;
365 m_velocity += dtime * m_acceleration;
368 if((m_prop.automatic_face_movement_dir) &&
369 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
371 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
372 + m_prop.automatic_face_movement_dir_offset;
373 float max_rotation_delta =
374 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
376 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
377 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
379 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
387 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
390 // Remove LuaEntity beyond terrain edges
392 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
394 if (!m_pending_deactivation &&
395 map->saoPositionOverLimit(m_base_position)) {
396 infostream << "Remove SAO " << m_id << "(" << m_init_name
397 << "), outside of limits" << std::endl;
398 m_pending_deactivation = true;
404 if (!send_recommended)
409 // TODO: force send when acceleration changes enough?
410 float minchange = 0.2*BS;
411 if(m_last_sent_position_timer > 1.0){
413 } else if(m_last_sent_position_timer > 0.2){
416 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
417 move_d += m_last_sent_move_precision;
418 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
419 if(move_d > minchange || vel_d > minchange ||
420 fabs(m_yaw - m_last_sent_yaw) > 1.0){
421 sendPosition(true, false);
425 if (!m_armor_groups_sent) {
426 m_armor_groups_sent = true;
427 std::string str = gob_cmd_update_armor_groups(
429 // create message and add to list
430 ActiveObjectMessage aom(getId(), true, str);
431 m_messages_out.push(aom);
434 if (!m_animation_sent) {
435 m_animation_sent = true;
436 std::string str = gob_cmd_update_animation(
437 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
438 // create message and add to list
439 ActiveObjectMessage aom(getId(), true, str);
440 m_messages_out.push(aom);
443 if (!m_bone_position_sent) {
444 m_bone_position_sent = true;
445 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
446 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
447 std::string str = gob_cmd_update_bone_position((*ii).first,
448 (*ii).second.X, (*ii).second.Y);
449 // create message and add to list
450 ActiveObjectMessage aom(getId(), true, str);
451 m_messages_out.push(aom);
455 if (!m_attachment_sent) {
456 m_attachment_sent = true;
457 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
458 // create message and add to list
459 ActiveObjectMessage aom(getId(), true, str);
460 m_messages_out.push(aom);
464 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
466 std::ostringstream os(std::ios::binary);
469 writeU8(os, 1); // version
470 os << serializeString(""); // name
471 writeU8(os, 0); // is_player
472 writeS16(os, getId()); //id
473 writeV3F1000(os, m_base_position);
474 writeF1000(os, m_yaw);
477 std::ostringstream msg_os(std::ios::binary);
478 msg_os << serializeLongString(getPropertyPacket()); // message 1
479 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
480 msg_os << serializeLongString(gob_cmd_update_animation(
481 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
482 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
483 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
484 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
485 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
487 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
488 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
489 int message_count = 4 + m_bone_position.size();
490 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
491 (ii != m_attachment_child_ids.end()); ++ii) {
492 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
494 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
495 obj->getClientInitializationData(protocol_version)));
499 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
502 writeU8(os, message_count);
503 os.write(msg_os.str().c_str(), msg_os.str().size());
509 void LuaEntitySAO::getStaticData(std::string *result) const
511 verbosestream<<FUNCTION_NAME<<std::endl;
512 std::ostringstream os(std::ios::binary);
516 os<<serializeString(m_init_name);
519 std::string state = m_env->getScriptIface()->
520 luaentity_GetStaticdata(m_id);
521 os<<serializeLongString(state);
523 os<<serializeLongString(m_init_state);
528 writeV3F1000(os, m_velocity);
530 writeF1000(os, m_yaw);
534 int LuaEntitySAO::punch(v3f dir,
535 const ToolCapabilities *toolcap,
536 ServerActiveObject *puncher,
537 float time_from_last_punch)
540 // Delete unknown LuaEntities when punched
545 // It's best that attachments cannot be punched
549 ItemStack *punchitem = NULL;
550 ItemStack punchitem_static;
552 punchitem_static = puncher->getWieldedItem();
553 punchitem = &punchitem_static;
556 PunchDamageResult result = getPunchDamage(
560 time_from_last_punch);
562 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
563 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
565 if (!damage_handled) {
566 if (result.did_punch) {
567 setHP(getHP() - result.damage);
569 if (result.damage > 0) {
570 std::string punchername = puncher ? puncher->getDescription() : "nil";
572 actionstream << getDescription() << " punched by "
573 << punchername << ", damage " << result.damage
574 << " hp, health now " << getHP() << " hp" << std::endl;
577 std::string str = gob_cmd_punched(result.damage, getHP());
578 // create message and add to list
579 ActiveObjectMessage aom(getId(), true, str);
580 m_messages_out.push(aom);
586 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
594 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
598 // It's best that attachments cannot be clicked
601 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
604 void LuaEntitySAO::setPos(const v3f &pos)
608 m_base_position = pos;
609 sendPosition(false, true);
612 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
616 m_base_position = pos;
618 sendPosition(true, true);
621 float LuaEntitySAO::getMinimumSavedMovement()
626 std::string LuaEntitySAO::getDescription()
628 std::ostringstream os(std::ios::binary);
629 os<<"LuaEntitySAO at (";
630 os<<(m_base_position.X/BS)<<",";
631 os<<(m_base_position.Y/BS)<<",";
632 os<<(m_base_position.Z/BS);
637 void LuaEntitySAO::setHP(s16 hp)
643 s16 LuaEntitySAO::getHP() const
648 void LuaEntitySAO::setVelocity(v3f velocity)
650 m_velocity = velocity;
653 v3f LuaEntitySAO::getVelocity()
658 void LuaEntitySAO::setAcceleration(v3f acceleration)
660 m_acceleration = acceleration;
663 v3f LuaEntitySAO::getAcceleration()
665 return m_acceleration;
668 void LuaEntitySAO::setTextureMod(const std::string &mod)
670 std::string str = gob_cmd_set_texture_mod(mod);
671 m_current_texture_modifier = mod;
672 // create message and add to list
673 ActiveObjectMessage aom(getId(), true, str);
674 m_messages_out.push(aom);
677 std::string LuaEntitySAO::getTextureMod() const
679 return m_current_texture_modifier;
682 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
683 bool select_horiz_by_yawpitch)
685 std::string str = gob_cmd_set_sprite(
689 select_horiz_by_yawpitch
691 // create message and add to list
692 ActiveObjectMessage aom(getId(), true, str);
693 m_messages_out.push(aom);
696 std::string LuaEntitySAO::getName()
701 std::string LuaEntitySAO::getPropertyPacket()
703 return gob_cmd_set_properties(m_prop);
706 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
708 // If the object is attached client-side, don't waste bandwidth sending its position to clients
712 m_last_sent_move_precision = m_base_position.getDistanceFrom(
713 m_last_sent_position);
714 m_last_sent_position_timer = 0;
715 m_last_sent_yaw = m_yaw;
716 m_last_sent_position = m_base_position;
717 m_last_sent_velocity = m_velocity;
718 //m_last_sent_acceleration = m_acceleration;
720 float update_interval = m_env->getSendRecommendedInterval();
722 std::string str = gob_cmd_update_position(
731 // create message and add to list
732 ActiveObjectMessage aom(getId(), false, str);
733 m_messages_out.push(aom);
736 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
740 //update collision box
741 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
742 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
744 toset->MinEdge += m_base_position;
745 toset->MaxEdge += m_base_position;
753 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
755 if (!m_prop.is_visible) {
759 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
760 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
765 bool LuaEntitySAO::collideWithObjects() const
767 return m_prop.collideWithObjects;
774 // No prototype, PlayerSAO does not need to be deserialized
776 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
777 bool is_singleplayer):
778 UnitSAO(env_, v3f(0,0,0)),
781 m_is_singleplayer(is_singleplayer)
783 assert(m_peer_id != 0); // pre-condition
785 m_prop.hp_max = PLAYER_MAX_HP;
786 m_prop.physical = false;
788 m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
789 // start of default appearance, this should be overwritten by LUA
790 m_prop.visual = "upright_sprite";
791 m_prop.visual_size = v2f(1, 2);
792 m_prop.textures.clear();
793 m_prop.textures.emplace_back("player.png");
794 m_prop.textures.emplace_back("player_back.png");
795 m_prop.colors.clear();
796 m_prop.colors.emplace_back(255, 255, 255, 255);
797 m_prop.spritediv = v2s16(1,1);
798 // end of default appearance
799 m_prop.is_visible = true;
800 m_prop.makes_footstep_sound = true;
801 m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
802 m_hp = PLAYER_MAX_HP;
805 PlayerSAO::~PlayerSAO()
807 if(m_inventory != &m_player->inventory)
811 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
816 m_inventory = &m_player->inventory;
819 v3f PlayerSAO::getEyeOffset() const
821 return v3f(0, BS * 1.625f, 0);
824 std::string PlayerSAO::getDescription()
826 return std::string("player ") + m_player->getName();
829 // Called after id has been set and has been inserted in environment
830 void PlayerSAO::addedToEnvironment(u32 dtime_s)
832 ServerActiveObject::addedToEnvironment(dtime_s);
833 ServerActiveObject::setBasePosition(m_base_position);
834 m_player->setPlayerSAO(this);
835 m_player->peer_id = m_peer_id;
836 m_last_good_position = m_base_position;
839 // Called before removing from environment
840 void PlayerSAO::removingFromEnvironment()
842 ServerActiveObject::removingFromEnvironment();
843 if (m_player->getPlayerSAO() == this) {
844 unlinkPlayerSessionAndSave();
845 for (u32 attached_particle_spawner : m_attached_particle_spawners) {
846 m_env->deleteParticleSpawner(attached_particle_spawner, false);
851 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
853 std::ostringstream os(std::ios::binary);
856 writeU8(os, 1); // version
857 os << serializeString(m_player->getName()); // name
858 writeU8(os, 1); // is_player
859 writeS16(os, getId()); //id
860 writeV3F1000(os, m_base_position);
861 writeF1000(os, m_yaw);
862 writeS16(os, getHP());
864 std::ostringstream msg_os(std::ios::binary);
865 msg_os << serializeLongString(getPropertyPacket()); // message 1
866 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
867 msg_os << serializeLongString(gob_cmd_update_animation(
868 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
869 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
870 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
871 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
872 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
874 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
875 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
876 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
877 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
878 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
879 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
880 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
881 int message_count = 6 + m_bone_position.size();
882 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
883 ii != m_attachment_child_ids.end(); ++ii) {
884 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
886 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
887 obj->getClientInitializationData(protocol_version)));
891 writeU8(os, message_count);
892 os.write(msg_os.str().c_str(), msg_os.str().size());
898 void PlayerSAO::getStaticData(std::string * result) const
900 FATAL_ERROR("Deprecated function");
903 void PlayerSAO::step(float dtime, bool send_recommended)
905 if (m_drowning_interval.step(dtime, 2.0)) {
907 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
908 MapNode n = m_env->getMap().getNodeNoEx(p);
909 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
910 // If node generates drown
911 if (c.drowning > 0 && m_hp > 0) {
913 setBreath(m_breath - 1);
915 // No more breath, damage player
917 setHP(m_hp - c.drowning);
918 m_env->getGameDef()->SendPlayerHPOrDie(this);
923 if (m_breathing_interval.step(dtime, 0.5)) {
925 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
926 MapNode n = m_env->getMap().getNodeNoEx(p);
927 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
928 // If player is alive & no drowning, breath
929 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
930 setBreath(m_breath + 1);
933 if (m_node_hurt_interval.step(dtime, 1.0)) {
934 // Feet, middle and head
935 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
936 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
937 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
938 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
939 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
940 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
942 u32 damage_per_second = 0;
943 damage_per_second = MYMAX(damage_per_second,
944 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
945 damage_per_second = MYMAX(damage_per_second,
946 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
947 damage_per_second = MYMAX(damage_per_second,
948 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
950 if (damage_per_second != 0 && m_hp > 0) {
951 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
953 m_env->getGameDef()->SendPlayerHPOrDie(this);
957 if (!m_properties_sent) {
958 m_properties_sent = true;
959 std::string str = getPropertyPacket();
960 // create message and add to list
961 ActiveObjectMessage aom(getId(), true, str);
962 m_messages_out.push(aom);
965 // If attached, check that our parent is still there. If it isn't, detach.
966 if(m_attachment_parent_id && !isAttached())
968 m_attachment_parent_id = 0;
969 m_attachment_bone = "";
970 m_attachment_position = v3f(0,0,0);
971 m_attachment_rotation = v3f(0,0,0);
972 setBasePosition(m_last_good_position);
973 m_env->getGameDef()->SendMovePlayer(m_peer_id);
976 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
978 // Set lag pool maximums based on estimated lag
979 const float LAG_POOL_MIN = 5.0;
980 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
981 if(lag_pool_max < LAG_POOL_MIN)
982 lag_pool_max = LAG_POOL_MIN;
983 m_dig_pool.setMax(lag_pool_max);
984 m_move_pool.setMax(lag_pool_max);
986 // Increment cheat prevention timers
987 m_dig_pool.add(dtime);
988 m_move_pool.add(dtime);
989 m_time_from_last_teleport += dtime;
990 m_time_from_last_punch += dtime;
991 m_nocheat_dig_time += dtime;
993 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
994 // If the object gets detached this comes into effect automatically from the last known origin
996 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
997 m_last_good_position = pos;
998 setBasePosition(pos);
1001 if (!send_recommended)
1004 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1005 if(m_position_not_sent && !isAttached())
1007 m_position_not_sent = false;
1008 float update_interval = m_env->getSendRecommendedInterval();
1010 if(isAttached()) // Just in case we ever do send attachment position too
1011 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1013 pos = m_base_position;
1014 std::string str = gob_cmd_update_position(
1023 // create message and add to list
1024 ActiveObjectMessage aom(getId(), false, str);
1025 m_messages_out.push(aom);
1028 if (!m_armor_groups_sent) {
1029 m_armor_groups_sent = true;
1030 std::string str = gob_cmd_update_armor_groups(
1032 // create message and add to list
1033 ActiveObjectMessage aom(getId(), true, str);
1034 m_messages_out.push(aom);
1037 if (!m_physics_override_sent) {
1038 m_physics_override_sent = true;
1039 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1040 m_physics_override_jump, m_physics_override_gravity,
1041 m_physics_override_sneak, m_physics_override_sneak_glitch,
1042 m_physics_override_new_move);
1043 // create message and add to list
1044 ActiveObjectMessage aom(getId(), true, str);
1045 m_messages_out.push(aom);
1048 if (!m_animation_sent) {
1049 m_animation_sent = true;
1050 std::string str = gob_cmd_update_animation(
1051 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1052 // create message and add to list
1053 ActiveObjectMessage aom(getId(), true, str);
1054 m_messages_out.push(aom);
1057 if (!m_bone_position_sent) {
1058 m_bone_position_sent = true;
1059 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1060 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1061 std::string str = gob_cmd_update_bone_position((*ii).first,
1062 (*ii).second.X, (*ii).second.Y);
1063 // create message and add to list
1064 ActiveObjectMessage aom(getId(), true, str);
1065 m_messages_out.push(aom);
1069 if (!m_attachment_sent){
1070 m_attachment_sent = true;
1071 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1072 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1073 // create message and add to list
1074 ActiveObjectMessage aom(getId(), true, str);
1075 m_messages_out.push(aom);
1079 void PlayerSAO::setBasePosition(const v3f &position)
1081 if (m_player && position != m_base_position)
1082 m_player->setDirty(true);
1084 // This needs to be ran for attachments too
1085 ServerActiveObject::setBasePosition(position);
1086 m_position_not_sent = true;
1089 void PlayerSAO::setPos(const v3f &pos)
1094 setBasePosition(pos);
1095 // Movement caused by this command is always valid
1096 m_last_good_position = pos;
1097 m_move_pool.empty();
1098 m_time_from_last_teleport = 0.0;
1099 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1102 void PlayerSAO::moveTo(v3f pos, bool continuous)
1107 setBasePosition(pos);
1108 // Movement caused by this command is always valid
1109 m_last_good_position = pos;
1110 m_move_pool.empty();
1111 m_time_from_last_teleport = 0.0;
1112 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1115 void PlayerSAO::setYaw(const float yaw)
1117 if (m_player && yaw != m_yaw)
1118 m_player->setDirty(true);
1120 UnitSAO::setYaw(yaw);
1123 void PlayerSAO::setFov(const float fov)
1125 if (m_player && fov != m_fov)
1126 m_player->setDirty(true);
1131 void PlayerSAO::setWantedRange(const s16 range)
1133 if (m_player && range != m_wanted_range)
1134 m_player->setDirty(true);
1136 m_wanted_range = range;
1139 void PlayerSAO::setYawAndSend(const float yaw)
1142 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1145 void PlayerSAO::setPitch(const float pitch)
1147 if (m_player && pitch != m_pitch)
1148 m_player->setDirty(true);
1153 void PlayerSAO::setPitchAndSend(const float pitch)
1156 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1159 int PlayerSAO::punch(v3f dir,
1160 const ToolCapabilities *toolcap,
1161 ServerActiveObject *puncher,
1162 float time_from_last_punch)
1164 // It's best that attachments cannot be punched
1171 // No effect if PvP disabled
1172 if (!g_settings->getBool("enable_pvp")) {
1173 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1174 std::string str = gob_cmd_punched(0, getHP());
1175 // create message and add to list
1176 ActiveObjectMessage aom(getId(), true, str);
1177 m_messages_out.push(aom);
1182 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1183 time_from_last_punch);
1185 std::string punchername = "nil";
1188 punchername = puncher->getDescription();
1190 PlayerSAO *playersao = m_player->getPlayerSAO();
1192 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1193 puncher, time_from_last_punch, toolcap, dir,
1196 if (!damage_handled) {
1197 setHP(getHP() - hitparams.hp);
1198 } else { // override client prediction
1199 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1200 std::string str = gob_cmd_punched(0, getHP());
1201 // create message and add to list
1202 ActiveObjectMessage aom(getId(), true, str);
1203 m_messages_out.push(aom);
1208 actionstream << "Player " << m_player->getName() << " punched by "
1210 if (!damage_handled) {
1211 actionstream << ", damage " << hitparams.hp << " HP";
1213 actionstream << ", damage handled by lua";
1215 actionstream << std::endl;
1217 return hitparams.wear;
1220 s16 PlayerSAO::readDamage()
1222 s16 damage = m_damage;
1227 void PlayerSAO::setHP(s16 hp)
1231 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1234 hp = oldhp + hp_change;
1238 else if (hp > PLAYER_MAX_HP)
1241 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1248 m_damage += (oldhp - hp);
1250 // Update properties on death
1251 if ((hp == 0) != (oldhp == 0))
1252 m_properties_sent = false;
1255 void PlayerSAO::setBreath(const u16 breath, bool send)
1257 if (m_player && breath != m_breath)
1258 m_player->setDirty(true);
1260 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1263 m_env->getGameDef()->SendPlayerBreath(this);
1266 Inventory* PlayerSAO::getInventory()
1270 const Inventory* PlayerSAO::getInventory() const
1275 InventoryLocation PlayerSAO::getInventoryLocation() const
1277 InventoryLocation loc;
1278 loc.setPlayer(m_player->getName());
1282 std::string PlayerSAO::getWieldList() const
1287 ItemStack PlayerSAO::getWieldedItem() const
1289 const Inventory *inv = getInventory();
1291 const InventoryList *mlist = inv->getList(getWieldList());
1292 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1293 ret = mlist->getItem(getWieldIndex());
1297 ItemStack PlayerSAO::getWieldedItemOrHand() const
1299 const Inventory *inv = getInventory();
1301 const InventoryList *mlist = inv->getList(getWieldList());
1302 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1303 ret = mlist->getItem(getWieldIndex());
1304 if (ret.name.empty()) {
1305 const InventoryList *hlist = inv->getList("hand");
1307 ret = hlist->getItem(0);
1312 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1314 Inventory *inv = getInventory();
1316 InventoryList *mlist = inv->getList(getWieldList());
1318 mlist->changeItem(getWieldIndex(), item);
1325 int PlayerSAO::getWieldIndex() const
1327 return m_wield_index;
1330 void PlayerSAO::setWieldIndex(int i)
1332 if(i != m_wield_index) {
1337 // Erase the peer id and make the object for removal
1338 void PlayerSAO::disconnected()
1344 void PlayerSAO::unlinkPlayerSessionAndSave()
1346 assert(m_player->getPlayerSAO() == this);
1347 m_player->peer_id = 0;
1348 m_env->savePlayer(m_player);
1349 m_player->setPlayerSAO(NULL);
1350 m_env->removePlayer(m_player);
1353 std::string PlayerSAO::getPropertyPacket()
1355 m_prop.is_visible = (true);
1356 return gob_cmd_set_properties(m_prop);
1359 bool PlayerSAO::checkMovementCheat()
1361 if (isAttached() || m_is_singleplayer ||
1362 g_settings->getBool("disable_anticheat")) {
1363 m_last_good_position = m_base_position;
1367 bool cheated = false;
1369 Check player movements
1371 NOTE: Actually the server should handle player physics like the
1372 client does and compare player's position to what is calculated
1373 on our side. This is required when eg. players fly due to an
1374 explosion. Altough a node-based alternative might be possible
1375 too, and much more lightweight.
1378 float player_max_speed = 0;
1380 if (m_privs.count("fast") != 0) {
1382 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1385 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1387 // Tolerance. The lag pool does this a bit.
1388 //player_max_speed *= 2.5;
1390 v3f diff = (m_base_position - m_last_good_position);
1391 float d_vert = diff.Y;
1393 float d_horiz = diff.getLength();
1394 float required_time = d_horiz / player_max_speed;
1396 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1397 required_time = d_vert / player_max_speed; // Moving upwards
1399 if (m_move_pool.grab(required_time)) {
1400 m_last_good_position = m_base_position;
1402 const float LAG_POOL_MIN = 5.0;
1403 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1404 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1405 if (m_time_from_last_teleport > lag_pool_max) {
1406 actionstream << "Player " << m_player->getName()
1407 << " moved too fast; resetting position"
1411 setBasePosition(m_last_good_position);
1416 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1418 //update collision box
1419 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
1420 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
1422 toset->MinEdge += m_base_position;
1423 toset->MaxEdge += m_base_position;
1427 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1429 if (!m_prop.is_visible) {
1433 getCollisionBox(toset);