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_game.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;
130 // Prototype (registers item for deserialization)
131 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
133 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
134 const std::string &name, const std::string &state):
140 m_acceleration(0,0,0),
142 m_last_sent_position(0,0,0),
143 m_last_sent_velocity(0,0,0),
144 m_last_sent_position_timer(0),
145 m_last_sent_move_precision(0)
147 // Only register type if no environment supplied
149 ServerActiveObject::registerType(getType(), create);
154 LuaEntitySAO::~LuaEntitySAO()
157 m_env->getScriptIface()->luaentity_Remove(m_id);
160 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
161 it != m_attached_particle_spawners.end(); ++it) {
162 m_env->deleteParticleSpawner(*it, false);
166 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
168 ServerActiveObject::addedToEnvironment(dtime_s);
170 // Create entity from name
171 m_registered = m_env->getScriptIface()->
172 luaentity_Add(m_id, m_init_name.c_str());
176 m_env->getScriptIface()->
177 luaentity_GetProperties(m_id, &m_prop);
178 // Initialize HP from properties
179 m_hp = m_prop.hp_max;
180 // Activate entity, supplying serialized state
181 m_env->getScriptIface()->
182 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
184 m_prop.infotext = m_init_name;
188 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
189 const std::string &data)
197 std::istringstream is(data, std::ios::binary);
199 u8 version = readU8(is);
200 // check if version is supported
202 name = deSerializeString(is);
203 state = deSerializeLongString(is);
205 else if(version == 1){
206 name = deSerializeString(is);
207 state = deSerializeLongString(is);
209 velocity = readV3F1000(is);
214 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
215 <<state<<"\")"<<std::endl;
216 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
218 sao->m_velocity = velocity;
223 bool LuaEntitySAO::isAttached()
225 if(!m_attachment_parent_id)
227 // Check if the parent still exists
228 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
234 void LuaEntitySAO::step(float dtime, bool send_recommended)
236 if(!m_properties_sent)
238 m_properties_sent = true;
239 std::string str = getPropertyPacket();
240 // create message and add to list
241 ActiveObjectMessage aom(getId(), true, str);
242 m_messages_out.push(aom);
245 // If attached, check that our parent is still there. If it isn't, detach.
246 if(m_attachment_parent_id && !isAttached())
248 m_attachment_parent_id = 0;
249 m_attachment_bone = "";
250 m_attachment_position = v3f(0,0,0);
251 m_attachment_rotation = v3f(0,0,0);
252 sendPosition(false, true);
255 m_last_sent_position_timer += dtime;
257 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
258 // If the object gets detached this comes into effect automatically from the last known origin
261 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
262 m_base_position = pos;
263 m_velocity = v3f(0,0,0);
264 m_acceleration = v3f(0,0,0);
269 aabb3f box = m_prop.collisionbox;
272 collisionMoveResult moveresult;
273 f32 pos_max_d = BS*0.25; // Distance per iteration
274 v3f p_pos = m_base_position;
275 v3f p_velocity = m_velocity;
276 v3f p_acceleration = m_acceleration;
277 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
278 pos_max_d, box, m_prop.stepheight, dtime,
279 &p_pos, &p_velocity, p_acceleration,
280 this, m_prop.collideWithObjects);
283 m_base_position = p_pos;
284 m_velocity = p_velocity;
285 m_acceleration = p_acceleration;
287 m_base_position += dtime * m_velocity + 0.5 * dtime
288 * dtime * m_acceleration;
289 m_velocity += dtime * m_acceleration;
292 if((m_prop.automatic_face_movement_dir) &&
293 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
295 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
296 + m_prop.automatic_face_movement_dir_offset;
297 float max_rotation_delta =
298 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
300 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
301 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
303 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
311 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
314 if(send_recommended == false)
319 // TODO: force send when acceleration changes enough?
320 float minchange = 0.2*BS;
321 if(m_last_sent_position_timer > 1.0){
323 } else if(m_last_sent_position_timer > 0.2){
326 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
327 move_d += m_last_sent_move_precision;
328 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
329 if(move_d > minchange || vel_d > minchange ||
330 fabs(m_yaw - m_last_sent_yaw) > 1.0){
331 sendPosition(true, false);
335 if(m_armor_groups_sent == false){
336 m_armor_groups_sent = true;
337 std::string str = gob_cmd_update_armor_groups(
339 // create message and add to list
340 ActiveObjectMessage aom(getId(), true, str);
341 m_messages_out.push(aom);
344 if(m_animation_sent == false){
345 m_animation_sent = true;
346 std::string str = gob_cmd_update_animation(
347 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
348 // create message and add to list
349 ActiveObjectMessage aom(getId(), true, str);
350 m_messages_out.push(aom);
353 if(m_bone_position_sent == false){
354 m_bone_position_sent = true;
355 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
356 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
357 std::string str = gob_cmd_update_bone_position((*ii).first,
358 (*ii).second.X, (*ii).second.Y);
359 // create message and add to list
360 ActiveObjectMessage aom(getId(), true, str);
361 m_messages_out.push(aom);
365 if(m_attachment_sent == false){
366 m_attachment_sent = true;
367 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
368 // create message and add to list
369 ActiveObjectMessage aom(getId(), true, str);
370 m_messages_out.push(aom);
374 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
376 std::ostringstream os(std::ios::binary);
378 if(protocol_version >= 14)
380 writeU8(os, 1); // version
381 os<<serializeString(""); // name
382 writeU8(os, 0); // is_player
383 writeS16(os, getId()); //id
384 writeV3F1000(os, m_base_position);
385 writeF1000(os, m_yaw);
388 std::ostringstream msg_os(std::ios::binary);
389 msg_os << serializeLongString(getPropertyPacket()); // message 1
390 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
391 msg_os << serializeLongString(gob_cmd_update_animation(
392 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
393 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
394 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
395 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
396 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
398 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
399 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
400 int message_count = 4 + m_bone_position.size();
401 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
402 (ii != m_attachment_child_ids.end()); ++ii) {
403 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
405 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
406 obj->getClientInitializationData(protocol_version)));
410 writeU8(os, message_count);
411 os.write(msg_os.str().c_str(), msg_os.str().size());
415 writeU8(os, 0); // version
416 os<<serializeString(""); // name
417 writeU8(os, 0); // is_player
418 writeV3F1000(os, m_base_position);
419 writeF1000(os, m_yaw);
421 writeU8(os, 2); // number of messages stuffed in here
422 os<<serializeLongString(getPropertyPacket()); // message 1
423 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
430 std::string LuaEntitySAO::getStaticData()
432 verbosestream<<FUNCTION_NAME<<std::endl;
433 std::ostringstream os(std::ios::binary);
437 os<<serializeString(m_init_name);
440 std::string state = m_env->getScriptIface()->
441 luaentity_GetStaticdata(m_id);
442 os<<serializeLongString(state);
444 os<<serializeLongString(m_init_state);
449 writeV3F1000(os, m_velocity);
451 writeF1000(os, m_yaw);
455 int LuaEntitySAO::punch(v3f dir,
456 const ToolCapabilities *toolcap,
457 ServerActiveObject *puncher,
458 float time_from_last_punch)
461 // Delete unknown LuaEntities when punched
466 // It's best that attachments cannot be punched
470 ItemStack *punchitem = NULL;
471 ItemStack punchitem_static;
473 punchitem_static = puncher->getWieldedItem();
474 punchitem = &punchitem_static;
477 PunchDamageResult result = getPunchDamage(
481 time_from_last_punch);
483 if (result.did_punch) {
484 setHP(getHP() - result.damage);
486 if (result.damage > 0) {
487 std::string punchername = puncher ? puncher->getDescription() : "nil";
489 actionstream << getDescription() << " punched by "
490 << punchername << ", damage " << result.damage
491 << " hp, health now " << getHP() << " hp" << std::endl;
494 std::string str = gob_cmd_punched(result.damage, getHP());
495 // create message and add to list
496 ActiveObjectMessage aom(getId(), true, str);
497 m_messages_out.push(aom);
503 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
504 time_from_last_punch, toolcap, dir);
509 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
513 // It's best that attachments cannot be clicked
516 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
519 void LuaEntitySAO::setPos(const v3f &pos)
523 m_base_position = pos;
524 sendPosition(false, true);
527 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
531 m_base_position = pos;
533 sendPosition(true, true);
536 float LuaEntitySAO::getMinimumSavedMovement()
541 std::string LuaEntitySAO::getDescription()
543 std::ostringstream os(std::ios::binary);
544 os<<"LuaEntitySAO at (";
545 os<<(m_base_position.X/BS)<<",";
546 os<<(m_base_position.Y/BS)<<",";
547 os<<(m_base_position.Z/BS);
552 void LuaEntitySAO::setHP(s16 hp)
558 s16 LuaEntitySAO::getHP() const
563 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
565 m_armor_groups = armor_groups;
566 m_armor_groups_sent = false;
569 const ItemGroupList &LuaEntitySAO::getArmorGroups()
571 return m_armor_groups;
574 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
576 m_animation_range = frame_range;
577 m_animation_speed = frame_speed;
578 m_animation_blend = frame_blend;
579 m_animation_loop = frame_loop;
580 m_animation_sent = false;
583 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
585 *frame_range = m_animation_range;
586 *frame_speed = m_animation_speed;
587 *frame_blend = m_animation_blend;
588 *frame_loop = m_animation_loop;
591 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
593 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
594 m_bone_position_sent = false;
597 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
599 *position = m_bone_position[bone].X;
600 *rotation = m_bone_position[bone].Y;
603 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
605 // Attachments need to be handled on both the server and client.
606 // If we just attach on the server, we can only copy the position of the parent. Attachments
607 // are still sent to clients at an interval so players might see them lagging, plus we can't
608 // read and attach to skeletal bones.
609 // If we just attach on the client, the server still sees the child at its original location.
610 // This breaks some things so we also give the server the most accurate representation
611 // even if players only see the client changes.
613 m_attachment_parent_id = parent_id;
614 m_attachment_bone = bone;
615 m_attachment_position = position;
616 m_attachment_rotation = rotation;
617 m_attachment_sent = false;
620 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
623 *parent_id = m_attachment_parent_id;
624 *bone = m_attachment_bone;
625 *position = m_attachment_position;
626 *rotation = m_attachment_rotation;
629 void LuaEntitySAO::addAttachmentChild(int child_id)
631 m_attachment_child_ids.insert(child_id);
634 void LuaEntitySAO::removeAttachmentChild(int child_id)
636 m_attachment_child_ids.erase(child_id);
639 const UNORDERED_SET<int> &LuaEntitySAO::getAttachmentChildIds()
641 return m_attachment_child_ids;
644 ObjectProperties* LuaEntitySAO::accessObjectProperties()
649 void LuaEntitySAO::notifyObjectPropertiesModified()
651 m_properties_sent = false;
654 void LuaEntitySAO::setVelocity(v3f velocity)
656 m_velocity = velocity;
659 v3f LuaEntitySAO::getVelocity()
664 void LuaEntitySAO::setAcceleration(v3f acceleration)
666 m_acceleration = acceleration;
669 v3f LuaEntitySAO::getAcceleration()
671 return m_acceleration;
674 void LuaEntitySAO::setTextureMod(const std::string &mod)
676 std::string str = gob_cmd_set_texture_mod(mod);
677 // create message and add to list
678 ActiveObjectMessage aom(getId(), true, str);
679 m_messages_out.push(aom);
682 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
683 bool select_horiz_by_yawpitch)
685 std::string str = gob_cmd_set_sprite(
689 select_horiz_by_yawpitch
691 // create message and add to list
692 ActiveObjectMessage aom(getId(), true, str);
693 m_messages_out.push(aom);
696 std::string LuaEntitySAO::getName()
701 std::string LuaEntitySAO::getPropertyPacket()
703 return gob_cmd_set_properties(m_prop);
706 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
708 // If the object is attached client-side, don't waste bandwidth sending its position to clients
712 m_last_sent_move_precision = m_base_position.getDistanceFrom(
713 m_last_sent_position);
714 m_last_sent_position_timer = 0;
715 m_last_sent_yaw = m_yaw;
716 m_last_sent_position = m_base_position;
717 m_last_sent_velocity = m_velocity;
718 //m_last_sent_acceleration = m_acceleration;
720 float update_interval = m_env->getSendRecommendedInterval();
722 std::string str = gob_cmd_update_position(
731 // create message and add to list
732 ActiveObjectMessage aom(getId(), false, str);
733 m_messages_out.push(aom);
736 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
740 //update collision box
741 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
742 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
744 toset->MinEdge += m_base_position;
745 toset->MaxEdge += m_base_position;
753 bool LuaEntitySAO::collideWithObjects() const
755 return m_prop.collideWithObjects;
762 // No prototype, PlayerSAO does not need to be deserialized
764 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
765 UnitSAO(env_, v3f(0,0,0)),
770 m_last_good_position(0,0,0),
771 m_time_from_last_punch(0),
772 m_nocheat_dig_pos(32767, 32767, 32767),
773 m_nocheat_dig_time(0),
775 m_position_not_sent(false),
776 m_is_singleplayer(is_singleplayer),
777 m_breath(PLAYER_MAX_BREATH),
782 m_physics_override_speed(1),
783 m_physics_override_jump(1),
784 m_physics_override_gravity(1),
785 m_physics_override_sneak(true),
786 m_physics_override_sneak_glitch(true),
787 m_physics_override_sent(false)
789 assert(m_peer_id != 0); // pre-condition
791 m_prop.hp_max = PLAYER_MAX_HP;
792 m_prop.physical = false;
794 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
795 // start of default appearance, this should be overwritten by LUA
796 m_prop.visual = "upright_sprite";
797 m_prop.visual_size = v2f(1, 2);
798 m_prop.textures.clear();
799 m_prop.textures.push_back("player.png");
800 m_prop.textures.push_back("player_back.png");
801 m_prop.colors.clear();
802 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
803 m_prop.spritediv = v2s16(1,1);
804 // end of default appearance
805 m_prop.is_visible = true;
806 m_prop.makes_footstep_sound = true;
807 m_hp = PLAYER_MAX_HP;
810 PlayerSAO::~PlayerSAO()
812 if(m_inventory != &m_player->inventory)
816 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
821 m_inventory = &m_player->inventory;
824 v3f PlayerSAO::getEyeOffset() const
826 return v3f(0, BS * 1.625f, 0);
829 std::string PlayerSAO::getDescription()
831 return std::string("player ") + m_player->getName();
834 // Called after id has been set and has been inserted in environment
835 void PlayerSAO::addedToEnvironment(u32 dtime_s)
837 ServerActiveObject::addedToEnvironment(dtime_s);
838 ServerActiveObject::setBasePosition(m_base_position);
839 m_player->setPlayerSAO(this);
840 m_player->peer_id = m_peer_id;
841 m_last_good_position = m_base_position;
844 // Called before removing from environment
845 void PlayerSAO::removingFromEnvironment()
847 ServerActiveObject::removingFromEnvironment();
848 if (m_player->getPlayerSAO() == this) {
849 unlinkPlayerSessionAndSave();
850 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
851 it != m_attached_particle_spawners.end(); ++it) {
852 m_env->deleteParticleSpawner(*it, false);
857 bool PlayerSAO::isStaticAllowed() const
862 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
864 std::ostringstream os(std::ios::binary);
866 if(protocol_version >= 15)
868 writeU8(os, 1); // version
869 os<<serializeString(m_player->getName()); // name
870 writeU8(os, 1); // is_player
871 writeS16(os, getId()); //id
872 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
873 writeF1000(os, m_yaw);
874 writeS16(os, getHP());
876 std::ostringstream msg_os(std::ios::binary);
877 msg_os << serializeLongString(getPropertyPacket()); // message 1
878 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
879 msg_os << serializeLongString(gob_cmd_update_animation(
880 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
881 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
882 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
883 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
884 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
886 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
887 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
888 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
889 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
890 m_physics_override_sneak_glitch)); // 5
891 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
892 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
893 int message_count = 6 + m_bone_position.size();
894 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
895 ii != m_attachment_child_ids.end(); ++ii) {
896 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
898 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
899 obj->getClientInitializationData(protocol_version)));
903 writeU8(os, message_count);
904 os.write(msg_os.str().c_str(), msg_os.str().size());
908 writeU8(os, 0); // version
909 os<<serializeString(m_player->getName()); // name
910 writeU8(os, 1); // is_player
911 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
912 writeF1000(os, m_yaw);
913 writeS16(os, getHP());
914 writeU8(os, 2); // number of messages stuffed in here
915 os<<serializeLongString(getPropertyPacket()); // message 1
916 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
923 std::string PlayerSAO::getStaticData()
925 FATAL_ERROR("Deprecated function (?)");
929 bool PlayerSAO::isAttached()
931 if(!m_attachment_parent_id)
933 // Check if the parent still exists
934 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
940 void PlayerSAO::step(float dtime, bool send_recommended)
942 if (m_drowning_interval.step(dtime, 2.0)) {
944 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
945 MapNode n = m_env->getMap().getNodeNoEx(p);
946 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
947 // If node generates drown
948 if (c.drowning > 0) {
949 if (m_hp > 0 && m_breath > 0)
950 setBreath(m_breath - 1);
952 // No more breath, damage player
954 setHP(m_hp - c.drowning);
955 m_env->getGameDef()->SendPlayerHPOrDie(this);
960 if (m_breathing_interval.step(dtime, 0.5)) {
962 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
963 MapNode n = m_env->getMap().getNodeNoEx(p);
964 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
965 // If player is alive & no drowning, breath
966 if (m_hp > 0 && c.drowning == 0)
967 setBreath(m_breath + 1);
970 if (!m_properties_sent) {
971 m_properties_sent = true;
972 std::string str = getPropertyPacket();
973 // create message and add to list
974 ActiveObjectMessage aom(getId(), true, str);
975 m_messages_out.push(aom);
978 // If attached, check that our parent is still there. If it isn't, detach.
979 if(m_attachment_parent_id && !isAttached())
981 m_attachment_parent_id = 0;
982 m_attachment_bone = "";
983 m_attachment_position = v3f(0,0,0);
984 m_attachment_rotation = v3f(0,0,0);
985 setBasePosition(m_last_good_position);
986 m_env->getGameDef()->SendMovePlayer(m_peer_id);
989 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
991 // Set lag pool maximums based on estimated lag
992 const float LAG_POOL_MIN = 5.0;
993 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
994 if(lag_pool_max < LAG_POOL_MIN)
995 lag_pool_max = LAG_POOL_MIN;
996 m_dig_pool.setMax(lag_pool_max);
997 m_move_pool.setMax(lag_pool_max);
999 // Increment cheat prevention timers
1000 m_dig_pool.add(dtime);
1001 m_move_pool.add(dtime);
1002 m_time_from_last_punch += dtime;
1003 m_nocheat_dig_time += dtime;
1005 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1006 // If the object gets detached this comes into effect automatically from the last known origin
1008 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1009 m_last_good_position = pos;
1010 setBasePosition(pos);
1013 if (!send_recommended)
1016 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1017 if(m_position_not_sent && !isAttached())
1019 m_position_not_sent = false;
1020 float update_interval = m_env->getSendRecommendedInterval();
1022 if(isAttached()) // Just in case we ever do send attachment position too
1023 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1025 pos = m_base_position + v3f(0,BS*1,0);
1026 std::string str = gob_cmd_update_position(
1035 // create message and add to list
1036 ActiveObjectMessage aom(getId(), false, str);
1037 m_messages_out.push(aom);
1040 if (!m_armor_groups_sent) {
1041 m_armor_groups_sent = true;
1042 std::string str = gob_cmd_update_armor_groups(
1044 // create message and add to list
1045 ActiveObjectMessage aom(getId(), true, str);
1046 m_messages_out.push(aom);
1049 if (!m_physics_override_sent) {
1050 m_physics_override_sent = true;
1051 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1052 m_physics_override_jump, m_physics_override_gravity,
1053 m_physics_override_sneak, m_physics_override_sneak_glitch);
1054 // create message and add to list
1055 ActiveObjectMessage aom(getId(), true, str);
1056 m_messages_out.push(aom);
1059 if (!m_animation_sent) {
1060 m_animation_sent = true;
1061 std::string str = gob_cmd_update_animation(
1062 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1063 // create message and add to list
1064 ActiveObjectMessage aom(getId(), true, str);
1065 m_messages_out.push(aom);
1068 if (!m_bone_position_sent) {
1069 m_bone_position_sent = true;
1070 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1071 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1072 std::string str = gob_cmd_update_bone_position((*ii).first,
1073 (*ii).second.X, (*ii).second.Y);
1074 // create message and add to list
1075 ActiveObjectMessage aom(getId(), true, str);
1076 m_messages_out.push(aom);
1080 if (!m_attachment_sent){
1081 m_attachment_sent = true;
1082 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1083 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1084 // create message and add to list
1085 ActiveObjectMessage aom(getId(), true, str);
1086 m_messages_out.push(aom);
1090 void PlayerSAO::setBasePosition(const v3f &position)
1092 if (m_player && position != m_base_position)
1093 m_player->setDirty(true);
1095 // This needs to be ran for attachments too
1096 ServerActiveObject::setBasePosition(position);
1097 m_position_not_sent = true;
1100 void PlayerSAO::setPos(const v3f &pos)
1105 setBasePosition(pos);
1106 // Movement caused by this command is always valid
1107 m_last_good_position = pos;
1108 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1111 void PlayerSAO::moveTo(v3f pos, bool continuous)
1116 setBasePosition(pos);
1117 // Movement caused by this command is always valid
1118 m_last_good_position = pos;
1119 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1122 void PlayerSAO::setYaw(const float yaw)
1124 if (m_player && yaw != m_yaw)
1125 m_player->setDirty(true);
1127 UnitSAO::setYaw(yaw);
1130 void PlayerSAO::setFov(const float fov)
1132 if (m_player && fov != m_fov)
1133 m_player->setDirty(true);
1138 void PlayerSAO::setWantedRange(const s16 range)
1140 if (m_player && range != m_wanted_range)
1141 m_player->setDirty(true);
1143 m_wanted_range = range;
1146 void PlayerSAO::setYawAndSend(const float yaw)
1149 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1152 void PlayerSAO::setPitch(const float pitch)
1154 if (m_player && pitch != m_pitch)
1155 m_player->setDirty(true);
1160 void PlayerSAO::setPitchAndSend(const float pitch)
1163 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1166 int PlayerSAO::punch(v3f dir,
1167 const ToolCapabilities *toolcap,
1168 ServerActiveObject *puncher,
1169 float time_from_last_punch)
1171 // It's best that attachments cannot be punched
1178 // No effect if PvP disabled
1179 if (g_settings->getBool("enable_pvp") == false) {
1180 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1181 std::string str = gob_cmd_punched(0, getHP());
1182 // create message and add to list
1183 ActiveObjectMessage aom(getId(), true, str);
1184 m_messages_out.push(aom);
1189 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1190 time_from_last_punch);
1192 std::string punchername = "nil";
1195 punchername = puncher->getDescription();
1197 PlayerSAO *playersao = m_player->getPlayerSAO();
1199 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1200 puncher, time_from_last_punch, toolcap, dir,
1203 if (!damage_handled) {
1204 setHP(getHP() - hitparams.hp);
1205 } else { // override client prediction
1206 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1207 std::string str = gob_cmd_punched(0, getHP());
1208 // create message and add to list
1209 ActiveObjectMessage aom(getId(), true, str);
1210 m_messages_out.push(aom);
1215 actionstream << "Player " << m_player->getName() << " punched by "
1217 if (!damage_handled) {
1218 actionstream << ", damage " << hitparams.hp << " HP";
1220 actionstream << ", damage handled by lua";
1222 actionstream << std::endl;
1224 return hitparams.wear;
1227 void PlayerSAO::rightClick(ServerActiveObject *)
1231 s16 PlayerSAO::readDamage()
1233 s16 damage = m_damage;
1238 void PlayerSAO::setHP(s16 hp)
1242 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1245 hp = oldhp + hp_change;
1249 else if (hp > PLAYER_MAX_HP)
1252 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1259 m_damage += (oldhp - hp);
1261 // Update properties on death
1262 if ((hp == 0) != (oldhp == 0))
1263 m_properties_sent = false;
1266 void PlayerSAO::setBreath(const u16 breath, bool send)
1268 if (m_player && breath != m_breath)
1269 m_player->setDirty(true);
1271 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1274 m_env->getGameDef()->SendPlayerBreath(this);
1277 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1279 m_armor_groups = armor_groups;
1280 m_armor_groups_sent = false;
1283 const ItemGroupList &PlayerSAO::getArmorGroups()
1285 return m_armor_groups;
1288 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1290 // store these so they can be updated to clients
1291 m_animation_range = frame_range;
1292 m_animation_speed = frame_speed;
1293 m_animation_blend = frame_blend;
1294 m_animation_loop = frame_loop;
1295 m_animation_sent = false;
1298 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1300 *frame_range = m_animation_range;
1301 *frame_speed = m_animation_speed;
1302 *frame_blend = m_animation_blend;
1303 *frame_loop = m_animation_loop;
1306 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1308 // store these so they can be updated to clients
1309 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1310 m_bone_position_sent = false;
1313 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1315 *position = m_bone_position[bone].X;
1316 *rotation = m_bone_position[bone].Y;
1319 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1321 // Attachments need to be handled on both the server and client.
1322 // If we just attach on the server, we can only copy the position of the parent. Attachments
1323 // are still sent to clients at an interval so players might see them lagging, plus we can't
1324 // read and attach to skeletal bones.
1325 // If we just attach on the client, the server still sees the child at its original location.
1326 // This breaks some things so we also give the server the most accurate representation
1327 // even if players only see the client changes.
1329 m_attachment_parent_id = parent_id;
1330 m_attachment_bone = bone;
1331 m_attachment_position = position;
1332 m_attachment_rotation = rotation;
1333 m_attachment_sent = false;
1336 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1339 *parent_id = m_attachment_parent_id;
1340 *bone = m_attachment_bone;
1341 *position = m_attachment_position;
1342 *rotation = m_attachment_rotation;
1345 void PlayerSAO::addAttachmentChild(int child_id)
1347 m_attachment_child_ids.insert(child_id);
1350 void PlayerSAO::removeAttachmentChild(int child_id)
1352 m_attachment_child_ids.erase(child_id);
1355 const UNORDERED_SET<int> &PlayerSAO::getAttachmentChildIds()
1357 return m_attachment_child_ids;
1360 ObjectProperties* PlayerSAO::accessObjectProperties()
1365 void PlayerSAO::notifyObjectPropertiesModified()
1367 m_properties_sent = false;
1370 Inventory* PlayerSAO::getInventory()
1374 const Inventory* PlayerSAO::getInventory() const
1379 InventoryLocation PlayerSAO::getInventoryLocation() const
1381 InventoryLocation loc;
1382 loc.setPlayer(m_player->getName());
1386 std::string PlayerSAO::getWieldList() const
1391 ItemStack PlayerSAO::getWieldedItem() const
1393 const Inventory *inv = getInventory();
1395 const InventoryList *mlist = inv->getList(getWieldList());
1396 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1397 ret = mlist->getItem(getWieldIndex());
1398 if (ret.name.empty()) {
1399 const InventoryList *hlist = inv->getList("hand");
1401 ret = hlist->getItem(0);
1406 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1408 Inventory *inv = getInventory();
1410 InventoryList *mlist = inv->getList(getWieldList());
1412 ItemStack olditem = mlist->getItem(getWieldIndex());
1413 if (olditem.name.empty()) {
1414 InventoryList *hlist = inv->getList("hand");
1416 hlist->changeItem(0, item);
1420 mlist->changeItem(getWieldIndex(), item);
1427 int PlayerSAO::getWieldIndex() const
1429 return m_wield_index;
1432 void PlayerSAO::setWieldIndex(int i)
1434 if(i != m_wield_index) {
1439 // Erase the peer id and make the object for removal
1440 void PlayerSAO::disconnected()
1446 void PlayerSAO::unlinkPlayerSessionAndSave()
1448 assert(m_player->getPlayerSAO() == this);
1449 m_player->peer_id = 0;
1450 m_env->savePlayer(m_player);
1451 m_player->setPlayerSAO(NULL);
1452 m_env->removePlayer(m_player);
1455 std::string PlayerSAO::getPropertyPacket()
1457 m_prop.is_visible = (true);
1458 return gob_cmd_set_properties(m_prop);
1461 bool PlayerSAO::checkMovementCheat()
1463 if (isAttached() || m_is_singleplayer ||
1464 g_settings->getBool("disable_anticheat")) {
1465 m_last_good_position = m_base_position;
1469 bool cheated = false;
1471 Check player movements
1473 NOTE: Actually the server should handle player physics like the
1474 client does and compare player's position to what is calculated
1475 on our side. This is required when eg. players fly due to an
1476 explosion. Altough a node-based alternative might be possible
1477 too, and much more lightweight.
1480 float player_max_speed = 0;
1482 if (m_privs.count("fast") != 0) {
1484 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1487 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1489 // Tolerance. The lag pool does this a bit.
1490 //player_max_speed *= 2.5;
1492 v3f diff = (m_base_position - m_last_good_position);
1493 float d_vert = diff.Y;
1495 float d_horiz = diff.getLength();
1496 float required_time = d_horiz / player_max_speed;
1498 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1499 required_time = d_vert / player_max_speed; // Moving upwards
1501 if (m_move_pool.grab(required_time)) {
1502 m_last_good_position = m_base_position;
1504 actionstream << "Player " << m_player->getName()
1505 << " moved too fast; resetting position"
1507 setBasePosition(m_last_good_position);
1513 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1515 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1516 toset->MinEdge += m_base_position;
1517 toset->MaxEdge += m_base_position;