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 "serverscripting.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_, u16 peer_id_, bool is_singleplayer):
768 UnitSAO(env_, v3f(0,0,0)),
773 m_last_good_position(0,0,0),
774 m_time_from_last_teleport(0),
775 m_time_from_last_punch(0),
776 m_nocheat_dig_pos(32767, 32767, 32767),
777 m_nocheat_dig_time(0),
779 m_position_not_sent(false),
780 m_is_singleplayer(is_singleplayer),
781 m_breath(PLAYER_MAX_BREATH),
785 m_extended_attributes_modified(false),
787 m_physics_override_speed(1),
788 m_physics_override_jump(1),
789 m_physics_override_gravity(1),
790 m_physics_override_sneak(true),
791 m_physics_override_sneak_glitch(false),
792 m_physics_override_new_move(true),
793 m_physics_override_sent(false)
795 assert(m_peer_id != 0); // pre-condition
797 m_prop.hp_max = PLAYER_MAX_HP;
798 m_prop.physical = false;
800 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
801 // start of default appearance, this should be overwritten by LUA
802 m_prop.visual = "upright_sprite";
803 m_prop.visual_size = v2f(1, 2);
804 m_prop.textures.clear();
805 m_prop.textures.push_back("player.png");
806 m_prop.textures.push_back("player_back.png");
807 m_prop.colors.clear();
808 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
809 m_prop.spritediv = v2s16(1,1);
810 // end of default appearance
811 m_prop.is_visible = true;
812 m_prop.makes_footstep_sound = true;
813 m_hp = PLAYER_MAX_HP;
816 PlayerSAO::~PlayerSAO()
818 if(m_inventory != &m_player->inventory)
822 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
827 m_inventory = &m_player->inventory;
830 v3f PlayerSAO::getEyeOffset() const
832 return v3f(0, BS * 1.625f, 0);
835 std::string PlayerSAO::getDescription()
837 return std::string("player ") + m_player->getName();
840 // Called after id has been set and has been inserted in environment
841 void PlayerSAO::addedToEnvironment(u32 dtime_s)
843 ServerActiveObject::addedToEnvironment(dtime_s);
844 ServerActiveObject::setBasePosition(m_base_position);
845 m_player->setPlayerSAO(this);
846 m_player->peer_id = m_peer_id;
847 m_last_good_position = m_base_position;
850 // Called before removing from environment
851 void PlayerSAO::removingFromEnvironment()
853 ServerActiveObject::removingFromEnvironment();
854 if (m_player->getPlayerSAO() == this) {
855 unlinkPlayerSessionAndSave();
856 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
857 it != m_attached_particle_spawners.end(); ++it) {
858 m_env->deleteParticleSpawner(*it, false);
863 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
865 std::ostringstream os(std::ios::binary);
868 writeU8(os, 1); // version
869 os << serializeString(m_player->getName()); // name
870 writeU8(os, 1); // is_player
871 writeS16(os, getId()); //id
872 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
873 writeF1000(os, m_yaw);
874 writeS16(os, getHP());
876 std::ostringstream msg_os(std::ios::binary);
877 msg_os << serializeLongString(getPropertyPacket()); // message 1
878 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
879 msg_os << serializeLongString(gob_cmd_update_animation(
880 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
881 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
882 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
883 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
884 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
886 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
887 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
888 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
889 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
890 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
891 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
892 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
893 int message_count = 6 + m_bone_position.size();
894 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
895 ii != m_attachment_child_ids.end(); ++ii) {
896 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
898 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
899 obj->getClientInitializationData(protocol_version)));
903 writeU8(os, message_count);
904 os.write(msg_os.str().c_str(), msg_os.str().size());
910 void PlayerSAO::getStaticData(std::string *result) const
912 FATAL_ERROR("Deprecated function");
915 void PlayerSAO::step(float dtime, bool send_recommended)
917 if (m_drowning_interval.step(dtime, 2.0)) {
919 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
920 MapNode n = m_env->getMap().getNodeNoEx(p);
921 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
922 // If node generates drown
923 if (c.drowning > 0 && m_hp > 0) {
925 setBreath(m_breath - 1);
927 // No more breath, damage player
929 setHP(m_hp - c.drowning);
930 m_env->getGameDef()->SendPlayerHPOrDie(this);
935 if (m_breathing_interval.step(dtime, 0.5)) {
937 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
938 MapNode n = m_env->getMap().getNodeNoEx(p);
939 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
940 // If player is alive & no drowning, breath
941 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
942 setBreath(m_breath + 1);
945 if (m_node_hurt_interval.step(dtime, 1.0)) {
946 // Feet, middle and head
947 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
948 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
949 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
950 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
951 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
952 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
954 u32 damage_per_second = 0;
955 damage_per_second = MYMAX(damage_per_second,
956 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
957 damage_per_second = MYMAX(damage_per_second,
958 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
959 damage_per_second = MYMAX(damage_per_second,
960 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
962 if (damage_per_second != 0 && m_hp > 0) {
963 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
965 m_env->getGameDef()->SendPlayerHPOrDie(this);
969 if (!m_properties_sent) {
970 m_properties_sent = true;
971 std::string str = getPropertyPacket();
972 // create message and add to list
973 ActiveObjectMessage aom(getId(), true, str);
974 m_messages_out.push(aom);
977 // If attached, check that our parent is still there. If it isn't, detach.
978 if(m_attachment_parent_id && !isAttached())
980 m_attachment_parent_id = 0;
981 m_attachment_bone = "";
982 m_attachment_position = v3f(0,0,0);
983 m_attachment_rotation = v3f(0,0,0);
984 setBasePosition(m_last_good_position);
985 m_env->getGameDef()->SendMovePlayer(m_peer_id);
988 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
990 // Set lag pool maximums based on estimated lag
991 const float LAG_POOL_MIN = 5.0;
992 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
993 if(lag_pool_max < LAG_POOL_MIN)
994 lag_pool_max = LAG_POOL_MIN;
995 m_dig_pool.setMax(lag_pool_max);
996 m_move_pool.setMax(lag_pool_max);
998 // Increment cheat prevention timers
999 m_dig_pool.add(dtime);
1000 m_move_pool.add(dtime);
1001 m_time_from_last_teleport += dtime;
1002 m_time_from_last_punch += dtime;
1003 m_nocheat_dig_time += dtime;
1005 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1006 // If the object gets detached this comes into effect automatically from the last known origin
1008 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1009 m_last_good_position = pos;
1010 setBasePosition(pos);
1013 if (!send_recommended)
1016 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1017 if(m_position_not_sent && !isAttached())
1019 m_position_not_sent = false;
1020 float update_interval = m_env->getSendRecommendedInterval();
1022 if(isAttached()) // Just in case we ever do send attachment position too
1023 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1025 pos = m_base_position + v3f(0,BS*1,0);
1026 std::string str = gob_cmd_update_position(
1035 // create message and add to list
1036 ActiveObjectMessage aom(getId(), false, str);
1037 m_messages_out.push(aom);
1040 if (!m_armor_groups_sent) {
1041 m_armor_groups_sent = true;
1042 std::string str = gob_cmd_update_armor_groups(
1044 // create message and add to list
1045 ActiveObjectMessage aom(getId(), true, str);
1046 m_messages_out.push(aom);
1049 if (!m_physics_override_sent) {
1050 m_physics_override_sent = true;
1051 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1052 m_physics_override_jump, m_physics_override_gravity,
1053 m_physics_override_sneak, m_physics_override_sneak_glitch,
1054 m_physics_override_new_move);
1055 // create message and add to list
1056 ActiveObjectMessage aom(getId(), true, str);
1057 m_messages_out.push(aom);
1060 if (!m_animation_sent) {
1061 m_animation_sent = true;
1062 std::string str = gob_cmd_update_animation(
1063 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1064 // create message and add to list
1065 ActiveObjectMessage aom(getId(), true, str);
1066 m_messages_out.push(aom);
1069 if (!m_bone_position_sent) {
1070 m_bone_position_sent = true;
1071 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1072 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1073 std::string str = gob_cmd_update_bone_position((*ii).first,
1074 (*ii).second.X, (*ii).second.Y);
1075 // create message and add to list
1076 ActiveObjectMessage aom(getId(), true, str);
1077 m_messages_out.push(aom);
1081 if (!m_attachment_sent){
1082 m_attachment_sent = true;
1083 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1084 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1085 // create message and add to list
1086 ActiveObjectMessage aom(getId(), true, str);
1087 m_messages_out.push(aom);
1091 void PlayerSAO::setBasePosition(const v3f &position)
1093 if (m_player && position != m_base_position)
1094 m_player->setDirty(true);
1096 // This needs to be ran for attachments too
1097 ServerActiveObject::setBasePosition(position);
1098 m_position_not_sent = true;
1101 void PlayerSAO::setPos(const v3f &pos)
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::moveTo(v3f pos, bool continuous)
1119 setBasePosition(pos);
1120 // Movement caused by this command is always valid
1121 m_last_good_position = pos;
1122 m_move_pool.empty();
1123 m_time_from_last_teleport = 0.0;
1124 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1127 void PlayerSAO::setYaw(const float yaw)
1129 if (m_player && yaw != m_yaw)
1130 m_player->setDirty(true);
1132 UnitSAO::setYaw(yaw);
1135 void PlayerSAO::setFov(const float fov)
1137 if (m_player && fov != m_fov)
1138 m_player->setDirty(true);
1143 void PlayerSAO::setWantedRange(const s16 range)
1145 if (m_player && range != m_wanted_range)
1146 m_player->setDirty(true);
1148 m_wanted_range = range;
1151 void PlayerSAO::setYawAndSend(const float yaw)
1154 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1157 void PlayerSAO::setPitch(const float pitch)
1159 if (m_player && pitch != m_pitch)
1160 m_player->setDirty(true);
1165 void PlayerSAO::setPitchAndSend(const float pitch)
1168 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1171 int PlayerSAO::punch(v3f dir,
1172 const ToolCapabilities *toolcap,
1173 ServerActiveObject *puncher,
1174 float time_from_last_punch)
1176 // It's best that attachments cannot be punched
1183 // No effect if PvP disabled
1184 if (g_settings->getBool("enable_pvp") == false) {
1185 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1186 std::string str = gob_cmd_punched(0, getHP());
1187 // create message and add to list
1188 ActiveObjectMessage aom(getId(), true, str);
1189 m_messages_out.push(aom);
1194 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1195 time_from_last_punch);
1197 std::string punchername = "nil";
1200 punchername = puncher->getDescription();
1202 PlayerSAO *playersao = m_player->getPlayerSAO();
1204 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1205 puncher, time_from_last_punch, toolcap, dir,
1208 if (!damage_handled) {
1209 setHP(getHP() - hitparams.hp);
1210 } else { // override client prediction
1211 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1212 std::string str = gob_cmd_punched(0, getHP());
1213 // create message and add to list
1214 ActiveObjectMessage aom(getId(), true, str);
1215 m_messages_out.push(aom);
1220 actionstream << "Player " << m_player->getName() << " punched by "
1222 if (!damage_handled) {
1223 actionstream << ", damage " << hitparams.hp << " HP";
1225 actionstream << ", damage handled by lua";
1227 actionstream << std::endl;
1229 return hitparams.wear;
1232 s16 PlayerSAO::readDamage()
1234 s16 damage = m_damage;
1239 void PlayerSAO::setHP(s16 hp)
1243 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1246 hp = oldhp + hp_change;
1250 else if (hp > PLAYER_MAX_HP)
1253 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1260 m_damage += (oldhp - hp);
1262 // Update properties on death
1263 if ((hp == 0) != (oldhp == 0))
1264 m_properties_sent = false;
1267 void PlayerSAO::setBreath(const u16 breath, bool send)
1269 if (m_player && breath != m_breath)
1270 m_player->setDirty(true);
1272 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1275 m_env->getGameDef()->SendPlayerBreath(this);
1278 Inventory* PlayerSAO::getInventory()
1282 const Inventory* PlayerSAO::getInventory() const
1287 InventoryLocation PlayerSAO::getInventoryLocation() const
1289 InventoryLocation loc;
1290 loc.setPlayer(m_player->getName());
1294 std::string PlayerSAO::getWieldList() const
1299 ItemStack PlayerSAO::getWieldedItem() const
1301 const Inventory *inv = getInventory();
1303 const InventoryList *mlist = inv->getList(getWieldList());
1304 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1305 ret = mlist->getItem(getWieldIndex());
1309 ItemStack PlayerSAO::getWieldedItemOrHand() const
1311 const Inventory *inv = getInventory();
1313 const InventoryList *mlist = inv->getList(getWieldList());
1314 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1315 ret = mlist->getItem(getWieldIndex());
1316 if (ret.name.empty()) {
1317 const InventoryList *hlist = inv->getList("hand");
1319 ret = hlist->getItem(0);
1324 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1326 Inventory *inv = getInventory();
1328 InventoryList *mlist = inv->getList(getWieldList());
1330 mlist->changeItem(getWieldIndex(), item);
1337 int PlayerSAO::getWieldIndex() const
1339 return m_wield_index;
1342 void PlayerSAO::setWieldIndex(int i)
1344 if(i != m_wield_index) {
1349 // Erase the peer id and make the object for removal
1350 void PlayerSAO::disconnected()
1356 void PlayerSAO::unlinkPlayerSessionAndSave()
1358 assert(m_player->getPlayerSAO() == this);
1359 m_player->peer_id = 0;
1360 m_env->savePlayer(m_player);
1361 m_player->setPlayerSAO(NULL);
1362 m_env->removePlayer(m_player);
1365 std::string PlayerSAO::getPropertyPacket()
1367 m_prop.is_visible = (true);
1368 return gob_cmd_set_properties(m_prop);
1371 bool PlayerSAO::checkMovementCheat()
1373 if (isAttached() || m_is_singleplayer ||
1374 g_settings->getBool("disable_anticheat")) {
1375 m_last_good_position = m_base_position;
1379 bool cheated = false;
1381 Check player movements
1383 NOTE: Actually the server should handle player physics like the
1384 client does and compare player's position to what is calculated
1385 on our side. This is required when eg. players fly due to an
1386 explosion. Altough a node-based alternative might be possible
1387 too, and much more lightweight.
1390 float player_max_speed = 0;
1392 if (m_privs.count("fast") != 0) {
1394 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1397 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1399 // Tolerance. The lag pool does this a bit.
1400 //player_max_speed *= 2.5;
1402 v3f diff = (m_base_position - m_last_good_position);
1403 float d_vert = diff.Y;
1405 float d_horiz = diff.getLength();
1406 float required_time = d_horiz / player_max_speed;
1408 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1409 required_time = d_vert / player_max_speed; // Moving upwards
1411 if (m_move_pool.grab(required_time)) {
1412 m_last_good_position = m_base_position;
1414 const float LAG_POOL_MIN = 5.0;
1415 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1416 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1417 if (m_time_from_last_teleport > lag_pool_max) {
1418 actionstream << "Player " << m_player->getName()
1419 << " moved too fast; resetting position"
1423 setBasePosition(m_last_good_position);
1428 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1430 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1431 toset->MinEdge += m_base_position;
1432 toset->MaxEdge += m_base_position;