3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "collision.h"
23 #include "environment.h"
24 #include "tool.h" // For ToolCapabilities
27 #include "remoteplayer.h"
29 #include "scripting_server.h"
30 #include "genericobject.h"
32 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
38 class TestSAO : public ServerActiveObject
41 TestSAO(ServerEnvironment *env, v3f pos):
42 ServerActiveObject(env, pos),
46 ServerActiveObject::registerType(getType(), create);
48 ActiveObjectType getType() const
49 { return ACTIVEOBJECT_TYPE_TEST; }
51 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
52 const std::string &data)
54 return new TestSAO(env, pos);
57 void step(float dtime, bool send_recommended)
66 m_base_position.Y += dtime * BS * 2;
67 if(m_base_position.Y > 8*BS)
68 m_base_position.Y = 2*BS;
70 if(send_recommended == false)
80 data += itos(0); // 0 = position
82 data += itos(m_base_position.X);
84 data += itos(m_base_position.Y);
86 data += itos(m_base_position.Z);
88 ActiveObjectMessage aom(getId(), false, data);
89 m_messages_out.push(aom);
93 bool getCollisionBox(aabb3f *toset) const { return false; }
94 bool collideWithObjects() const { return false; }
101 // Prototype (registers item for deserialization)
102 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
108 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
109 ServerActiveObject(env, pos),
112 m_properties_sent(true),
113 m_armor_groups_sent(false),
114 m_animation_range(0,0),
115 m_animation_speed(0),
116 m_animation_blend(0),
117 m_animation_loop(true),
118 m_animation_sent(false),
119 m_bone_position_sent(false),
120 m_attachment_parent_id(0),
121 m_attachment_sent(false)
123 // Initialize something to armor groups
124 m_armor_groups["fleshy"] = 100;
127 bool UnitSAO::isAttached() const
129 if (!m_attachment_parent_id)
131 // Check if the parent still exists
132 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
138 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
140 m_armor_groups = armor_groups;
141 m_armor_groups_sent = false;
144 const ItemGroupList &UnitSAO::getArmorGroups()
146 return m_armor_groups;
149 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
151 // store these so they can be updated to clients
152 m_animation_range = frame_range;
153 m_animation_speed = frame_speed;
154 m_animation_blend = frame_blend;
155 m_animation_loop = frame_loop;
156 m_animation_sent = false;
159 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
161 *frame_range = m_animation_range;
162 *frame_speed = m_animation_speed;
163 *frame_blend = m_animation_blend;
164 *frame_loop = m_animation_loop;
167 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
169 // store these so they can be updated to clients
170 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
171 m_bone_position_sent = false;
174 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
176 *position = m_bone_position[bone].X;
177 *rotation = m_bone_position[bone].Y;
180 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
182 // Attachments need to be handled on both the server and client.
183 // If we just attach on the server, we can only copy the position of the parent. Attachments
184 // are still sent to clients at an interval so players might see them lagging, plus we can't
185 // read and attach to skeletal bones.
186 // If we just attach on the client, the server still sees the child at its original location.
187 // This breaks some things so we also give the server the most accurate representation
188 // even if players only see the client changes.
190 m_attachment_parent_id = parent_id;
191 m_attachment_bone = bone;
192 m_attachment_position = position;
193 m_attachment_rotation = rotation;
194 m_attachment_sent = false;
197 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
200 *parent_id = m_attachment_parent_id;
201 *bone = m_attachment_bone;
202 *position = m_attachment_position;
203 *rotation = m_attachment_rotation;
206 void UnitSAO::addAttachmentChild(int child_id)
208 m_attachment_child_ids.insert(child_id);
211 void UnitSAO::removeAttachmentChild(int child_id)
213 m_attachment_child_ids.erase(child_id);
216 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
218 return m_attachment_child_ids;
221 ObjectProperties* UnitSAO::accessObjectProperties()
226 void UnitSAO::notifyObjectPropertiesModified()
228 m_properties_sent = false;
235 // Prototype (registers item for deserialization)
236 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
238 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
239 const std::string &name, const std::string &state):
245 m_acceleration(0,0,0),
247 m_last_sent_position(0,0,0),
248 m_last_sent_velocity(0,0,0),
249 m_last_sent_position_timer(0),
250 m_last_sent_move_precision(0),
251 m_current_texture_modifier("")
253 // Only register type if no environment supplied
255 ServerActiveObject::registerType(getType(), create);
260 LuaEntitySAO::~LuaEntitySAO()
263 m_env->getScriptIface()->luaentity_Remove(m_id);
266 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
267 it != m_attached_particle_spawners.end(); ++it) {
268 m_env->deleteParticleSpawner(*it, false);
272 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
274 ServerActiveObject::addedToEnvironment(dtime_s);
276 // Create entity from name
277 m_registered = m_env->getScriptIface()->
278 luaentity_Add(m_id, m_init_name.c_str());
282 m_env->getScriptIface()->
283 luaentity_GetProperties(m_id, &m_prop);
284 // Initialize HP from properties
285 m_hp = m_prop.hp_max;
286 // Activate entity, supplying serialized state
287 m_env->getScriptIface()->
288 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
290 m_prop.infotext = m_init_name;
294 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
295 const std::string &data)
303 std::istringstream is(data, std::ios::binary);
305 u8 version = readU8(is);
306 // check if version is supported
308 name = deSerializeString(is);
309 state = deSerializeLongString(is);
311 else if(version == 1){
312 name = deSerializeString(is);
313 state = deSerializeLongString(is);
315 velocity = readV3F1000(is);
320 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
321 <<state<<"\")"<<std::endl;
322 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
324 sao->m_velocity = velocity;
329 void LuaEntitySAO::step(float dtime, bool send_recommended)
331 if(!m_properties_sent)
333 m_properties_sent = true;
334 std::string str = getPropertyPacket();
335 // create message and add to list
336 ActiveObjectMessage aom(getId(), true, str);
337 m_messages_out.push(aom);
340 // If attached, check that our parent is still there. If it isn't, detach.
341 if(m_attachment_parent_id && !isAttached())
343 m_attachment_parent_id = 0;
344 m_attachment_bone = "";
345 m_attachment_position = v3f(0,0,0);
346 m_attachment_rotation = v3f(0,0,0);
347 sendPosition(false, true);
350 m_last_sent_position_timer += dtime;
352 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
353 // If the object gets detached this comes into effect automatically from the last known origin
356 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
357 m_base_position = pos;
358 m_velocity = v3f(0,0,0);
359 m_acceleration = v3f(0,0,0);
364 aabb3f box = m_prop.collisionbox;
367 collisionMoveResult moveresult;
368 f32 pos_max_d = BS*0.25; // Distance per iteration
369 v3f p_pos = m_base_position;
370 v3f p_velocity = m_velocity;
371 v3f p_acceleration = m_acceleration;
372 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
373 pos_max_d, box, m_prop.stepheight, dtime,
374 &p_pos, &p_velocity, p_acceleration,
375 this, m_prop.collideWithObjects);
378 m_base_position = p_pos;
379 m_velocity = p_velocity;
380 m_acceleration = p_acceleration;
382 m_base_position += dtime * m_velocity + 0.5 * dtime
383 * dtime * m_acceleration;
384 m_velocity += dtime * m_acceleration;
387 if((m_prop.automatic_face_movement_dir) &&
388 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
390 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
391 + m_prop.automatic_face_movement_dir_offset;
392 float max_rotation_delta =
393 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
395 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
396 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
398 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
406 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
409 // Remove LuaEntity beyond terrain edges
411 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
413 if (!m_pending_deactivation &&
414 map->saoPositionOverLimit(m_base_position)) {
415 infostream << "Remove SAO " << m_id << "(" << m_init_name
416 << "), outside of limits" << std::endl;
417 m_pending_deactivation = true;
423 if(send_recommended == false)
428 // TODO: force send when acceleration changes enough?
429 float minchange = 0.2*BS;
430 if(m_last_sent_position_timer > 1.0){
432 } else if(m_last_sent_position_timer > 0.2){
435 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
436 move_d += m_last_sent_move_precision;
437 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
438 if(move_d > minchange || vel_d > minchange ||
439 fabs(m_yaw - m_last_sent_yaw) > 1.0){
440 sendPosition(true, false);
444 if(m_armor_groups_sent == false){
445 m_armor_groups_sent = true;
446 std::string str = gob_cmd_update_armor_groups(
448 // create message and add to list
449 ActiveObjectMessage aom(getId(), true, str);
450 m_messages_out.push(aom);
453 if(m_animation_sent == false){
454 m_animation_sent = true;
455 std::string str = gob_cmd_update_animation(
456 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
457 // create message and add to list
458 ActiveObjectMessage aom(getId(), true, str);
459 m_messages_out.push(aom);
462 if(m_bone_position_sent == false){
463 m_bone_position_sent = true;
464 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
465 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
466 std::string str = gob_cmd_update_bone_position((*ii).first,
467 (*ii).second.X, (*ii).second.Y);
468 // create message and add to list
469 ActiveObjectMessage aom(getId(), true, str);
470 m_messages_out.push(aom);
474 if(m_attachment_sent == false){
475 m_attachment_sent = true;
476 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
477 // create message and add to list
478 ActiveObjectMessage aom(getId(), true, str);
479 m_messages_out.push(aom);
483 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
485 std::ostringstream os(std::ios::binary);
488 writeU8(os, 1); // version
489 os << serializeString(""); // name
490 writeU8(os, 0); // is_player
491 writeS16(os, getId()); //id
492 writeV3F1000(os, m_base_position);
493 writeF1000(os, m_yaw);
496 std::ostringstream msg_os(std::ios::binary);
497 msg_os << serializeLongString(getPropertyPacket()); // message 1
498 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
499 msg_os << serializeLongString(gob_cmd_update_animation(
500 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
501 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
502 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
503 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
504 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
506 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
507 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
508 int message_count = 4 + m_bone_position.size();
509 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
510 (ii != m_attachment_child_ids.end()); ++ii) {
511 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
513 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
514 obj->getClientInitializationData(protocol_version)));
518 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
521 writeU8(os, message_count);
522 os.write(msg_os.str().c_str(), msg_os.str().size());
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 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
582 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
584 if (!damage_handled) {
585 if (result.did_punch) {
586 setHP(getHP() - result.damage);
588 if (result.damage > 0) {
589 std::string punchername = puncher ? puncher->getDescription() : "nil";
591 actionstream << getDescription() << " punched by "
592 << punchername << ", damage " << result.damage
593 << " hp, health now " << getHP() << " hp" << std::endl;
596 std::string str = gob_cmd_punched(result.damage, getHP());
597 // create message and add to list
598 ActiveObjectMessage aom(getId(), true, str);
599 m_messages_out.push(aom);
611 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
615 // It's best that attachments cannot be clicked
618 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
621 void LuaEntitySAO::setPos(const v3f &pos)
625 m_base_position = pos;
626 sendPosition(false, true);
629 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
633 m_base_position = pos;
635 sendPosition(true, true);
638 float LuaEntitySAO::getMinimumSavedMovement()
643 std::string LuaEntitySAO::getDescription()
645 std::ostringstream os(std::ios::binary);
646 os<<"LuaEntitySAO at (";
647 os<<(m_base_position.X/BS)<<",";
648 os<<(m_base_position.Y/BS)<<",";
649 os<<(m_base_position.Z/BS);
654 void LuaEntitySAO::setHP(s16 hp)
660 s16 LuaEntitySAO::getHP() const
665 void LuaEntitySAO::setVelocity(v3f velocity)
667 m_velocity = velocity;
670 v3f LuaEntitySAO::getVelocity()
675 void LuaEntitySAO::setAcceleration(v3f acceleration)
677 m_acceleration = acceleration;
680 v3f LuaEntitySAO::getAcceleration()
682 return m_acceleration;
685 void LuaEntitySAO::setTextureMod(const std::string &mod)
687 std::string str = gob_cmd_set_texture_mod(mod);
688 m_current_texture_modifier = mod;
689 // create message and add to list
690 ActiveObjectMessage aom(getId(), true, str);
691 m_messages_out.push(aom);
694 std::string LuaEntitySAO::getTextureMod() const
696 return m_current_texture_modifier;
699 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
700 bool select_horiz_by_yawpitch)
702 std::string str = gob_cmd_set_sprite(
706 select_horiz_by_yawpitch
708 // create message and add to list
709 ActiveObjectMessage aom(getId(), true, str);
710 m_messages_out.push(aom);
713 std::string LuaEntitySAO::getName()
718 std::string LuaEntitySAO::getPropertyPacket()
720 return gob_cmd_set_properties(m_prop);
723 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
725 // If the object is attached client-side, don't waste bandwidth sending its position to clients
729 m_last_sent_move_precision = m_base_position.getDistanceFrom(
730 m_last_sent_position);
731 m_last_sent_position_timer = 0;
732 m_last_sent_yaw = m_yaw;
733 m_last_sent_position = m_base_position;
734 m_last_sent_velocity = m_velocity;
735 //m_last_sent_acceleration = m_acceleration;
737 float update_interval = m_env->getSendRecommendedInterval();
739 std::string str = gob_cmd_update_position(
748 // create message and add to list
749 ActiveObjectMessage aom(getId(), false, str);
750 m_messages_out.push(aom);
753 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
757 //update collision box
758 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
759 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
761 toset->MinEdge += m_base_position;
762 toset->MaxEdge += m_base_position;
770 bool LuaEntitySAO::collideWithObjects() const
772 return m_prop.collideWithObjects;
779 // No prototype, PlayerSAO does not need to be deserialized
781 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
782 bool is_singleplayer):
783 UnitSAO(env_, v3f(0,0,0)),
788 m_last_good_position(0,0,0),
789 m_time_from_last_teleport(0),
790 m_time_from_last_punch(0),
791 m_nocheat_dig_pos(32767, 32767, 32767),
792 m_nocheat_dig_time(0),
794 m_position_not_sent(false),
795 m_is_singleplayer(is_singleplayer),
796 m_breath(PLAYER_MAX_BREATH),
800 m_extended_attributes_modified(false),
802 m_physics_override_speed(1),
803 m_physics_override_jump(1),
804 m_physics_override_gravity(1),
805 m_physics_override_sneak(true),
806 m_physics_override_sneak_glitch(false),
807 m_physics_override_new_move(true),
808 m_physics_override_sent(false)
810 assert(m_peer_id != 0); // pre-condition
812 m_prop.hp_max = PLAYER_MAX_HP;
813 m_prop.physical = false;
815 m_prop.collisionbox = aabb3f(-0.3f, -1.0f, -0.3f, 0.3f, 0.75f, 0.3f);
816 // start of default appearance, this should be overwritten by LUA
817 m_prop.visual = "upright_sprite";
818 m_prop.visual_size = v2f(1, 2);
819 m_prop.textures.clear();
820 m_prop.textures.push_back("player.png");
821 m_prop.textures.push_back("player_back.png");
822 m_prop.colors.clear();
823 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
824 m_prop.spritediv = v2s16(1,1);
825 // end of default appearance
826 m_prop.is_visible = true;
827 m_prop.makes_footstep_sound = true;
828 m_hp = PLAYER_MAX_HP;
831 PlayerSAO::~PlayerSAO()
833 if(m_inventory != &m_player->inventory)
837 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
842 m_inventory = &m_player->inventory;
845 v3f PlayerSAO::getEyeOffset() const
847 return v3f(0, BS * 1.625f, 0);
850 std::string PlayerSAO::getDescription()
852 return std::string("player ") + m_player->getName();
855 // Called after id has been set and has been inserted in environment
856 void PlayerSAO::addedToEnvironment(u32 dtime_s)
858 ServerActiveObject::addedToEnvironment(dtime_s);
859 ServerActiveObject::setBasePosition(m_base_position);
860 m_player->setPlayerSAO(this);
861 m_player->peer_id = m_peer_id;
862 m_last_good_position = m_base_position;
865 // Called before removing from environment
866 void PlayerSAO::removingFromEnvironment()
868 ServerActiveObject::removingFromEnvironment();
869 if (m_player->getPlayerSAO() == this) {
870 unlinkPlayerSessionAndSave();
871 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
872 it != m_attached_particle_spawners.end(); ++it) {
873 m_env->deleteParticleSpawner(*it, false);
878 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
880 std::ostringstream os(std::ios::binary);
883 writeU8(os, 1); // version
884 os << serializeString(m_player->getName()); // name
885 writeU8(os, 1); // is_player
886 writeS16(os, getId()); //id
887 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
888 writeF1000(os, m_yaw);
889 writeS16(os, getHP());
891 std::ostringstream msg_os(std::ios::binary);
892 msg_os << serializeLongString(getPropertyPacket()); // message 1
893 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
894 msg_os << serializeLongString(gob_cmd_update_animation(
895 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
896 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
897 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
898 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
899 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
901 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
902 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
903 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
904 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
905 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
906 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
907 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
908 int message_count = 6 + m_bone_position.size();
909 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
910 ii != m_attachment_child_ids.end(); ++ii) {
911 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
913 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
914 obj->getClientInitializationData(protocol_version)));
918 writeU8(os, message_count);
919 os.write(msg_os.str().c_str(), msg_os.str().size());
925 void PlayerSAO::getStaticData(std::string *result) const
927 FATAL_ERROR("Deprecated function");
930 void PlayerSAO::step(float dtime, bool send_recommended)
932 if (m_drowning_interval.step(dtime, 2.0)) {
934 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
935 MapNode n = m_env->getMap().getNodeNoEx(p);
936 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
937 // If node generates drown
938 if (c.drowning > 0 && m_hp > 0) {
940 setBreath(m_breath - 1);
942 // No more breath, damage player
944 setHP(m_hp - c.drowning);
945 m_env->getGameDef()->SendPlayerHPOrDie(this);
950 if (m_breathing_interval.step(dtime, 0.5)) {
952 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
953 MapNode n = m_env->getMap().getNodeNoEx(p);
954 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
955 // If player is alive & no drowning, breath
956 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
957 setBreath(m_breath + 1);
960 if (m_node_hurt_interval.step(dtime, 1.0)) {
961 // Feet, middle and head
962 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
963 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
964 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
965 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
966 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
967 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
969 u32 damage_per_second = 0;
970 damage_per_second = MYMAX(damage_per_second,
971 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
972 damage_per_second = MYMAX(damage_per_second,
973 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
974 damage_per_second = MYMAX(damage_per_second,
975 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
977 if (damage_per_second != 0 && m_hp > 0) {
978 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
980 m_env->getGameDef()->SendPlayerHPOrDie(this);
984 if (!m_properties_sent) {
985 m_properties_sent = true;
986 std::string str = getPropertyPacket();
987 // create message and add to list
988 ActiveObjectMessage aom(getId(), true, str);
989 m_messages_out.push(aom);
992 // If attached, check that our parent is still there. If it isn't, detach.
993 if(m_attachment_parent_id && !isAttached())
995 m_attachment_parent_id = 0;
996 m_attachment_bone = "";
997 m_attachment_position = v3f(0,0,0);
998 m_attachment_rotation = v3f(0,0,0);
999 setBasePosition(m_last_good_position);
1000 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1003 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1005 // Set lag pool maximums based on estimated lag
1006 const float LAG_POOL_MIN = 5.0;
1007 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1008 if(lag_pool_max < LAG_POOL_MIN)
1009 lag_pool_max = LAG_POOL_MIN;
1010 m_dig_pool.setMax(lag_pool_max);
1011 m_move_pool.setMax(lag_pool_max);
1013 // Increment cheat prevention timers
1014 m_dig_pool.add(dtime);
1015 m_move_pool.add(dtime);
1016 m_time_from_last_teleport += dtime;
1017 m_time_from_last_punch += dtime;
1018 m_nocheat_dig_time += dtime;
1020 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1021 // If the object gets detached this comes into effect automatically from the last known origin
1023 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1024 m_last_good_position = pos;
1025 setBasePosition(pos);
1028 if (!send_recommended)
1031 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1032 if(m_position_not_sent && !isAttached())
1034 m_position_not_sent = false;
1035 float update_interval = m_env->getSendRecommendedInterval();
1037 if(isAttached()) // Just in case we ever do send attachment position too
1038 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1040 pos = m_base_position + v3f(0,BS*1,0);
1041 std::string str = gob_cmd_update_position(
1050 // create message and add to list
1051 ActiveObjectMessage aom(getId(), false, str);
1052 m_messages_out.push(aom);
1055 if (!m_armor_groups_sent) {
1056 m_armor_groups_sent = true;
1057 std::string str = gob_cmd_update_armor_groups(
1059 // create message and add to list
1060 ActiveObjectMessage aom(getId(), true, str);
1061 m_messages_out.push(aom);
1064 if (!m_physics_override_sent) {
1065 m_physics_override_sent = true;
1066 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1067 m_physics_override_jump, m_physics_override_gravity,
1068 m_physics_override_sneak, m_physics_override_sneak_glitch,
1069 m_physics_override_new_move);
1070 // create message and add to list
1071 ActiveObjectMessage aom(getId(), true, str);
1072 m_messages_out.push(aom);
1075 if (!m_animation_sent) {
1076 m_animation_sent = true;
1077 std::string str = gob_cmd_update_animation(
1078 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1079 // create message and add to list
1080 ActiveObjectMessage aom(getId(), true, str);
1081 m_messages_out.push(aom);
1084 if (!m_bone_position_sent) {
1085 m_bone_position_sent = true;
1086 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1087 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1088 std::string str = gob_cmd_update_bone_position((*ii).first,
1089 (*ii).second.X, (*ii).second.Y);
1090 // create message and add to list
1091 ActiveObjectMessage aom(getId(), true, str);
1092 m_messages_out.push(aom);
1096 if (!m_attachment_sent){
1097 m_attachment_sent = true;
1098 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1099 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1100 // create message and add to list
1101 ActiveObjectMessage aom(getId(), true, str);
1102 m_messages_out.push(aom);
1106 void PlayerSAO::setBasePosition(const v3f &position)
1108 if (m_player && position != m_base_position)
1109 m_player->setDirty(true);
1111 // This needs to be ran for attachments too
1112 ServerActiveObject::setBasePosition(position);
1113 m_position_not_sent = true;
1116 void PlayerSAO::setPos(const v3f &pos)
1121 setBasePosition(pos);
1122 // Movement caused by this command is always valid
1123 m_last_good_position = pos;
1124 m_move_pool.empty();
1125 m_time_from_last_teleport = 0.0;
1126 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1129 void PlayerSAO::moveTo(v3f pos, bool continuous)
1134 setBasePosition(pos);
1135 // Movement caused by this command is always valid
1136 m_last_good_position = pos;
1137 m_move_pool.empty();
1138 m_time_from_last_teleport = 0.0;
1139 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1142 void PlayerSAO::setYaw(const float yaw)
1144 if (m_player && yaw != m_yaw)
1145 m_player->setDirty(true);
1147 UnitSAO::setYaw(yaw);
1150 void PlayerSAO::setFov(const float fov)
1152 if (m_player && fov != m_fov)
1153 m_player->setDirty(true);
1158 void PlayerSAO::setWantedRange(const s16 range)
1160 if (m_player && range != m_wanted_range)
1161 m_player->setDirty(true);
1163 m_wanted_range = range;
1166 void PlayerSAO::setYawAndSend(const float yaw)
1169 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1172 void PlayerSAO::setPitch(const float pitch)
1174 if (m_player && pitch != m_pitch)
1175 m_player->setDirty(true);
1180 void PlayerSAO::setPitchAndSend(const float pitch)
1183 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1186 int PlayerSAO::punch(v3f dir,
1187 const ToolCapabilities *toolcap,
1188 ServerActiveObject *puncher,
1189 float time_from_last_punch)
1191 // It's best that attachments cannot be punched
1198 // No effect if PvP disabled
1199 if (g_settings->getBool("enable_pvp") == false) {
1200 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1201 std::string str = gob_cmd_punched(0, getHP());
1202 // create message and add to list
1203 ActiveObjectMessage aom(getId(), true, str);
1204 m_messages_out.push(aom);
1209 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1210 time_from_last_punch);
1212 std::string punchername = "nil";
1215 punchername = puncher->getDescription();
1217 PlayerSAO *playersao = m_player->getPlayerSAO();
1219 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1220 puncher, time_from_last_punch, toolcap, dir,
1223 if (!damage_handled) {
1224 setHP(getHP() - hitparams.hp);
1225 } else { // override client prediction
1226 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1227 std::string str = gob_cmd_punched(0, getHP());
1228 // create message and add to list
1229 ActiveObjectMessage aom(getId(), true, str);
1230 m_messages_out.push(aom);
1235 actionstream << "Player " << m_player->getName() << " punched by "
1237 if (!damage_handled) {
1238 actionstream << ", damage " << hitparams.hp << " HP";
1240 actionstream << ", damage handled by lua";
1242 actionstream << std::endl;
1244 return hitparams.wear;
1247 s16 PlayerSAO::readDamage()
1249 s16 damage = m_damage;
1254 void PlayerSAO::setHP(s16 hp)
1258 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1261 hp = oldhp + hp_change;
1265 else if (hp > PLAYER_MAX_HP)
1268 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1275 m_damage += (oldhp - hp);
1277 // Update properties on death
1278 if ((hp == 0) != (oldhp == 0))
1279 m_properties_sent = false;
1282 void PlayerSAO::setBreath(const u16 breath, bool send)
1284 if (m_player && breath != m_breath)
1285 m_player->setDirty(true);
1287 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1290 m_env->getGameDef()->SendPlayerBreath(this);
1293 Inventory* PlayerSAO::getInventory()
1297 const Inventory* PlayerSAO::getInventory() const
1302 InventoryLocation PlayerSAO::getInventoryLocation() const
1304 InventoryLocation loc;
1305 loc.setPlayer(m_player->getName());
1309 std::string PlayerSAO::getWieldList() const
1314 ItemStack PlayerSAO::getWieldedItem() const
1316 const Inventory *inv = getInventory();
1318 const InventoryList *mlist = inv->getList(getWieldList());
1319 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1320 ret = mlist->getItem(getWieldIndex());
1324 ItemStack PlayerSAO::getWieldedItemOrHand() const
1326 const Inventory *inv = getInventory();
1328 const InventoryList *mlist = inv->getList(getWieldList());
1329 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1330 ret = mlist->getItem(getWieldIndex());
1331 if (ret.name.empty()) {
1332 const InventoryList *hlist = inv->getList("hand");
1334 ret = hlist->getItem(0);
1339 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1341 Inventory *inv = getInventory();
1343 InventoryList *mlist = inv->getList(getWieldList());
1345 mlist->changeItem(getWieldIndex(), item);
1352 int PlayerSAO::getWieldIndex() const
1354 return m_wield_index;
1357 void PlayerSAO::setWieldIndex(int i)
1359 if(i != m_wield_index) {
1364 // Erase the peer id and make the object for removal
1365 void PlayerSAO::disconnected()
1371 void PlayerSAO::unlinkPlayerSessionAndSave()
1373 assert(m_player->getPlayerSAO() == this);
1374 m_player->peer_id = 0;
1375 m_env->savePlayer(m_player);
1376 m_player->setPlayerSAO(NULL);
1377 m_env->removePlayer(m_player);
1380 std::string PlayerSAO::getPropertyPacket()
1382 m_prop.is_visible = (true);
1383 return gob_cmd_set_properties(m_prop);
1386 bool PlayerSAO::checkMovementCheat()
1388 if (isAttached() || m_is_singleplayer ||
1389 g_settings->getBool("disable_anticheat")) {
1390 m_last_good_position = m_base_position;
1394 bool cheated = false;
1396 Check player movements
1398 NOTE: Actually the server should handle player physics like the
1399 client does and compare player's position to what is calculated
1400 on our side. This is required when eg. players fly due to an
1401 explosion. Altough a node-based alternative might be possible
1402 too, and much more lightweight.
1405 float player_max_speed = 0;
1407 if (m_privs.count("fast") != 0) {
1409 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1412 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1414 // Tolerance. The lag pool does this a bit.
1415 //player_max_speed *= 2.5;
1417 v3f diff = (m_base_position - m_last_good_position);
1418 float d_vert = diff.Y;
1420 float d_horiz = diff.getLength();
1421 float required_time = d_horiz / player_max_speed;
1423 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1424 required_time = d_vert / player_max_speed; // Moving upwards
1426 if (m_move_pool.grab(required_time)) {
1427 m_last_good_position = m_base_position;
1429 const float LAG_POOL_MIN = 5.0;
1430 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1431 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1432 if (m_time_from_last_teleport > lag_pool_max) {
1433 actionstream << "Player " << m_player->getName()
1434 << " moved too fast; resetting position"
1438 setBasePosition(m_last_good_position);
1443 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1445 *toset = aabb3f(-0.3f * BS, 0.0f, -0.3f * BS, 0.3f * BS, 1.75f * BS, 0.3f * BS);
1447 toset->MinEdge += m_base_position;
1448 toset->MaxEdge += m_base_position;