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(true),
792 m_physics_override_sent(false)
794 assert(m_peer_id != 0); // pre-condition
796 m_prop.hp_max = PLAYER_MAX_HP;
797 m_prop.physical = false;
799 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
800 // start of default appearance, this should be overwritten by LUA
801 m_prop.visual = "upright_sprite";
802 m_prop.visual_size = v2f(1, 2);
803 m_prop.textures.clear();
804 m_prop.textures.push_back("player.png");
805 m_prop.textures.push_back("player_back.png");
806 m_prop.colors.clear();
807 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
808 m_prop.spritediv = v2s16(1,1);
809 // end of default appearance
810 m_prop.is_visible = true;
811 m_prop.makes_footstep_sound = true;
812 m_hp = PLAYER_MAX_HP;
815 PlayerSAO::~PlayerSAO()
817 if(m_inventory != &m_player->inventory)
821 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
826 m_inventory = &m_player->inventory;
829 v3f PlayerSAO::getEyeOffset() const
831 return v3f(0, BS * 1.625f, 0);
834 std::string PlayerSAO::getDescription()
836 return std::string("player ") + m_player->getName();
839 // Called after id has been set and has been inserted in environment
840 void PlayerSAO::addedToEnvironment(u32 dtime_s)
842 ServerActiveObject::addedToEnvironment(dtime_s);
843 ServerActiveObject::setBasePosition(m_base_position);
844 m_player->setPlayerSAO(this);
845 m_player->peer_id = m_peer_id;
846 m_last_good_position = m_base_position;
849 // Called before removing from environment
850 void PlayerSAO::removingFromEnvironment()
852 ServerActiveObject::removingFromEnvironment();
853 if (m_player->getPlayerSAO() == this) {
854 unlinkPlayerSessionAndSave();
855 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
856 it != m_attached_particle_spawners.end(); ++it) {
857 m_env->deleteParticleSpawner(*it, false);
862 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
864 std::ostringstream os(std::ios::binary);
867 writeU8(os, 1); // version
868 os << serializeString(m_player->getName()); // name
869 writeU8(os, 1); // is_player
870 writeS16(os, getId()); //id
871 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
872 writeF1000(os, m_yaw);
873 writeS16(os, getHP());
875 std::ostringstream msg_os(std::ios::binary);
876 msg_os << serializeLongString(getPropertyPacket()); // message 1
877 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
878 msg_os << serializeLongString(gob_cmd_update_animation(
879 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
880 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
881 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
882 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
883 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
885 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
886 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
887 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
888 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
889 m_physics_override_sneak_glitch)); // 5
890 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
891 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
892 int message_count = 6 + m_bone_position.size();
893 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
894 ii != m_attachment_child_ids.end(); ++ii) {
895 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
897 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
898 obj->getClientInitializationData(protocol_version)));
902 writeU8(os, message_count);
903 os.write(msg_os.str().c_str(), msg_os.str().size());
909 void PlayerSAO::getStaticData(std::string *result) const
911 FATAL_ERROR("Deprecated function");
914 void PlayerSAO::step(float dtime, bool send_recommended)
916 if (m_drowning_interval.step(dtime, 2.0)) {
918 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
919 MapNode n = m_env->getMap().getNodeNoEx(p);
920 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
921 // If node generates drown
922 if (c.drowning > 0 && m_hp > 0) {
924 setBreath(m_breath - 1);
926 // No more breath, damage player
928 setHP(m_hp - c.drowning);
929 m_env->getGameDef()->SendPlayerHPOrDie(this);
934 if (m_breathing_interval.step(dtime, 0.5)) {
936 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
937 MapNode n = m_env->getMap().getNodeNoEx(p);
938 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
939 // If player is alive & no drowning, breath
940 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
941 setBreath(m_breath + 1);
944 if (m_node_hurt_interval.step(dtime, 1.0)) {
945 // Feet, middle and head
946 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
947 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
948 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
949 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
950 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
951 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
953 u32 damage_per_second = 0;
954 damage_per_second = MYMAX(damage_per_second,
955 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
956 damage_per_second = MYMAX(damage_per_second,
957 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
958 damage_per_second = MYMAX(damage_per_second,
959 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
961 if (damage_per_second != 0 && m_hp > 0) {
962 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
964 m_env->getGameDef()->SendPlayerHPOrDie(this);
968 if (!m_properties_sent) {
969 m_properties_sent = true;
970 std::string str = getPropertyPacket();
971 // create message and add to list
972 ActiveObjectMessage aom(getId(), true, str);
973 m_messages_out.push(aom);
976 // If attached, check that our parent is still there. If it isn't, detach.
977 if(m_attachment_parent_id && !isAttached())
979 m_attachment_parent_id = 0;
980 m_attachment_bone = "";
981 m_attachment_position = v3f(0,0,0);
982 m_attachment_rotation = v3f(0,0,0);
983 setBasePosition(m_last_good_position);
984 m_env->getGameDef()->SendMovePlayer(m_peer_id);
987 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
989 // Set lag pool maximums based on estimated lag
990 const float LAG_POOL_MIN = 5.0;
991 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
992 if(lag_pool_max < LAG_POOL_MIN)
993 lag_pool_max = LAG_POOL_MIN;
994 m_dig_pool.setMax(lag_pool_max);
995 m_move_pool.setMax(lag_pool_max);
997 // Increment cheat prevention timers
998 m_dig_pool.add(dtime);
999 m_move_pool.add(dtime);
1000 m_time_from_last_teleport += dtime;
1001 m_time_from_last_punch += dtime;
1002 m_nocheat_dig_time += dtime;
1004 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1005 // If the object gets detached this comes into effect automatically from the last known origin
1007 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1008 m_last_good_position = pos;
1009 setBasePosition(pos);
1012 if (!send_recommended)
1015 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1016 if(m_position_not_sent && !isAttached())
1018 m_position_not_sent = false;
1019 float update_interval = m_env->getSendRecommendedInterval();
1021 if(isAttached()) // Just in case we ever do send attachment position too
1022 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1024 pos = m_base_position + v3f(0,BS*1,0);
1025 std::string str = gob_cmd_update_position(
1034 // create message and add to list
1035 ActiveObjectMessage aom(getId(), false, str);
1036 m_messages_out.push(aom);
1039 if (!m_armor_groups_sent) {
1040 m_armor_groups_sent = true;
1041 std::string str = gob_cmd_update_armor_groups(
1043 // create message and add to list
1044 ActiveObjectMessage aom(getId(), true, str);
1045 m_messages_out.push(aom);
1048 if (!m_physics_override_sent) {
1049 m_physics_override_sent = true;
1050 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1051 m_physics_override_jump, m_physics_override_gravity,
1052 m_physics_override_sneak, m_physics_override_sneak_glitch);
1053 // create message and add to list
1054 ActiveObjectMessage aom(getId(), true, str);
1055 m_messages_out.push(aom);
1058 if (!m_animation_sent) {
1059 m_animation_sent = true;
1060 std::string str = gob_cmd_update_animation(
1061 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1062 // create message and add to list
1063 ActiveObjectMessage aom(getId(), true, str);
1064 m_messages_out.push(aom);
1067 if (!m_bone_position_sent) {
1068 m_bone_position_sent = true;
1069 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1070 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1071 std::string str = gob_cmd_update_bone_position((*ii).first,
1072 (*ii).second.X, (*ii).second.Y);
1073 // create message and add to list
1074 ActiveObjectMessage aom(getId(), true, str);
1075 m_messages_out.push(aom);
1079 if (!m_attachment_sent){
1080 m_attachment_sent = true;
1081 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1082 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1083 // create message and add to list
1084 ActiveObjectMessage aom(getId(), true, str);
1085 m_messages_out.push(aom);
1089 void PlayerSAO::setBasePosition(const v3f &position)
1091 if (m_player && position != m_base_position)
1092 m_player->setDirty(true);
1094 // This needs to be ran for attachments too
1095 ServerActiveObject::setBasePosition(position);
1096 m_position_not_sent = true;
1099 void PlayerSAO::setPos(const v3f &pos)
1104 setBasePosition(pos);
1105 // Movement caused by this command is always valid
1106 m_last_good_position = pos;
1107 m_move_pool.empty();
1108 m_time_from_last_teleport = 0.0;
1109 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1112 void PlayerSAO::moveTo(v3f pos, bool continuous)
1117 setBasePosition(pos);
1118 // Movement caused by this command is always valid
1119 m_last_good_position = pos;
1120 m_move_pool.empty();
1121 m_time_from_last_teleport = 0.0;
1122 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1125 void PlayerSAO::setYaw(const float yaw)
1127 if (m_player && yaw != m_yaw)
1128 m_player->setDirty(true);
1130 UnitSAO::setYaw(yaw);
1133 void PlayerSAO::setFov(const float fov)
1135 if (m_player && fov != m_fov)
1136 m_player->setDirty(true);
1141 void PlayerSAO::setWantedRange(const s16 range)
1143 if (m_player && range != m_wanted_range)
1144 m_player->setDirty(true);
1146 m_wanted_range = range;
1149 void PlayerSAO::setYawAndSend(const float yaw)
1152 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1155 void PlayerSAO::setPitch(const float pitch)
1157 if (m_player && pitch != m_pitch)
1158 m_player->setDirty(true);
1163 void PlayerSAO::setPitchAndSend(const float pitch)
1166 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1169 int PlayerSAO::punch(v3f dir,
1170 const ToolCapabilities *toolcap,
1171 ServerActiveObject *puncher,
1172 float time_from_last_punch)
1174 // It's best that attachments cannot be punched
1181 // No effect if PvP disabled
1182 if (g_settings->getBool("enable_pvp") == false) {
1183 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1184 std::string str = gob_cmd_punched(0, getHP());
1185 // create message and add to list
1186 ActiveObjectMessage aom(getId(), true, str);
1187 m_messages_out.push(aom);
1192 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1193 time_from_last_punch);
1195 std::string punchername = "nil";
1198 punchername = puncher->getDescription();
1200 PlayerSAO *playersao = m_player->getPlayerSAO();
1202 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1203 puncher, time_from_last_punch, toolcap, dir,
1206 if (!damage_handled) {
1207 setHP(getHP() - hitparams.hp);
1208 } else { // override client prediction
1209 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1210 std::string str = gob_cmd_punched(0, getHP());
1211 // create message and add to list
1212 ActiveObjectMessage aom(getId(), true, str);
1213 m_messages_out.push(aom);
1218 actionstream << "Player " << m_player->getName() << " punched by "
1220 if (!damage_handled) {
1221 actionstream << ", damage " << hitparams.hp << " HP";
1223 actionstream << ", damage handled by lua";
1225 actionstream << std::endl;
1227 return hitparams.wear;
1230 s16 PlayerSAO::readDamage()
1232 s16 damage = m_damage;
1237 void PlayerSAO::setHP(s16 hp)
1241 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1244 hp = oldhp + hp_change;
1248 else if (hp > PLAYER_MAX_HP)
1251 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1258 m_damage += (oldhp - hp);
1260 // Update properties on death
1261 if ((hp == 0) != (oldhp == 0))
1262 m_properties_sent = false;
1265 void PlayerSAO::setBreath(const u16 breath, bool send)
1267 if (m_player && breath != m_breath)
1268 m_player->setDirty(true);
1270 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1273 m_env->getGameDef()->SendPlayerBreath(this);
1276 Inventory* PlayerSAO::getInventory()
1280 const Inventory* PlayerSAO::getInventory() const
1285 InventoryLocation PlayerSAO::getInventoryLocation() const
1287 InventoryLocation loc;
1288 loc.setPlayer(m_player->getName());
1292 std::string PlayerSAO::getWieldList() const
1297 ItemStack PlayerSAO::getWieldedItem() const
1299 const Inventory *inv = getInventory();
1301 const InventoryList *mlist = inv->getList(getWieldList());
1302 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1303 ret = mlist->getItem(getWieldIndex());
1307 ItemStack PlayerSAO::getWieldedItemOrHand() const
1309 const Inventory *inv = getInventory();
1311 const InventoryList *mlist = inv->getList(getWieldList());
1312 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1313 ret = mlist->getItem(getWieldIndex());
1314 if (ret.name.empty()) {
1315 const InventoryList *hlist = inv->getList("hand");
1317 ret = hlist->getItem(0);
1322 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1324 Inventory *inv = getInventory();
1326 InventoryList *mlist = inv->getList(getWieldList());
1328 mlist->changeItem(getWieldIndex(), item);
1335 int PlayerSAO::getWieldIndex() const
1337 return m_wield_index;
1340 void PlayerSAO::setWieldIndex(int i)
1342 if(i != m_wield_index) {
1347 // Erase the peer id and make the object for removal
1348 void PlayerSAO::disconnected()
1354 void PlayerSAO::unlinkPlayerSessionAndSave()
1356 assert(m_player->getPlayerSAO() == this);
1357 m_player->peer_id = 0;
1358 m_env->savePlayer(m_player);
1359 m_player->setPlayerSAO(NULL);
1360 m_env->removePlayer(m_player);
1363 std::string PlayerSAO::getPropertyPacket()
1365 m_prop.is_visible = (true);
1366 return gob_cmd_set_properties(m_prop);
1369 bool PlayerSAO::checkMovementCheat()
1371 if (isAttached() || m_is_singleplayer ||
1372 g_settings->getBool("disable_anticheat")) {
1373 m_last_good_position = m_base_position;
1377 bool cheated = false;
1379 Check player movements
1381 NOTE: Actually the server should handle player physics like the
1382 client does and compare player's position to what is calculated
1383 on our side. This is required when eg. players fly due to an
1384 explosion. Altough a node-based alternative might be possible
1385 too, and much more lightweight.
1388 float player_max_speed = 0;
1390 if (m_privs.count("fast") != 0) {
1392 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1395 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1397 // Tolerance. The lag pool does this a bit.
1398 //player_max_speed *= 2.5;
1400 v3f diff = (m_base_position - m_last_good_position);
1401 float d_vert = diff.Y;
1403 float d_horiz = diff.getLength();
1404 float required_time = d_horiz / player_max_speed;
1406 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1407 required_time = d_vert / player_max_speed; // Moving upwards
1409 if (m_move_pool.grab(required_time)) {
1410 m_last_good_position = m_base_position;
1412 const float LAG_POOL_MIN = 5.0;
1413 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1414 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1415 if (m_time_from_last_teleport > lag_pool_max) {
1416 actionstream << "Player " << m_player->getName()
1417 << " moved too fast; resetting position"
1421 setBasePosition(m_last_good_position);
1426 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1428 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1429 toset->MinEdge += m_base_position;
1430 toset->MaxEdge += m_base_position;