3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "collision.h"
23 #include "environment.h"
24 #include "tool.h" // For ToolCapabilities
27 #include "remoteplayer.h"
29 #include "scripting_server.h"
30 #include "genericobject.h"
32 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
38 class TestSAO : public ServerActiveObject
41 TestSAO(ServerEnvironment *env, v3f pos):
42 ServerActiveObject(env, pos),
46 ServerActiveObject::registerType(getType(), create);
48 ActiveObjectType getType() const
49 { return ACTIVEOBJECT_TYPE_TEST; }
51 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
52 const std::string &data)
54 return new TestSAO(env, pos);
57 void step(float dtime, bool send_recommended)
66 m_base_position.Y += dtime * BS * 2;
67 if(m_base_position.Y > 8*BS)
68 m_base_position.Y = 2*BS;
70 if(send_recommended == false)
80 data += itos(0); // 0 = position
82 data += itos(m_base_position.X);
84 data += itos(m_base_position.Y);
86 data += itos(m_base_position.Z);
88 ActiveObjectMessage aom(getId(), false, data);
89 m_messages_out.push(aom);
93 bool getCollisionBox(aabb3f *toset) const { return false; }
94 bool collideWithObjects() const { return false; }
101 // Prototype (registers item for deserialization)
102 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
108 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
109 ServerActiveObject(env, pos),
112 m_properties_sent(true),
113 m_armor_groups_sent(false),
114 m_animation_range(0,0),
115 m_animation_speed(0),
116 m_animation_blend(0),
117 m_animation_loop(true),
118 m_animation_sent(false),
119 m_bone_position_sent(false),
120 m_attachment_parent_id(0),
121 m_attachment_sent(false)
123 // Initialize something to armor groups
124 m_armor_groups["fleshy"] = 100;
127 bool UnitSAO::isAttached() const
129 if (!m_attachment_parent_id)
131 // Check if the parent still exists
132 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
138 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
140 m_armor_groups = armor_groups;
141 m_armor_groups_sent = false;
144 const ItemGroupList &UnitSAO::getArmorGroups()
146 return m_armor_groups;
149 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
151 // store these so they can be updated to clients
152 m_animation_range = frame_range;
153 m_animation_speed = frame_speed;
154 m_animation_blend = frame_blend;
155 m_animation_loop = frame_loop;
156 m_animation_sent = false;
159 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
161 *frame_range = m_animation_range;
162 *frame_speed = m_animation_speed;
163 *frame_blend = m_animation_blend;
164 *frame_loop = m_animation_loop;
167 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
169 // store these so they can be updated to clients
170 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
171 m_bone_position_sent = false;
174 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
176 *position = m_bone_position[bone].X;
177 *rotation = m_bone_position[bone].Y;
180 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
182 // Attachments need to be handled on both the server and client.
183 // If we just attach on the server, we can only copy the position of the parent. Attachments
184 // are still sent to clients at an interval so players might see them lagging, plus we can't
185 // read and attach to skeletal bones.
186 // If we just attach on the client, the server still sees the child at its original location.
187 // This breaks some things so we also give the server the most accurate representation
188 // even if players only see the client changes.
190 m_attachment_parent_id = parent_id;
191 m_attachment_bone = bone;
192 m_attachment_position = position;
193 m_attachment_rotation = rotation;
194 m_attachment_sent = false;
197 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
200 *parent_id = m_attachment_parent_id;
201 *bone = m_attachment_bone;
202 *position = m_attachment_position;
203 *rotation = m_attachment_rotation;
206 void UnitSAO::addAttachmentChild(int child_id)
208 m_attachment_child_ids.insert(child_id);
211 void UnitSAO::removeAttachmentChild(int child_id)
213 m_attachment_child_ids.erase(child_id);
216 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
218 return m_attachment_child_ids;
221 ObjectProperties* UnitSAO::accessObjectProperties()
226 void UnitSAO::notifyObjectPropertiesModified()
228 m_properties_sent = false;
235 // Prototype (registers item for deserialization)
236 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
238 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
239 const std::string &name, const std::string &state):
245 m_acceleration(0,0,0),
247 m_last_sent_position(0,0,0),
248 m_last_sent_velocity(0,0,0),
249 m_last_sent_position_timer(0),
250 m_last_sent_move_precision(0),
251 m_current_texture_modifier("")
253 // Only register type if no environment supplied
255 ServerActiveObject::registerType(getType(), create);
260 LuaEntitySAO::~LuaEntitySAO()
263 m_env->getScriptIface()->luaentity_Remove(m_id);
266 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
267 it != m_attached_particle_spawners.end(); ++it) {
268 m_env->deleteParticleSpawner(*it, false);
272 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
274 ServerActiveObject::addedToEnvironment(dtime_s);
276 // Create entity from name
277 m_registered = m_env->getScriptIface()->
278 luaentity_Add(m_id, m_init_name.c_str());
282 m_env->getScriptIface()->
283 luaentity_GetProperties(m_id, &m_prop);
284 // Initialize HP from properties
285 m_hp = m_prop.hp_max;
286 // Activate entity, supplying serialized state
287 m_env->getScriptIface()->
288 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
290 m_prop.infotext = m_init_name;
294 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
295 const std::string &data)
303 std::istringstream is(data, std::ios::binary);
305 u8 version = readU8(is);
306 // check if version is supported
308 name = deSerializeString(is);
309 state = deSerializeLongString(is);
311 else if(version == 1){
312 name = deSerializeString(is);
313 state = deSerializeLongString(is);
315 velocity = readV3F1000(is);
320 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
321 <<state<<"\")"<<std::endl;
322 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
324 sao->m_velocity = velocity;
329 void LuaEntitySAO::step(float dtime, bool send_recommended)
331 if(!m_properties_sent)
333 m_properties_sent = true;
334 std::string str = getPropertyPacket();
335 // create message and add to list
336 ActiveObjectMessage aom(getId(), true, str);
337 m_messages_out.push(aom);
340 // If attached, check that our parent is still there. If it isn't, detach.
341 if(m_attachment_parent_id && !isAttached())
343 m_attachment_parent_id = 0;
344 m_attachment_bone = "";
345 m_attachment_position = v3f(0,0,0);
346 m_attachment_rotation = v3f(0,0,0);
347 sendPosition(false, true);
350 m_last_sent_position_timer += dtime;
352 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
353 // If the object gets detached this comes into effect automatically from the last known origin
356 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
357 m_base_position = pos;
358 m_velocity = v3f(0,0,0);
359 m_acceleration = v3f(0,0,0);
364 aabb3f box = m_prop.collisionbox;
367 collisionMoveResult moveresult;
368 f32 pos_max_d = BS*0.25; // Distance per iteration
369 v3f p_pos = m_base_position;
370 v3f p_velocity = m_velocity;
371 v3f p_acceleration = m_acceleration;
372 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
373 pos_max_d, box, m_prop.stepheight, dtime,
374 &p_pos, &p_velocity, p_acceleration,
375 this, m_prop.collideWithObjects);
378 m_base_position = p_pos;
379 m_velocity = p_velocity;
380 m_acceleration = p_acceleration;
382 m_base_position += dtime * m_velocity + 0.5 * dtime
383 * dtime * m_acceleration;
384 m_velocity += dtime * m_acceleration;
387 if((m_prop.automatic_face_movement_dir) &&
388 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
390 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
391 + m_prop.automatic_face_movement_dir_offset;
392 float max_rotation_delta =
393 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
395 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
396 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
398 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
406 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
409 if(send_recommended == false)
414 // TODO: force send when acceleration changes enough?
415 float minchange = 0.2*BS;
416 if(m_last_sent_position_timer > 1.0){
418 } else if(m_last_sent_position_timer > 0.2){
421 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
422 move_d += m_last_sent_move_precision;
423 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
424 if(move_d > minchange || vel_d > minchange ||
425 fabs(m_yaw - m_last_sent_yaw) > 1.0){
426 sendPosition(true, false);
430 if(m_armor_groups_sent == false){
431 m_armor_groups_sent = true;
432 std::string str = gob_cmd_update_armor_groups(
434 // create message and add to list
435 ActiveObjectMessage aom(getId(), true, str);
436 m_messages_out.push(aom);
439 if(m_animation_sent == false){
440 m_animation_sent = true;
441 std::string str = gob_cmd_update_animation(
442 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
443 // create message and add to list
444 ActiveObjectMessage aom(getId(), true, str);
445 m_messages_out.push(aom);
448 if(m_bone_position_sent == false){
449 m_bone_position_sent = true;
450 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
451 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
452 std::string str = gob_cmd_update_bone_position((*ii).first,
453 (*ii).second.X, (*ii).second.Y);
454 // create message and add to list
455 ActiveObjectMessage aom(getId(), true, str);
456 m_messages_out.push(aom);
460 if(m_attachment_sent == false){
461 m_attachment_sent = true;
462 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
463 // create message and add to list
464 ActiveObjectMessage aom(getId(), true, str);
465 m_messages_out.push(aom);
469 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
471 std::ostringstream os(std::ios::binary);
474 writeU8(os, 1); // version
475 os << serializeString(""); // name
476 writeU8(os, 0); // is_player
477 writeS16(os, getId()); //id
478 writeV3F1000(os, m_base_position);
479 writeF1000(os, m_yaw);
482 std::ostringstream msg_os(std::ios::binary);
483 msg_os << serializeLongString(getPropertyPacket()); // message 1
484 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
485 msg_os << serializeLongString(gob_cmd_update_animation(
486 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
487 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
488 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
489 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
490 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
492 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
493 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
494 int message_count = 4 + m_bone_position.size();
495 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
496 (ii != m_attachment_child_ids.end()); ++ii) {
497 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
499 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
500 obj->getClientInitializationData(protocol_version)));
504 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
507 writeU8(os, message_count);
508 os.write(msg_os.str().c_str(), msg_os.str().size());
514 void LuaEntitySAO::getStaticData(std::string *result) const
516 verbosestream<<FUNCTION_NAME<<std::endl;
517 std::ostringstream os(std::ios::binary);
521 os<<serializeString(m_init_name);
524 std::string state = m_env->getScriptIface()->
525 luaentity_GetStaticdata(m_id);
526 os<<serializeLongString(state);
528 os<<serializeLongString(m_init_state);
533 writeV3F1000(os, m_velocity);
535 writeF1000(os, m_yaw);
539 int LuaEntitySAO::punch(v3f dir,
540 const ToolCapabilities *toolcap,
541 ServerActiveObject *puncher,
542 float time_from_last_punch)
545 // Delete unknown LuaEntities when punched
550 // It's best that attachments cannot be punched
554 ItemStack *punchitem = NULL;
555 ItemStack punchitem_static;
557 punchitem_static = puncher->getWieldedItem();
558 punchitem = &punchitem_static;
561 PunchDamageResult result = getPunchDamage(
565 time_from_last_punch);
567 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
568 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
570 if (!damage_handled) {
571 if (result.did_punch) {
572 setHP(getHP() - result.damage);
574 if (result.damage > 0) {
575 std::string punchername = puncher ? puncher->getDescription() : "nil";
577 actionstream << getDescription() << " punched by "
578 << punchername << ", damage " << result.damage
579 << " hp, health now " << getHP() << " hp" << std::endl;
582 std::string str = gob_cmd_punched(result.damage, getHP());
583 // create message and add to list
584 ActiveObjectMessage aom(getId(), true, str);
585 m_messages_out.push(aom);
597 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
601 // It's best that attachments cannot be clicked
604 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
607 void LuaEntitySAO::setPos(const v3f &pos)
611 m_base_position = pos;
612 sendPosition(false, true);
615 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
619 m_base_position = pos;
621 sendPosition(true, true);
624 float LuaEntitySAO::getMinimumSavedMovement()
629 std::string LuaEntitySAO::getDescription()
631 std::ostringstream os(std::ios::binary);
632 os<<"LuaEntitySAO at (";
633 os<<(m_base_position.X/BS)<<",";
634 os<<(m_base_position.Y/BS)<<",";
635 os<<(m_base_position.Z/BS);
640 void LuaEntitySAO::setHP(s16 hp)
646 s16 LuaEntitySAO::getHP() const
651 void LuaEntitySAO::setVelocity(v3f velocity)
653 m_velocity = velocity;
656 v3f LuaEntitySAO::getVelocity()
661 void LuaEntitySAO::setAcceleration(v3f acceleration)
663 m_acceleration = acceleration;
666 v3f LuaEntitySAO::getAcceleration()
668 return m_acceleration;
671 void LuaEntitySAO::setTextureMod(const std::string &mod)
673 std::string str = gob_cmd_set_texture_mod(mod);
674 m_current_texture_modifier = mod;
675 // create message and add to list
676 ActiveObjectMessage aom(getId(), true, str);
677 m_messages_out.push(aom);
680 std::string LuaEntitySAO::getTextureMod() const
682 return m_current_texture_modifier;
685 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
686 bool select_horiz_by_yawpitch)
688 std::string str = gob_cmd_set_sprite(
692 select_horiz_by_yawpitch
694 // create message and add to list
695 ActiveObjectMessage aom(getId(), true, str);
696 m_messages_out.push(aom);
699 std::string LuaEntitySAO::getName()
704 std::string LuaEntitySAO::getPropertyPacket()
706 return gob_cmd_set_properties(m_prop);
709 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
711 // If the object is attached client-side, don't waste bandwidth sending its position to clients
715 m_last_sent_move_precision = m_base_position.getDistanceFrom(
716 m_last_sent_position);
717 m_last_sent_position_timer = 0;
718 m_last_sent_yaw = m_yaw;
719 m_last_sent_position = m_base_position;
720 m_last_sent_velocity = m_velocity;
721 //m_last_sent_acceleration = m_acceleration;
723 float update_interval = m_env->getSendRecommendedInterval();
725 std::string str = gob_cmd_update_position(
734 // create message and add to list
735 ActiveObjectMessage aom(getId(), false, str);
736 m_messages_out.push(aom);
739 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
743 //update collision box
744 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
745 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
747 toset->MinEdge += m_base_position;
748 toset->MaxEdge += m_base_position;
756 bool LuaEntitySAO::collideWithObjects() const
758 return m_prop.collideWithObjects;
765 // No prototype, PlayerSAO does not need to be deserialized
767 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
768 bool is_singleplayer):
769 UnitSAO(env_, v3f(0,0,0)),
774 m_last_good_position(0,0,0),
775 m_time_from_last_teleport(0),
776 m_time_from_last_punch(0),
777 m_nocheat_dig_pos(32767, 32767, 32767),
778 m_nocheat_dig_time(0),
780 m_position_not_sent(false),
781 m_is_singleplayer(is_singleplayer),
782 m_breath(PLAYER_MAX_BREATH),
786 m_extended_attributes_modified(false),
788 m_physics_override_speed(1),
789 m_physics_override_jump(1),
790 m_physics_override_gravity(1),
791 m_physics_override_sneak(true),
792 m_physics_override_sneak_glitch(false),
793 m_physics_override_new_move(true),
794 m_physics_override_sent(false)
796 assert(m_peer_id != 0); // pre-condition
798 m_prop.hp_max = PLAYER_MAX_HP;
799 m_prop.physical = false;
800 m_prop.weight = PLAYER_DEFAULT_WEIGHT;
801 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
802 // start of default appearance, this should be overwritten by LUA
803 m_prop.visual = "upright_sprite";
804 m_prop.visual_size = v2f(1, 2);
805 m_prop.textures.clear();
806 m_prop.textures.push_back("player.png");
807 m_prop.textures.push_back("player_back.png");
808 m_prop.colors.clear();
809 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
810 m_prop.spritediv = v2s16(1,1);
811 // end of default appearance
812 m_prop.is_visible = true;
813 m_prop.makes_footstep_sound = true;
814 m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT;
815 m_hp = PLAYER_MAX_HP;
818 PlayerSAO::~PlayerSAO()
820 if(m_inventory != &m_player->inventory)
824 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
829 m_inventory = &m_player->inventory;
832 v3f PlayerSAO::getEyeOffset() const
834 return v3f(0, BS * 1.625f, 0);
837 std::string PlayerSAO::getDescription()
839 return std::string("player ") + m_player->getName();
842 // Called after id has been set and has been inserted in environment
843 void PlayerSAO::addedToEnvironment(u32 dtime_s)
845 ServerActiveObject::addedToEnvironment(dtime_s);
846 ServerActiveObject::setBasePosition(m_base_position);
847 m_player->setPlayerSAO(this);
848 m_player->peer_id = m_peer_id;
849 m_last_good_position = m_base_position;
852 // Called before removing from environment
853 void PlayerSAO::removingFromEnvironment()
855 ServerActiveObject::removingFromEnvironment();
856 if (m_player->getPlayerSAO() == this) {
857 unlinkPlayerSessionAndSave();
858 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
859 it != m_attached_particle_spawners.end(); ++it) {
860 m_env->deleteParticleSpawner(*it, false);
865 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
867 std::ostringstream os(std::ios::binary);
870 writeU8(os, 1); // version
871 os << serializeString(m_player->getName()); // name
872 writeU8(os, 1); // is_player
873 writeS16(os, getId()); //id
874 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
875 writeF1000(os, m_yaw);
876 writeS16(os, getHP());
878 std::ostringstream msg_os(std::ios::binary);
879 msg_os << serializeLongString(getPropertyPacket()); // message 1
880 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
881 msg_os << serializeLongString(gob_cmd_update_animation(
882 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
883 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
884 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
885 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
886 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
888 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
889 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
890 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
891 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
892 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
893 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
894 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
895 int message_count = 6 + m_bone_position.size();
896 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
897 ii != m_attachment_child_ids.end(); ++ii) {
898 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
900 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
901 obj->getClientInitializationData(protocol_version)));
905 writeU8(os, message_count);
906 os.write(msg_os.str().c_str(), msg_os.str().size());
912 void PlayerSAO::getStaticData(std::string *result) const
914 FATAL_ERROR("Deprecated function");
917 void PlayerSAO::step(float dtime, bool send_recommended)
919 if (m_drowning_interval.step(dtime, 2.0)) {
921 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
922 MapNode n = m_env->getMap().getNodeNoEx(p);
923 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
924 // If node generates drown
925 if (c.drowning > 0 && m_hp > 0) {
927 setBreath(m_breath - 1);
929 // No more breath, damage player
931 setHP(m_hp - c.drowning);
932 m_env->getGameDef()->SendPlayerHPOrDie(this);
937 if (m_breathing_interval.step(dtime, 0.5)) {
939 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
940 MapNode n = m_env->getMap().getNodeNoEx(p);
941 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
942 // If player is alive & no drowning, breath
943 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
944 setBreath(m_breath + 1);
947 if (m_node_hurt_interval.step(dtime, 1.0)) {
948 // Feet, middle and head
949 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
950 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
951 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
952 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
953 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
954 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
956 u32 damage_per_second = 0;
957 damage_per_second = MYMAX(damage_per_second,
958 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
959 damage_per_second = MYMAX(damage_per_second,
960 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
961 damage_per_second = MYMAX(damage_per_second,
962 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
964 if (damage_per_second != 0 && m_hp > 0) {
965 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
967 m_env->getGameDef()->SendPlayerHPOrDie(this);
971 if (!m_properties_sent) {
972 m_properties_sent = true;
973 std::string str = getPropertyPacket();
974 // create message and add to list
975 ActiveObjectMessage aom(getId(), true, str);
976 m_messages_out.push(aom);
979 // If attached, check that our parent is still there. If it isn't, detach.
980 if(m_attachment_parent_id && !isAttached())
982 m_attachment_parent_id = 0;
983 m_attachment_bone = "";
984 m_attachment_position = v3f(0,0,0);
985 m_attachment_rotation = v3f(0,0,0);
986 setBasePosition(m_last_good_position);
987 m_env->getGameDef()->SendMovePlayer(m_peer_id);
990 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
992 // Set lag pool maximums based on estimated lag
993 const float LAG_POOL_MIN = 5.0;
994 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
995 if(lag_pool_max < LAG_POOL_MIN)
996 lag_pool_max = LAG_POOL_MIN;
997 m_dig_pool.setMax(lag_pool_max);
998 m_move_pool.setMax(lag_pool_max);
1000 // Increment cheat prevention timers
1001 m_dig_pool.add(dtime);
1002 m_move_pool.add(dtime);
1003 m_time_from_last_teleport += dtime;
1004 m_time_from_last_punch += dtime;
1005 m_nocheat_dig_time += dtime;
1007 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1008 // If the object gets detached this comes into effect automatically from the last known origin
1010 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1011 m_last_good_position = pos;
1012 setBasePosition(pos);
1015 if (!send_recommended)
1018 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1019 if(m_position_not_sent && !isAttached())
1021 m_position_not_sent = false;
1022 float update_interval = m_env->getSendRecommendedInterval();
1024 if(isAttached()) // Just in case we ever do send attachment position too
1025 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1027 pos = m_base_position + v3f(0,BS*1,0);
1028 std::string str = gob_cmd_update_position(
1037 // create message and add to list
1038 ActiveObjectMessage aom(getId(), false, str);
1039 m_messages_out.push(aom);
1042 if (!m_armor_groups_sent) {
1043 m_armor_groups_sent = true;
1044 std::string str = gob_cmd_update_armor_groups(
1046 // create message and add to list
1047 ActiveObjectMessage aom(getId(), true, str);
1048 m_messages_out.push(aom);
1051 if (!m_physics_override_sent) {
1052 m_physics_override_sent = true;
1053 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1054 m_physics_override_jump, m_physics_override_gravity,
1055 m_physics_override_sneak, m_physics_override_sneak_glitch,
1056 m_physics_override_new_move);
1057 // create message and add to list
1058 ActiveObjectMessage aom(getId(), true, str);
1059 m_messages_out.push(aom);
1062 if (!m_animation_sent) {
1063 m_animation_sent = true;
1064 std::string str = gob_cmd_update_animation(
1065 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1066 // create message and add to list
1067 ActiveObjectMessage aom(getId(), true, str);
1068 m_messages_out.push(aom);
1071 if (!m_bone_position_sent) {
1072 m_bone_position_sent = true;
1073 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1074 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1075 std::string str = gob_cmd_update_bone_position((*ii).first,
1076 (*ii).second.X, (*ii).second.Y);
1077 // create message and add to list
1078 ActiveObjectMessage aom(getId(), true, str);
1079 m_messages_out.push(aom);
1083 if (!m_attachment_sent){
1084 m_attachment_sent = true;
1085 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1086 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1087 // create message and add to list
1088 ActiveObjectMessage aom(getId(), true, str);
1089 m_messages_out.push(aom);
1093 void PlayerSAO::setBasePosition(const v3f &position)
1095 if (m_player && position != m_base_position)
1096 m_player->setDirty(true);
1098 // This needs to be ran for attachments too
1099 ServerActiveObject::setBasePosition(position);
1100 m_position_not_sent = true;
1103 void PlayerSAO::setPos(const v3f &pos)
1108 setBasePosition(pos);
1109 // Movement caused by this command is always valid
1110 m_last_good_position = pos;
1111 m_move_pool.empty();
1112 m_time_from_last_teleport = 0.0;
1113 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1116 void PlayerSAO::moveTo(v3f pos, bool continuous)
1121 setBasePosition(pos);
1122 // Movement caused by this command is always valid
1123 m_last_good_position = pos;
1124 m_move_pool.empty();
1125 m_time_from_last_teleport = 0.0;
1126 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1129 void PlayerSAO::setYaw(const float yaw)
1131 if (m_player && yaw != m_yaw)
1132 m_player->setDirty(true);
1134 UnitSAO::setYaw(yaw);
1137 void PlayerSAO::setFov(const float fov)
1139 if (m_player && fov != m_fov)
1140 m_player->setDirty(true);
1145 void PlayerSAO::setWantedRange(const s16 range)
1147 if (m_player && range != m_wanted_range)
1148 m_player->setDirty(true);
1150 m_wanted_range = range;
1153 void PlayerSAO::setYawAndSend(const float yaw)
1156 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1159 void PlayerSAO::setPitch(const float pitch)
1161 if (m_player && pitch != m_pitch)
1162 m_player->setDirty(true);
1167 void PlayerSAO::setPitchAndSend(const float pitch)
1170 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1173 int PlayerSAO::punch(v3f dir,
1174 const ToolCapabilities *toolcap,
1175 ServerActiveObject *puncher,
1176 float time_from_last_punch)
1178 // It's best that attachments cannot be punched
1185 // No effect if PvP disabled
1186 if (g_settings->getBool("enable_pvp") == false) {
1187 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1188 std::string str = gob_cmd_punched(0, getHP());
1189 // create message and add to list
1190 ActiveObjectMessage aom(getId(), true, str);
1191 m_messages_out.push(aom);
1196 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1197 time_from_last_punch);
1199 std::string punchername = "nil";
1202 punchername = puncher->getDescription();
1204 PlayerSAO *playersao = m_player->getPlayerSAO();
1206 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1207 puncher, time_from_last_punch, toolcap, dir,
1210 if (!damage_handled) {
1211 setHP(getHP() - hitparams.hp);
1212 } else { // override client prediction
1213 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1214 std::string str = gob_cmd_punched(0, getHP());
1215 // create message and add to list
1216 ActiveObjectMessage aom(getId(), true, str);
1217 m_messages_out.push(aom);
1222 actionstream << "Player " << m_player->getName() << " punched by "
1224 if (!damage_handled) {
1225 actionstream << ", damage " << hitparams.hp << " HP";
1227 actionstream << ", damage handled by lua";
1229 actionstream << std::endl;
1231 return hitparams.wear;
1234 s16 PlayerSAO::readDamage()
1236 s16 damage = m_damage;
1241 void PlayerSAO::setHP(s16 hp)
1245 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1248 hp = oldhp + hp_change;
1252 else if (hp > PLAYER_MAX_HP)
1255 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1262 m_damage += (oldhp - hp);
1264 // Update properties on death
1265 if ((hp == 0) != (oldhp == 0))
1266 m_properties_sent = false;
1269 void PlayerSAO::setBreath(const u16 breath, bool send)
1271 if (m_player && breath != m_breath)
1272 m_player->setDirty(true);
1274 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1277 m_env->getGameDef()->SendPlayerBreath(this);
1280 Inventory* PlayerSAO::getInventory()
1284 const Inventory* PlayerSAO::getInventory() const
1289 InventoryLocation PlayerSAO::getInventoryLocation() const
1291 InventoryLocation loc;
1292 loc.setPlayer(m_player->getName());
1296 std::string PlayerSAO::getWieldList() const
1301 ItemStack PlayerSAO::getWieldedItem() const
1303 const Inventory *inv = getInventory();
1305 const InventoryList *mlist = inv->getList(getWieldList());
1306 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1307 ret = mlist->getItem(getWieldIndex());
1311 ItemStack PlayerSAO::getWieldedItemOrHand() const
1313 const Inventory *inv = getInventory();
1315 const InventoryList *mlist = inv->getList(getWieldList());
1316 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1317 ret = mlist->getItem(getWieldIndex());
1318 if (ret.name.empty()) {
1319 const InventoryList *hlist = inv->getList("hand");
1321 ret = hlist->getItem(0);
1326 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1328 Inventory *inv = getInventory();
1330 InventoryList *mlist = inv->getList(getWieldList());
1332 mlist->changeItem(getWieldIndex(), item);
1339 int PlayerSAO::getWieldIndex() const
1341 return m_wield_index;
1344 void PlayerSAO::setWieldIndex(int i)
1346 if(i != m_wield_index) {
1351 // Erase the peer id and make the object for removal
1352 void PlayerSAO::disconnected()
1358 void PlayerSAO::unlinkPlayerSessionAndSave()
1360 assert(m_player->getPlayerSAO() == this);
1361 m_player->peer_id = 0;
1362 m_env->savePlayer(m_player);
1363 m_player->setPlayerSAO(NULL);
1364 m_env->removePlayer(m_player);
1367 std::string PlayerSAO::getPropertyPacket()
1369 m_prop.is_visible = (true);
1370 return gob_cmd_set_properties(m_prop);
1373 bool PlayerSAO::checkMovementCheat()
1375 if (isAttached() || m_is_singleplayer ||
1376 g_settings->getBool("disable_anticheat")) {
1377 m_last_good_position = m_base_position;
1381 bool cheated = false;
1383 Check player movements
1385 NOTE: Actually the server should handle player physics like the
1386 client does and compare player's position to what is calculated
1387 on our side. This is required when eg. players fly due to an
1388 explosion. Altough a node-based alternative might be possible
1389 too, and much more lightweight.
1392 float player_max_speed = 0;
1394 if (m_privs.count("fast") != 0) {
1396 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1399 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1401 // Tolerance. The lag pool does this a bit.
1402 //player_max_speed *= 2.5;
1404 v3f diff = (m_base_position - m_last_good_position);
1405 float d_vert = diff.Y;
1407 float d_horiz = diff.getLength();
1408 float required_time = d_horiz / player_max_speed;
1410 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1411 required_time = d_vert / player_max_speed; // Moving upwards
1413 if (m_move_pool.grab(required_time)) {
1414 m_last_good_position = m_base_position;
1416 const float LAG_POOL_MIN = 5.0;
1417 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1418 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1419 if (m_time_from_last_teleport > lag_pool_max) {
1420 actionstream << "Player " << m_player->getName()
1421 << " moved too fast; resetting position"
1425 setBasePosition(m_last_good_position);
1430 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1432 //update collision box
1433 toset->MinEdge = m_prop.collisionbox.MinEdge * BS + v3f(0, BS, 0);
1434 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS + v3f(0, BS, 0);
1436 toset->MinEdge += m_base_position;
1437 toset->MaxEdge += m_base_position;