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;
127 bool UnitSAO::isAttached() const
129 if (!m_attachment_parent_id)
131 // Check if the parent still exists
132 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
138 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
140 m_armor_groups = armor_groups;
141 m_armor_groups_sent = false;
144 const ItemGroupList &UnitSAO::getArmorGroups()
146 return m_armor_groups;
149 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
151 // store these so they can be updated to clients
152 m_animation_range = frame_range;
153 m_animation_speed = frame_speed;
154 m_animation_blend = frame_blend;
155 m_animation_loop = frame_loop;
156 m_animation_sent = false;
159 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
161 *frame_range = m_animation_range;
162 *frame_speed = m_animation_speed;
163 *frame_blend = m_animation_blend;
164 *frame_loop = m_animation_loop;
167 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
169 // store these so they can be updated to clients
170 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
171 m_bone_position_sent = false;
174 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
176 *position = m_bone_position[bone].X;
177 *rotation = m_bone_position[bone].Y;
180 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
182 // Attachments need to be handled on both the server and client.
183 // If we just attach on the server, we can only copy the position of the parent. Attachments
184 // are still sent to clients at an interval so players might see them lagging, plus we can't
185 // read and attach to skeletal bones.
186 // If we just attach on the client, the server still sees the child at its original location.
187 // This breaks some things so we also give the server the most accurate representation
188 // even if players only see the client changes.
190 m_attachment_parent_id = parent_id;
191 m_attachment_bone = bone;
192 m_attachment_position = position;
193 m_attachment_rotation = rotation;
194 m_attachment_sent = false;
197 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
200 *parent_id = m_attachment_parent_id;
201 *bone = m_attachment_bone;
202 *position = m_attachment_position;
203 *rotation = m_attachment_rotation;
206 void UnitSAO::addAttachmentChild(int child_id)
208 m_attachment_child_ids.insert(child_id);
211 void UnitSAO::removeAttachmentChild(int child_id)
213 m_attachment_child_ids.erase(child_id);
216 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
218 return m_attachment_child_ids;
221 ObjectProperties* UnitSAO::accessObjectProperties()
226 void UnitSAO::notifyObjectPropertiesModified()
228 m_properties_sent = false;
235 // Prototype (registers item for deserialization)
236 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
238 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
239 const std::string &name, const std::string &state):
245 m_acceleration(0,0,0),
247 m_last_sent_position(0,0,0),
248 m_last_sent_velocity(0,0,0),
249 m_last_sent_position_timer(0),
250 m_last_sent_move_precision(0),
251 m_current_texture_modifier("")
253 // Only register type if no environment supplied
255 ServerActiveObject::registerType(getType(), create);
260 LuaEntitySAO::~LuaEntitySAO()
263 m_env->getScriptIface()->luaentity_Remove(m_id);
266 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
267 it != m_attached_particle_spawners.end(); ++it) {
268 m_env->deleteParticleSpawner(*it, false);
272 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
274 ServerActiveObject::addedToEnvironment(dtime_s);
276 // Create entity from name
277 m_registered = m_env->getScriptIface()->
278 luaentity_Add(m_id, m_init_name.c_str());
282 m_env->getScriptIface()->
283 luaentity_GetProperties(m_id, &m_prop);
284 // Initialize HP from properties
285 m_hp = m_prop.hp_max;
286 // Activate entity, supplying serialized state
287 m_env->getScriptIface()->
288 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
290 m_prop.infotext = m_init_name;
294 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
295 const std::string &data)
303 std::istringstream is(data, std::ios::binary);
305 u8 version = readU8(is);
306 // check if version is supported
308 name = deSerializeString(is);
309 state = deSerializeLongString(is);
311 else if(version == 1){
312 name = deSerializeString(is);
313 state = deSerializeLongString(is);
315 velocity = readV3F1000(is);
320 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
321 <<state<<"\")"<<std::endl;
322 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
324 sao->m_velocity = velocity;
329 void LuaEntitySAO::step(float dtime, bool send_recommended)
331 if(!m_properties_sent)
333 m_properties_sent = true;
334 std::string str = getPropertyPacket();
335 // create message and add to list
336 ActiveObjectMessage aom(getId(), true, str);
337 m_messages_out.push(aom);
340 // If attached, check that our parent is still there. If it isn't, detach.
341 if(m_attachment_parent_id && !isAttached())
343 m_attachment_parent_id = 0;
344 m_attachment_bone = "";
345 m_attachment_position = v3f(0,0,0);
346 m_attachment_rotation = v3f(0,0,0);
347 sendPosition(false, true);
350 m_last_sent_position_timer += dtime;
352 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
353 // If the object gets detached this comes into effect automatically from the last known origin
356 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
357 m_base_position = pos;
358 m_velocity = v3f(0,0,0);
359 m_acceleration = v3f(0,0,0);
364 aabb3f box = m_prop.collisionbox;
367 collisionMoveResult moveresult;
368 f32 pos_max_d = BS*0.25; // Distance per iteration
369 v3f p_pos = m_base_position;
370 v3f p_velocity = m_velocity;
371 v3f p_acceleration = m_acceleration;
372 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
373 pos_max_d, box, m_prop.stepheight, dtime,
374 &p_pos, &p_velocity, p_acceleration,
375 this, m_prop.collideWithObjects);
378 m_base_position = p_pos;
379 m_velocity = p_velocity;
380 m_acceleration = p_acceleration;
382 m_base_position += dtime * m_velocity + 0.5 * dtime
383 * dtime * m_acceleration;
384 m_velocity += dtime * m_acceleration;
387 if((m_prop.automatic_face_movement_dir) &&
388 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
390 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
391 + m_prop.automatic_face_movement_dir_offset;
392 float max_rotation_delta =
393 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
395 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
396 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
398 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
406 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
409 if(send_recommended == false)
414 // TODO: force send when acceleration changes enough?
415 float minchange = 0.2*BS;
416 if(m_last_sent_position_timer > 1.0){
418 } else if(m_last_sent_position_timer > 0.2){
421 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
422 move_d += m_last_sent_move_precision;
423 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
424 if(move_d > minchange || vel_d > minchange ||
425 fabs(m_yaw - m_last_sent_yaw) > 1.0){
426 sendPosition(true, false);
430 if(m_armor_groups_sent == false){
431 m_armor_groups_sent = true;
432 std::string str = gob_cmd_update_armor_groups(
434 // create message and add to list
435 ActiveObjectMessage aom(getId(), true, str);
436 m_messages_out.push(aom);
439 if(m_animation_sent == false){
440 m_animation_sent = true;
441 std::string str = gob_cmd_update_animation(
442 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
443 // create message and add to list
444 ActiveObjectMessage aom(getId(), true, str);
445 m_messages_out.push(aom);
448 if(m_bone_position_sent == false){
449 m_bone_position_sent = true;
450 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
451 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
452 std::string str = gob_cmd_update_bone_position((*ii).first,
453 (*ii).second.X, (*ii).second.Y);
454 // create message and add to list
455 ActiveObjectMessage aom(getId(), true, str);
456 m_messages_out.push(aom);
460 if(m_attachment_sent == false){
461 m_attachment_sent = true;
462 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
463 // create message and add to list
464 ActiveObjectMessage aom(getId(), true, str);
465 m_messages_out.push(aom);
469 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
471 std::ostringstream os(std::ios::binary);
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 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
508 writeU8(os, message_count);
509 os.write(msg_os.str().c_str(), msg_os.str().size());
513 writeU8(os, 0); // version
514 os<<serializeString(""); // name
515 writeU8(os, 0); // is_player
516 writeV3F1000(os, m_base_position);
517 writeF1000(os, m_yaw);
519 writeU8(os, 2); // number of messages stuffed in here
520 os<<serializeLongString(getPropertyPacket()); // message 1
521 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
528 void LuaEntitySAO::getStaticData(std::string *result) const
530 verbosestream<<FUNCTION_NAME<<std::endl;
531 std::ostringstream os(std::ios::binary);
535 os<<serializeString(m_init_name);
538 std::string state = m_env->getScriptIface()->
539 luaentity_GetStaticdata(m_id);
540 os<<serializeLongString(state);
542 os<<serializeLongString(m_init_state);
547 writeV3F1000(os, m_velocity);
549 writeF1000(os, m_yaw);
553 int LuaEntitySAO::punch(v3f dir,
554 const ToolCapabilities *toolcap,
555 ServerActiveObject *puncher,
556 float time_from_last_punch)
559 // Delete unknown LuaEntities when punched
564 // It's best that attachments cannot be punched
568 ItemStack *punchitem = NULL;
569 ItemStack punchitem_static;
571 punchitem_static = puncher->getWieldedItem();
572 punchitem = &punchitem_static;
575 PunchDamageResult result = getPunchDamage(
579 time_from_last_punch);
581 if (result.did_punch) {
582 setHP(getHP() - result.damage);
584 if (result.damage > 0) {
585 std::string punchername = puncher ? puncher->getDescription() : "nil";
587 actionstream << getDescription() << " punched by "
588 << punchername << ", damage " << result.damage
589 << " hp, health now " << getHP() << " hp" << std::endl;
592 std::string str = gob_cmd_punched(result.damage, getHP());
593 // create message and add to list
594 ActiveObjectMessage aom(getId(), true, str);
595 m_messages_out.push(aom);
601 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
602 time_from_last_punch, toolcap, dir);
607 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
611 // It's best that attachments cannot be clicked
614 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
617 void LuaEntitySAO::setPos(const v3f &pos)
621 m_base_position = pos;
622 sendPosition(false, true);
625 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
629 m_base_position = pos;
631 sendPosition(true, true);
634 float LuaEntitySAO::getMinimumSavedMovement()
639 std::string LuaEntitySAO::getDescription()
641 std::ostringstream os(std::ios::binary);
642 os<<"LuaEntitySAO at (";
643 os<<(m_base_position.X/BS)<<",";
644 os<<(m_base_position.Y/BS)<<",";
645 os<<(m_base_position.Z/BS);
650 void LuaEntitySAO::setHP(s16 hp)
656 s16 LuaEntitySAO::getHP() const
661 void LuaEntitySAO::setVelocity(v3f velocity)
663 m_velocity = velocity;
666 v3f LuaEntitySAO::getVelocity()
671 void LuaEntitySAO::setAcceleration(v3f acceleration)
673 m_acceleration = acceleration;
676 v3f LuaEntitySAO::getAcceleration()
678 return m_acceleration;
681 void LuaEntitySAO::setTextureMod(const std::string &mod)
683 std::string str = gob_cmd_set_texture_mod(mod);
684 m_current_texture_modifier = mod;
685 // create message and add to list
686 ActiveObjectMessage aom(getId(), true, str);
687 m_messages_out.push(aom);
690 std::string LuaEntitySAO::getTextureMod() const
692 return m_current_texture_modifier;
695 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
696 bool select_horiz_by_yawpitch)
698 std::string str = gob_cmd_set_sprite(
702 select_horiz_by_yawpitch
704 // create message and add to list
705 ActiveObjectMessage aom(getId(), true, str);
706 m_messages_out.push(aom);
709 std::string LuaEntitySAO::getName()
714 std::string LuaEntitySAO::getPropertyPacket()
716 return gob_cmd_set_properties(m_prop);
719 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
721 // If the object is attached client-side, don't waste bandwidth sending its position to clients
725 m_last_sent_move_precision = m_base_position.getDistanceFrom(
726 m_last_sent_position);
727 m_last_sent_position_timer = 0;
728 m_last_sent_yaw = m_yaw;
729 m_last_sent_position = m_base_position;
730 m_last_sent_velocity = m_velocity;
731 //m_last_sent_acceleration = m_acceleration;
733 float update_interval = m_env->getSendRecommendedInterval();
735 std::string str = gob_cmd_update_position(
744 // create message and add to list
745 ActiveObjectMessage aom(getId(), false, str);
746 m_messages_out.push(aom);
749 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
753 //update collision box
754 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
755 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
757 toset->MinEdge += m_base_position;
758 toset->MaxEdge += m_base_position;
766 bool LuaEntitySAO::collideWithObjects() const
768 return m_prop.collideWithObjects;
775 // No prototype, PlayerSAO does not need to be deserialized
777 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
778 UnitSAO(env_, v3f(0,0,0)),
783 m_last_good_position(0,0,0),
784 m_time_from_last_punch(0),
785 m_nocheat_dig_pos(32767, 32767, 32767),
786 m_nocheat_dig_time(0),
788 m_position_not_sent(false),
789 m_is_singleplayer(is_singleplayer),
790 m_breath(PLAYER_MAX_BREATH),
795 m_physics_override_speed(1),
796 m_physics_override_jump(1),
797 m_physics_override_gravity(1),
798 m_physics_override_sneak(true),
799 m_physics_override_sneak_glitch(true),
800 m_physics_override_sent(false)
802 assert(m_peer_id != 0); // pre-condition
804 m_prop.hp_max = PLAYER_MAX_HP;
805 m_prop.physical = false;
807 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
808 // start of default appearance, this should be overwritten by LUA
809 m_prop.visual = "upright_sprite";
810 m_prop.visual_size = v2f(1, 2);
811 m_prop.textures.clear();
812 m_prop.textures.push_back("player.png");
813 m_prop.textures.push_back("player_back.png");
814 m_prop.colors.clear();
815 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
816 m_prop.spritediv = v2s16(1,1);
817 // end of default appearance
818 m_prop.is_visible = true;
819 m_prop.makes_footstep_sound = true;
820 m_hp = PLAYER_MAX_HP;
823 PlayerSAO::~PlayerSAO()
825 if(m_inventory != &m_player->inventory)
829 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
834 m_inventory = &m_player->inventory;
837 v3f PlayerSAO::getEyeOffset() const
839 return v3f(0, BS * 1.625f, 0);
842 std::string PlayerSAO::getDescription()
844 return std::string("player ") + m_player->getName();
847 // Called after id has been set and has been inserted in environment
848 void PlayerSAO::addedToEnvironment(u32 dtime_s)
850 ServerActiveObject::addedToEnvironment(dtime_s);
851 ServerActiveObject::setBasePosition(m_base_position);
852 m_player->setPlayerSAO(this);
853 m_player->peer_id = m_peer_id;
854 m_last_good_position = m_base_position;
857 // Called before removing from environment
858 void PlayerSAO::removingFromEnvironment()
860 ServerActiveObject::removingFromEnvironment();
861 if (m_player->getPlayerSAO() == this) {
862 unlinkPlayerSessionAndSave();
863 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
864 it != m_attached_particle_spawners.end(); ++it) {
865 m_env->deleteParticleSpawner(*it, false);
870 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
872 std::ostringstream os(std::ios::binary);
874 if(protocol_version >= 15)
876 writeU8(os, 1); // version
877 os<<serializeString(m_player->getName()); // name
878 writeU8(os, 1); // is_player
879 writeS16(os, getId()); //id
880 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
881 writeF1000(os, m_yaw);
882 writeS16(os, getHP());
884 std::ostringstream msg_os(std::ios::binary);
885 msg_os << serializeLongString(getPropertyPacket()); // message 1
886 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
887 msg_os << serializeLongString(gob_cmd_update_animation(
888 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
889 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
890 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
891 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
892 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
894 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
895 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
896 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
897 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
898 m_physics_override_sneak_glitch)); // 5
899 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
900 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
901 int message_count = 6 + m_bone_position.size();
902 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
903 ii != m_attachment_child_ids.end(); ++ii) {
904 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
906 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
907 obj->getClientInitializationData(protocol_version)));
911 writeU8(os, message_count);
912 os.write(msg_os.str().c_str(), msg_os.str().size());
916 writeU8(os, 0); // version
917 os<<serializeString(m_player->getName()); // name
918 writeU8(os, 1); // is_player
919 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
920 writeF1000(os, m_yaw);
921 writeS16(os, getHP());
922 writeU8(os, 2); // number of messages stuffed in here
923 os<<serializeLongString(getPropertyPacket()); // message 1
924 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
931 void PlayerSAO::getStaticData(std::string *result) const
933 FATAL_ERROR("Deprecated function");
936 void PlayerSAO::step(float dtime, bool send_recommended)
938 if (m_drowning_interval.step(dtime, 2.0)) {
940 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
941 MapNode n = m_env->getMap().getNodeNoEx(p);
942 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
943 // If node generates drown
944 if (c.drowning > 0) {
945 if (m_hp > 0 && m_breath > 0)
946 setBreath(m_breath - 1);
948 // No more breath, damage player
950 setHP(m_hp - c.drowning);
951 m_env->getGameDef()->SendPlayerHPOrDie(this);
956 if (m_breathing_interval.step(dtime, 0.5)) {
958 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
959 MapNode n = m_env->getMap().getNodeNoEx(p);
960 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
961 // If player is alive & no drowning, breath
962 if (m_hp > 0 && c.drowning == 0)
963 setBreath(m_breath + 1);
966 if (!m_properties_sent) {
967 m_properties_sent = true;
968 std::string str = getPropertyPacket();
969 // create message and add to list
970 ActiveObjectMessage aom(getId(), true, str);
971 m_messages_out.push(aom);
974 // If attached, check that our parent is still there. If it isn't, detach.
975 if(m_attachment_parent_id && !isAttached())
977 m_attachment_parent_id = 0;
978 m_attachment_bone = "";
979 m_attachment_position = v3f(0,0,0);
980 m_attachment_rotation = v3f(0,0,0);
981 setBasePosition(m_last_good_position);
982 m_env->getGameDef()->SendMovePlayer(m_peer_id);
985 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
987 // Set lag pool maximums based on estimated lag
988 const float LAG_POOL_MIN = 5.0;
989 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
990 if(lag_pool_max < LAG_POOL_MIN)
991 lag_pool_max = LAG_POOL_MIN;
992 m_dig_pool.setMax(lag_pool_max);
993 m_move_pool.setMax(lag_pool_max);
995 // Increment cheat prevention timers
996 m_dig_pool.add(dtime);
997 m_move_pool.add(dtime);
998 m_time_from_last_punch += dtime;
999 m_nocheat_dig_time += dtime;
1001 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1002 // If the object gets detached this comes into effect automatically from the last known origin
1004 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1005 m_last_good_position = pos;
1006 setBasePosition(pos);
1009 if (!send_recommended)
1012 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1013 if(m_position_not_sent && !isAttached())
1015 m_position_not_sent = false;
1016 float update_interval = m_env->getSendRecommendedInterval();
1018 if(isAttached()) // Just in case we ever do send attachment position too
1019 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1021 pos = m_base_position + v3f(0,BS*1,0);
1022 std::string str = gob_cmd_update_position(
1031 // create message and add to list
1032 ActiveObjectMessage aom(getId(), false, str);
1033 m_messages_out.push(aom);
1036 if (!m_armor_groups_sent) {
1037 m_armor_groups_sent = true;
1038 std::string str = gob_cmd_update_armor_groups(
1040 // create message and add to list
1041 ActiveObjectMessage aom(getId(), true, str);
1042 m_messages_out.push(aom);
1045 if (!m_physics_override_sent) {
1046 m_physics_override_sent = true;
1047 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1048 m_physics_override_jump, m_physics_override_gravity,
1049 m_physics_override_sneak, m_physics_override_sneak_glitch);
1050 // create message and add to list
1051 ActiveObjectMessage aom(getId(), true, str);
1052 m_messages_out.push(aom);
1055 if (!m_animation_sent) {
1056 m_animation_sent = true;
1057 std::string str = gob_cmd_update_animation(
1058 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1059 // create message and add to list
1060 ActiveObjectMessage aom(getId(), true, str);
1061 m_messages_out.push(aom);
1064 if (!m_bone_position_sent) {
1065 m_bone_position_sent = true;
1066 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1067 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1068 std::string str = gob_cmd_update_bone_position((*ii).first,
1069 (*ii).second.X, (*ii).second.Y);
1070 // create message and add to list
1071 ActiveObjectMessage aom(getId(), true, str);
1072 m_messages_out.push(aom);
1076 if (!m_attachment_sent){
1077 m_attachment_sent = true;
1078 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1079 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1080 // create message and add to list
1081 ActiveObjectMessage aom(getId(), true, str);
1082 m_messages_out.push(aom);
1086 void PlayerSAO::setBasePosition(const v3f &position)
1088 if (m_player && position != m_base_position)
1089 m_player->setDirty(true);
1091 // This needs to be ran for attachments too
1092 ServerActiveObject::setBasePosition(position);
1093 m_position_not_sent = true;
1096 void PlayerSAO::setPos(const v3f &pos)
1101 setBasePosition(pos);
1102 // Movement caused by this command is always valid
1103 m_last_good_position = pos;
1104 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1107 void PlayerSAO::moveTo(v3f pos, bool continuous)
1112 setBasePosition(pos);
1113 // Movement caused by this command is always valid
1114 m_last_good_position = pos;
1115 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1118 void PlayerSAO::setYaw(const float yaw)
1120 if (m_player && yaw != m_yaw)
1121 m_player->setDirty(true);
1123 UnitSAO::setYaw(yaw);
1126 void PlayerSAO::setFov(const float fov)
1128 if (m_player && fov != m_fov)
1129 m_player->setDirty(true);
1134 void PlayerSAO::setWantedRange(const s16 range)
1136 if (m_player && range != m_wanted_range)
1137 m_player->setDirty(true);
1139 m_wanted_range = range;
1142 void PlayerSAO::setYawAndSend(const float yaw)
1145 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1148 void PlayerSAO::setPitch(const float pitch)
1150 if (m_player && pitch != m_pitch)
1151 m_player->setDirty(true);
1156 void PlayerSAO::setPitchAndSend(const float pitch)
1159 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1162 int PlayerSAO::punch(v3f dir,
1163 const ToolCapabilities *toolcap,
1164 ServerActiveObject *puncher,
1165 float time_from_last_punch)
1167 // It's best that attachments cannot be punched
1174 // No effect if PvP disabled
1175 if (g_settings->getBool("enable_pvp") == false) {
1176 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1177 std::string str = gob_cmd_punched(0, getHP());
1178 // create message and add to list
1179 ActiveObjectMessage aom(getId(), true, str);
1180 m_messages_out.push(aom);
1185 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1186 time_from_last_punch);
1188 std::string punchername = "nil";
1191 punchername = puncher->getDescription();
1193 PlayerSAO *playersao = m_player->getPlayerSAO();
1195 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1196 puncher, time_from_last_punch, toolcap, dir,
1199 if (!damage_handled) {
1200 setHP(getHP() - hitparams.hp);
1201 } else { // override client prediction
1202 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1203 std::string str = gob_cmd_punched(0, getHP());
1204 // create message and add to list
1205 ActiveObjectMessage aom(getId(), true, str);
1206 m_messages_out.push(aom);
1211 actionstream << "Player " << m_player->getName() << " punched by "
1213 if (!damage_handled) {
1214 actionstream << ", damage " << hitparams.hp << " HP";
1216 actionstream << ", damage handled by lua";
1218 actionstream << std::endl;
1220 return hitparams.wear;
1223 s16 PlayerSAO::readDamage()
1225 s16 damage = m_damage;
1230 void PlayerSAO::setHP(s16 hp)
1234 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1237 hp = oldhp + hp_change;
1241 else if (hp > PLAYER_MAX_HP)
1244 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1251 m_damage += (oldhp - hp);
1253 // Update properties on death
1254 if ((hp == 0) != (oldhp == 0))
1255 m_properties_sent = false;
1258 void PlayerSAO::setBreath(const u16 breath, bool send)
1260 if (m_player && breath != m_breath)
1261 m_player->setDirty(true);
1263 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1266 m_env->getGameDef()->SendPlayerBreath(this);
1269 Inventory* PlayerSAO::getInventory()
1273 const Inventory* PlayerSAO::getInventory() const
1278 InventoryLocation PlayerSAO::getInventoryLocation() const
1280 InventoryLocation loc;
1281 loc.setPlayer(m_player->getName());
1285 std::string PlayerSAO::getWieldList() const
1290 ItemStack PlayerSAO::getWieldedItem() const
1292 const Inventory *inv = getInventory();
1294 const InventoryList *mlist = inv->getList(getWieldList());
1295 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1296 ret = mlist->getItem(getWieldIndex());
1297 if (ret.name.empty()) {
1298 const InventoryList *hlist = inv->getList("hand");
1300 ret = hlist->getItem(0);
1305 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1307 Inventory *inv = getInventory();
1309 InventoryList *mlist = inv->getList(getWieldList());
1311 ItemStack olditem = mlist->getItem(getWieldIndex());
1312 if (olditem.name.empty()) {
1313 InventoryList *hlist = inv->getList("hand");
1315 hlist->changeItem(0, item);
1319 mlist->changeItem(getWieldIndex(), item);
1326 int PlayerSAO::getWieldIndex() const
1328 return m_wield_index;
1331 void PlayerSAO::setWieldIndex(int i)
1333 if(i != m_wield_index) {
1338 // Erase the peer id and make the object for removal
1339 void PlayerSAO::disconnected()
1345 void PlayerSAO::unlinkPlayerSessionAndSave()
1347 assert(m_player->getPlayerSAO() == this);
1348 m_player->peer_id = 0;
1349 m_env->savePlayer(m_player);
1350 m_player->setPlayerSAO(NULL);
1351 m_env->removePlayer(m_player);
1354 std::string PlayerSAO::getPropertyPacket()
1356 m_prop.is_visible = (true);
1357 return gob_cmd_set_properties(m_prop);
1360 bool PlayerSAO::checkMovementCheat()
1362 if (isAttached() || m_is_singleplayer ||
1363 g_settings->getBool("disable_anticheat")) {
1364 m_last_good_position = m_base_position;
1368 bool cheated = false;
1370 Check player movements
1372 NOTE: Actually the server should handle player physics like the
1373 client does and compare player's position to what is calculated
1374 on our side. This is required when eg. players fly due to an
1375 explosion. Altough a node-based alternative might be possible
1376 too, and much more lightweight.
1379 float player_max_speed = 0;
1381 if (m_privs.count("fast") != 0) {
1383 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1386 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1388 // Tolerance. The lag pool does this a bit.
1389 //player_max_speed *= 2.5;
1391 v3f diff = (m_base_position - m_last_good_position);
1392 float d_vert = diff.Y;
1394 float d_horiz = diff.getLength();
1395 float required_time = d_horiz / player_max_speed;
1397 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1398 required_time = d_vert / player_max_speed; // Moving upwards
1400 if (m_move_pool.grab(required_time)) {
1401 m_last_good_position = m_base_position;
1403 actionstream << "Player " << m_player->getName()
1404 << " moved too fast; resetting position"
1406 setBasePosition(m_last_good_position);
1412 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1414 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1415 toset->MinEdge += m_base_position;
1416 toset->MaxEdge += m_base_position;