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/mathconstants.h"
22 #include "util/serialize.h"
23 #include "collision.h"
24 #include "environment.h"
25 #include "tool.h" // For ToolCapabilities
28 #include "remoteplayer.h"
30 #include "scripting_game.h"
31 #include "genericobject.h"
33 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
39 class TestSAO : public ServerActiveObject
42 TestSAO(ServerEnvironment *env, v3f pos):
43 ServerActiveObject(env, pos),
47 ServerActiveObject::registerType(getType(), create);
49 ActiveObjectType getType() const
50 { return ACTIVEOBJECT_TYPE_TEST; }
52 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
53 const std::string &data)
55 return new TestSAO(env, pos);
58 void step(float dtime, bool send_recommended)
67 m_base_position.Y += dtime * BS * 2;
68 if(m_base_position.Y > 8*BS)
69 m_base_position.Y = 2*BS;
71 if(send_recommended == false)
81 data += itos(0); // 0 = position
83 data += itos(m_base_position.X);
85 data += itos(m_base_position.Y);
87 data += itos(m_base_position.Z);
89 ActiveObjectMessage aom(getId(), false, data);
90 m_messages_out.push(aom);
94 bool getCollisionBox(aabb3f *toset) const { return false; }
95 bool collideWithObjects() const { return false; }
102 // Prototype (registers item for deserialization)
103 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
109 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
110 ServerActiveObject(env, pos),
113 m_properties_sent(true),
114 m_armor_groups_sent(false),
115 m_animation_range(0,0),
116 m_animation_speed(0),
117 m_animation_blend(0),
118 m_animation_loop(true),
119 m_animation_sent(false),
120 m_bone_position_sent(false),
121 m_attachment_parent_id(0),
122 m_attachment_sent(false)
124 // Initialize something to armor groups
125 m_armor_groups["fleshy"] = 100;
128 bool UnitSAO::isAttached() const
130 if (!m_attachment_parent_id)
132 // Check if the parent still exists
133 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
139 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
141 m_armor_groups = armor_groups;
142 m_armor_groups_sent = false;
145 const ItemGroupList &UnitSAO::getArmorGroups()
147 return m_armor_groups;
150 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
152 // store these so they can be updated to clients
153 m_animation_range = frame_range;
154 m_animation_speed = frame_speed;
155 m_animation_blend = frame_blend;
156 m_animation_loop = frame_loop;
157 m_animation_sent = false;
160 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
162 *frame_range = m_animation_range;
163 *frame_speed = m_animation_speed;
164 *frame_blend = m_animation_blend;
165 *frame_loop = m_animation_loop;
168 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
170 // store these so they can be updated to clients
171 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
172 m_bone_position_sent = false;
175 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
177 *position = m_bone_position[bone].X;
178 *rotation = m_bone_position[bone].Y;
181 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
183 // Attachments need to be handled on both the server and client.
184 // If we just attach on the server, we can only copy the position of the parent. Attachments
185 // are still sent to clients at an interval so players might see them lagging, plus we can't
186 // read and attach to skeletal bones.
187 // If we just attach on the client, the server still sees the child at its original location.
188 // This breaks some things so we also give the server the most accurate representation
189 // even if players only see the client changes.
191 m_attachment_parent_id = parent_id;
192 m_attachment_bone = bone;
193 m_attachment_position = position;
194 m_attachment_rotation = rotation;
195 m_attachment_sent = false;
198 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
201 *parent_id = m_attachment_parent_id;
202 *bone = m_attachment_bone;
203 *position = m_attachment_position;
204 *rotation = m_attachment_rotation;
207 void UnitSAO::addAttachmentChild(int child_id)
209 m_attachment_child_ids.insert(child_id);
212 void UnitSAO::removeAttachmentChild(int child_id)
214 m_attachment_child_ids.erase(child_id);
217 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
219 return m_attachment_child_ids;
222 ObjectProperties* UnitSAO::accessObjectProperties()
227 void UnitSAO::notifyObjectPropertiesModified()
229 m_properties_sent = false;
236 // Prototype (registers item for deserialization)
237 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
239 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
240 const std::string &name, const std::string &state):
246 m_acceleration(0,0,0),
248 m_last_sent_position(0,0,0),
249 m_last_sent_velocity(0,0,0),
250 m_last_sent_position_timer(0),
251 m_last_sent_move_precision(0)
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);
473 if(protocol_version >= 14)
475 writeU8(os, 1); // version
476 os<<serializeString(""); // name
477 writeU8(os, 0); // is_player
478 writeS16(os, getId()); //id
479 writeV3F1000(os, m_base_position);
480 writeF1000(os, m_yaw);
483 std::ostringstream msg_os(std::ios::binary);
484 msg_os << serializeLongString(getPropertyPacket()); // message 1
485 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
486 msg_os << serializeLongString(gob_cmd_update_animation(
487 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
488 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
489 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
490 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
491 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
493 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
494 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
495 int message_count = 4 + m_bone_position.size();
496 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
497 (ii != m_attachment_child_ids.end()); ++ii) {
498 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
500 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
501 obj->getClientInitializationData(protocol_version)));
505 writeU8(os, message_count);
506 os.write(msg_os.str().c_str(), msg_os.str().size());
510 writeU8(os, 0); // version
511 os<<serializeString(""); // name
512 writeU8(os, 0); // is_player
513 writeV3F1000(os, m_base_position);
514 writeF1000(os, m_yaw);
516 writeU8(os, 2); // number of messages stuffed in here
517 os<<serializeLongString(getPropertyPacket()); // message 1
518 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
525 void LuaEntitySAO::getStaticData(std::string *result) const
527 verbosestream<<FUNCTION_NAME<<std::endl;
528 std::ostringstream os(std::ios::binary);
532 os<<serializeString(m_init_name);
535 std::string state = m_env->getScriptIface()->
536 luaentity_GetStaticdata(m_id);
537 os<<serializeLongString(state);
539 os<<serializeLongString(m_init_state);
544 writeV3F1000(os, m_velocity);
546 writeF1000(os, m_yaw);
550 int LuaEntitySAO::punch(v3f dir,
551 const ToolCapabilities *toolcap,
552 ServerActiveObject *puncher,
553 float time_from_last_punch)
556 // Delete unknown LuaEntities when punched
561 // It's best that attachments cannot be punched
565 ItemStack *punchitem = NULL;
566 ItemStack punchitem_static;
568 punchitem_static = puncher->getWieldedItem();
569 punchitem = &punchitem_static;
572 PunchDamageResult result = getPunchDamage(
576 time_from_last_punch);
578 if (result.did_punch) {
579 setHP(getHP() - result.damage);
581 if (result.damage > 0) {
582 std::string punchername = puncher ? puncher->getDescription() : "nil";
584 actionstream << getDescription() << " punched by "
585 << punchername << ", damage " << result.damage
586 << " hp, health now " << getHP() << " hp" << std::endl;
589 std::string str = gob_cmd_punched(result.damage, getHP());
590 // create message and add to list
591 ActiveObjectMessage aom(getId(), true, str);
592 m_messages_out.push(aom);
598 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
599 time_from_last_punch, toolcap, dir);
604 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
608 // It's best that attachments cannot be clicked
611 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
614 void LuaEntitySAO::setPos(const v3f &pos)
618 m_base_position = pos;
619 sendPosition(false, true);
622 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
626 m_base_position = pos;
628 sendPosition(true, true);
631 float LuaEntitySAO::getMinimumSavedMovement()
636 std::string LuaEntitySAO::getDescription()
638 std::ostringstream os(std::ios::binary);
639 os<<"LuaEntitySAO at (";
640 os<<(m_base_position.X/BS)<<",";
641 os<<(m_base_position.Y/BS)<<",";
642 os<<(m_base_position.Z/BS);
647 void LuaEntitySAO::setHP(s16 hp)
653 s16 LuaEntitySAO::getHP() const
658 void LuaEntitySAO::setVelocity(v3f velocity)
660 m_velocity = velocity;
663 v3f LuaEntitySAO::getVelocity()
668 void LuaEntitySAO::setAcceleration(v3f acceleration)
670 m_acceleration = acceleration;
673 v3f LuaEntitySAO::getAcceleration()
675 return m_acceleration;
678 void LuaEntitySAO::setTextureMod(const std::string &mod)
680 std::string str = gob_cmd_set_texture_mod(mod);
681 // create message and add to list
682 ActiveObjectMessage aom(getId(), true, str);
683 m_messages_out.push(aom);
686 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
687 bool select_horiz_by_yawpitch)
689 std::string str = gob_cmd_set_sprite(
693 select_horiz_by_yawpitch
695 // create message and add to list
696 ActiveObjectMessage aom(getId(), true, str);
697 m_messages_out.push(aom);
700 std::string LuaEntitySAO::getName()
705 std::string LuaEntitySAO::getPropertyPacket()
707 return gob_cmd_set_properties(m_prop);
710 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
712 // If the object is attached client-side, don't waste bandwidth sending its position to clients
716 m_last_sent_move_precision = m_base_position.getDistanceFrom(
717 m_last_sent_position);
718 m_last_sent_position_timer = 0;
719 m_last_sent_yaw = m_yaw;
720 m_last_sent_position = m_base_position;
721 m_last_sent_velocity = m_velocity;
722 //m_last_sent_acceleration = m_acceleration;
724 float update_interval = m_env->getSendRecommendedInterval();
726 std::string str = gob_cmd_update_position(
735 // create message and add to list
736 ActiveObjectMessage aom(getId(), false, str);
737 m_messages_out.push(aom);
740 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
744 //update collision box
745 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
746 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
748 toset->MinEdge += m_base_position;
749 toset->MaxEdge += m_base_position;
757 bool LuaEntitySAO::collideWithObjects() const
759 return m_prop.collideWithObjects;
766 // No prototype, PlayerSAO does not need to be deserialized
768 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
769 UnitSAO(env_, v3f(0,0,0)),
774 m_last_good_position(0,0,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),
786 m_physics_override_speed(1),
787 m_physics_override_jump(1),
788 m_physics_override_gravity(1),
789 m_physics_override_sneak(true),
790 m_physics_override_sneak_glitch(true),
791 m_physics_override_sent(false)
793 assert(m_peer_id != 0); // pre-condition
795 m_prop.hp_max = PLAYER_MAX_HP;
796 m_prop.physical = false;
798 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
799 // start of default appearance, this should be overwritten by LUA
800 m_prop.visual = "upright_sprite";
801 m_prop.visual_size = v2f(1, 2);
802 m_prop.textures.clear();
803 m_prop.textures.push_back("player.png");
804 m_prop.textures.push_back("player_back.png");
805 m_prop.colors.clear();
806 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
807 m_prop.spritediv = v2s16(1,1);
808 // end of default appearance
809 m_prop.is_visible = true;
810 m_prop.makes_footstep_sound = true;
811 m_hp = PLAYER_MAX_HP;
814 PlayerSAO::~PlayerSAO()
816 if(m_inventory != &m_player->inventory)
820 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
825 m_inventory = &m_player->inventory;
828 v3f PlayerSAO::getEyeOffset() const
830 return v3f(0, BS * 1.625f, 0);
833 std::string PlayerSAO::getDescription()
835 return std::string("player ") + m_player->getName();
838 // Called after id has been set and has been inserted in environment
839 void PlayerSAO::addedToEnvironment(u32 dtime_s)
841 ServerActiveObject::addedToEnvironment(dtime_s);
842 ServerActiveObject::setBasePosition(m_base_position);
843 m_player->setPlayerSAO(this);
844 m_player->peer_id = m_peer_id;
845 m_last_good_position = m_base_position;
848 // Called before removing from environment
849 void PlayerSAO::removingFromEnvironment()
851 ServerActiveObject::removingFromEnvironment();
852 if (m_player->getPlayerSAO() == this) {
853 unlinkPlayerSessionAndSave();
854 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
855 it != m_attached_particle_spawners.end(); ++it) {
856 m_env->deleteParticleSpawner(*it, false);
861 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
863 std::ostringstream os(std::ios::binary);
865 if(protocol_version >= 15)
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());
907 writeU8(os, 0); // version
908 os<<serializeString(m_player->getName()); // name
909 writeU8(os, 1); // is_player
910 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
911 writeF1000(os, m_yaw);
912 writeS16(os, getHP());
913 writeU8(os, 2); // number of messages stuffed in here
914 os<<serializeLongString(getPropertyPacket()); // message 1
915 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
922 void PlayerSAO::getStaticData(std::string *result) const
924 FATAL_ERROR("Deprecated function");
927 void PlayerSAO::step(float dtime, bool send_recommended)
929 if (m_drowning_interval.step(dtime, 2.0)) {
931 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
932 MapNode n = m_env->getMap().getNodeNoEx(p);
933 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
934 // If node generates drown
935 if (c.drowning > 0) {
936 if (m_hp > 0 && m_breath > 0)
937 setBreath(m_breath - 1);
939 // No more breath, damage player
941 setHP(m_hp - c.drowning);
942 m_env->getGameDef()->SendPlayerHPOrDie(this);
947 if (m_breathing_interval.step(dtime, 0.5)) {
949 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
950 MapNode n = m_env->getMap().getNodeNoEx(p);
951 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
952 // If player is alive & no drowning, breath
953 if (m_hp > 0 && c.drowning == 0)
954 setBreath(m_breath + 1);
957 if (!m_properties_sent) {
958 m_properties_sent = true;
959 std::string str = getPropertyPacket();
960 // create message and add to list
961 ActiveObjectMessage aom(getId(), true, str);
962 m_messages_out.push(aom);
965 // If attached, check that our parent is still there. If it isn't, detach.
966 if(m_attachment_parent_id && !isAttached())
968 m_attachment_parent_id = 0;
969 m_attachment_bone = "";
970 m_attachment_position = v3f(0,0,0);
971 m_attachment_rotation = v3f(0,0,0);
972 setBasePosition(m_last_good_position);
973 m_env->getGameDef()->SendMovePlayer(m_peer_id);
976 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
978 // Set lag pool maximums based on estimated lag
979 const float LAG_POOL_MIN = 5.0;
980 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
981 if(lag_pool_max < LAG_POOL_MIN)
982 lag_pool_max = LAG_POOL_MIN;
983 m_dig_pool.setMax(lag_pool_max);
984 m_move_pool.setMax(lag_pool_max);
986 // Increment cheat prevention timers
987 m_dig_pool.add(dtime);
988 m_move_pool.add(dtime);
989 m_time_from_last_punch += dtime;
990 m_nocheat_dig_time += dtime;
992 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
993 // If the object gets detached this comes into effect automatically from the last known origin
995 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
996 m_last_good_position = pos;
997 setBasePosition(pos);
1000 if (!send_recommended)
1003 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1004 if(m_position_not_sent && !isAttached())
1006 m_position_not_sent = false;
1007 float update_interval = m_env->getSendRecommendedInterval();
1009 if(isAttached()) // Just in case we ever do send attachment position too
1010 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1012 pos = m_base_position + v3f(0,BS*1,0);
1013 std::string str = gob_cmd_update_position(
1022 // create message and add to list
1023 ActiveObjectMessage aom(getId(), false, str);
1024 m_messages_out.push(aom);
1027 if (!m_armor_groups_sent) {
1028 m_armor_groups_sent = true;
1029 std::string str = gob_cmd_update_armor_groups(
1031 // create message and add to list
1032 ActiveObjectMessage aom(getId(), true, str);
1033 m_messages_out.push(aom);
1036 if (!m_physics_override_sent) {
1037 m_physics_override_sent = true;
1038 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1039 m_physics_override_jump, m_physics_override_gravity,
1040 m_physics_override_sneak, m_physics_override_sneak_glitch);
1041 // create message and add to list
1042 ActiveObjectMessage aom(getId(), true, str);
1043 m_messages_out.push(aom);
1046 if (!m_animation_sent) {
1047 m_animation_sent = true;
1048 std::string str = gob_cmd_update_animation(
1049 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1050 // create message and add to list
1051 ActiveObjectMessage aom(getId(), true, str);
1052 m_messages_out.push(aom);
1055 if (!m_bone_position_sent) {
1056 m_bone_position_sent = true;
1057 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1058 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1059 std::string str = gob_cmd_update_bone_position((*ii).first,
1060 (*ii).second.X, (*ii).second.Y);
1061 // create message and add to list
1062 ActiveObjectMessage aom(getId(), true, str);
1063 m_messages_out.push(aom);
1067 if (!m_attachment_sent){
1068 m_attachment_sent = true;
1069 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1070 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1071 // create message and add to list
1072 ActiveObjectMessage aom(getId(), true, str);
1073 m_messages_out.push(aom);
1077 void PlayerSAO::setBasePosition(const v3f &position)
1079 if (m_player && position != m_base_position)
1080 m_player->setDirty(true);
1082 // This needs to be ran for attachments too
1083 ServerActiveObject::setBasePosition(position);
1084 m_position_not_sent = true;
1087 void PlayerSAO::setPos(const v3f &pos)
1092 setBasePosition(pos);
1093 // Movement caused by this command is always valid
1094 m_last_good_position = pos;
1095 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1098 void PlayerSAO::moveTo(v3f pos, bool continuous)
1103 setBasePosition(pos);
1104 // Movement caused by this command is always valid
1105 m_last_good_position = pos;
1106 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1109 void PlayerSAO::setYaw(const float yaw)
1111 if (m_player && yaw != m_yaw)
1112 m_player->setDirty(true);
1114 UnitSAO::setYaw(yaw);
1117 void PlayerSAO::setFov(const float fov)
1119 if (m_player && fov != m_fov)
1120 m_player->setDirty(true);
1125 void PlayerSAO::setWantedRange(const s16 range)
1127 if (m_player && range != m_wanted_range)
1128 m_player->setDirty(true);
1130 m_wanted_range = range;
1133 void PlayerSAO::setYawAndSend(const float yaw)
1136 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1139 void PlayerSAO::setPitch(const float pitch)
1141 if (m_player && pitch != m_pitch)
1142 m_player->setDirty(true);
1147 void PlayerSAO::setPitchAndSend(const float pitch)
1150 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1153 int PlayerSAO::punch(v3f dir,
1154 const ToolCapabilities *toolcap,
1155 ServerActiveObject *puncher,
1156 float time_from_last_punch)
1158 // It's best that attachments cannot be punched
1165 // No effect if PvP disabled
1166 if (g_settings->getBool("enable_pvp") == false) {
1167 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1168 std::string str = gob_cmd_punched(0, getHP());
1169 // create message and add to list
1170 ActiveObjectMessage aom(getId(), true, str);
1171 m_messages_out.push(aom);
1176 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1177 time_from_last_punch);
1179 std::string punchername = "nil";
1182 punchername = puncher->getDescription();
1184 PlayerSAO *playersao = m_player->getPlayerSAO();
1186 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1187 puncher, time_from_last_punch, toolcap, dir,
1190 if (!damage_handled) {
1191 setHP(getHP() - hitparams.hp);
1192 } else { // override client prediction
1193 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1194 std::string str = gob_cmd_punched(0, getHP());
1195 // create message and add to list
1196 ActiveObjectMessage aom(getId(), true, str);
1197 m_messages_out.push(aom);
1202 actionstream << "Player " << m_player->getName() << " punched by "
1204 if (!damage_handled) {
1205 actionstream << ", damage " << hitparams.hp << " HP";
1207 actionstream << ", damage handled by lua";
1209 actionstream << std::endl;
1211 return hitparams.wear;
1214 s16 PlayerSAO::readDamage()
1216 s16 damage = m_damage;
1221 void PlayerSAO::setHP(s16 hp)
1225 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1228 hp = oldhp + hp_change;
1232 else if (hp > PLAYER_MAX_HP)
1235 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1242 m_damage += (oldhp - hp);
1244 // Update properties on death
1245 if ((hp == 0) != (oldhp == 0))
1246 m_properties_sent = false;
1249 void PlayerSAO::setBreath(const u16 breath, bool send)
1251 if (m_player && breath != m_breath)
1252 m_player->setDirty(true);
1254 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1257 m_env->getGameDef()->SendPlayerBreath(this);
1260 Inventory* PlayerSAO::getInventory()
1264 const Inventory* PlayerSAO::getInventory() const
1269 InventoryLocation PlayerSAO::getInventoryLocation() const
1271 InventoryLocation loc;
1272 loc.setPlayer(m_player->getName());
1276 std::string PlayerSAO::getWieldList() const
1281 ItemStack PlayerSAO::getWieldedItem() const
1283 const Inventory *inv = getInventory();
1285 const InventoryList *mlist = inv->getList(getWieldList());
1286 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1287 ret = mlist->getItem(getWieldIndex());
1288 if (ret.name.empty()) {
1289 const InventoryList *hlist = inv->getList("hand");
1291 ret = hlist->getItem(0);
1296 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1298 Inventory *inv = getInventory();
1300 InventoryList *mlist = inv->getList(getWieldList());
1302 ItemStack olditem = mlist->getItem(getWieldIndex());
1303 if (olditem.name.empty()) {
1304 InventoryList *hlist = inv->getList("hand");
1306 hlist->changeItem(0, item);
1310 mlist->changeItem(getWieldIndex(), item);
1317 int PlayerSAO::getWieldIndex() const
1319 return m_wield_index;
1322 void PlayerSAO::setWieldIndex(int i)
1324 if(i != m_wield_index) {
1329 // Erase the peer id and make the object for removal
1330 void PlayerSAO::disconnected()
1336 void PlayerSAO::unlinkPlayerSessionAndSave()
1338 assert(m_player->getPlayerSAO() == this);
1339 m_player->peer_id = 0;
1340 m_env->savePlayer(m_player);
1341 m_player->setPlayerSAO(NULL);
1342 m_env->removePlayer(m_player);
1345 std::string PlayerSAO::getPropertyPacket()
1347 m_prop.is_visible = (true);
1348 return gob_cmd_set_properties(m_prop);
1351 bool PlayerSAO::checkMovementCheat()
1353 if (isAttached() || m_is_singleplayer ||
1354 g_settings->getBool("disable_anticheat")) {
1355 m_last_good_position = m_base_position;
1359 bool cheated = false;
1361 Check player movements
1363 NOTE: Actually the server should handle player physics like the
1364 client does and compare player's position to what is calculated
1365 on our side. This is required when eg. players fly due to an
1366 explosion. Altough a node-based alternative might be possible
1367 too, and much more lightweight.
1370 float player_max_speed = 0;
1372 if (m_privs.count("fast") != 0) {
1374 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1377 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1379 // Tolerance. The lag pool does this a bit.
1380 //player_max_speed *= 2.5;
1382 v3f diff = (m_base_position - m_last_good_position);
1383 float d_vert = diff.Y;
1385 float d_horiz = diff.getLength();
1386 float required_time = d_horiz / player_max_speed;
1388 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1389 required_time = d_vert / player_max_speed; // Moving upwards
1391 if (m_move_pool.grab(required_time)) {
1392 m_last_good_position = m_base_position;
1394 actionstream << "Player " << m_player->getName()
1395 << " moved too fast; resetting position"
1397 setBasePosition(m_last_good_position);
1403 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1405 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1406 toset->MinEdge += m_base_position;
1407 toset->MaxEdge += m_base_position;