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 "util/mathconstants.h"
23 #include "collision.h"
24 #include "environment.h"
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
30 #include "remoteplayer.h"
32 #include "scripting_game.h"
33 #include "genericobject.h"
36 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
42 class TestSAO : public ServerActiveObject
45 TestSAO(ServerEnvironment *env, v3f pos):
46 ServerActiveObject(env, pos),
50 ServerActiveObject::registerType(getType(), create);
52 ActiveObjectType getType() const
53 { return ACTIVEOBJECT_TYPE_TEST; }
55 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56 const std::string &data)
58 return new TestSAO(env, pos);
61 void step(float dtime, bool send_recommended)
70 m_base_position.Y += dtime * BS * 2;
71 if(m_base_position.Y > 8*BS)
72 m_base_position.Y = 2*BS;
74 if(send_recommended == false)
84 data += itos(0); // 0 = position
86 data += itos(m_base_position.X);
88 data += itos(m_base_position.Y);
90 data += itos(m_base_position.Z);
92 ActiveObjectMessage aom(getId(), false, data);
93 m_messages_out.push(aom);
97 bool getCollisionBox(aabb3f *toset) {
101 bool collideWithObjects() {
110 // Prototype (registers item for deserialization)
111 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
117 // Prototype (registers item for deserialization)
118 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
120 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
121 const std::string &name, const std::string &state):
127 m_acceleration(0,0,0),
128 m_properties_sent(true),
130 m_last_sent_position(0,0,0),
131 m_last_sent_velocity(0,0,0),
132 m_last_sent_position_timer(0),
133 m_last_sent_move_precision(0),
134 m_armor_groups_sent(false),
135 m_animation_speed(0),
136 m_animation_blend(0),
137 m_animation_loop(true),
138 m_animation_sent(false),
139 m_bone_position_sent(false),
140 m_attachment_parent_id(0),
141 m_attachment_sent(false)
143 // Only register type if no environment supplied
145 ServerActiveObject::registerType(getType(), create);
149 // Initialize something to armor groups
150 m_armor_groups["fleshy"] = 100;
153 LuaEntitySAO::~LuaEntitySAO()
156 m_env->getScriptIface()->luaentity_Remove(m_id);
159 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
160 it != m_attached_particle_spawners.end(); ++it) {
161 m_env->deleteParticleSpawner(*it, false);
165 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
167 ServerActiveObject::addedToEnvironment(dtime_s);
169 // Create entity from name
170 m_registered = m_env->getScriptIface()->
171 luaentity_Add(m_id, m_init_name.c_str());
175 m_env->getScriptIface()->
176 luaentity_GetProperties(m_id, &m_prop);
177 // Initialize HP from properties
178 m_hp = m_prop.hp_max;
179 // Activate entity, supplying serialized state
180 m_env->getScriptIface()->
181 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
183 m_prop.infotext = m_init_name;
187 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
188 const std::string &data)
196 std::istringstream is(data, std::ios::binary);
198 u8 version = readU8(is);
199 // check if version is supported
201 name = deSerializeString(is);
202 state = deSerializeLongString(is);
204 else if(version == 1){
205 name = deSerializeString(is);
206 state = deSerializeLongString(is);
208 velocity = readV3F1000(is);
213 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
214 <<state<<"\")"<<std::endl;
215 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
217 sao->m_velocity = velocity;
222 bool LuaEntitySAO::isAttached()
224 if(!m_attachment_parent_id)
226 // Check if the parent still exists
227 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
233 void LuaEntitySAO::step(float dtime, bool send_recommended)
235 if(!m_properties_sent)
237 m_properties_sent = true;
238 std::string str = getPropertyPacket();
239 // create message and add to list
240 ActiveObjectMessage aom(getId(), true, str);
241 m_messages_out.push(aom);
244 // If attached, check that our parent is still there. If it isn't, detach.
245 if(m_attachment_parent_id && !isAttached())
247 m_attachment_parent_id = 0;
248 m_attachment_bone = "";
249 m_attachment_position = v3f(0,0,0);
250 m_attachment_rotation = v3f(0,0,0);
251 sendPosition(false, true);
254 m_last_sent_position_timer += dtime;
256 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
257 // If the object gets detached this comes into effect automatically from the last known origin
260 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
261 m_base_position = pos;
262 m_velocity = v3f(0,0,0);
263 m_acceleration = v3f(0,0,0);
268 aabb3f box = m_prop.collisionbox;
271 collisionMoveResult moveresult;
272 f32 pos_max_d = BS*0.25; // Distance per iteration
273 v3f p_pos = m_base_position;
274 v3f p_velocity = m_velocity;
275 v3f p_acceleration = m_acceleration;
276 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
277 pos_max_d, box, m_prop.stepheight, dtime,
278 &p_pos, &p_velocity, p_acceleration,
279 this, m_prop.collideWithObjects);
282 m_base_position = p_pos;
283 m_velocity = p_velocity;
284 m_acceleration = p_acceleration;
286 m_base_position += dtime * m_velocity + 0.5 * dtime
287 * dtime * m_acceleration;
288 m_velocity += dtime * m_acceleration;
291 if((m_prop.automatic_face_movement_dir) &&
292 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
294 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
295 + m_prop.automatic_face_movement_dir_offset;
296 float max_rotation_delta =
297 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
299 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
300 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
302 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
310 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
313 if(send_recommended == false)
318 // TODO: force send when acceleration changes enough?
319 float minchange = 0.2*BS;
320 if(m_last_sent_position_timer > 1.0){
322 } else if(m_last_sent_position_timer > 0.2){
325 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
326 move_d += m_last_sent_move_precision;
327 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
328 if(move_d > minchange || vel_d > minchange ||
329 fabs(m_yaw - m_last_sent_yaw) > 1.0){
330 sendPosition(true, false);
334 if(m_armor_groups_sent == false){
335 m_armor_groups_sent = true;
336 std::string str = gob_cmd_update_armor_groups(
338 // create message and add to list
339 ActiveObjectMessage aom(getId(), true, str);
340 m_messages_out.push(aom);
343 if(m_animation_sent == false){
344 m_animation_sent = true;
345 std::string str = gob_cmd_update_animation(
346 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
347 // create message and add to list
348 ActiveObjectMessage aom(getId(), true, str);
349 m_messages_out.push(aom);
352 if(m_bone_position_sent == false){
353 m_bone_position_sent = true;
354 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
355 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
356 std::string str = gob_cmd_update_bone_position((*ii).first,
357 (*ii).second.X, (*ii).second.Y);
358 // create message and add to list
359 ActiveObjectMessage aom(getId(), true, str);
360 m_messages_out.push(aom);
364 if(m_attachment_sent == false){
365 m_attachment_sent = true;
366 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
367 // create message and add to list
368 ActiveObjectMessage aom(getId(), true, str);
369 m_messages_out.push(aom);
373 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
375 std::ostringstream os(std::ios::binary);
377 if(protocol_version >= 14)
379 writeU8(os, 1); // version
380 os<<serializeString(""); // name
381 writeU8(os, 0); // is_player
382 writeS16(os, getId()); //id
383 writeV3F1000(os, m_base_position);
384 writeF1000(os, m_yaw);
387 std::ostringstream msg_os(std::ios::binary);
388 msg_os << serializeLongString(getPropertyPacket()); // message 1
389 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
390 msg_os << serializeLongString(gob_cmd_update_animation(
391 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
392 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
393 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
394 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
395 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
397 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
398 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
399 int message_count = 4 + m_bone_position.size();
400 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
401 (ii != m_attachment_child_ids.end()); ++ii) {
402 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
404 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
405 obj->getClientInitializationData(protocol_version)));
409 writeU8(os, message_count);
410 os.write(msg_os.str().c_str(), msg_os.str().size());
414 writeU8(os, 0); // version
415 os<<serializeString(""); // name
416 writeU8(os, 0); // is_player
417 writeV3F1000(os, m_base_position);
418 writeF1000(os, m_yaw);
420 writeU8(os, 2); // number of messages stuffed in here
421 os<<serializeLongString(getPropertyPacket()); // message 1
422 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
429 std::string LuaEntitySAO::getStaticData()
431 verbosestream<<FUNCTION_NAME<<std::endl;
432 std::ostringstream os(std::ios::binary);
436 os<<serializeString(m_init_name);
439 std::string state = m_env->getScriptIface()->
440 luaentity_GetStaticdata(m_id);
441 os<<serializeLongString(state);
443 os<<serializeLongString(m_init_state);
448 writeV3F1000(os, m_velocity);
450 writeF1000(os, m_yaw);
454 int LuaEntitySAO::punch(v3f dir,
455 const ToolCapabilities *toolcap,
456 ServerActiveObject *puncher,
457 float time_from_last_punch)
460 // Delete unknown LuaEntities when punched
465 // It's best that attachments cannot be punched
469 ItemStack *punchitem = NULL;
470 ItemStack punchitem_static;
472 punchitem_static = puncher->getWieldedItem();
473 punchitem = &punchitem_static;
476 PunchDamageResult result = getPunchDamage(
480 time_from_last_punch);
482 if (result.did_punch) {
483 setHP(getHP() - result.damage);
485 if (result.damage > 0) {
486 std::string punchername = puncher ? puncher->getDescription() : "nil";
488 actionstream << getDescription() << " punched by "
489 << punchername << ", damage " << result.damage
490 << " hp, health now " << getHP() << " hp" << std::endl;
493 std::string str = gob_cmd_punched(result.damage, getHP());
494 // create message and add to list
495 ActiveObjectMessage aom(getId(), true, str);
496 m_messages_out.push(aom);
502 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
503 time_from_last_punch, toolcap, dir);
508 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
512 // It's best that attachments cannot be clicked
515 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
518 void LuaEntitySAO::setPos(const v3f &pos)
522 m_base_position = pos;
523 sendPosition(false, true);
526 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
530 m_base_position = pos;
532 sendPosition(true, true);
535 float LuaEntitySAO::getMinimumSavedMovement()
540 std::string LuaEntitySAO::getDescription()
542 std::ostringstream os(std::ios::binary);
543 os<<"LuaEntitySAO at (";
544 os<<(m_base_position.X/BS)<<",";
545 os<<(m_base_position.Y/BS)<<",";
546 os<<(m_base_position.Z/BS);
551 void LuaEntitySAO::setHP(s16 hp)
557 s16 LuaEntitySAO::getHP() const
562 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
564 m_armor_groups = armor_groups;
565 m_armor_groups_sent = false;
568 ItemGroupList LuaEntitySAO::getArmorGroups()
570 return m_armor_groups;
573 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
575 m_animation_range = frame_range;
576 m_animation_speed = frame_speed;
577 m_animation_blend = frame_blend;
578 m_animation_loop = frame_loop;
579 m_animation_sent = false;
582 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
584 *frame_range = m_animation_range;
585 *frame_speed = m_animation_speed;
586 *frame_blend = m_animation_blend;
587 *frame_loop = m_animation_loop;
590 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
592 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
593 m_bone_position_sent = false;
596 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
598 *position = m_bone_position[bone].X;
599 *rotation = m_bone_position[bone].Y;
602 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
604 // Attachments need to be handled on both the server and client.
605 // If we just attach on the server, we can only copy the position of the parent. Attachments
606 // are still sent to clients at an interval so players might see them lagging, plus we can't
607 // read and attach to skeletal bones.
608 // If we just attach on the client, the server still sees the child at its original location.
609 // This breaks some things so we also give the server the most accurate representation
610 // even if players only see the client changes.
612 m_attachment_parent_id = parent_id;
613 m_attachment_bone = bone;
614 m_attachment_position = position;
615 m_attachment_rotation = rotation;
616 m_attachment_sent = false;
619 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
622 *parent_id = m_attachment_parent_id;
623 *bone = m_attachment_bone;
624 *position = m_attachment_position;
625 *rotation = m_attachment_rotation;
628 void LuaEntitySAO::addAttachmentChild(int child_id)
630 m_attachment_child_ids.insert(child_id);
633 void LuaEntitySAO::removeAttachmentChild(int child_id)
635 m_attachment_child_ids.erase(child_id);
638 UNORDERED_SET<int> LuaEntitySAO::getAttachmentChildIds()
640 return m_attachment_child_ids;
643 ObjectProperties* LuaEntitySAO::accessObjectProperties()
648 void LuaEntitySAO::notifyObjectPropertiesModified()
650 m_properties_sent = false;
653 void LuaEntitySAO::setVelocity(v3f velocity)
655 m_velocity = velocity;
658 v3f LuaEntitySAO::getVelocity()
663 void LuaEntitySAO::setAcceleration(v3f acceleration)
665 m_acceleration = acceleration;
668 v3f LuaEntitySAO::getAcceleration()
670 return m_acceleration;
673 void LuaEntitySAO::setTextureMod(const std::string &mod)
675 std::string str = gob_cmd_set_texture_mod(mod);
676 // create message and add to list
677 ActiveObjectMessage aom(getId(), true, str);
678 m_messages_out.push(aom);
681 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
682 bool select_horiz_by_yawpitch)
684 std::string str = gob_cmd_set_sprite(
688 select_horiz_by_yawpitch
690 // create message and add to list
691 ActiveObjectMessage aom(getId(), true, str);
692 m_messages_out.push(aom);
695 std::string LuaEntitySAO::getName()
700 std::string LuaEntitySAO::getPropertyPacket()
702 return gob_cmd_set_properties(m_prop);
705 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
707 // If the object is attached client-side, don't waste bandwidth sending its position to clients
711 m_last_sent_move_precision = m_base_position.getDistanceFrom(
712 m_last_sent_position);
713 m_last_sent_position_timer = 0;
714 m_last_sent_yaw = m_yaw;
715 m_last_sent_position = m_base_position;
716 m_last_sent_velocity = m_velocity;
717 //m_last_sent_acceleration = m_acceleration;
719 float update_interval = m_env->getSendRecommendedInterval();
721 std::string str = gob_cmd_update_position(
730 // create message and add to list
731 ActiveObjectMessage aom(getId(), false, str);
732 m_messages_out.push(aom);
735 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
738 //update collision box
739 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
740 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
742 toset->MinEdge += m_base_position;
743 toset->MaxEdge += m_base_position;
751 bool LuaEntitySAO::collideWithObjects(){
752 return m_prop.collideWithObjects;
759 // No prototype, PlayerSAO does not need to be deserialized
761 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
762 UnitSAO(env_, v3f(0,0,0)),
767 m_last_good_position(0,0,0),
768 m_time_from_last_punch(0),
769 m_nocheat_dig_pos(32767, 32767, 32767),
770 m_nocheat_dig_time(0),
772 m_position_not_sent(false),
773 m_armor_groups_sent(false),
774 m_properties_sent(true),
775 m_is_singleplayer(is_singleplayer),
776 m_animation_speed(0),
777 m_animation_blend(0),
778 m_animation_loop(true),
779 m_animation_sent(false),
780 m_bone_position_sent(false),
781 m_attachment_parent_id(0),
782 m_attachment_sent(false),
783 m_breath(PLAYER_MAX_BREATH),
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(true),
793 m_physics_override_sent(false)
795 assert(m_peer_id != 0); // pre-condition
796 m_armor_groups["fleshy"] = 100;
798 m_prop.hp_max = PLAYER_MAX_HP;
799 m_prop.physical = false;
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_hp = PLAYER_MAX_HP;
817 PlayerSAO::~PlayerSAO()
819 if(m_inventory != &m_player->inventory)
823 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
828 m_inventory = &m_player->inventory;
831 std::string PlayerSAO::getDescription()
833 return std::string("player ") + m_player->getName();
836 // Called after id has been set and has been inserted in environment
837 void PlayerSAO::addedToEnvironment(u32 dtime_s)
839 ServerActiveObject::addedToEnvironment(dtime_s);
840 ServerActiveObject::setBasePosition(m_base_position);
841 m_player->setPlayerSAO(this);
842 m_player->peer_id = m_peer_id;
843 m_last_good_position = m_base_position;
846 // Called before removing from environment
847 void PlayerSAO::removingFromEnvironment()
849 ServerActiveObject::removingFromEnvironment();
850 if (m_player->getPlayerSAO() == this) {
851 unlinkPlayerSessionAndSave();
852 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
853 it != m_attached_particle_spawners.end(); ++it) {
854 m_env->deleteParticleSpawner(*it, false);
859 bool PlayerSAO::isStaticAllowed() const
864 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
866 std::ostringstream os(std::ios::binary);
868 if(protocol_version >= 15)
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)); // 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());
910 writeU8(os, 0); // version
911 os<<serializeString(m_player->getName()); // name
912 writeU8(os, 1); // is_player
913 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
914 writeF1000(os, m_yaw);
915 writeS16(os, getHP());
916 writeU8(os, 2); // number of messages stuffed in here
917 os<<serializeLongString(getPropertyPacket()); // message 1
918 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
925 std::string PlayerSAO::getStaticData()
927 FATAL_ERROR("Deprecated function (?)");
931 bool PlayerSAO::isAttached()
933 if(!m_attachment_parent_id)
935 // Check if the parent still exists
936 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
942 void PlayerSAO::step(float dtime, bool send_recommended)
944 if (m_drowning_interval.step(dtime, 2.0)) {
946 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
947 MapNode n = m_env->getMap().getNodeNoEx(p);
948 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
949 // If node generates drown
950 if (c.drowning > 0) {
951 if (m_hp > 0 && m_breath > 0)
952 setBreath(m_breath - 1);
954 // No more breath, damage player
956 setHP(m_hp - c.drowning);
957 m_env->getGameDef()->SendPlayerHPOrDie(this);
962 if (m_breathing_interval.step(dtime, 0.5)) {
964 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
965 MapNode n = m_env->getMap().getNodeNoEx(p);
966 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
967 // If player is alive & no drowning, breath
968 if (m_hp > 0 && c.drowning == 0)
969 setBreath(m_breath + 1);
972 if (!m_properties_sent) {
973 m_properties_sent = true;
974 std::string str = getPropertyPacket();
975 // create message and add to list
976 ActiveObjectMessage aom(getId(), true, str);
977 m_messages_out.push(aom);
980 // If attached, check that our parent is still there. If it isn't, detach.
981 if(m_attachment_parent_id && !isAttached())
983 m_attachment_parent_id = 0;
984 m_attachment_bone = "";
985 m_attachment_position = v3f(0,0,0);
986 m_attachment_rotation = v3f(0,0,0);
987 setBasePosition(m_last_good_position);
988 m_env->getGameDef()->SendMovePlayer(m_peer_id);
991 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
993 // Set lag pool maximums based on estimated lag
994 const float LAG_POOL_MIN = 5.0;
995 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
996 if(lag_pool_max < LAG_POOL_MIN)
997 lag_pool_max = LAG_POOL_MIN;
998 m_dig_pool.setMax(lag_pool_max);
999 m_move_pool.setMax(lag_pool_max);
1001 // Increment cheat prevention timers
1002 m_dig_pool.add(dtime);
1003 m_move_pool.add(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 // create message and add to list
1057 ActiveObjectMessage aom(getId(), true, str);
1058 m_messages_out.push(aom);
1061 if (!m_animation_sent) {
1062 m_animation_sent = true;
1063 std::string str = gob_cmd_update_animation(
1064 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1065 // create message and add to list
1066 ActiveObjectMessage aom(getId(), true, str);
1067 m_messages_out.push(aom);
1070 if (!m_bone_position_sent) {
1071 m_bone_position_sent = true;
1072 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1073 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1074 std::string str = gob_cmd_update_bone_position((*ii).first,
1075 (*ii).second.X, (*ii).second.Y);
1076 // create message and add to list
1077 ActiveObjectMessage aom(getId(), true, str);
1078 m_messages_out.push(aom);
1082 if (!m_attachment_sent){
1083 m_attachment_sent = true;
1084 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1085 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1086 // create message and add to list
1087 ActiveObjectMessage aom(getId(), true, str);
1088 m_messages_out.push(aom);
1092 void PlayerSAO::setBasePosition(const v3f &position)
1094 if (m_player && position != m_base_position)
1095 m_player->setDirty(true);
1097 // This needs to be ran for attachments too
1098 ServerActiveObject::setBasePosition(position);
1099 m_position_not_sent = true;
1102 void PlayerSAO::setPos(const v3f &pos)
1107 setBasePosition(pos);
1108 // Movement caused by this command is always valid
1109 m_last_good_position = pos;
1110 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1113 void PlayerSAO::moveTo(v3f pos, bool continuous)
1118 setBasePosition(pos);
1119 // Movement caused by this command is always valid
1120 m_last_good_position = pos;
1121 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1124 void PlayerSAO::setYaw(const float yaw)
1126 if (m_player && yaw != m_yaw)
1127 m_player->setDirty(true);
1129 UnitSAO::setYaw(yaw);
1132 void PlayerSAO::setFov(const float fov)
1134 if (m_player && fov != m_fov)
1135 m_player->setDirty(true);
1140 void PlayerSAO::setWantedRange(const s16 range)
1142 if (m_player && range != m_wanted_range)
1143 m_player->setDirty(true);
1145 m_wanted_range = range;
1148 void PlayerSAO::setYawAndSend(const float yaw)
1151 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1154 void PlayerSAO::setPitch(const float pitch)
1156 if (m_player && pitch != m_pitch)
1157 m_player->setDirty(true);
1162 void PlayerSAO::setPitchAndSend(const float pitch)
1165 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1168 int PlayerSAO::punch(v3f dir,
1169 const ToolCapabilities *toolcap,
1170 ServerActiveObject *puncher,
1171 float time_from_last_punch)
1173 // It's best that attachments cannot be punched
1180 // No effect if PvP disabled
1181 if (g_settings->getBool("enable_pvp") == false) {
1182 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1183 std::string str = gob_cmd_punched(0, getHP());
1184 // create message and add to list
1185 ActiveObjectMessage aom(getId(), true, str);
1186 m_messages_out.push(aom);
1191 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1192 time_from_last_punch);
1194 std::string punchername = "nil";
1197 punchername = puncher->getDescription();
1199 PlayerSAO *playersao = m_player->getPlayerSAO();
1201 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1202 puncher, time_from_last_punch, toolcap, dir,
1205 if (!damage_handled) {
1206 setHP(getHP() - hitparams.hp);
1207 } else { // override client prediction
1208 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1209 std::string str = gob_cmd_punched(0, getHP());
1210 // create message and add to list
1211 ActiveObjectMessage aom(getId(), true, str);
1212 m_messages_out.push(aom);
1217 actionstream << "Player " << m_player->getName() << " punched by "
1219 if (!damage_handled) {
1220 actionstream << ", damage " << hitparams.hp << " HP";
1222 actionstream << ", damage handled by lua";
1224 actionstream << std::endl;
1226 return hitparams.wear;
1229 void PlayerSAO::rightClick(ServerActiveObject *)
1233 s16 PlayerSAO::readDamage()
1235 s16 damage = m_damage;
1240 void PlayerSAO::setHP(s16 hp)
1244 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1247 hp = oldhp + hp_change;
1251 else if (hp > PLAYER_MAX_HP)
1254 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1261 m_damage += (oldhp - hp);
1263 // Update properties on death
1264 if ((hp == 0) != (oldhp == 0))
1265 m_properties_sent = false;
1268 void PlayerSAO::setBreath(const u16 breath, bool send)
1270 if (m_player && breath != m_breath)
1271 m_player->setDirty(true);
1273 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1276 m_env->getGameDef()->SendPlayerBreath(this);
1279 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1281 m_armor_groups = armor_groups;
1282 m_armor_groups_sent = false;
1285 ItemGroupList PlayerSAO::getArmorGroups()
1287 return m_armor_groups;
1290 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1292 // store these so they can be updated to clients
1293 m_animation_range = frame_range;
1294 m_animation_speed = frame_speed;
1295 m_animation_blend = frame_blend;
1296 m_animation_loop = frame_loop;
1297 m_animation_sent = false;
1300 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1302 *frame_range = m_animation_range;
1303 *frame_speed = m_animation_speed;
1304 *frame_blend = m_animation_blend;
1305 *frame_loop = m_animation_loop;
1308 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1310 // store these so they can be updated to clients
1311 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1312 m_bone_position_sent = false;
1315 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1317 *position = m_bone_position[bone].X;
1318 *rotation = m_bone_position[bone].Y;
1321 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1323 // Attachments need to be handled on both the server and client.
1324 // If we just attach on the server, we can only copy the position of the parent. Attachments
1325 // are still sent to clients at an interval so players might see them lagging, plus we can't
1326 // read and attach to skeletal bones.
1327 // If we just attach on the client, the server still sees the child at its original location.
1328 // This breaks some things so we also give the server the most accurate representation
1329 // even if players only see the client changes.
1331 m_attachment_parent_id = parent_id;
1332 m_attachment_bone = bone;
1333 m_attachment_position = position;
1334 m_attachment_rotation = rotation;
1335 m_attachment_sent = false;
1338 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1341 *parent_id = m_attachment_parent_id;
1342 *bone = m_attachment_bone;
1343 *position = m_attachment_position;
1344 *rotation = m_attachment_rotation;
1347 void PlayerSAO::addAttachmentChild(int child_id)
1349 m_attachment_child_ids.insert(child_id);
1352 void PlayerSAO::removeAttachmentChild(int child_id)
1354 m_attachment_child_ids.erase(child_id);
1357 UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
1359 return m_attachment_child_ids;
1362 ObjectProperties* PlayerSAO::accessObjectProperties()
1367 void PlayerSAO::notifyObjectPropertiesModified()
1369 m_properties_sent = false;
1372 Inventory* PlayerSAO::getInventory()
1376 const Inventory* PlayerSAO::getInventory() const
1381 InventoryLocation PlayerSAO::getInventoryLocation() const
1383 InventoryLocation loc;
1384 loc.setPlayer(m_player->getName());
1388 std::string PlayerSAO::getWieldList() const
1393 ItemStack PlayerSAO::getWieldedItem() const
1395 const Inventory *inv = getInventory();
1397 const InventoryList *mlist = inv->getList(getWieldList());
1398 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1399 ret = mlist->getItem(getWieldIndex());
1400 if (ret.name.empty()) {
1401 const InventoryList *hlist = inv->getList("hand");
1403 ret = hlist->getItem(0);
1408 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1410 Inventory *inv = getInventory();
1412 InventoryList *mlist = inv->getList(getWieldList());
1414 ItemStack olditem = mlist->getItem(getWieldIndex());
1415 if (olditem.name.empty()) {
1416 InventoryList *hlist = inv->getList("hand");
1418 hlist->changeItem(0, item);
1422 mlist->changeItem(getWieldIndex(), item);
1429 int PlayerSAO::getWieldIndex() const
1431 return m_wield_index;
1434 void PlayerSAO::setWieldIndex(int i)
1436 if(i != m_wield_index) {
1441 // Erase the peer id and make the object for removal
1442 void PlayerSAO::disconnected()
1448 void PlayerSAO::unlinkPlayerSessionAndSave()
1450 assert(m_player->getPlayerSAO() == this);
1451 m_player->peer_id = 0;
1452 m_env->savePlayer(m_player);
1453 m_player->setPlayerSAO(NULL);
1454 m_env->removePlayer(m_player);
1457 std::string PlayerSAO::getPropertyPacket()
1459 m_prop.is_visible = (true);
1460 return gob_cmd_set_properties(m_prop);
1463 bool PlayerSAO::checkMovementCheat()
1465 if (isAttached() || m_is_singleplayer ||
1466 g_settings->getBool("disable_anticheat")) {
1467 m_last_good_position = m_base_position;
1471 bool cheated = false;
1473 Check player movements
1475 NOTE: Actually the server should handle player physics like the
1476 client does and compare player's position to what is calculated
1477 on our side. This is required when eg. players fly due to an
1478 explosion. Altough a node-based alternative might be possible
1479 too, and much more lightweight.
1482 float player_max_speed = 0;
1484 if (m_privs.count("fast") != 0) {
1486 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1489 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1491 // Tolerance. The lag pool does this a bit.
1492 //player_max_speed *= 2.5;
1494 v3f diff = (m_base_position - m_last_good_position);
1495 float d_vert = diff.Y;
1497 float d_horiz = diff.getLength();
1498 float required_time = d_horiz / player_max_speed;
1500 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1501 required_time = d_vert / player_max_speed; // Moving upwards
1503 if (m_move_pool.grab(required_time)) {
1504 m_last_good_position = m_base_position;
1506 actionstream << "Player " << m_player->getName()
1507 << " moved too fast; resetting position"
1509 setBasePosition(m_last_good_position);
1515 bool PlayerSAO::getCollisionBox(aabb3f *toset)
1517 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1518 toset->MinEdge += m_base_position;
1519 toset->MaxEdge += m_base_position;
1523 bool PlayerSAO::collideWithObjects()