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; }
94 bool collideWithObjects() const { return false; }
101 // Prototype (registers item for deserialization)
102 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
108 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
109 ServerActiveObject(env, pos),
112 m_properties_sent(true),
113 m_armor_groups_sent(false),
114 m_animation_range(0,0),
115 m_animation_speed(0),
116 m_animation_blend(0),
117 m_animation_loop(true),
118 m_animation_sent(false),
119 m_bone_position_sent(false),
120 m_attachment_parent_id(0),
121 m_attachment_sent(false)
123 // Initialize something to armor groups
124 m_armor_groups["fleshy"] = 100;
127 bool UnitSAO::isAttached() const
129 if (!m_attachment_parent_id)
131 // Check if the parent still exists
132 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
138 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
140 m_armor_groups = armor_groups;
141 m_armor_groups_sent = false;
144 const ItemGroupList &UnitSAO::getArmorGroups()
146 return m_armor_groups;
149 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
151 // store these so they can be updated to clients
152 m_animation_range = frame_range;
153 m_animation_speed = frame_speed;
154 m_animation_blend = frame_blend;
155 m_animation_loop = frame_loop;
156 m_animation_sent = false;
159 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
161 *frame_range = m_animation_range;
162 *frame_speed = m_animation_speed;
163 *frame_blend = m_animation_blend;
164 *frame_loop = m_animation_loop;
167 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
169 // store these so they can be updated to clients
170 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
171 m_bone_position_sent = false;
174 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
176 *position = m_bone_position[bone].X;
177 *rotation = m_bone_position[bone].Y;
180 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
182 // Attachments need to be handled on both the server and client.
183 // If we just attach on the server, we can only copy the position of the parent. Attachments
184 // are still sent to clients at an interval so players might see them lagging, plus we can't
185 // read and attach to skeletal bones.
186 // If we just attach on the client, the server still sees the child at its original location.
187 // This breaks some things so we also give the server the most accurate representation
188 // even if players only see the client changes.
190 m_attachment_parent_id = parent_id;
191 m_attachment_bone = bone;
192 m_attachment_position = position;
193 m_attachment_rotation = rotation;
194 m_attachment_sent = false;
197 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
200 *parent_id = m_attachment_parent_id;
201 *bone = m_attachment_bone;
202 *position = m_attachment_position;
203 *rotation = m_attachment_rotation;
206 void UnitSAO::addAttachmentChild(int child_id)
208 m_attachment_child_ids.insert(child_id);
211 void UnitSAO::removeAttachmentChild(int child_id)
213 m_attachment_child_ids.erase(child_id);
216 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
218 return m_attachment_child_ids;
221 ObjectProperties* UnitSAO::accessObjectProperties()
226 void UnitSAO::notifyObjectPropertiesModified()
228 m_properties_sent = false;
235 // Prototype (registers item for deserialization)
236 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
238 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
239 const std::string &name, const std::string &state):
245 m_acceleration(0,0,0),
247 m_last_sent_position(0,0,0),
248 m_last_sent_velocity(0,0,0),
249 m_last_sent_position_timer(0),
250 m_last_sent_move_precision(0),
251 m_current_texture_modifier("")
253 // Only register type if no environment supplied
255 ServerActiveObject::registerType(getType(), create);
260 LuaEntitySAO::~LuaEntitySAO()
263 m_env->getScriptIface()->luaentity_Remove(m_id);
266 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
267 it != m_attached_particle_spawners.end(); ++it) {
268 m_env->deleteParticleSpawner(*it, false);
272 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
274 ServerActiveObject::addedToEnvironment(dtime_s);
276 // Create entity from name
277 m_registered = m_env->getScriptIface()->
278 luaentity_Add(m_id, m_init_name.c_str());
282 m_env->getScriptIface()->
283 luaentity_GetProperties(m_id, &m_prop);
284 // Initialize HP from properties
285 m_hp = m_prop.hp_max;
286 // Activate entity, supplying serialized state
287 m_env->getScriptIface()->
288 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
290 m_prop.infotext = m_init_name;
294 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
295 const std::string &data)
303 std::istringstream is(data, std::ios::binary);
305 u8 version = readU8(is);
306 // check if version is supported
308 name = deSerializeString(is);
309 state = deSerializeLongString(is);
311 else if(version == 1){
312 name = deSerializeString(is);
313 state = deSerializeLongString(is);
315 velocity = readV3F1000(is);
320 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
321 <<state<<"\")"<<std::endl;
322 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
324 sao->m_velocity = velocity;
329 void LuaEntitySAO::step(float dtime, bool send_recommended)
331 if(!m_properties_sent)
333 m_properties_sent = true;
334 std::string str = getPropertyPacket();
335 // create message and add to list
336 ActiveObjectMessage aom(getId(), true, str);
337 m_messages_out.push(aom);
340 // If attached, check that our parent is still there. If it isn't, detach.
341 if(m_attachment_parent_id && !isAttached())
343 m_attachment_parent_id = 0;
344 m_attachment_bone = "";
345 m_attachment_position = v3f(0,0,0);
346 m_attachment_rotation = v3f(0,0,0);
347 sendPosition(false, true);
350 m_last_sent_position_timer += dtime;
352 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
353 // If the object gets detached this comes into effect automatically from the last known origin
356 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
357 m_base_position = pos;
358 m_velocity = v3f(0,0,0);
359 m_acceleration = v3f(0,0,0);
364 aabb3f box = m_prop.collisionbox;
367 collisionMoveResult moveresult;
368 f32 pos_max_d = BS*0.25; // Distance per iteration
369 v3f p_pos = m_base_position;
370 v3f p_velocity = m_velocity;
371 v3f p_acceleration = m_acceleration;
372 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
373 pos_max_d, box, m_prop.stepheight, dtime,
374 &p_pos, &p_velocity, p_acceleration,
375 this, m_prop.collideWithObjects);
378 m_base_position = p_pos;
379 m_velocity = p_velocity;
380 m_acceleration = p_acceleration;
382 m_base_position += dtime * m_velocity + 0.5 * dtime
383 * dtime * m_acceleration;
384 m_velocity += dtime * m_acceleration;
387 if((m_prop.automatic_face_movement_dir) &&
388 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
390 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
391 + m_prop.automatic_face_movement_dir_offset;
392 float max_rotation_delta =
393 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
395 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
396 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
398 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
406 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
409 if(send_recommended == false)
414 // TODO: force send when acceleration changes enough?
415 float minchange = 0.2*BS;
416 if(m_last_sent_position_timer > 1.0){
418 } else if(m_last_sent_position_timer > 0.2){
421 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
422 move_d += m_last_sent_move_precision;
423 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
424 if(move_d > minchange || vel_d > minchange ||
425 fabs(m_yaw - m_last_sent_yaw) > 1.0){
426 sendPosition(true, false);
430 if(m_armor_groups_sent == false){
431 m_armor_groups_sent = true;
432 std::string str = gob_cmd_update_armor_groups(
434 // create message and add to list
435 ActiveObjectMessage aom(getId(), true, str);
436 m_messages_out.push(aom);
439 if(m_animation_sent == false){
440 m_animation_sent = true;
441 std::string str = gob_cmd_update_animation(
442 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
443 // create message and add to list
444 ActiveObjectMessage aom(getId(), true, str);
445 m_messages_out.push(aom);
448 if(m_bone_position_sent == false){
449 m_bone_position_sent = true;
450 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
451 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
452 std::string str = gob_cmd_update_bone_position((*ii).first,
453 (*ii).second.X, (*ii).second.Y);
454 // create message and add to list
455 ActiveObjectMessage aom(getId(), true, str);
456 m_messages_out.push(aom);
460 if(m_attachment_sent == false){
461 m_attachment_sent = true;
462 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
463 // create message and add to list
464 ActiveObjectMessage aom(getId(), true, str);
465 m_messages_out.push(aom);
469 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
471 std::ostringstream os(std::ios::binary);
474 writeU8(os, 1); // version
475 os << serializeString(""); // name
476 writeU8(os, 0); // is_player
477 writeS16(os, getId()); //id
478 writeV3F1000(os, m_base_position);
479 writeF1000(os, m_yaw);
482 std::ostringstream msg_os(std::ios::binary);
483 msg_os << serializeLongString(getPropertyPacket()); // message 1
484 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
485 msg_os << serializeLongString(gob_cmd_update_animation(
486 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
487 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
488 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
489 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
490 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
492 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
493 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
494 int message_count = 4 + m_bone_position.size();
495 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
496 (ii != m_attachment_child_ids.end()); ++ii) {
497 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
499 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
500 obj->getClientInitializationData(protocol_version)));
504 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
507 writeU8(os, message_count);
508 os.write(msg_os.str().c_str(), msg_os.str().size());
514 void LuaEntitySAO::getStaticData(std::string *result) const
516 verbosestream<<FUNCTION_NAME<<std::endl;
517 std::ostringstream os(std::ios::binary);
521 os<<serializeString(m_init_name);
524 std::string state = m_env->getScriptIface()->
525 luaentity_GetStaticdata(m_id);
526 os<<serializeLongString(state);
528 os<<serializeLongString(m_init_state);
533 writeV3F1000(os, m_velocity);
535 writeF1000(os, m_yaw);
539 int LuaEntitySAO::punch(v3f dir,
540 const ToolCapabilities *toolcap,
541 ServerActiveObject *puncher,
542 float time_from_last_punch)
545 // Delete unknown LuaEntities when punched
550 // It's best that attachments cannot be punched
554 ItemStack *punchitem = NULL;
555 ItemStack punchitem_static;
557 punchitem_static = puncher->getWieldedItem();
558 punchitem = &punchitem_static;
561 PunchDamageResult result = getPunchDamage(
565 time_from_last_punch);
567 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
568 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
570 if (!damage_handled) {
571 if (result.did_punch) {
572 setHP(getHP() - result.damage);
574 if (result.damage > 0) {
575 std::string punchername = puncher ? puncher->getDescription() : "nil";
577 actionstream << getDescription() << " punched by "
578 << punchername << ", damage " << result.damage
579 << " hp, health now " << getHP() << " hp" << std::endl;
582 std::string str = gob_cmd_punched(result.damage, getHP());
583 // create message and add to list
584 ActiveObjectMessage aom(getId(), true, str);
585 m_messages_out.push(aom);
597 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
601 // It's best that attachments cannot be clicked
604 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
607 void LuaEntitySAO::setPos(const v3f &pos)
611 m_base_position = pos;
612 sendPosition(false, true);
615 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
619 m_base_position = pos;
621 sendPosition(true, true);
624 float LuaEntitySAO::getMinimumSavedMovement()
629 std::string LuaEntitySAO::getDescription()
631 std::ostringstream os(std::ios::binary);
632 os<<"LuaEntitySAO at (";
633 os<<(m_base_position.X/BS)<<",";
634 os<<(m_base_position.Y/BS)<<",";
635 os<<(m_base_position.Z/BS);
640 void LuaEntitySAO::setHP(s16 hp)
646 s16 LuaEntitySAO::getHP() const
651 void LuaEntitySAO::setVelocity(v3f velocity)
653 m_velocity = velocity;
656 v3f LuaEntitySAO::getVelocity()
661 void LuaEntitySAO::setAcceleration(v3f acceleration)
663 m_acceleration = acceleration;
666 v3f LuaEntitySAO::getAcceleration()
668 return m_acceleration;
671 void LuaEntitySAO::setTextureMod(const std::string &mod)
673 std::string str = gob_cmd_set_texture_mod(mod);
674 m_current_texture_modifier = mod;
675 // create message and add to list
676 ActiveObjectMessage aom(getId(), true, str);
677 m_messages_out.push(aom);
680 std::string LuaEntitySAO::getTextureMod() const
682 return m_current_texture_modifier;
685 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
686 bool select_horiz_by_yawpitch)
688 std::string str = gob_cmd_set_sprite(
692 select_horiz_by_yawpitch
694 // create message and add to list
695 ActiveObjectMessage aom(getId(), true, str);
696 m_messages_out.push(aom);
699 std::string LuaEntitySAO::getName()
704 std::string LuaEntitySAO::getPropertyPacket()
706 return gob_cmd_set_properties(m_prop);
709 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
711 // If the object is attached client-side, don't waste bandwidth sending its position to clients
715 m_last_sent_move_precision = m_base_position.getDistanceFrom(
716 m_last_sent_position);
717 m_last_sent_position_timer = 0;
718 m_last_sent_yaw = m_yaw;
719 m_last_sent_position = m_base_position;
720 m_last_sent_velocity = m_velocity;
721 //m_last_sent_acceleration = m_acceleration;
723 float update_interval = m_env->getSendRecommendedInterval();
725 std::string str = gob_cmd_update_position(
734 // create message and add to list
735 ActiveObjectMessage aom(getId(), false, str);
736 m_messages_out.push(aom);
739 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
743 //update collision box
744 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
745 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
747 toset->MinEdge += m_base_position;
748 toset->MaxEdge += m_base_position;
756 bool LuaEntitySAO::collideWithObjects() const
758 return m_prop.collideWithObjects;
765 // No prototype, PlayerSAO does not need to be deserialized
767 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
768 bool is_singleplayer):
769 UnitSAO(env_, v3f(0,0,0)),
774 m_last_good_position(0,0,0),
775 m_time_from_last_teleport(0),
776 m_time_from_last_punch(0),
777 m_nocheat_dig_pos(32767, 32767, 32767),
778 m_nocheat_dig_time(0),
780 m_position_not_sent(false),
781 m_is_singleplayer(is_singleplayer),
782 m_breath(PLAYER_MAX_BREATH),
786 m_extended_attributes_modified(false),
788 m_physics_override_speed(1),
789 m_physics_override_jump(1),
790 m_physics_override_gravity(1),
791 m_physics_override_sneak(true),
792 m_physics_override_sneak_glitch(false),
793 m_physics_override_new_move(true),
794 m_physics_override_sent(false)
796 assert(m_peer_id != 0); // pre-condition
798 m_prop.hp_max = PLAYER_MAX_HP;
799 m_prop.physical = false;
801 m_prop.collisionbox = aabb3f(-0.3f, -1.0f, -0.3f, 0.3f, 0.75f, 0.3f);
802 // start of default appearance, this should be overwritten by LUA
803 m_prop.visual = "upright_sprite";
804 m_prop.visual_size = v2f(1, 2);
805 m_prop.textures.clear();
806 m_prop.textures.push_back("player.png");
807 m_prop.textures.push_back("player_back.png");
808 m_prop.colors.clear();
809 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
810 m_prop.spritediv = v2s16(1,1);
811 // end of default appearance
812 m_prop.is_visible = true;
813 m_prop.makes_footstep_sound = true;
814 m_hp = PLAYER_MAX_HP;
817 PlayerSAO::~PlayerSAO()
819 if(m_inventory != &m_player->inventory)
823 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
828 m_inventory = &m_player->inventory;
831 v3f PlayerSAO::getEyeOffset() const
833 return v3f(0, BS * 1.625f, 0);
836 std::string PlayerSAO::getDescription()
838 return std::string("player ") + m_player->getName();
841 // Called after id has been set and has been inserted in environment
842 void PlayerSAO::addedToEnvironment(u32 dtime_s)
844 ServerActiveObject::addedToEnvironment(dtime_s);
845 ServerActiveObject::setBasePosition(m_base_position);
846 m_player->setPlayerSAO(this);
847 m_player->peer_id = m_peer_id;
848 m_last_good_position = m_base_position;
851 // Called before removing from environment
852 void PlayerSAO::removingFromEnvironment()
854 ServerActiveObject::removingFromEnvironment();
855 if (m_player->getPlayerSAO() == this) {
856 unlinkPlayerSessionAndSave();
857 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
858 it != m_attached_particle_spawners.end(); ++it) {
859 m_env->deleteParticleSpawner(*it, false);
864 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
866 std::ostringstream os(std::ios::binary);
869 writeU8(os, 1); // version
870 os << serializeString(m_player->getName()); // name
871 writeU8(os, 1); // is_player
872 writeS16(os, getId()); //id
873 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
874 writeF1000(os, m_yaw);
875 writeS16(os, getHP());
877 std::ostringstream msg_os(std::ios::binary);
878 msg_os << serializeLongString(getPropertyPacket()); // message 1
879 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
880 msg_os << serializeLongString(gob_cmd_update_animation(
881 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
882 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
883 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
884 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
885 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
887 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
888 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
889 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
890 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
891 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
892 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
893 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
894 int message_count = 6 + m_bone_position.size();
895 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
896 ii != m_attachment_child_ids.end(); ++ii) {
897 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
899 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
900 obj->getClientInitializationData(protocol_version)));
904 writeU8(os, message_count);
905 os.write(msg_os.str().c_str(), msg_os.str().size());
911 void PlayerSAO::getStaticData(std::string *result) const
913 FATAL_ERROR("Deprecated function");
916 void PlayerSAO::step(float dtime, bool send_recommended)
918 if (m_drowning_interval.step(dtime, 2.0)) {
920 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
921 MapNode n = m_env->getMap().getNodeNoEx(p);
922 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
923 // If node generates drown
924 if (c.drowning > 0 && m_hp > 0) {
926 setBreath(m_breath - 1);
928 // No more breath, damage player
930 setHP(m_hp - c.drowning);
931 m_env->getGameDef()->SendPlayerHPOrDie(this);
936 if (m_breathing_interval.step(dtime, 0.5)) {
938 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
939 MapNode n = m_env->getMap().getNodeNoEx(p);
940 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
941 // If player is alive & no drowning, breath
942 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
943 setBreath(m_breath + 1);
946 if (m_node_hurt_interval.step(dtime, 1.0)) {
947 // Feet, middle and head
948 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
949 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
950 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
951 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
952 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
953 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
955 u32 damage_per_second = 0;
956 damage_per_second = MYMAX(damage_per_second,
957 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
958 damage_per_second = MYMAX(damage_per_second,
959 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
960 damage_per_second = MYMAX(damage_per_second,
961 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
963 if (damage_per_second != 0 && m_hp > 0) {
964 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
966 m_env->getGameDef()->SendPlayerHPOrDie(this);
970 if (!m_properties_sent) {
971 m_properties_sent = true;
972 std::string str = getPropertyPacket();
973 // create message and add to list
974 ActiveObjectMessage aom(getId(), true, str);
975 m_messages_out.push(aom);
978 // If attached, check that our parent is still there. If it isn't, detach.
979 if(m_attachment_parent_id && !isAttached())
981 m_attachment_parent_id = 0;
982 m_attachment_bone = "";
983 m_attachment_position = v3f(0,0,0);
984 m_attachment_rotation = v3f(0,0,0);
985 setBasePosition(m_last_good_position);
986 m_env->getGameDef()->SendMovePlayer(m_peer_id);
989 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
991 // Set lag pool maximums based on estimated lag
992 const float LAG_POOL_MIN = 5.0;
993 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
994 if(lag_pool_max < LAG_POOL_MIN)
995 lag_pool_max = LAG_POOL_MIN;
996 m_dig_pool.setMax(lag_pool_max);
997 m_move_pool.setMax(lag_pool_max);
999 // Increment cheat prevention timers
1000 m_dig_pool.add(dtime);
1001 m_move_pool.add(dtime);
1002 m_time_from_last_teleport += dtime;
1003 m_time_from_last_punch += dtime;
1004 m_nocheat_dig_time += dtime;
1006 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1007 // If the object gets detached this comes into effect automatically from the last known origin
1009 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1010 m_last_good_position = pos;
1011 setBasePosition(pos);
1014 if (!send_recommended)
1017 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1018 if(m_position_not_sent && !isAttached())
1020 m_position_not_sent = false;
1021 float update_interval = m_env->getSendRecommendedInterval();
1023 if(isAttached()) // Just in case we ever do send attachment position too
1024 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1026 pos = m_base_position + v3f(0,BS*1,0);
1027 std::string str = gob_cmd_update_position(
1036 // create message and add to list
1037 ActiveObjectMessage aom(getId(), false, str);
1038 m_messages_out.push(aom);
1041 if (!m_armor_groups_sent) {
1042 m_armor_groups_sent = true;
1043 std::string str = gob_cmd_update_armor_groups(
1045 // create message and add to list
1046 ActiveObjectMessage aom(getId(), true, str);
1047 m_messages_out.push(aom);
1050 if (!m_physics_override_sent) {
1051 m_physics_override_sent = true;
1052 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1053 m_physics_override_jump, m_physics_override_gravity,
1054 m_physics_override_sneak, m_physics_override_sneak_glitch,
1055 m_physics_override_new_move);
1056 // create message and add to list
1057 ActiveObjectMessage aom(getId(), true, str);
1058 m_messages_out.push(aom);
1061 if (!m_animation_sent) {
1062 m_animation_sent = true;
1063 std::string str = gob_cmd_update_animation(
1064 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1065 // create message and add to list
1066 ActiveObjectMessage aom(getId(), true, str);
1067 m_messages_out.push(aom);
1070 if (!m_bone_position_sent) {
1071 m_bone_position_sent = true;
1072 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1073 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1074 std::string str = gob_cmd_update_bone_position((*ii).first,
1075 (*ii).second.X, (*ii).second.Y);
1076 // create message and add to list
1077 ActiveObjectMessage aom(getId(), true, str);
1078 m_messages_out.push(aom);
1082 if (!m_attachment_sent){
1083 m_attachment_sent = true;
1084 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1085 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1086 // create message and add to list
1087 ActiveObjectMessage aom(getId(), true, str);
1088 m_messages_out.push(aom);
1092 void PlayerSAO::setBasePosition(const v3f &position)
1094 if (m_player && position != m_base_position)
1095 m_player->setDirty(true);
1097 // This needs to be ran for attachments too
1098 ServerActiveObject::setBasePosition(position);
1099 m_position_not_sent = true;
1102 void PlayerSAO::setPos(const v3f &pos)
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::moveTo(v3f pos, bool continuous)
1120 setBasePosition(pos);
1121 // Movement caused by this command is always valid
1122 m_last_good_position = pos;
1123 m_move_pool.empty();
1124 m_time_from_last_teleport = 0.0;
1125 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1128 void PlayerSAO::setYaw(const float yaw)
1130 if (m_player && yaw != m_yaw)
1131 m_player->setDirty(true);
1133 UnitSAO::setYaw(yaw);
1136 void PlayerSAO::setFov(const float fov)
1138 if (m_player && fov != m_fov)
1139 m_player->setDirty(true);
1144 void PlayerSAO::setWantedRange(const s16 range)
1146 if (m_player && range != m_wanted_range)
1147 m_player->setDirty(true);
1149 m_wanted_range = range;
1152 void PlayerSAO::setYawAndSend(const float yaw)
1155 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1158 void PlayerSAO::setPitch(const float pitch)
1160 if (m_player && pitch != m_pitch)
1161 m_player->setDirty(true);
1166 void PlayerSAO::setPitchAndSend(const float pitch)
1169 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1172 int PlayerSAO::punch(v3f dir,
1173 const ToolCapabilities *toolcap,
1174 ServerActiveObject *puncher,
1175 float time_from_last_punch)
1177 // It's best that attachments cannot be punched
1184 // No effect if PvP disabled
1185 if (g_settings->getBool("enable_pvp") == false) {
1186 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1187 std::string str = gob_cmd_punched(0, getHP());
1188 // create message and add to list
1189 ActiveObjectMessage aom(getId(), true, str);
1190 m_messages_out.push(aom);
1195 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1196 time_from_last_punch);
1198 std::string punchername = "nil";
1201 punchername = puncher->getDescription();
1203 PlayerSAO *playersao = m_player->getPlayerSAO();
1205 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1206 puncher, time_from_last_punch, toolcap, dir,
1209 if (!damage_handled) {
1210 setHP(getHP() - hitparams.hp);
1211 } else { // override client prediction
1212 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1213 std::string str = gob_cmd_punched(0, getHP());
1214 // create message and add to list
1215 ActiveObjectMessage aom(getId(), true, str);
1216 m_messages_out.push(aom);
1221 actionstream << "Player " << m_player->getName() << " punched by "
1223 if (!damage_handled) {
1224 actionstream << ", damage " << hitparams.hp << " HP";
1226 actionstream << ", damage handled by lua";
1228 actionstream << std::endl;
1230 return hitparams.wear;
1233 s16 PlayerSAO::readDamage()
1235 s16 damage = m_damage;
1240 void PlayerSAO::setHP(s16 hp)
1244 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1247 hp = oldhp + hp_change;
1251 else if (hp > PLAYER_MAX_HP)
1254 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1261 m_damage += (oldhp - hp);
1263 // Update properties on death
1264 if ((hp == 0) != (oldhp == 0))
1265 m_properties_sent = false;
1268 void PlayerSAO::setBreath(const u16 breath, bool send)
1270 if (m_player && breath != m_breath)
1271 m_player->setDirty(true);
1273 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1276 m_env->getGameDef()->SendPlayerBreath(this);
1279 Inventory* PlayerSAO::getInventory()
1283 const Inventory* PlayerSAO::getInventory() const
1288 InventoryLocation PlayerSAO::getInventoryLocation() const
1290 InventoryLocation loc;
1291 loc.setPlayer(m_player->getName());
1295 std::string PlayerSAO::getWieldList() const
1300 ItemStack PlayerSAO::getWieldedItem() const
1302 const Inventory *inv = getInventory();
1304 const InventoryList *mlist = inv->getList(getWieldList());
1305 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1306 ret = mlist->getItem(getWieldIndex());
1310 ItemStack PlayerSAO::getWieldedItemOrHand() const
1312 const Inventory *inv = getInventory();
1314 const InventoryList *mlist = inv->getList(getWieldList());
1315 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1316 ret = mlist->getItem(getWieldIndex());
1317 if (ret.name.empty()) {
1318 const InventoryList *hlist = inv->getList("hand");
1320 ret = hlist->getItem(0);
1325 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1327 Inventory *inv = getInventory();
1329 InventoryList *mlist = inv->getList(getWieldList());
1331 mlist->changeItem(getWieldIndex(), item);
1338 int PlayerSAO::getWieldIndex() const
1340 return m_wield_index;
1343 void PlayerSAO::setWieldIndex(int i)
1345 if(i != m_wield_index) {
1350 // Erase the peer id and make the object for removal
1351 void PlayerSAO::disconnected()
1357 void PlayerSAO::unlinkPlayerSessionAndSave()
1359 assert(m_player->getPlayerSAO() == this);
1360 m_player->peer_id = 0;
1361 m_env->savePlayer(m_player);
1362 m_player->setPlayerSAO(NULL);
1363 m_env->removePlayer(m_player);
1366 std::string PlayerSAO::getPropertyPacket()
1368 m_prop.is_visible = (true);
1369 return gob_cmd_set_properties(m_prop);
1372 bool PlayerSAO::checkMovementCheat()
1374 if (isAttached() || m_is_singleplayer ||
1375 g_settings->getBool("disable_anticheat")) {
1376 m_last_good_position = m_base_position;
1380 bool cheated = false;
1382 Check player movements
1384 NOTE: Actually the server should handle player physics like the
1385 client does and compare player's position to what is calculated
1386 on our side. This is required when eg. players fly due to an
1387 explosion. Altough a node-based alternative might be possible
1388 too, and much more lightweight.
1391 float player_max_speed = 0;
1393 if (m_privs.count("fast") != 0) {
1395 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1398 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1400 // Tolerance. The lag pool does this a bit.
1401 //player_max_speed *= 2.5;
1403 v3f diff = (m_base_position - m_last_good_position);
1404 float d_vert = diff.Y;
1406 float d_horiz = diff.getLength();
1407 float required_time = d_horiz / player_max_speed;
1409 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1410 required_time = d_vert / player_max_speed; // Moving upwards
1412 if (m_move_pool.grab(required_time)) {
1413 m_last_good_position = m_base_position;
1415 const float LAG_POOL_MIN = 5.0;
1416 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1417 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1418 if (m_time_from_last_teleport > lag_pool_max) {
1419 actionstream << "Player " << m_player->getName()
1420 << " moved too fast; resetting position"
1424 setBasePosition(m_last_good_position);
1429 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1431 *toset = aabb3f(-0.3f * BS, 0.0f, -0.3f * BS, 0.3f * BS, 1.75f * BS, 0.3f * BS);
1433 toset->MinEdge += m_base_position;
1434 toset->MaxEdge += m_base_position;