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 == false)
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 (std::unordered_set<u32>::iterator it = m_attached_particle_spawners.begin();
249 it != m_attached_particle_spawners.end(); ++it) {
250 m_env->deleteParticleSpawner(*it, false);
254 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
256 ServerActiveObject::addedToEnvironment(dtime_s);
258 // Create entity from name
259 m_registered = m_env->getScriptIface()->
260 luaentity_Add(m_id, m_init_name.c_str());
264 m_env->getScriptIface()->
265 luaentity_GetProperties(m_id, &m_prop);
266 // Initialize HP from properties
267 m_hp = m_prop.hp_max;
268 // Activate entity, supplying serialized state
269 m_env->getScriptIface()->
270 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
272 m_prop.infotext = m_init_name;
276 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
277 const std::string &data)
285 std::istringstream is(data, std::ios::binary);
287 u8 version = readU8(is);
288 // check if version is supported
290 name = deSerializeString(is);
291 state = deSerializeLongString(is);
293 else if(version == 1){
294 name = deSerializeString(is);
295 state = deSerializeLongString(is);
297 velocity = readV3F1000(is);
302 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
303 <<state<<"\")"<<std::endl;
304 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
306 sao->m_velocity = velocity;
311 void LuaEntitySAO::step(float dtime, bool send_recommended)
313 if(!m_properties_sent)
315 m_properties_sent = true;
316 std::string str = getPropertyPacket();
317 // create message and add to list
318 ActiveObjectMessage aom(getId(), true, str);
319 m_messages_out.push(aom);
322 // If attached, check that our parent is still there. If it isn't, detach.
323 if(m_attachment_parent_id && !isAttached())
325 m_attachment_parent_id = 0;
326 m_attachment_bone = "";
327 m_attachment_position = v3f(0,0,0);
328 m_attachment_rotation = v3f(0,0,0);
329 sendPosition(false, true);
332 m_last_sent_position_timer += dtime;
334 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
335 // If the object gets detached this comes into effect automatically from the last known origin
338 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
339 m_base_position = pos;
340 m_velocity = v3f(0,0,0);
341 m_acceleration = v3f(0,0,0);
346 aabb3f box = m_prop.collisionbox;
349 collisionMoveResult moveresult;
350 f32 pos_max_d = BS*0.25; // Distance per iteration
351 v3f p_pos = m_base_position;
352 v3f p_velocity = m_velocity;
353 v3f p_acceleration = m_acceleration;
354 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
355 pos_max_d, box, m_prop.stepheight, dtime,
356 &p_pos, &p_velocity, p_acceleration,
357 this, m_prop.collideWithObjects);
360 m_base_position = p_pos;
361 m_velocity = p_velocity;
362 m_acceleration = p_acceleration;
364 m_base_position += dtime * m_velocity + 0.5 * dtime
365 * dtime * m_acceleration;
366 m_velocity += dtime * m_acceleration;
369 if((m_prop.automatic_face_movement_dir) &&
370 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
372 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
373 + m_prop.automatic_face_movement_dir_offset;
374 float max_rotation_delta =
375 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
377 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
378 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
380 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
388 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
391 // Remove LuaEntity beyond terrain edges
393 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
395 if (!m_pending_deactivation &&
396 map->saoPositionOverLimit(m_base_position)) {
397 infostream << "Remove SAO " << m_id << "(" << m_init_name
398 << "), outside of limits" << std::endl;
399 m_pending_deactivation = true;
405 if(send_recommended == false)
410 // TODO: force send when acceleration changes enough?
411 float minchange = 0.2*BS;
412 if(m_last_sent_position_timer > 1.0){
414 } else if(m_last_sent_position_timer > 0.2){
417 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
418 move_d += m_last_sent_move_precision;
419 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
420 if(move_d > minchange || vel_d > minchange ||
421 fabs(m_yaw - m_last_sent_yaw) > 1.0){
422 sendPosition(true, false);
426 if(m_armor_groups_sent == false){
427 m_armor_groups_sent = true;
428 std::string str = gob_cmd_update_armor_groups(
430 // create message and add to list
431 ActiveObjectMessage aom(getId(), true, str);
432 m_messages_out.push(aom);
435 if(m_animation_sent == false){
436 m_animation_sent = true;
437 std::string str = gob_cmd_update_animation(
438 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
439 // create message and add to list
440 ActiveObjectMessage aom(getId(), true, str);
441 m_messages_out.push(aom);
444 if(m_bone_position_sent == false){
445 m_bone_position_sent = true;
446 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
447 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
448 std::string str = gob_cmd_update_bone_position((*ii).first,
449 (*ii).second.X, (*ii).second.Y);
450 // create message and add to list
451 ActiveObjectMessage aom(getId(), true, str);
452 m_messages_out.push(aom);
456 if(m_attachment_sent == false){
457 m_attachment_sent = true;
458 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
459 // create message and add to list
460 ActiveObjectMessage aom(getId(), true, str);
461 m_messages_out.push(aom);
465 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
467 std::ostringstream os(std::ios::binary);
470 writeU8(os, 1); // version
471 os << serializeString(""); // name
472 writeU8(os, 0); // is_player
473 writeS16(os, getId()); //id
474 writeV3F1000(os, m_base_position);
475 writeF1000(os, m_yaw);
478 std::ostringstream msg_os(std::ios::binary);
479 msg_os << serializeLongString(getPropertyPacket()); // message 1
480 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
481 msg_os << serializeLongString(gob_cmd_update_animation(
482 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
483 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
484 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
485 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
486 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
488 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
489 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
490 int message_count = 4 + m_bone_position.size();
491 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
492 (ii != m_attachment_child_ids.end()); ++ii) {
493 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
495 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
496 obj->getClientInitializationData(protocol_version)));
500 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
503 writeU8(os, message_count);
504 os.write(msg_os.str().c_str(), msg_os.str().size());
510 void LuaEntitySAO::getStaticData(std::string *result) const
512 verbosestream<<FUNCTION_NAME<<std::endl;
513 std::ostringstream os(std::ios::binary);
517 os<<serializeString(m_init_name);
520 std::string state = m_env->getScriptIface()->
521 luaentity_GetStaticdata(m_id);
522 os<<serializeLongString(state);
524 os<<serializeLongString(m_init_state);
529 writeV3F1000(os, m_velocity);
531 writeF1000(os, m_yaw);
535 int LuaEntitySAO::punch(v3f dir,
536 const ToolCapabilities *toolcap,
537 ServerActiveObject *puncher,
538 float time_from_last_punch)
541 // Delete unknown LuaEntities when punched
546 // It's best that attachments cannot be punched
550 ItemStack *punchitem = NULL;
551 ItemStack punchitem_static;
553 punchitem_static = puncher->getWieldedItem();
554 punchitem = &punchitem_static;
557 PunchDamageResult result = getPunchDamage(
561 time_from_last_punch);
563 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
564 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
566 if (!damage_handled) {
567 if (result.did_punch) {
568 setHP(getHP() - result.damage);
570 if (result.damage > 0) {
571 std::string punchername = puncher ? puncher->getDescription() : "nil";
573 actionstream << getDescription() << " punched by "
574 << punchername << ", damage " << result.damage
575 << " hp, health now " << getHP() << " hp" << std::endl;
578 std::string str = gob_cmd_punched(result.damage, getHP());
579 // create message and add to list
580 ActiveObjectMessage aom(getId(), true, str);
581 m_messages_out.push(aom);
593 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
597 // It's best that attachments cannot be clicked
600 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
603 void LuaEntitySAO::setPos(const v3f &pos)
607 m_base_position = pos;
608 sendPosition(false, true);
611 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
615 m_base_position = pos;
617 sendPosition(true, true);
620 float LuaEntitySAO::getMinimumSavedMovement()
625 std::string LuaEntitySAO::getDescription()
627 std::ostringstream os(std::ios::binary);
628 os<<"LuaEntitySAO at (";
629 os<<(m_base_position.X/BS)<<",";
630 os<<(m_base_position.Y/BS)<<",";
631 os<<(m_base_position.Z/BS);
636 void LuaEntitySAO::setHP(s16 hp)
642 s16 LuaEntitySAO::getHP() const
647 void LuaEntitySAO::setVelocity(v3f velocity)
649 m_velocity = velocity;
652 v3f LuaEntitySAO::getVelocity()
657 void LuaEntitySAO::setAcceleration(v3f acceleration)
659 m_acceleration = acceleration;
662 v3f LuaEntitySAO::getAcceleration()
664 return m_acceleration;
667 void LuaEntitySAO::setTextureMod(const std::string &mod)
669 std::string str = gob_cmd_set_texture_mod(mod);
670 m_current_texture_modifier = mod;
671 // create message and add to list
672 ActiveObjectMessage aom(getId(), true, str);
673 m_messages_out.push(aom);
676 std::string LuaEntitySAO::getTextureMod() const
678 return m_current_texture_modifier;
681 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
682 bool select_horiz_by_yawpitch)
684 std::string str = gob_cmd_set_sprite(
688 select_horiz_by_yawpitch
690 // create message and add to list
691 ActiveObjectMessage aom(getId(), true, str);
692 m_messages_out.push(aom);
695 std::string LuaEntitySAO::getName()
700 std::string LuaEntitySAO::getPropertyPacket()
702 return gob_cmd_set_properties(m_prop);
705 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
707 // If the object is attached client-side, don't waste bandwidth sending its position to clients
711 m_last_sent_move_precision = m_base_position.getDistanceFrom(
712 m_last_sent_position);
713 m_last_sent_position_timer = 0;
714 m_last_sent_yaw = m_yaw;
715 m_last_sent_position = m_base_position;
716 m_last_sent_velocity = m_velocity;
717 //m_last_sent_acceleration = m_acceleration;
719 float update_interval = m_env->getSendRecommendedInterval();
721 std::string str = gob_cmd_update_position(
730 // create message and add to list
731 ActiveObjectMessage aom(getId(), false, str);
732 m_messages_out.push(aom);
735 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
739 //update collision box
740 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
741 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
743 toset->MinEdge += m_base_position;
744 toset->MaxEdge += m_base_position;
752 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
754 if (!m_prop.is_visible) {
758 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
759 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
764 bool LuaEntitySAO::collideWithObjects() const
766 return m_prop.collideWithObjects;
773 // No prototype, PlayerSAO does not need to be deserialized
775 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
776 bool is_singleplayer):
777 UnitSAO(env_, v3f(0,0,0)),
780 m_is_singleplayer(is_singleplayer)
782 assert(m_peer_id != 0); // pre-condition
784 m_prop.hp_max = PLAYER_MAX_HP;
785 m_prop.physical = false;
787 m_prop.collisionbox = aabb3f(-0.3f, -1.0f, -0.3f, 0.3f, 0.75f, 0.3f);
788 // start of default appearance, this should be overwritten by LUA
789 m_prop.visual = "upright_sprite";
790 m_prop.visual_size = v2f(1, 2);
791 m_prop.textures.clear();
792 m_prop.textures.push_back("player.png");
793 m_prop.textures.push_back("player_back.png");
794 m_prop.colors.clear();
795 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
796 m_prop.spritediv = v2s16(1,1);
797 // end of default appearance
798 m_prop.is_visible = true;
799 m_prop.makes_footstep_sound = true;
800 m_hp = PLAYER_MAX_HP;
803 PlayerSAO::~PlayerSAO()
805 if(m_inventory != &m_player->inventory)
809 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
814 m_inventory = &m_player->inventory;
817 v3f PlayerSAO::getEyeOffset() const
819 return v3f(0, BS * 1.625f, 0);
822 std::string PlayerSAO::getDescription()
824 return std::string("player ") + m_player->getName();
827 // Called after id has been set and has been inserted in environment
828 void PlayerSAO::addedToEnvironment(u32 dtime_s)
830 ServerActiveObject::addedToEnvironment(dtime_s);
831 ServerActiveObject::setBasePosition(m_base_position);
832 m_player->setPlayerSAO(this);
833 m_player->peer_id = m_peer_id;
834 m_last_good_position = m_base_position;
837 // Called before removing from environment
838 void PlayerSAO::removingFromEnvironment()
840 ServerActiveObject::removingFromEnvironment();
841 if (m_player->getPlayerSAO() == this) {
842 unlinkPlayerSessionAndSave();
843 for (std::unordered_set<u32>::iterator it = m_attached_particle_spawners.begin();
844 it != m_attached_particle_spawners.end(); ++it) {
845 m_env->deleteParticleSpawner(*it, false);
850 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
852 std::ostringstream os(std::ios::binary);
855 writeU8(os, 1); // version
856 os << serializeString(m_player->getName()); // name
857 writeU8(os, 1); // is_player
858 writeS16(os, getId()); //id
859 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
860 writeF1000(os, m_yaw);
861 writeS16(os, getHP());
863 std::ostringstream msg_os(std::ios::binary);
864 msg_os << serializeLongString(getPropertyPacket()); // message 1
865 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
866 msg_os << serializeLongString(gob_cmd_update_animation(
867 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
868 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
869 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
870 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
871 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
873 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
874 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
875 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
876 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
877 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
878 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
879 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
880 int message_count = 6 + m_bone_position.size();
881 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
882 ii != m_attachment_child_ids.end(); ++ii) {
883 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
885 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
886 obj->getClientInitializationData(protocol_version)));
890 writeU8(os, message_count);
891 os.write(msg_os.str().c_str(), msg_os.str().size());
897 void PlayerSAO::getStaticData(std::string *result) const
899 FATAL_ERROR("Deprecated function");
902 void PlayerSAO::step(float dtime, bool send_recommended)
904 if (m_drowning_interval.step(dtime, 2.0)) {
906 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
907 MapNode n = m_env->getMap().getNodeNoEx(p);
908 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
909 // If node generates drown
910 if (c.drowning > 0 && m_hp > 0) {
912 setBreath(m_breath - 1);
914 // No more breath, damage player
916 setHP(m_hp - c.drowning);
917 m_env->getGameDef()->SendPlayerHPOrDie(this);
922 if (m_breathing_interval.step(dtime, 0.5)) {
924 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
925 MapNode n = m_env->getMap().getNodeNoEx(p);
926 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
927 // If player is alive & no drowning, breath
928 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
929 setBreath(m_breath + 1);
932 if (m_node_hurt_interval.step(dtime, 1.0)) {
933 // Feet, middle and head
934 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
935 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
936 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
937 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
938 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
939 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
941 u32 damage_per_second = 0;
942 damage_per_second = MYMAX(damage_per_second,
943 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
944 damage_per_second = MYMAX(damage_per_second,
945 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
946 damage_per_second = MYMAX(damage_per_second,
947 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
949 if (damage_per_second != 0 && m_hp > 0) {
950 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
952 m_env->getGameDef()->SendPlayerHPOrDie(this);
956 if (!m_properties_sent) {
957 m_properties_sent = true;
958 std::string str = getPropertyPacket();
959 // create message and add to list
960 ActiveObjectMessage aom(getId(), true, str);
961 m_messages_out.push(aom);
964 // If attached, check that our parent is still there. If it isn't, detach.
965 if(m_attachment_parent_id && !isAttached())
967 m_attachment_parent_id = 0;
968 m_attachment_bone = "";
969 m_attachment_position = v3f(0,0,0);
970 m_attachment_rotation = v3f(0,0,0);
971 setBasePosition(m_last_good_position);
972 m_env->getGameDef()->SendMovePlayer(m_peer_id);
975 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
977 // Set lag pool maximums based on estimated lag
978 const float LAG_POOL_MIN = 5.0;
979 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
980 if(lag_pool_max < LAG_POOL_MIN)
981 lag_pool_max = LAG_POOL_MIN;
982 m_dig_pool.setMax(lag_pool_max);
983 m_move_pool.setMax(lag_pool_max);
985 // Increment cheat prevention timers
986 m_dig_pool.add(dtime);
987 m_move_pool.add(dtime);
988 m_time_from_last_teleport += dtime;
989 m_time_from_last_punch += dtime;
990 m_nocheat_dig_time += dtime;
992 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
993 // If the object gets detached this comes into effect automatically from the last known origin
995 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
996 m_last_good_position = pos;
997 setBasePosition(pos);
1000 if (!send_recommended)
1003 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1004 if(m_position_not_sent && !isAttached())
1006 m_position_not_sent = false;
1007 float update_interval = m_env->getSendRecommendedInterval();
1009 if(isAttached()) // Just in case we ever do send attachment position too
1010 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1012 pos = m_base_position + v3f(0,BS*1,0);
1013 std::string str = gob_cmd_update_position(
1022 // create message and add to list
1023 ActiveObjectMessage aom(getId(), false, str);
1024 m_messages_out.push(aom);
1027 if (!m_armor_groups_sent) {
1028 m_armor_groups_sent = true;
1029 std::string str = gob_cmd_update_armor_groups(
1031 // create message and add to list
1032 ActiveObjectMessage aom(getId(), true, str);
1033 m_messages_out.push(aom);
1036 if (!m_physics_override_sent) {
1037 m_physics_override_sent = true;
1038 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1039 m_physics_override_jump, m_physics_override_gravity,
1040 m_physics_override_sneak, m_physics_override_sneak_glitch,
1041 m_physics_override_new_move);
1042 // create message and add to list
1043 ActiveObjectMessage aom(getId(), true, str);
1044 m_messages_out.push(aom);
1047 if (!m_animation_sent) {
1048 m_animation_sent = true;
1049 std::string str = gob_cmd_update_animation(
1050 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1051 // create message and add to list
1052 ActiveObjectMessage aom(getId(), true, str);
1053 m_messages_out.push(aom);
1056 if (!m_bone_position_sent) {
1057 m_bone_position_sent = true;
1058 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1059 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1060 std::string str = gob_cmd_update_bone_position((*ii).first,
1061 (*ii).second.X, (*ii).second.Y);
1062 // create message and add to list
1063 ActiveObjectMessage aom(getId(), true, str);
1064 m_messages_out.push(aom);
1068 if (!m_attachment_sent){
1069 m_attachment_sent = true;
1070 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1071 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1072 // create message and add to list
1073 ActiveObjectMessage aom(getId(), true, str);
1074 m_messages_out.push(aom);
1078 void PlayerSAO::setBasePosition(const v3f &position)
1080 if (m_player && position != m_base_position)
1081 m_player->setDirty(true);
1083 // This needs to be ran for attachments too
1084 ServerActiveObject::setBasePosition(position);
1085 m_position_not_sent = true;
1088 void PlayerSAO::setPos(const v3f &pos)
1093 setBasePosition(pos);
1094 // Movement caused by this command is always valid
1095 m_last_good_position = pos;
1096 m_move_pool.empty();
1097 m_time_from_last_teleport = 0.0;
1098 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1101 void PlayerSAO::moveTo(v3f pos, bool continuous)
1106 setBasePosition(pos);
1107 // Movement caused by this command is always valid
1108 m_last_good_position = pos;
1109 m_move_pool.empty();
1110 m_time_from_last_teleport = 0.0;
1111 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1114 void PlayerSAO::setYaw(const float yaw)
1116 if (m_player && yaw != m_yaw)
1117 m_player->setDirty(true);
1119 UnitSAO::setYaw(yaw);
1122 void PlayerSAO::setFov(const float fov)
1124 if (m_player && fov != m_fov)
1125 m_player->setDirty(true);
1130 void PlayerSAO::setWantedRange(const s16 range)
1132 if (m_player && range != m_wanted_range)
1133 m_player->setDirty(true);
1135 m_wanted_range = range;
1138 void PlayerSAO::setYawAndSend(const float yaw)
1141 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1144 void PlayerSAO::setPitch(const float pitch)
1146 if (m_player && pitch != m_pitch)
1147 m_player->setDirty(true);
1152 void PlayerSAO::setPitchAndSend(const float pitch)
1155 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1158 int PlayerSAO::punch(v3f dir,
1159 const ToolCapabilities *toolcap,
1160 ServerActiveObject *puncher,
1161 float time_from_last_punch)
1163 // It's best that attachments cannot be punched
1170 // No effect if PvP disabled
1171 if (g_settings->getBool("enable_pvp") == false) {
1172 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1173 std::string str = gob_cmd_punched(0, getHP());
1174 // create message and add to list
1175 ActiveObjectMessage aom(getId(), true, str);
1176 m_messages_out.push(aom);
1181 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1182 time_from_last_punch);
1184 std::string punchername = "nil";
1187 punchername = puncher->getDescription();
1189 PlayerSAO *playersao = m_player->getPlayerSAO();
1191 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1192 puncher, time_from_last_punch, toolcap, dir,
1195 if (!damage_handled) {
1196 setHP(getHP() - hitparams.hp);
1197 } else { // override client prediction
1198 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1199 std::string str = gob_cmd_punched(0, getHP());
1200 // create message and add to list
1201 ActiveObjectMessage aom(getId(), true, str);
1202 m_messages_out.push(aom);
1207 actionstream << "Player " << m_player->getName() << " punched by "
1209 if (!damage_handled) {
1210 actionstream << ", damage " << hitparams.hp << " HP";
1212 actionstream << ", damage handled by lua";
1214 actionstream << std::endl;
1216 return hitparams.wear;
1219 s16 PlayerSAO::readDamage()
1221 s16 damage = m_damage;
1226 void PlayerSAO::setHP(s16 hp)
1230 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1233 hp = oldhp + hp_change;
1237 else if (hp > PLAYER_MAX_HP)
1240 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1247 m_damage += (oldhp - hp);
1249 // Update properties on death
1250 if ((hp == 0) != (oldhp == 0))
1251 m_properties_sent = false;
1254 void PlayerSAO::setBreath(const u16 breath, bool send)
1256 if (m_player && breath != m_breath)
1257 m_player->setDirty(true);
1259 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1262 m_env->getGameDef()->SendPlayerBreath(this);
1265 Inventory* PlayerSAO::getInventory()
1269 const Inventory* PlayerSAO::getInventory() const
1274 InventoryLocation PlayerSAO::getInventoryLocation() const
1276 InventoryLocation loc;
1277 loc.setPlayer(m_player->getName());
1281 std::string PlayerSAO::getWieldList() const
1286 ItemStack PlayerSAO::getWieldedItem() const
1288 const Inventory *inv = getInventory();
1290 const InventoryList *mlist = inv->getList(getWieldList());
1291 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1292 ret = mlist->getItem(getWieldIndex());
1296 ItemStack PlayerSAO::getWieldedItemOrHand() const
1298 const Inventory *inv = getInventory();
1300 const InventoryList *mlist = inv->getList(getWieldList());
1301 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1302 ret = mlist->getItem(getWieldIndex());
1303 if (ret.name.empty()) {
1304 const InventoryList *hlist = inv->getList("hand");
1306 ret = hlist->getItem(0);
1311 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1313 Inventory *inv = getInventory();
1315 InventoryList *mlist = inv->getList(getWieldList());
1317 mlist->changeItem(getWieldIndex(), item);
1324 int PlayerSAO::getWieldIndex() const
1326 return m_wield_index;
1329 void PlayerSAO::setWieldIndex(int i)
1331 if(i != m_wield_index) {
1336 // Erase the peer id and make the object for removal
1337 void PlayerSAO::disconnected()
1343 void PlayerSAO::unlinkPlayerSessionAndSave()
1345 assert(m_player->getPlayerSAO() == this);
1346 m_player->peer_id = 0;
1347 m_env->savePlayer(m_player);
1348 m_player->setPlayerSAO(NULL);
1349 m_env->removePlayer(m_player);
1352 std::string PlayerSAO::getPropertyPacket()
1354 m_prop.is_visible = (true);
1355 return gob_cmd_set_properties(m_prop);
1358 bool PlayerSAO::checkMovementCheat()
1360 if (isAttached() || m_is_singleplayer ||
1361 g_settings->getBool("disable_anticheat")) {
1362 m_last_good_position = m_base_position;
1366 bool cheated = false;
1368 Check player movements
1370 NOTE: Actually the server should handle player physics like the
1371 client does and compare player's position to what is calculated
1372 on our side. This is required when eg. players fly due to an
1373 explosion. Altough a node-based alternative might be possible
1374 too, and much more lightweight.
1377 float player_max_speed = 0;
1379 if (m_privs.count("fast") != 0) {
1381 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1384 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1386 // Tolerance. The lag pool does this a bit.
1387 //player_max_speed *= 2.5;
1389 v3f diff = (m_base_position - m_last_good_position);
1390 float d_vert = diff.Y;
1392 float d_horiz = diff.getLength();
1393 float required_time = d_horiz / player_max_speed;
1395 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1396 required_time = d_vert / player_max_speed; // Moving upwards
1398 if (m_move_pool.grab(required_time)) {
1399 m_last_good_position = m_base_position;
1401 const float LAG_POOL_MIN = 5.0;
1402 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1403 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1404 if (m_time_from_last_teleport > lag_pool_max) {
1405 actionstream << "Player " << m_player->getName()
1406 << " moved too fast; resetting position"
1410 setBasePosition(m_last_good_position);
1415 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1417 *toset = aabb3f(-0.3f * BS, 0.0f, -0.3f * BS, 0.3f * BS, 1.75f * BS, 0.3f * BS);
1419 toset->MinEdge += m_base_position;
1420 toset->MaxEdge += m_base_position;
1424 bool PlayerSAO::getSelectionBox(aabb3f *toset) const
1426 if (!m_prop.is_visible) {
1430 getCollisionBox(toset);