X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fcontent_sao.cpp;h=4ee92f4d34255408e83f1159cfadb73f359fb26b;hb=b8ba6318d6ed9edd59455674d39ff1d5018393df;hp=568e4b1da67cf7681636de9a07ec532fc5255776;hpb=989aba1966373885d4cac306daea25e102f8d68d;p=minetest.git diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 568e4b1da..4ee92f4d3 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1,18 +1,18 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ @@ -26,8 +26,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" // For compressZlib #include "tool.h" // For ToolCapabilities #include "gamedef.h" +#include "player.h" +#include "scripting_game.h" +#include "genericobject.h" +#include "util/serialize.h" +#include "util/mathconstants.h" -core::map ServerActiveObject::m_types; +std::map ServerActiveObject::m_types; /* DummyLoadSAO @@ -60,6 +65,14 @@ class DummyLoadSAO : public ServerActiveObject infostream<<"DummyLoadSAO step"<getGameDef(); - moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, - pos_max_d, box, dtime, pos_f, m_speed_f); + v3f accel_f = v3f(0,0,0); + f32 stepheight = 0; + moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), + pos_max_d, box, stepheight, dtime, + pos_f, m_speed_f, accel_f); if(send_recommended == false) return; @@ -235,7 +261,7 @@ class ItemSAO : public ServerActiveObject } } - std::string getClientInitializationData() + std::string getClientInitializationData(u16 protocol_version) { std::ostringstream os(std::ios::binary); // version @@ -282,13 +308,6 @@ class ItemSAO : public ServerActiveObject ServerActiveObject *puncher, float time_from_last_punch) { - // Directly delete item in creative mode - if(g_settings->getBool("creative_mode") == true) - { - m_removed = true; - return 0; - } - // Take item into inventory ItemStack item = createItemStack(); Inventory *inv = puncher->getInventory(); @@ -311,6 +330,13 @@ class ItemSAO : public ServerActiveObject return 0; } + bool getCollisionBox(aabb3f *toset) { + return false; + } + + bool collideWithObjects() { + return false; + } private: std::string m_itemstring; @@ -324,7 +350,7 @@ class ItemSAO : public ServerActiveObject ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), ""); ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos, - const std::string itemstring) + const std::string &itemstring) { return new ItemSAO(env, pos, itemstring); } @@ -333,9 +359,6 @@ ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos, LuaEntitySAO */ -#include "scriptapi.h" -#include "luaentity_common.h" - // Prototype (registers item for deserialization) LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", ""); @@ -345,17 +368,23 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, m_init_name(name), m_init_state(state), m_registered(false), - m_prop(new LuaEntityProperties), m_hp(-1), m_velocity(0,0,0), m_acceleration(0,0,0), m_yaw(0), + m_properties_sent(true), m_last_sent_yaw(0), m_last_sent_position(0,0,0), m_last_sent_velocity(0,0,0), m_last_sent_position_timer(0), m_last_sent_move_precision(0), - m_armor_groups_sent(false) + m_armor_groups_sent(false), + m_animation_speed(0), + m_animation_blend(0), + m_animation_sent(false), + m_bone_position_sent(false), + m_attachment_parent_id(0), + m_attachment_sent(false) { // Only register type if no environment supplied if(env == NULL){ @@ -364,59 +393,60 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, } // Initialize something to armor groups - m_armor_groups["fleshy"] = 3; - m_armor_groups["snappy"] = 2; + m_armor_groups["fleshy"] = 100; } LuaEntitySAO::~LuaEntitySAO() { if(m_registered){ - lua_State *L = m_env->getLua(); - scriptapi_luaentity_rm(L, m_id); + m_env->getScriptIface()->luaentity_Remove(m_id); } - delete m_prop; } -void LuaEntitySAO::addedToEnvironment() +void LuaEntitySAO::addedToEnvironment(u32 dtime_s) { - ServerActiveObject::addedToEnvironment(); + ServerActiveObject::addedToEnvironment(dtime_s); - // Create entity from name and state - lua_State *L = m_env->getLua(); - m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str(), - m_init_state.c_str()); + // Create entity from name + m_registered = m_env->getScriptIface()-> + luaentity_Add(m_id, m_init_name.c_str()); if(m_registered){ // Get properties - scriptapi_luaentity_get_properties(L, m_id, m_prop); + m_env->getScriptIface()-> + luaentity_GetProperties(m_id, &m_prop); + // Initialize HP from properties + m_hp = m_prop.hp_max; + // Activate entity, supplying serialized state + m_env->getScriptIface()-> + luaentity_Activate(m_id, m_init_state.c_str(), dtime_s); } } ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos, const std::string &data) { - std::istringstream is(data, std::ios::binary); - // read version - u8 version = readU8(is); std::string name; std::string state; s16 hp = 1; v3f velocity; float yaw = 0; - // check if version is supported - if(version == 0){ - name = deSerializeString(is); - state = deSerializeLongString(is); - } - else if(version == 1){ - name = deSerializeString(is); - state = deSerializeLongString(is); - hp = readS16(is); - velocity = readV3F1000(is); - yaw = readF1000(is); - } - else{ - return NULL; + if(data != ""){ + std::istringstream is(data, std::ios::binary); + // read version + u8 version = readU8(is); + // check if version is supported + if(version == 0){ + name = deSerializeString(is); + state = deSerializeLongString(is); + } + else if(version == 1){ + name = deSerializeString(is); + state = deSerializeLongString(is); + hp = readS16(is); + velocity = readV3F1000(is); + yaw = readF1000(is); + } } // create object infostream<<"LuaEntitySAO::create(name=\""<getActiveObject(m_attachment_parent_id); + if(obj) + return true; + return false; +} + void LuaEntitySAO::step(float dtime, bool send_recommended) { + if(!m_properties_sent) + { + m_properties_sent = true; + std::string str = getPropertyPacket(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + + // If attached, check that our parent is still there. If it isn't, detach. + if(m_attachment_parent_id && !isAttached()) + { + m_attachment_parent_id = 0; + m_attachment_bone = ""; + m_attachment_position = v3f(0,0,0); + m_attachment_rotation = v3f(0,0,0); + sendPosition(false, true); + } + m_last_sent_position_timer += dtime; - - if(m_prop->physical){ - core::aabbox3d box = m_prop->collisionbox; - box.MinEdge *= BS; - box.MaxEdge *= BS; - collisionMoveResult moveresult; - f32 pos_max_d = BS*0.25; // Distance per iteration - v3f p_pos = getBasePosition(); - v3f p_velocity = m_velocity; - IGameDef *gamedef = m_env->getGameDef(); - moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, - pos_max_d, box, dtime, p_pos, p_velocity); - // Apply results - setBasePosition(p_pos); - m_velocity = p_velocity; - - m_velocity += dtime * m_acceleration; - } else { - m_base_position += dtime * m_velocity + 0.5 * dtime - * dtime * m_acceleration; - m_velocity += dtime * m_acceleration; + + // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally + // If the object gets detached this comes into effect automatically from the last known origin + if(isAttached()) + { + v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + m_base_position = pos; + m_velocity = v3f(0,0,0); + m_acceleration = v3f(0,0,0); + } + else + { + if(m_prop.physical){ + core::aabbox3d box = m_prop.collisionbox; + box.MinEdge *= BS; + box.MaxEdge *= BS; + collisionMoveResult moveresult; + f32 pos_max_d = BS*0.25; // Distance per iteration + v3f p_pos = m_base_position; + v3f p_velocity = m_velocity; + v3f p_acceleration = m_acceleration; + moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), + pos_max_d, box, m_prop.stepheight, dtime, + p_pos, p_velocity, p_acceleration, + this, m_prop.collideWithObjects); + + // Apply results + m_base_position = p_pos; + m_velocity = p_velocity; + m_acceleration = p_acceleration; + } else { + m_base_position += dtime * m_velocity + 0.5 * dtime + * dtime * m_acceleration; + m_velocity += dtime * m_acceleration; + } + + if((m_prop.automatic_face_movement_dir) && + (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)){ + m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset; + } } if(m_registered){ - lua_State *L = m_env->getLua(); - scriptapi_luaentity_step(L, m_id, dtime); + m_env->getScriptIface()->luaentity_Step(m_id, dtime); } if(send_recommended == false) return; - - // TODO: force send when acceleration changes enough? - float minchange = 0.2*BS; - if(m_last_sent_position_timer > 1.0){ - minchange = 0.01*BS; - } else if(m_last_sent_position_timer > 0.2){ - minchange = 0.05*BS; - } - float move_d = m_base_position.getDistanceFrom(m_last_sent_position); - move_d += m_last_sent_move_precision; - float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity); - if(move_d > minchange || vel_d > minchange || - fabs(m_yaw - m_last_sent_yaw) > 1.0){ - sendPosition(true, false); + + if(!isAttached()) + { + // TODO: force send when acceleration changes enough? + float minchange = 0.2*BS; + if(m_last_sent_position_timer > 1.0){ + minchange = 0.01*BS; + } else if(m_last_sent_position_timer > 0.2){ + minchange = 0.05*BS; + } + float move_d = m_base_position.getDistanceFrom(m_last_sent_position); + move_d += m_last_sent_move_precision; + float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity); + if(move_d > minchange || vel_d > minchange || + fabs(m_yaw - m_last_sent_yaw) > 1.0){ + sendPosition(true, false); + } } if(m_armor_groups_sent == false){ m_armor_groups_sent = true; - std::ostringstream os(std::ios::binary); - writeU8(os, LUAENTITY_CMD_UPDATE_ARMOR_GROUPS); - writeU16(os, m_armor_groups.size()); - for(ItemGroupList::const_iterator i = m_armor_groups.begin(); - i != m_armor_groups.end(); i++){ - os<first); - writeS16(os, i->second); + std::string str = gob_cmd_update_armor_groups( + m_armor_groups); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + + if(m_animation_sent == false){ + m_animation_sent = true; + std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + + if(m_bone_position_sent == false){ + m_bone_position_sent = true; + for(std::map >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); } + } + + if(m_attachment_sent == false){ + m_attachment_sent = true; + std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation); // create message and add to list - ActiveObjectMessage aom(getId(), true, os.str()); + ActiveObjectMessage aom(getId(), true, str); m_messages_out.push_back(aom); } } -std::string LuaEntitySAO::getClientInitializationData() +std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) { std::ostringstream os(std::ios::binary); - // version - writeU8(os, 1); - // pos - writeV3F1000(os, m_base_position); - // yaw - writeF1000(os, m_yaw); - // hp - writeS16(os, m_hp); - // properties - std::ostringstream prop_os(std::ios::binary); - m_prop->serialize(prop_os); - os<= 14) + { + writeU8(os, 1); // version + os< >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + os<getLua(); - std::string state = scriptapi_luaentity_get_staticdata(L, m_id); + std::string state = m_env->getScriptIface()-> + luaentity_GetStaticdata(m_id); os<getDescription(); + actionstream<getDescription()<<", damage "<getLua(); - scriptapi_luaentity_punch(L, m_id, puncher, + m_env->getScriptIface()->luaentity_Punch(m_id, puncher, time_from_last_punch, toolcap, dir); return result.wear; @@ -594,29 +722,24 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker) { if(!m_registered) return; - lua_State *L = m_env->getLua(); - scriptapi_luaentity_rightclick(L, m_id, clicker); -} - -void LuaEntitySAO::setHP(s16 hp) -{ - if(hp < 0) hp = 0; - m_hp = hp; -} - -s16 LuaEntitySAO::getHP() -{ - return m_hp; + // It's best that attachments cannot be clicked + if(isAttached()) + return; + m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker); } void LuaEntitySAO::setPos(v3f pos) { + if(isAttached()) + return; m_base_position = pos; sendPosition(false, true); } void LuaEntitySAO::moveTo(v3f pos, bool continuous) { + if(isAttached()) + return; m_base_position = pos; if(!continuous) sendPosition(true, true); @@ -635,7 +758,65 @@ std::string LuaEntitySAO::getDescription() os<<(m_base_position.Y/BS)<<","; os<<(m_base_position.Z/BS); os<<")"; - return std::string("LuaEntitySAO"); + return os.str(); +} + +void LuaEntitySAO::setHP(s16 hp) +{ + if(hp < 0) hp = 0; + m_hp = hp; +} + +s16 LuaEntitySAO::getHP() const +{ + return m_hp; +} + +void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups) +{ + m_armor_groups = armor_groups; + m_armor_groups_sent = false; +} + +void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend) +{ + m_animation_range = frame_range; + m_animation_speed = frame_speed; + m_animation_blend = frame_blend; + m_animation_sent = false; +} + +void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation) +{ + m_bone_position[bone] = core::vector2d(position, rotation); + m_bone_position_sent = false; +} + +void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation) +{ + // Attachments need to be handled on both the server and client. + // If we just attach on the server, we can only copy the position of the parent. Attachments + // are still sent to clients at an interval so players might see them lagging, plus we can't + // read and attach to skeletal bones. + // If we just attach on the client, the server still sees the child at its original location. + // This breaks some things so we also give the server the most accurate representation + // even if players only see the client changes. + + m_attachment_parent_id = parent_id; + m_attachment_bone = bone; + m_attachment_position = position; + m_attachment_rotation = rotation; + m_attachment_sent = false; +} + +ObjectProperties* LuaEntitySAO::accessObjectProperties() +{ + return &m_prop; +} + +void LuaEntitySAO::notifyObjectPropertiesModified() +{ + m_properties_sent = false; } void LuaEntitySAO::setVelocity(v3f velocity) @@ -670,29 +851,23 @@ float LuaEntitySAO::getYaw() void LuaEntitySAO::setTextureMod(const std::string &mod) { - std::ostringstream os(std::ios::binary); - // command - writeU8(os, LUAENTITY_CMD_SET_TEXTURE_MOD); - // parameters - os<getSendRecommendedInterval(); + std::string str = gob_cmd_update_position( + m_base_position, + m_velocity, + m_acceleration, + m_yaw, + do_interpolate, + is_movement_end, + update_interval + ); + // create message and add to list + ActiveObjectMessage aom(getId(), false, str); + m_messages_out.push_back(aom); +} + +bool LuaEntitySAO::getCollisionBox(aabb3f *toset) { + if (m_prop.physical) + { + //update collision box + toset->MinEdge = m_prop.collisionbox.MinEdge * BS; + toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; + + toset->MinEdge += m_base_position; + toset->MaxEdge += m_base_position; + + return true; + } + + return false; +} + +bool LuaEntitySAO::collideWithObjects(){ + return m_prop.collideWithObjects; +} + +/* + PlayerSAO +*/ + +// No prototype, PlayerSAO does not need to be deserialized + +PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_, + const std::set &privs, bool is_singleplayer): + ServerActiveObject(env_, v3f(0,0,0)), + m_player(player_), + m_peer_id(peer_id_), + m_inventory(NULL), + m_damage(0), + m_last_good_position(0,0,0), + m_time_from_last_punch(0), + m_nocheat_dig_pos(32767, 32767, 32767), + m_nocheat_dig_time(0), + m_wield_index(0), + m_position_not_sent(false), + m_armor_groups_sent(false), + m_properties_sent(true), + m_privs(privs), + m_is_singleplayer(is_singleplayer), + m_animation_speed(0), + m_animation_blend(0), + m_animation_sent(false), + m_bone_position_sent(false), + m_attachment_parent_id(0), + m_attachment_sent(false), + // public + m_moved(false), + m_inventory_not_sent(false), + m_hp_not_sent(false), + m_breath_not_sent(false), + m_wielded_item_not_sent(false), + m_physics_override_speed(1), + m_physics_override_jump(1), + m_physics_override_gravity(1), + m_physics_override_sneak(true), + m_physics_override_sneak_glitch(true), + m_physics_override_sent(false) +{ + assert(m_player); + assert(m_peer_id != 0); + setBasePosition(m_player->getPosition()); + m_inventory = &m_player->inventory; + m_armor_groups["fleshy"] = 100; + + m_prop.hp_max = PLAYER_MAX_HP; + m_prop.physical = false; + m_prop.weight = 75; + m_prop.collisionbox = core::aabbox3d(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.); + // start of default appearance, this should be overwritten by LUA + m_prop.visual = "upright_sprite"; + m_prop.visual_size = v2f(1, 2); + m_prop.textures.clear(); + m_prop.textures.push_back("player.png"); + m_prop.textures.push_back("player_back.png"); + m_prop.colors.clear(); + m_prop.colors.push_back(video::SColor(255, 255, 255, 255)); + m_prop.spritediv = v2s16(1,1); + // end of default appearance + m_prop.is_visible = true; + m_prop.makes_footstep_sound = true; +} + +PlayerSAO::~PlayerSAO() +{ + if(m_inventory != &m_player->inventory) + delete m_inventory; + +} + +std::string PlayerSAO::getDescription() +{ + return std::string("player ") + m_player->getName(); +} + +// Called after id has been set and has been inserted in environment +void PlayerSAO::addedToEnvironment(u32 dtime_s) +{ + ServerActiveObject::addedToEnvironment(dtime_s); + ServerActiveObject::setBasePosition(m_player->getPosition()); + m_player->setPlayerSAO(this); + m_player->peer_id = m_peer_id; + m_last_good_position = m_player->getPosition(); +} + +// Called before removing from environment +void PlayerSAO::removingFromEnvironment() +{ + ServerActiveObject::removingFromEnvironment(); + if(m_player->getPlayerSAO() == this) + { + m_player->setPlayerSAO(NULL); + m_player->peer_id = 0; + m_env->savePlayer(m_player->getName()); + m_env->removePlayer(m_player->getName()); + } +} + +bool PlayerSAO::isStaticAllowed() const +{ + return false; +} + +bool PlayerSAO::unlimitedTransferDistance() const +{ + return g_settings->getBool("unlimited_player_transfer_distance"); +} + +std::string PlayerSAO::getClientInitializationData(u16 protocol_version) +{ std::ostringstream os(std::ios::binary); - // command - writeU8(os, LUAENTITY_CMD_UPDATE_POSITION); - // do_interpolate - writeU8(os, do_interpolate); - // pos - writeV3F1000(os, m_base_position); - // velocity - writeV3F1000(os, m_velocity); - // acceleration - writeV3F1000(os, m_acceleration); - // yaw - writeF1000(os, m_yaw); - // is_end_position (for interpolation) - writeU8(os, is_movement_end); - // update_interval (for interpolation) - writeF1000(os, update_interval); + if(protocol_version >= 15) + { + writeU8(os, 1); // version + os<getName()); // name + writeU8(os, 1); // is_player + writeS16(os, getId()); //id + writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0)); + writeF1000(os, m_player->getYaw()); + writeS16(os, getHP()); + + writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here + os< >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + os<getName()); // name + writeU8(os, 1); // is_player + writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0)); + writeF1000(os, m_player->getYaw()); + writeS16(os, getHP()); + writeU8(os, 2); // number of messages stuffed in here + os<getActiveObject(m_attachment_parent_id); + if(obj) + return true; + return false; +} + +void PlayerSAO::step(float dtime, bool send_recommended) +{ + if(!m_properties_sent) + { + m_properties_sent = true; + std::string str = getPropertyPacket(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + + // If attached, check that our parent is still there. If it isn't, detach. + if(m_attachment_parent_id && !isAttached()) + { + m_attachment_parent_id = 0; + m_attachment_bone = ""; + m_attachment_position = v3f(0,0,0); + m_attachment_rotation = v3f(0,0,0); + m_player->setPosition(m_last_good_position); + m_moved = true; + } + + //dstream<<"PlayerSAO::step: dtime: "<getMaxLagEstimate() * 2.0; + if(lag_pool_max < LAG_POOL_MIN) + lag_pool_max = LAG_POOL_MIN; + m_dig_pool.setMax(lag_pool_max); + m_move_pool.setMax(lag_pool_max); + + // Increment cheat prevention timers + m_dig_pool.add(dtime); + m_move_pool.add(dtime); + m_time_from_last_punch += dtime; + m_nocheat_dig_time += dtime; + + // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally + // If the object gets detached this comes into effect automatically from the last known origin + if(isAttached()) + { + v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + m_last_good_position = pos; + m_player->setPosition(pos); + } + + if(send_recommended == false) + return; + + // If the object is attached client-side, don't waste bandwidth sending its position to clients + if(m_position_not_sent && !isAttached()) + { + m_position_not_sent = false; + float update_interval = m_env->getSendRecommendedInterval(); + v3f pos; + if(isAttached()) // Just in case we ever do send attachment position too + pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + else + pos = m_player->getPosition() + v3f(0,BS*1,0); + std::string str = gob_cmd_update_position( + pos, + v3f(0,0,0), + v3f(0,0,0), + m_player->getYaw(), + true, + false, + update_interval + ); + // create message and add to list + ActiveObjectMessage aom(getId(), false, str); + m_messages_out.push_back(aom); + } + + if(m_wielded_item_not_sent) + { + m_wielded_item_not_sent = false; + // GenericCAO has no special way to show this + } + + if(m_armor_groups_sent == false){ + m_armor_groups_sent = true; + std::string str = gob_cmd_update_armor_groups( + m_armor_groups); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + + if(m_physics_override_sent == false){ + m_physics_override_sent = true; + std::string str = gob_cmd_update_physics_override(m_physics_override_speed, + m_physics_override_jump, m_physics_override_gravity, + m_physics_override_sneak, m_physics_override_sneak_glitch); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + + if(m_animation_sent == false){ + m_animation_sent = true; + std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + + if(m_bone_position_sent == false){ + m_bone_position_sent = true; + for(std::map >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } + } + + if(m_attachment_sent == false){ + m_attachment_sent = true; + std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + } +} + +void PlayerSAO::setBasePosition(const v3f &position) +{ + // This needs to be ran for attachments too + ServerActiveObject::setBasePosition(position); + m_position_not_sent = true; +} + +void PlayerSAO::setPos(v3f pos) +{ + if(isAttached()) + return; + m_player->setPosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + // Force position change on client + m_moved = true; +} + +void PlayerSAO::moveTo(v3f pos, bool continuous) +{ + if(isAttached()) + return; + m_player->setPosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + // Force position change on client + m_moved = true; +} + +void PlayerSAO::setYaw(float yaw) +{ + m_player->setYaw(yaw); + // Force change on client + m_moved = true; +} + +void PlayerSAO::setPitch(float pitch) +{ + m_player->setPitch(pitch); + // Force change on client + m_moved = true; +} + +int PlayerSAO::punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch) +{ + // It's best that attachments cannot be punched + if(isAttached()) + return 0; + + if(!toolcap) + return 0; + + // No effect if PvP disabled + if(g_settings->getBool("enable_pvp") == false){ + if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){ + std::string str = gob_cmd_punched(0, getHP()); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push_back(aom); + return 0; + } + } + + HitParams hitparams = getHitParams(m_armor_groups, toolcap, + time_from_last_punch); + + std::string punchername = "nil"; + + if ( puncher != 0 ) + punchername = puncher->getDescription(); + + actionstream<<"Player "<getName()<<" punched by " + <hp; +} + +s16 PlayerSAO::readDamage() +{ + s16 damage = m_damage; + m_damage = 0; + return damage; +} + +void PlayerSAO::setHP(s16 hp) +{ + s16 oldhp = m_player->hp; + + if(hp < 0) + hp = 0; + else if(hp > PLAYER_MAX_HP) + hp = PLAYER_MAX_HP; + + if(hp < oldhp && g_settings->getBool("enable_damage") == false) + { + m_hp_not_sent = true; // fix wrong prediction on client + return; + } + + m_player->hp = hp; + + if(hp != oldhp) { + m_hp_not_sent = true; + if(oldhp > hp) + m_damage += oldhp - hp; + } + + // Update properties on death + if((hp == 0) != (oldhp == 0)) + m_properties_sent = false; +} + +u16 PlayerSAO::getBreath() const +{ + return m_player->getBreath(); +} + +void PlayerSAO::setBreath(u16 breath) +{ + m_player->setBreath(breath); +} + +void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) +{ + m_armor_groups = armor_groups; + m_armor_groups_sent = false; +} + +void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend) +{ + // store these so they can be updated to clients + m_animation_range = frame_range; + m_animation_speed = frame_speed; + m_animation_blend = frame_blend; + m_animation_sent = false; +} + +void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation) +{ + // store these so they can be updated to clients + m_bone_position[bone] = core::vector2d(position, rotation); + m_bone_position_sent = false; +} + +void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation) +{ + // Attachments need to be handled on both the server and client. + // If we just attach on the server, we can only copy the position of the parent. Attachments + // are still sent to clients at an interval so players might see them lagging, plus we can't + // read and attach to skeletal bones. + // If we just attach on the client, the server still sees the child at its original location. + // This breaks some things so we also give the server the most accurate representation + // even if players only see the client changes. + + m_attachment_parent_id = parent_id; + m_attachment_bone = bone; + m_attachment_position = position; + m_attachment_rotation = rotation; + m_attachment_sent = false; +} + +ObjectProperties* PlayerSAO::accessObjectProperties() +{ + return &m_prop; +} + +void PlayerSAO::notifyObjectPropertiesModified() +{ + m_properties_sent = false; +} + +Inventory* PlayerSAO::getInventory() +{ + return m_inventory; +} +const Inventory* PlayerSAO::getInventory() const +{ + return m_inventory; +} + +InventoryLocation PlayerSAO::getInventoryLocation() const +{ + InventoryLocation loc; + loc.setPlayer(m_player->getName()); + return loc; +} + +void PlayerSAO::setInventoryModified() +{ + m_inventory_not_sent = true; +} + +std::string PlayerSAO::getWieldList() const +{ + return "main"; +} + +int PlayerSAO::getWieldIndex() const +{ + return m_wield_index; +} + +void PlayerSAO::setWieldIndex(int i) +{ + if(i != m_wield_index) + { + m_wield_index = i; + m_wielded_item_not_sent = true; + } +} + +void PlayerSAO::disconnected() +{ + m_peer_id = 0; + m_removed = true; + if(m_player->getPlayerSAO() == this) + { + m_player->setPlayerSAO(NULL); + m_player->peer_id = 0; + } +} + +std::string PlayerSAO::getPropertyPacket() +{ + m_prop.is_visible = (true); + return gob_cmd_set_properties(m_prop); +} + +bool PlayerSAO::checkMovementCheat() +{ + bool cheated = false; + if(isAttached() || m_is_singleplayer || + g_settings->getBool("disable_anticheat")) + { + m_last_good_position = m_player->getPosition(); + } + else + { + /* + Check player movements + + NOTE: Actually the server should handle player physics like the + client does and compare player's position to what is calculated + on our side. This is required when eg. players fly due to an + explosion. Altough a node-based alternative might be possible + too, and much more lightweight. + */ + + float player_max_speed = 0; + float player_max_speed_up = 0; + if(m_privs.count("fast") != 0){ + // Fast speed + player_max_speed = m_player->movement_speed_fast; + player_max_speed_up = m_player->movement_speed_fast; + } else { + // Normal speed + player_max_speed = m_player->movement_speed_walk; + player_max_speed_up = m_player->movement_speed_walk; + } + // Tolerance. With the lag pool we shouldn't need it. + //player_max_speed *= 2.5; + //player_max_speed_up *= 2.5; + + v3f diff = (m_player->getPosition() - m_last_good_position); + float d_vert = diff.Y; + diff.Y = 0; + float d_horiz = diff.getLength(); + float required_time = d_horiz/player_max_speed; + if(d_vert > 0 && d_vert/player_max_speed > required_time) + required_time = d_vert/player_max_speed; + if(m_move_pool.grab(required_time)){ + m_last_good_position = m_player->getPosition(); + } else { + actionstream<<"Player "<getName() + <<" moved too fast; resetting position" + <setPosition(m_last_good_position); + m_moved = true; + cheated = true; + } + } + return cheated; +} + +bool PlayerSAO::getCollisionBox(aabb3f *toset) { + //update collision box + *toset = m_player->getCollisionbox(); + + toset->MinEdge += m_base_position; + toset->MaxEdge += m_base_position; + + return true; +} + +bool PlayerSAO::collideWithObjects(){ + return true; }