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)
111 // Initialize something to armor groups
112 m_armor_groups["fleshy"] = 100;
115 bool UnitSAO::isAttached() const
117 if (!m_attachment_parent_id)
119 // Check if the parent still exists
120 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
126 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
128 m_armor_groups = armor_groups;
129 m_armor_groups_sent = false;
132 const ItemGroupList &UnitSAO::getArmorGroups()
134 return m_armor_groups;
137 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
139 // store these so they can be updated to clients
140 m_animation_range = frame_range;
141 m_animation_speed = frame_speed;
142 m_animation_blend = frame_blend;
143 m_animation_loop = frame_loop;
144 m_animation_sent = false;
147 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
149 *frame_range = m_animation_range;
150 *frame_speed = m_animation_speed;
151 *frame_blend = m_animation_blend;
152 *frame_loop = m_animation_loop;
155 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
157 // store these so they can be updated to clients
158 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
159 m_bone_position_sent = false;
162 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
164 *position = m_bone_position[bone].X;
165 *rotation = m_bone_position[bone].Y;
168 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
170 // Attachments need to be handled on both the server and client.
171 // If we just attach on the server, we can only copy the position of the parent. Attachments
172 // are still sent to clients at an interval so players might see them lagging, plus we can't
173 // read and attach to skeletal bones.
174 // If we just attach on the client, the server still sees the child at its original location.
175 // This breaks some things so we also give the server the most accurate representation
176 // even if players only see the client changes.
178 m_attachment_parent_id = parent_id;
179 m_attachment_bone = bone;
180 m_attachment_position = position;
181 m_attachment_rotation = rotation;
182 m_attachment_sent = false;
185 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
188 *parent_id = m_attachment_parent_id;
189 *bone = m_attachment_bone;
190 *position = m_attachment_position;
191 *rotation = m_attachment_rotation;
194 void UnitSAO::addAttachmentChild(int child_id)
196 m_attachment_child_ids.insert(child_id);
199 void UnitSAO::removeAttachmentChild(int child_id)
201 m_attachment_child_ids.erase(child_id);
204 const std::unordered_set<int> &UnitSAO::getAttachmentChildIds()
206 return m_attachment_child_ids;
209 ObjectProperties* UnitSAO::accessObjectProperties()
214 void UnitSAO::notifyObjectPropertiesModified()
216 m_properties_sent = false;
223 // Prototype (registers item for deserialization)
224 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
226 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
227 const std::string &name, const std::string &state):
232 // Only register type if no environment supplied
234 ServerActiveObject::registerType(getType(), create);
239 LuaEntitySAO::~LuaEntitySAO()
242 m_env->getScriptIface()->luaentity_Remove(m_id);
245 for (std::unordered_set<u32>::iterator it = m_attached_particle_spawners.begin();
246 it != m_attached_particle_spawners.end(); ++it) {
247 m_env->deleteParticleSpawner(*it, false);
251 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
253 ServerActiveObject::addedToEnvironment(dtime_s);
255 // Create entity from name
256 m_registered = m_env->getScriptIface()->
257 luaentity_Add(m_id, m_init_name.c_str());
261 m_env->getScriptIface()->
262 luaentity_GetProperties(m_id, &m_prop);
263 // Initialize HP from properties
264 m_hp = m_prop.hp_max;
265 // Activate entity, supplying serialized state
266 m_env->getScriptIface()->
267 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
269 m_prop.infotext = m_init_name;
273 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
274 const std::string &data)
282 std::istringstream is(data, std::ios::binary);
284 u8 version = readU8(is);
285 // check if version is supported
287 name = deSerializeString(is);
288 state = deSerializeLongString(is);
290 else if(version == 1){
291 name = deSerializeString(is);
292 state = deSerializeLongString(is);
294 velocity = readV3F1000(is);
299 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
300 <<state<<"\")"<<std::endl;
301 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
303 sao->m_velocity = velocity;
308 void LuaEntitySAO::step(float dtime, bool send_recommended)
310 if(!m_properties_sent)
312 m_properties_sent = true;
313 std::string str = getPropertyPacket();
314 // create message and add to list
315 ActiveObjectMessage aom(getId(), true, str);
316 m_messages_out.push(aom);
319 // If attached, check that our parent is still there. If it isn't, detach.
320 if(m_attachment_parent_id && !isAttached())
322 m_attachment_parent_id = 0;
323 m_attachment_bone = "";
324 m_attachment_position = v3f(0,0,0);
325 m_attachment_rotation = v3f(0,0,0);
326 sendPosition(false, true);
329 m_last_sent_position_timer += dtime;
331 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
332 // If the object gets detached this comes into effect automatically from the last known origin
335 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
336 m_base_position = pos;
337 m_velocity = v3f(0,0,0);
338 m_acceleration = v3f(0,0,0);
343 aabb3f box = m_prop.collisionbox;
346 collisionMoveResult moveresult;
347 f32 pos_max_d = BS*0.25; // Distance per iteration
348 v3f p_pos = m_base_position;
349 v3f p_velocity = m_velocity;
350 v3f p_acceleration = m_acceleration;
351 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
352 pos_max_d, box, m_prop.stepheight, dtime,
353 &p_pos, &p_velocity, p_acceleration,
354 this, m_prop.collideWithObjects);
357 m_base_position = p_pos;
358 m_velocity = p_velocity;
359 m_acceleration = p_acceleration;
361 m_base_position += dtime * m_velocity + 0.5 * dtime
362 * dtime * m_acceleration;
363 m_velocity += dtime * m_acceleration;
366 if((m_prop.automatic_face_movement_dir) &&
367 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
369 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
370 + m_prop.automatic_face_movement_dir_offset;
371 float max_rotation_delta =
372 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
374 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
375 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
377 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
385 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
388 // Remove LuaEntity beyond terrain edges
390 ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
392 if (!m_pending_deactivation &&
393 map->saoPositionOverLimit(m_base_position)) {
394 infostream << "Remove SAO " << m_id << "(" << m_init_name
395 << "), outside of limits" << std::endl;
396 m_pending_deactivation = true;
402 if(send_recommended == false)
407 // TODO: force send when acceleration changes enough?
408 float minchange = 0.2*BS;
409 if(m_last_sent_position_timer > 1.0){
411 } else if(m_last_sent_position_timer > 0.2){
414 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
415 move_d += m_last_sent_move_precision;
416 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
417 if(move_d > minchange || vel_d > minchange ||
418 fabs(m_yaw - m_last_sent_yaw) > 1.0){
419 sendPosition(true, false);
423 if(m_armor_groups_sent == false){
424 m_armor_groups_sent = true;
425 std::string str = gob_cmd_update_armor_groups(
427 // create message and add to list
428 ActiveObjectMessage aom(getId(), true, str);
429 m_messages_out.push(aom);
432 if(m_animation_sent == false){
433 m_animation_sent = true;
434 std::string str = gob_cmd_update_animation(
435 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
436 // create message and add to list
437 ActiveObjectMessage aom(getId(), true, str);
438 m_messages_out.push(aom);
441 if(m_bone_position_sent == false){
442 m_bone_position_sent = true;
443 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
444 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
445 std::string str = gob_cmd_update_bone_position((*ii).first,
446 (*ii).second.X, (*ii).second.Y);
447 // create message and add to list
448 ActiveObjectMessage aom(getId(), true, str);
449 m_messages_out.push(aom);
453 if(m_attachment_sent == false){
454 m_attachment_sent = true;
455 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
456 // create message and add to list
457 ActiveObjectMessage aom(getId(), true, str);
458 m_messages_out.push(aom);
462 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
464 std::ostringstream os(std::ios::binary);
467 writeU8(os, 1); // version
468 os << serializeString(""); // name
469 writeU8(os, 0); // is_player
470 writeS16(os, getId()); //id
471 writeV3F1000(os, m_base_position);
472 writeF1000(os, m_yaw);
475 std::ostringstream msg_os(std::ios::binary);
476 msg_os << serializeLongString(getPropertyPacket()); // message 1
477 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
478 msg_os << serializeLongString(gob_cmd_update_animation(
479 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
480 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
481 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
482 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
483 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
485 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
486 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
487 int message_count = 4 + m_bone_position.size();
488 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
489 (ii != m_attachment_child_ids.end()); ++ii) {
490 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
492 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
493 obj->getClientInitializationData(protocol_version)));
497 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
500 writeU8(os, message_count);
501 os.write(msg_os.str().c_str(), msg_os.str().size());
507 void LuaEntitySAO::getStaticData(std::string *result) const
509 verbosestream<<FUNCTION_NAME<<std::endl;
510 std::ostringstream os(std::ios::binary);
514 os<<serializeString(m_init_name);
517 std::string state = m_env->getScriptIface()->
518 luaentity_GetStaticdata(m_id);
519 os<<serializeLongString(state);
521 os<<serializeLongString(m_init_state);
526 writeV3F1000(os, m_velocity);
528 writeF1000(os, m_yaw);
532 int LuaEntitySAO::punch(v3f dir,
533 const ToolCapabilities *toolcap,
534 ServerActiveObject *puncher,
535 float time_from_last_punch)
538 // Delete unknown LuaEntities when punched
543 // It's best that attachments cannot be punched
547 ItemStack *punchitem = NULL;
548 ItemStack punchitem_static;
550 punchitem_static = puncher->getWieldedItem();
551 punchitem = &punchitem_static;
554 PunchDamageResult result = getPunchDamage(
558 time_from_last_punch);
560 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
561 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
563 if (!damage_handled) {
564 if (result.did_punch) {
565 setHP(getHP() - result.damage);
567 if (result.damage > 0) {
568 std::string punchername = puncher ? puncher->getDescription() : "nil";
570 actionstream << getDescription() << " punched by "
571 << punchername << ", damage " << result.damage
572 << " hp, health now " << getHP() << " hp" << std::endl;
575 std::string str = gob_cmd_punched(result.damage, getHP());
576 // create message and add to list
577 ActiveObjectMessage aom(getId(), true, str);
578 m_messages_out.push(aom);
590 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
594 // It's best that attachments cannot be clicked
597 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
600 void LuaEntitySAO::setPos(const v3f &pos)
604 m_base_position = pos;
605 sendPosition(false, true);
608 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
612 m_base_position = pos;
614 sendPosition(true, true);
617 float LuaEntitySAO::getMinimumSavedMovement()
622 std::string LuaEntitySAO::getDescription()
624 std::ostringstream os(std::ios::binary);
625 os<<"LuaEntitySAO at (";
626 os<<(m_base_position.X/BS)<<",";
627 os<<(m_base_position.Y/BS)<<",";
628 os<<(m_base_position.Z/BS);
633 void LuaEntitySAO::setHP(s16 hp)
639 s16 LuaEntitySAO::getHP() const
644 void LuaEntitySAO::setVelocity(v3f velocity)
646 m_velocity = velocity;
649 v3f LuaEntitySAO::getVelocity()
654 void LuaEntitySAO::setAcceleration(v3f acceleration)
656 m_acceleration = acceleration;
659 v3f LuaEntitySAO::getAcceleration()
661 return m_acceleration;
664 void LuaEntitySAO::setTextureMod(const std::string &mod)
666 std::string str = gob_cmd_set_texture_mod(mod);
667 m_current_texture_modifier = mod;
668 // create message and add to list
669 ActiveObjectMessage aom(getId(), true, str);
670 m_messages_out.push(aom);
673 std::string LuaEntitySAO::getTextureMod() const
675 return m_current_texture_modifier;
678 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
679 bool select_horiz_by_yawpitch)
681 std::string str = gob_cmd_set_sprite(
685 select_horiz_by_yawpitch
687 // create message and add to list
688 ActiveObjectMessage aom(getId(), true, str);
689 m_messages_out.push(aom);
692 std::string LuaEntitySAO::getName()
697 std::string LuaEntitySAO::getPropertyPacket()
699 return gob_cmd_set_properties(m_prop);
702 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
704 // If the object is attached client-side, don't waste bandwidth sending its position to clients
708 m_last_sent_move_precision = m_base_position.getDistanceFrom(
709 m_last_sent_position);
710 m_last_sent_position_timer = 0;
711 m_last_sent_yaw = m_yaw;
712 m_last_sent_position = m_base_position;
713 m_last_sent_velocity = m_velocity;
714 //m_last_sent_acceleration = m_acceleration;
716 float update_interval = m_env->getSendRecommendedInterval();
718 std::string str = gob_cmd_update_position(
727 // create message and add to list
728 ActiveObjectMessage aom(getId(), false, str);
729 m_messages_out.push(aom);
732 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
736 //update collision box
737 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
738 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
740 toset->MinEdge += m_base_position;
741 toset->MaxEdge += m_base_position;
749 bool LuaEntitySAO::collideWithObjects() const
751 return m_prop.collideWithObjects;
758 // No prototype, PlayerSAO does not need to be deserialized
760 PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
761 bool is_singleplayer):
762 UnitSAO(env_, v3f(0,0,0)),
765 m_is_singleplayer(is_singleplayer)
767 assert(m_peer_id != 0); // pre-condition
769 m_prop.hp_max = PLAYER_MAX_HP;
770 m_prop.physical = false;
772 m_prop.collisionbox = aabb3f(-0.3f, -1.0f, -0.3f, 0.3f, 0.75f, 0.3f);
773 // start of default appearance, this should be overwritten by LUA
774 m_prop.visual = "upright_sprite";
775 m_prop.visual_size = v2f(1, 2);
776 m_prop.textures.clear();
777 m_prop.textures.push_back("player.png");
778 m_prop.textures.push_back("player_back.png");
779 m_prop.colors.clear();
780 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
781 m_prop.spritediv = v2s16(1,1);
782 // end of default appearance
783 m_prop.is_visible = true;
784 m_prop.makes_footstep_sound = true;
785 m_hp = PLAYER_MAX_HP;
788 PlayerSAO::~PlayerSAO()
790 if(m_inventory != &m_player->inventory)
794 void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
799 m_inventory = &m_player->inventory;
802 v3f PlayerSAO::getEyeOffset() const
804 return v3f(0, BS * 1.625f, 0);
807 std::string PlayerSAO::getDescription()
809 return std::string("player ") + m_player->getName();
812 // Called after id has been set and has been inserted in environment
813 void PlayerSAO::addedToEnvironment(u32 dtime_s)
815 ServerActiveObject::addedToEnvironment(dtime_s);
816 ServerActiveObject::setBasePosition(m_base_position);
817 m_player->setPlayerSAO(this);
818 m_player->peer_id = m_peer_id;
819 m_last_good_position = m_base_position;
822 // Called before removing from environment
823 void PlayerSAO::removingFromEnvironment()
825 ServerActiveObject::removingFromEnvironment();
826 if (m_player->getPlayerSAO() == this) {
827 unlinkPlayerSessionAndSave();
828 for (std::unordered_set<u32>::iterator it = m_attached_particle_spawners.begin();
829 it != m_attached_particle_spawners.end(); ++it) {
830 m_env->deleteParticleSpawner(*it, false);
835 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
837 std::ostringstream os(std::ios::binary);
840 writeU8(os, 1); // version
841 os << serializeString(m_player->getName()); // name
842 writeU8(os, 1); // is_player
843 writeS16(os, getId()); //id
844 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
845 writeF1000(os, m_yaw);
846 writeS16(os, getHP());
848 std::ostringstream msg_os(std::ios::binary);
849 msg_os << serializeLongString(getPropertyPacket()); // message 1
850 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
851 msg_os << serializeLongString(gob_cmd_update_animation(
852 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
853 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
854 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
855 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
856 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
858 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
859 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
860 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
861 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
862 m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5
863 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
864 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
865 int message_count = 6 + m_bone_position.size();
866 for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
867 ii != m_attachment_child_ids.end(); ++ii) {
868 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
870 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
871 obj->getClientInitializationData(protocol_version)));
875 writeU8(os, message_count);
876 os.write(msg_os.str().c_str(), msg_os.str().size());
882 void PlayerSAO::getStaticData(std::string *result) const
884 FATAL_ERROR("Deprecated function");
887 void PlayerSAO::step(float dtime, bool send_recommended)
889 if (m_drowning_interval.step(dtime, 2.0)) {
891 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
892 MapNode n = m_env->getMap().getNodeNoEx(p);
893 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
894 // If node generates drown
895 if (c.drowning > 0 && m_hp > 0) {
897 setBreath(m_breath - 1);
899 // No more breath, damage player
901 setHP(m_hp - c.drowning);
902 m_env->getGameDef()->SendPlayerHPOrDie(this);
907 if (m_breathing_interval.step(dtime, 0.5)) {
909 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
910 MapNode n = m_env->getMap().getNodeNoEx(p);
911 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
912 // If player is alive & no drowning, breath
913 if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0)
914 setBreath(m_breath + 1);
917 if (m_node_hurt_interval.step(dtime, 1.0)) {
918 // Feet, middle and head
919 v3s16 p1 = floatToInt(m_base_position + v3f(0, BS*0.1, 0), BS);
920 MapNode n1 = m_env->getMap().getNodeNoEx(p1);
921 v3s16 p2 = floatToInt(m_base_position + v3f(0, BS*0.8, 0), BS);
922 MapNode n2 = m_env->getMap().getNodeNoEx(p2);
923 v3s16 p3 = floatToInt(m_base_position + v3f(0, BS*1.6, 0), BS);
924 MapNode n3 = m_env->getMap().getNodeNoEx(p3);
926 u32 damage_per_second = 0;
927 damage_per_second = MYMAX(damage_per_second,
928 m_env->getGameDef()->ndef()->get(n1).damage_per_second);
929 damage_per_second = MYMAX(damage_per_second,
930 m_env->getGameDef()->ndef()->get(n2).damage_per_second);
931 damage_per_second = MYMAX(damage_per_second,
932 m_env->getGameDef()->ndef()->get(n3).damage_per_second);
934 if (damage_per_second != 0 && m_hp > 0) {
935 s16 newhp = ((s32) damage_per_second > m_hp ? 0 : m_hp - damage_per_second);
937 m_env->getGameDef()->SendPlayerHPOrDie(this);
941 if (!m_properties_sent) {
942 m_properties_sent = true;
943 std::string str = getPropertyPacket();
944 // create message and add to list
945 ActiveObjectMessage aom(getId(), true, str);
946 m_messages_out.push(aom);
949 // If attached, check that our parent is still there. If it isn't, detach.
950 if(m_attachment_parent_id && !isAttached())
952 m_attachment_parent_id = 0;
953 m_attachment_bone = "";
954 m_attachment_position = v3f(0,0,0);
955 m_attachment_rotation = v3f(0,0,0);
956 setBasePosition(m_last_good_position);
957 m_env->getGameDef()->SendMovePlayer(m_peer_id);
960 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
962 // Set lag pool maximums based on estimated lag
963 const float LAG_POOL_MIN = 5.0;
964 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
965 if(lag_pool_max < LAG_POOL_MIN)
966 lag_pool_max = LAG_POOL_MIN;
967 m_dig_pool.setMax(lag_pool_max);
968 m_move_pool.setMax(lag_pool_max);
970 // Increment cheat prevention timers
971 m_dig_pool.add(dtime);
972 m_move_pool.add(dtime);
973 m_time_from_last_teleport += dtime;
974 m_time_from_last_punch += dtime;
975 m_nocheat_dig_time += dtime;
977 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
978 // If the object gets detached this comes into effect automatically from the last known origin
980 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
981 m_last_good_position = pos;
982 setBasePosition(pos);
985 if (!send_recommended)
988 // If the object is attached client-side, don't waste bandwidth sending its position to clients
989 if(m_position_not_sent && !isAttached())
991 m_position_not_sent = false;
992 float update_interval = m_env->getSendRecommendedInterval();
994 if(isAttached()) // Just in case we ever do send attachment position too
995 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
997 pos = m_base_position + v3f(0,BS*1,0);
998 std::string str = gob_cmd_update_position(
1007 // create message and add to list
1008 ActiveObjectMessage aom(getId(), false, str);
1009 m_messages_out.push(aom);
1012 if (!m_armor_groups_sent) {
1013 m_armor_groups_sent = true;
1014 std::string str = gob_cmd_update_armor_groups(
1016 // create message and add to list
1017 ActiveObjectMessage aom(getId(), true, str);
1018 m_messages_out.push(aom);
1021 if (!m_physics_override_sent) {
1022 m_physics_override_sent = true;
1023 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1024 m_physics_override_jump, m_physics_override_gravity,
1025 m_physics_override_sneak, m_physics_override_sneak_glitch,
1026 m_physics_override_new_move);
1027 // create message and add to list
1028 ActiveObjectMessage aom(getId(), true, str);
1029 m_messages_out.push(aom);
1032 if (!m_animation_sent) {
1033 m_animation_sent = true;
1034 std::string str = gob_cmd_update_animation(
1035 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1036 // create message and add to list
1037 ActiveObjectMessage aom(getId(), true, str);
1038 m_messages_out.push(aom);
1041 if (!m_bone_position_sent) {
1042 m_bone_position_sent = true;
1043 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
1044 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1045 std::string str = gob_cmd_update_bone_position((*ii).first,
1046 (*ii).second.X, (*ii).second.Y);
1047 // create message and add to list
1048 ActiveObjectMessage aom(getId(), true, str);
1049 m_messages_out.push(aom);
1053 if (!m_attachment_sent){
1054 m_attachment_sent = true;
1055 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1056 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1057 // create message and add to list
1058 ActiveObjectMessage aom(getId(), true, str);
1059 m_messages_out.push(aom);
1063 void PlayerSAO::setBasePosition(const v3f &position)
1065 if (m_player && position != m_base_position)
1066 m_player->setDirty(true);
1068 // This needs to be ran for attachments too
1069 ServerActiveObject::setBasePosition(position);
1070 m_position_not_sent = true;
1073 void PlayerSAO::setPos(const v3f &pos)
1078 setBasePosition(pos);
1079 // Movement caused by this command is always valid
1080 m_last_good_position = pos;
1081 m_move_pool.empty();
1082 m_time_from_last_teleport = 0.0;
1083 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1086 void PlayerSAO::moveTo(v3f pos, bool continuous)
1091 setBasePosition(pos);
1092 // Movement caused by this command is always valid
1093 m_last_good_position = pos;
1094 m_move_pool.empty();
1095 m_time_from_last_teleport = 0.0;
1096 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1099 void PlayerSAO::setYaw(const float yaw)
1101 if (m_player && yaw != m_yaw)
1102 m_player->setDirty(true);
1104 UnitSAO::setYaw(yaw);
1107 void PlayerSAO::setFov(const float fov)
1109 if (m_player && fov != m_fov)
1110 m_player->setDirty(true);
1115 void PlayerSAO::setWantedRange(const s16 range)
1117 if (m_player && range != m_wanted_range)
1118 m_player->setDirty(true);
1120 m_wanted_range = range;
1123 void PlayerSAO::setYawAndSend(const float yaw)
1126 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1129 void PlayerSAO::setPitch(const float pitch)
1131 if (m_player && pitch != m_pitch)
1132 m_player->setDirty(true);
1137 void PlayerSAO::setPitchAndSend(const float pitch)
1140 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1143 int PlayerSAO::punch(v3f dir,
1144 const ToolCapabilities *toolcap,
1145 ServerActiveObject *puncher,
1146 float time_from_last_punch)
1148 // It's best that attachments cannot be punched
1155 // No effect if PvP disabled
1156 if (g_settings->getBool("enable_pvp") == false) {
1157 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1158 std::string str = gob_cmd_punched(0, getHP());
1159 // create message and add to list
1160 ActiveObjectMessage aom(getId(), true, str);
1161 m_messages_out.push(aom);
1166 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1167 time_from_last_punch);
1169 std::string punchername = "nil";
1172 punchername = puncher->getDescription();
1174 PlayerSAO *playersao = m_player->getPlayerSAO();
1176 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1177 puncher, time_from_last_punch, toolcap, dir,
1180 if (!damage_handled) {
1181 setHP(getHP() - hitparams.hp);
1182 } else { // override client prediction
1183 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1184 std::string str = gob_cmd_punched(0, getHP());
1185 // create message and add to list
1186 ActiveObjectMessage aom(getId(), true, str);
1187 m_messages_out.push(aom);
1192 actionstream << "Player " << m_player->getName() << " punched by "
1194 if (!damage_handled) {
1195 actionstream << ", damage " << hitparams.hp << " HP";
1197 actionstream << ", damage handled by lua";
1199 actionstream << std::endl;
1201 return hitparams.wear;
1204 s16 PlayerSAO::readDamage()
1206 s16 damage = m_damage;
1211 void PlayerSAO::setHP(s16 hp)
1215 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1218 hp = oldhp + hp_change;
1222 else if (hp > PLAYER_MAX_HP)
1225 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1232 m_damage += (oldhp - hp);
1234 // Update properties on death
1235 if ((hp == 0) != (oldhp == 0))
1236 m_properties_sent = false;
1239 void PlayerSAO::setBreath(const u16 breath, bool send)
1241 if (m_player && breath != m_breath)
1242 m_player->setDirty(true);
1244 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1247 m_env->getGameDef()->SendPlayerBreath(this);
1250 Inventory* PlayerSAO::getInventory()
1254 const Inventory* PlayerSAO::getInventory() const
1259 InventoryLocation PlayerSAO::getInventoryLocation() const
1261 InventoryLocation loc;
1262 loc.setPlayer(m_player->getName());
1266 std::string PlayerSAO::getWieldList() const
1271 ItemStack PlayerSAO::getWieldedItem() const
1273 const Inventory *inv = getInventory();
1275 const InventoryList *mlist = inv->getList(getWieldList());
1276 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1277 ret = mlist->getItem(getWieldIndex());
1281 ItemStack PlayerSAO::getWieldedItemOrHand() const
1283 const Inventory *inv = getInventory();
1285 const InventoryList *mlist = inv->getList(getWieldList());
1286 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1287 ret = mlist->getItem(getWieldIndex());
1288 if (ret.name.empty()) {
1289 const InventoryList *hlist = inv->getList("hand");
1291 ret = hlist->getItem(0);
1296 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1298 Inventory *inv = getInventory();
1300 InventoryList *mlist = inv->getList(getWieldList());
1302 mlist->changeItem(getWieldIndex(), item);
1309 int PlayerSAO::getWieldIndex() const
1311 return m_wield_index;
1314 void PlayerSAO::setWieldIndex(int i)
1316 if(i != m_wield_index) {
1321 // Erase the peer id and make the object for removal
1322 void PlayerSAO::disconnected()
1328 void PlayerSAO::unlinkPlayerSessionAndSave()
1330 assert(m_player->getPlayerSAO() == this);
1331 m_player->peer_id = 0;
1332 m_env->savePlayer(m_player);
1333 m_player->setPlayerSAO(NULL);
1334 m_env->removePlayer(m_player);
1337 std::string PlayerSAO::getPropertyPacket()
1339 m_prop.is_visible = (true);
1340 return gob_cmd_set_properties(m_prop);
1343 bool PlayerSAO::checkMovementCheat()
1345 if (isAttached() || m_is_singleplayer ||
1346 g_settings->getBool("disable_anticheat")) {
1347 m_last_good_position = m_base_position;
1351 bool cheated = false;
1353 Check player movements
1355 NOTE: Actually the server should handle player physics like the
1356 client does and compare player's position to what is calculated
1357 on our side. This is required when eg. players fly due to an
1358 explosion. Altough a node-based alternative might be possible
1359 too, and much more lightweight.
1362 float player_max_speed = 0;
1364 if (m_privs.count("fast") != 0) {
1366 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1369 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1371 // Tolerance. The lag pool does this a bit.
1372 //player_max_speed *= 2.5;
1374 v3f diff = (m_base_position - m_last_good_position);
1375 float d_vert = diff.Y;
1377 float d_horiz = diff.getLength();
1378 float required_time = d_horiz / player_max_speed;
1380 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1381 required_time = d_vert / player_max_speed; // Moving upwards
1383 if (m_move_pool.grab(required_time)) {
1384 m_last_good_position = m_base_position;
1386 const float LAG_POOL_MIN = 5.0;
1387 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1388 lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
1389 if (m_time_from_last_teleport > lag_pool_max) {
1390 actionstream << "Player " << m_player->getName()
1391 << " moved too fast; resetting position"
1395 setBasePosition(m_last_good_position);
1400 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1402 *toset = aabb3f(-0.3f * BS, 0.0f, -0.3f * BS, 0.3f * BS, 1.75f * BS, 0.3f * BS);
1404 toset->MinEdge += m_base_position;
1405 toset->MaxEdge += m_base_position;