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_properties_sent) {
945 m_properties_sent = true;
946 std::string str = getPropertyPacket();
947 // create message and add to list
948 ActiveObjectMessage aom(getId(), true, str);
949 m_messages_out.push(aom);
952 // If attached, check that our parent is still there. If it isn't, detach.
953 if(m_attachment_parent_id && !isAttached())
955 m_attachment_parent_id = 0;
956 m_attachment_bone = "";
957 m_attachment_position = v3f(0,0,0);
958 m_attachment_rotation = v3f(0,0,0);
959 setBasePosition(m_last_good_position);
960 m_env->getGameDef()->SendMovePlayer(m_peer_id);
963 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
965 // Set lag pool maximums based on estimated lag
966 const float LAG_POOL_MIN = 5.0;
967 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
968 if(lag_pool_max < LAG_POOL_MIN)
969 lag_pool_max = LAG_POOL_MIN;
970 m_dig_pool.setMax(lag_pool_max);
971 m_move_pool.setMax(lag_pool_max);
973 // Increment cheat prevention timers
974 m_dig_pool.add(dtime);
975 m_move_pool.add(dtime);
976 m_time_from_last_teleport += dtime;
977 m_time_from_last_punch += dtime;
978 m_nocheat_dig_time += dtime;
980 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
981 // If the object gets detached this comes into effect automatically from the last known origin
983 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
984 m_last_good_position = pos;
985 setBasePosition(pos);
988 if (!send_recommended)
991 // If the object is attached client-side, don't waste bandwidth sending its position to clients
992 if(m_position_not_sent && !isAttached())
994 m_position_not_sent = false;
995 float update_interval = m_env->getSendRecommendedInterval();
997 if(isAttached()) // Just in case we ever do send attachment position too
998 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1000 pos = m_base_position + v3f(0,BS*1,0);
1001 std::string str = gob_cmd_update_position(
1010 // create message and add to list
1011 ActiveObjectMessage aom(getId(), false, str);
1012 m_messages_out.push(aom);
1015 if (!m_armor_groups_sent) {
1016 m_armor_groups_sent = true;
1017 std::string str = gob_cmd_update_armor_groups(
1019 // create message and add to list
1020 ActiveObjectMessage aom(getId(), true, str);
1021 m_messages_out.push(aom);
1024 if (!m_physics_override_sent) {
1025 m_physics_override_sent = true;
1026 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1027 m_physics_override_jump, m_physics_override_gravity,
1028 m_physics_override_sneak, m_physics_override_sneak_glitch);
1029 // create message and add to list
1030 ActiveObjectMessage aom(getId(), true, str);
1031 m_messages_out.push(aom);
1034 if (!m_animation_sent) {
1035 m_animation_sent = true;
1036 std::string str = gob_cmd_update_animation(
1037 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1038 // create message and add to list
1039 ActiveObjectMessage aom(getId(), true, str);
1040 m_messages_out.push(aom);
1043 if (!m_bone_position_sent) {
1044 m_bone_position_sent = true;
1045 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1046 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1047 std::string str = gob_cmd_update_bone_position((*ii).first,
1048 (*ii).second.X, (*ii).second.Y);
1049 // create message and add to list
1050 ActiveObjectMessage aom(getId(), true, str);
1051 m_messages_out.push(aom);
1055 if (!m_attachment_sent){
1056 m_attachment_sent = true;
1057 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1058 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1059 // create message and add to list
1060 ActiveObjectMessage aom(getId(), true, str);
1061 m_messages_out.push(aom);
1065 void PlayerSAO::setBasePosition(const v3f &position)
1067 if (m_player && position != m_base_position)
1068 m_player->setDirty(true);
1070 // This needs to be ran for attachments too
1071 ServerActiveObject::setBasePosition(position);
1072 m_position_not_sent = true;
1075 void PlayerSAO::setPos(const v3f &pos)
1080 setBasePosition(pos);
1081 // Movement caused by this command is always valid
1082 m_last_good_position = pos;
1083 m_move_pool.empty();
1084 m_time_from_last_teleport = 0.0;
1085 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1088 void PlayerSAO::moveTo(v3f pos, bool continuous)
1093 setBasePosition(pos);
1094 // Movement caused by this command is always valid
1095 m_last_good_position = pos;
1096 m_move_pool.empty();
1097 m_time_from_last_teleport = 0.0;
1098 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1101 void PlayerSAO::setYaw(const float yaw)
1103 if (m_player && yaw != m_yaw)
1104 m_player->setDirty(true);
1106 UnitSAO::setYaw(yaw);
1109 void PlayerSAO::setFov(const float fov)
1111 if (m_player && fov != m_fov)
1112 m_player->setDirty(true);
1117 void PlayerSAO::setWantedRange(const s16 range)
1119 if (m_player && range != m_wanted_range)
1120 m_player->setDirty(true);
1122 m_wanted_range = range;
1125 void PlayerSAO::setYawAndSend(const float yaw)
1128 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1131 void PlayerSAO::setPitch(const float pitch)
1133 if (m_player && pitch != m_pitch)
1134 m_player->setDirty(true);
1139 void PlayerSAO::setPitchAndSend(const float pitch)
1142 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1145 int PlayerSAO::punch(v3f dir,
1146 const ToolCapabilities *toolcap,
1147 ServerActiveObject *puncher,
1148 float time_from_last_punch)
1150 // It's best that attachments cannot be punched
1157 // No effect if PvP disabled
1158 if (g_settings->getBool("enable_pvp") == false) {
1159 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1160 std::string str = gob_cmd_punched(0, getHP());
1161 // create message and add to list
1162 ActiveObjectMessage aom(getId(), true, str);
1163 m_messages_out.push(aom);
1168 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1169 time_from_last_punch);
1171 std::string punchername = "nil";
1174 punchername = puncher->getDescription();
1176 PlayerSAO *playersao = m_player->getPlayerSAO();
1178 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1179 puncher, time_from_last_punch, toolcap, dir,
1182 if (!damage_handled) {
1183 setHP(getHP() - hitparams.hp);
1184 } else { // override client prediction
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 actionstream << "Player " << m_player->getName() << " punched by "
1196 if (!damage_handled) {
1197 actionstream << ", damage " << hitparams.hp << " HP";
1199 actionstream << ", damage handled by lua";
1201 actionstream << std::endl;
1203 return hitparams.wear;
1206 s16 PlayerSAO::readDamage()
1208 s16 damage = m_damage;
1213 void PlayerSAO::setHP(s16 hp)
1217 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1220 hp = oldhp + hp_change;
1224 else if (hp > PLAYER_MAX_HP)
1227 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1234 m_damage += (oldhp - hp);
1236 // Update properties on death
1237 if ((hp == 0) != (oldhp == 0))
1238 m_properties_sent = false;
1241 void PlayerSAO::setBreath(const u16 breath, bool send)
1243 if (m_player && breath != m_breath)
1244 m_player->setDirty(true);
1246 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1249 m_env->getGameDef()->SendPlayerBreath(this);
1252 Inventory* PlayerSAO::getInventory()
1256 const Inventory* PlayerSAO::getInventory() const
1261 InventoryLocation PlayerSAO::getInventoryLocation() const
1263 InventoryLocation loc;
1264 loc.setPlayer(m_player->getName());
1268 std::string PlayerSAO::getWieldList() const
1273 ItemStack PlayerSAO::getWieldedItem() const
1275 const Inventory *inv = getInventory();
1277 const InventoryList *mlist = inv->getList(getWieldList());
1278 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1279 ret = mlist->getItem(getWieldIndex());
1280 if (ret.name.empty()) {
1281 const InventoryList *hlist = inv->getList("hand");
1283 ret = hlist->getItem(0);
1288 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1290 Inventory *inv = getInventory();
1292 InventoryList *mlist = inv->getList(getWieldList());
1294 ItemStack olditem = mlist->getItem(getWieldIndex());
1295 if (olditem.name.empty()) {
1296 InventoryList *hlist = inv->getList("hand");
1298 hlist->changeItem(0, item);
1302 mlist->changeItem(getWieldIndex(), item);
1309 int PlayerSAO::getWieldIndex() const
1311 return m_wield_index;
1314 void PlayerSAO::setWieldIndex(int i)
1316 if(i != m_wield_index) {
1321 // Erase the peer id and make the object for removal
1322 void PlayerSAO::disconnected()
1328 void PlayerSAO::unlinkPlayerSessionAndSave()
1330 assert(m_player->getPlayerSAO() == this);
1331 m_player->peer_id = 0;
1332 m_env->savePlayer(m_player);
1333 m_player->setPlayerSAO(NULL);
1334 m_env->removePlayer(m_player);
1337 std::string PlayerSAO::getPropertyPacket()
1339 m_prop.is_visible = (true);
1340 return gob_cmd_set_properties(m_prop);
1343 bool PlayerSAO::checkMovementCheat()
1345 if (isAttached() || m_is_singleplayer ||
1346 g_settings->getBool("disable_anticheat")) {
1347 m_last_good_position = m_base_position;
1351 bool cheated = false;
1353 Check player movements
1355 NOTE: Actually the server should handle player physics like the
1356 client does and compare player's position to what is calculated
1357 on our side. This is required when eg. players fly due to an
1358 explosion. Altough a node-based alternative might be possible
1359 too, and much more lightweight.
1362 float player_max_speed = 0;
1364 if (m_privs.count("fast") != 0) {
1366 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1369 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1371 // Tolerance. The lag pool does this a bit.
1372 //player_max_speed *= 2.5;
1374 v3f diff = (m_base_position - m_last_good_position);
1375 float d_vert = diff.Y;
1377 float d_horiz = diff.getLength();
1378 float required_time = d_horiz / player_max_speed;
1380 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1381 required_time = d_vert / player_max_speed; // Moving upwards
1383 if (m_move_pool.grab(required_time)) {
1384 m_last_good_position = m_base_position;
1386 const float LAG_POOL_MIN = 5.0;
1387 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1388 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1389 if (m_time_from_last_teleport > lag_pool_max) {
1390 actionstream << "Player " << m_player->getName()
1391 << " moved too fast; resetting position"
1395 setBasePosition(m_last_good_position);
1400 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1402 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1403 toset->MinEdge += m_base_position;
1404 toset->MaxEdge += m_base_position;