From de73f989eb1397b1103236031fd91309b294583c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 8 Apr 2020 20:13:23 +0200 Subject: Overall improvements to log messages (#9598) Hide some unnecessarily verbose ones behind --trace or disable them entirely. Remove duplicate ones. Improve their contents in some places. --- src/server/mods.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src/server') diff --git a/src/server/mods.cpp b/src/server/mods.cpp index c5616dcd6..c8d8a28e2 100644 --- a/src/server/mods.cpp +++ b/src/server/mods.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "scripting_server.h" #include "content/subgames.h" +#include "porting.h" /** * Manage server mods @@ -66,14 +67,10 @@ void ServerModManager::loadMods(ServerScripting *script) "Only characters [a-z0-9_] are allowed."); } std::string script_path = mod.path + DIR_DELIM + "init.lua"; - infostream << " [" << padStringRight(mod.name, 12) << "] [\"" - << script_path << "\"]" << std::endl; - auto t = std::chrono::steady_clock::now(); + auto t = porting::getTimeMs(); script->loadMod(script_path, mod.name); infostream << "Mod \"" << mod.name << "\" loaded after " - << std::chrono::duration_cast( - std::chrono::steady_clock::now() - t).count() * 0.001f - << " seconds" << std::endl; + << (porting::getTimeMs() - t) << " ms" << std::endl; } // Run a callback when mods are loaded -- cgit v1.2.3 From 6d43736172f8459cb70219186ae003c56c389f2a Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Fri, 10 Apr 2020 21:25:42 +0200 Subject: Move serveractiveobject & unitsao Move serverobject.{cpp,h} to server/serveractiveobject.{cpp,h} Move UnitSAO class to dedicated files --- build/android/jni/Android.mk | 2 +- src/CMakeLists.txt | 1 - src/collision.cpp | 2 +- src/content_sao.cpp | 296 ------------------------------ src/content_sao.h | 92 +--------- src/emerge.cpp | 1 - src/environment.cpp | 1 - src/inventorymanager.cpp | 2 +- src/mapgen/mapgen_v6.cpp | 1 - src/script/common/c_content.cpp | 2 +- src/script/cpp_api/s_base.cpp | 2 +- src/script/lua_api/l_object.cpp | 2 +- src/server.cpp | 2 +- src/server/CMakeLists.txt | 2 + src/server/activeobjectmgr.h | 2 +- src/server/serveractiveobject.cpp | 114 ++++++++++++ src/server/serveractiveobject.h | 265 +++++++++++++++++++++++++++ src/server/unit_sao.cpp | 318 +++++++++++++++++++++++++++++++++ src/server/unit_sao.h | 113 ++++++++++++ src/serverobject.cpp | 114 ------------ src/serverobject.h | 265 --------------------------- util/travis/clang-format-whitelist.txt | 6 +- 22 files changed, 823 insertions(+), 782 deletions(-) create mode 100644 src/server/serveractiveobject.cpp create mode 100644 src/server/serveractiveobject.h create mode 100644 src/server/unit_sao.cpp create mode 100644 src/server/unit_sao.h delete mode 100644 src/serverobject.cpp delete mode 100644 src/serverobject.h (limited to 'src/server') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index a2f32440a..32a16c174 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -259,7 +259,7 @@ LOCAL_SRC_FILES := \ jni/src/serverenvironment.cpp \ jni/src/serverlist.cpp \ jni/src/server/mods.cpp \ - jni/src/serverobject.cpp \ + jni/src/server/serveractiveobject.cpp \ jni/src/settings.cpp \ jni/src/staticobject.cpp \ jni/src/tileanimation.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4b1d6d647..faa117d41 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -421,7 +421,6 @@ set(common_SRCS server.cpp serverenvironment.cpp serverlist.cpp - serverobject.cpp settings.cpp staticobject.cpp terminal_chat_console.cpp diff --git a/src/collision.cpp b/src/collision.cpp index 0d37ea436..d9fbd3202 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/localplayer.h" #endif #include "serverenvironment.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "util/timetaker.h" #include "profiler.h" diff --git a/src/content_sao.cpp b/src/content_sao.cpp index be7674f52..0d387b53a 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -106,302 +106,6 @@ private: // Prototype (registers item for deserialization) TestSAO proto_TestSAO(NULL, v3f(0,0,0)); -/* - UnitSAO - */ - -UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos) -{ - // Initialize something to armor groups - m_armor_groups["fleshy"] = 100; -} - -ServerActiveObject *UnitSAO::getParent() const -{ - if (!m_attachment_parent_id) - return nullptr; - // Check if the parent still exists - ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); - - return obj; -} - -void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups) -{ - m_armor_groups = armor_groups; - m_armor_groups_sent = false; -} - -const ItemGroupList &UnitSAO::getArmorGroups() const -{ - return m_armor_groups; -} - -void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) -{ - // 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_loop = frame_loop; - m_animation_sent = false; -} - -void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop) -{ - *frame_range = m_animation_range; - *frame_speed = m_animation_speed; - *frame_blend = m_animation_blend; - *frame_loop = m_animation_loop; -} - -void UnitSAO::setAnimationSpeed(float frame_speed) -{ - m_animation_speed = frame_speed; - m_animation_speed_sent = false; -} - -void UnitSAO::setBonePosition(const 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 UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) -{ - *position = m_bone_position[bone].X; - *rotation = m_bone_position[bone].Y; -} - -void UnitSAO::setAttachment(int parent_id, const 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. - - int old_parent = m_attachment_parent_id; - m_attachment_parent_id = parent_id; - m_attachment_bone = bone; - m_attachment_position = position; - m_attachment_rotation = rotation; - m_attachment_sent = false; - - if (parent_id != old_parent) { - onDetach(old_parent); - onAttach(parent_id); - } -} - -void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const -{ - *parent_id = m_attachment_parent_id; - *bone = m_attachment_bone; - *position = m_attachment_position; - *rotation = m_attachment_rotation; -} - -void UnitSAO::clearChildAttachments() -{ - for (int child_id : m_attachment_child_ids) { - // Child can be NULL if it was deleted earlier - if (ServerActiveObject *child = m_env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); - } - m_attachment_child_ids.clear(); -} - -void UnitSAO::clearParentAttachment() -{ - ServerActiveObject *parent = nullptr; - if (m_attachment_parent_id) { - parent = m_env->getActiveObject(m_attachment_parent_id); - setAttachment(0, "", m_attachment_position, m_attachment_rotation); - } else { - setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); - } - // Do it - if (parent) - parent->removeAttachmentChild(m_id); -} - -void UnitSAO::addAttachmentChild(int child_id) -{ - m_attachment_child_ids.insert(child_id); -} - -void UnitSAO::removeAttachmentChild(int child_id) -{ - m_attachment_child_ids.erase(child_id); -} - -const std::unordered_set &UnitSAO::getAttachmentChildIds() const -{ - return m_attachment_child_ids; -} - -void UnitSAO::onAttach(int parent_id) -{ - if (!parent_id) - return; - - ServerActiveObject *parent = m_env->getActiveObject(parent_id); - - if (!parent || parent->isGone()) - return; // Do not try to notify soon gone parent - - if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { - // Call parent's on_attach field - m_env->getScriptIface()->luaentity_on_attach_child(parent_id, this); - } -} - -void UnitSAO::onDetach(int parent_id) -{ - if (!parent_id) - return; - - ServerActiveObject *parent = m_env->getActiveObject(parent_id); - if (getType() == ACTIVEOBJECT_TYPE_LUAENTITY) - m_env->getScriptIface()->luaentity_on_detach(m_id, parent); - - if (!parent || parent->isGone()) - return; // Do not try to notify soon gone parent - - if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) - m_env->getScriptIface()->luaentity_on_detach_child(parent_id, this); -} - -ObjectProperties* UnitSAO::accessObjectProperties() -{ - return &m_prop; -} - -void UnitSAO::notifyObjectPropertiesModified() -{ - m_properties_sent = false; -} - -std::string UnitSAO::generateUpdateAttachmentCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_ATTACH_TO); - // parameters - writeS16(os, m_attachment_parent_id); - os << serializeString(m_attachment_bone); - writeV3F32(os, m_attachment_position); - writeV3F32(os, m_attachment_rotation); - return os.str(); -} - -std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation) -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_BONE_POSITION); - // parameters - os << serializeString(bone); - writeV3F32(os, position); - writeV3F32(os, rotation); - return os.str(); -} - - -std::string UnitSAO::generateUpdateAnimationSpeedCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_ANIMATION_SPEED); - // parameters - writeF32(os, m_animation_speed); - return os.str(); -} - -std::string UnitSAO::generateUpdateAnimationCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_ANIMATION); - // parameters - writeV2F32(os, m_animation_range); - writeF32(os, m_animation_speed); - writeF32(os, m_animation_blend); - // these are sent inverted so we get true when the server sends nothing - writeU8(os, !m_animation_loop); - return os.str(); -} - - -std::string UnitSAO::generateUpdateArmorGroupsCommand() const -{ - std::ostringstream os(std::ios::binary); - writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS); - writeU16(os, m_armor_groups.size()); - for (const auto &armor_group : m_armor_groups) { - os< &getAttachmentChildIds() const; - ServerActiveObject *getParent() const; - ObjectProperties* accessObjectProperties(); - void notifyObjectPropertiesModified(); - - std::string generateUpdateAttachmentCommand() const; - std::string generateUpdateAnimationSpeedCommand() const; - std::string generateUpdateAnimationCommand() const; - std::string generateUpdateArmorGroupsCommand() const; - static std::string generateUpdatePositionCommand(const v3f &position, const v3f &velocity, - const v3f &acceleration, const v3f &rotation, bool do_interpolate, - bool is_movement_end, f32 update_interval); - std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; - void sendPunchCommand(); - static std::string generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation); - -protected: - u16 m_hp = 1; - - v3f m_rotation; - - bool m_properties_sent = true; - ObjectProperties m_prop; - - ItemGroupList m_armor_groups; - bool m_armor_groups_sent = false; - - v2f m_animation_range; - float m_animation_speed = 0.0f; - float m_animation_blend = 0.0f; - bool m_animation_loop = true; - bool m_animation_sent = false; - bool m_animation_speed_sent = false; - - // Stores position and rotation for each bone name - std::unordered_map> m_bone_position; - bool m_bone_position_sent = false; - - int m_attachment_parent_id = 0; - std::unordered_set m_attachment_child_ids; - std::string m_attachment_bone = ""; - v3f m_attachment_position; - v3f m_attachment_rotation; - bool m_attachment_sent = false; -private: - void onAttach(int parent_id); - void onDetach(int parent_id); - - std::string generatePunchCommand(u16 result_hp) const; -}; - /* LuaEntitySAO needs some internals exposed. */ diff --git a/src/emerge.cpp b/src/emerge.cpp index 4835c3fad..fe885447c 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -42,7 +42,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "scripting_server.h" #include "server.h" -#include "serverobject.h" #include "settings.h" #include "voxel.h" diff --git a/src/environment.cpp b/src/environment.cpp index c997be3ff..6751f39e4 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "collision.h" #include "raycast.h" -#include "serverobject.h" #include "scripting_server.h" #include "server.h" #include "daynightratio.h" diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 5a24f95a4..b6f464901 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "serverenvironment.h" #include "scripting_server.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "settings.h" #include "craftdef.h" #include "rollback_interface.h" diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp index 8a863c044..f473f725d 100644 --- a/src/mapgen/mapgen_v6.cpp +++ b/src/mapgen/mapgen_v6.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -//#include "serverobject.h" #include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index accbb1a87..60f12052f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "log.h" #include "tool.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "porting.h" #include "mapgen/mg_schematic.h" #include "noise.h" diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index f234a15d4..16c20eeae 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_security.h" #include "lua_api/l_object.h" #include "common/c_converter.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "filesys.h" #include "content/mods.h" #include "porting.h" diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 5cd978d73..1ea144a1c 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" #include "tool.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "content_sao.h" #include "remoteplayer.h" #include "server.h" diff --git a/src/server.cpp b/src/server.cpp index 062fe0798..529466f6b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "filesys.h" #include "mapblock.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "settings.h" #include "profiler.h" #include "log.h" diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index e964c69ff..9fa5ed9fa 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,4 +1,6 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/activeobjectmgr.h b/src/server/activeobjectmgr.h index a502ac6ed..5fea1bea6 100644 --- a/src/server/activeobjectmgr.h +++ b/src/server/activeobjectmgr.h @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "../activeobjectmgr.h" -#include "serverobject.h" +#include "serveractiveobject.h" namespace server { diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp new file mode 100644 index 000000000..3aa78c7d5 --- /dev/null +++ b/src/server/serveractiveobject.cpp @@ -0,0 +1,114 @@ +/* +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 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 Lesser General Public License for more details. + +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. +*/ + +#include "serveractiveobject.h" +#include +#include "inventory.h" +#include "constants.h" // BS +#include "log.h" + +ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos): + ActiveObject(0), + m_env(env), + m_base_position(pos) +{ +} + +ServerActiveObject* ServerActiveObject::create(ActiveObjectType type, + ServerEnvironment *env, u16 id, v3f pos, + const std::string &data) +{ + // Find factory function + std::map::iterator n; + n = m_types.find(type); + if(n == m_types.end()) { + // These are 0.3 entity types, return without error. + if (ACTIVEOBJECT_TYPE_ITEM <= type && type <= ACTIVEOBJECT_TYPE_MOBV2) { + return NULL; + } + + // If factory is not found, just return. + warningstream<<"ServerActiveObject: No factory for type=" + <second; + ServerActiveObject *object = (*f)(env, pos, data); + return object; +} + +void ServerActiveObject::registerType(u16 type, Factory f) +{ + std::map::iterator n; + n = m_types.find(type); + if(n != m_types.end()) + return; + m_types[type] = f; +} + +float ServerActiveObject::getMinimumSavedMovement() +{ + return 2.0*BS; +} + +ItemStack ServerActiveObject::getWieldedItem(ItemStack *selected, ItemStack *hand) const +{ + *selected = ItemStack(); + if (hand) + *hand = ItemStack(); + + return ItemStack(); +} + +bool ServerActiveObject::setWieldedItem(const ItemStack &item) +{ + return false; +} + +std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SPAWN_INFANT); + // parameters + writeU16(os, infant_id); + writeU8(os, getSendType()); + os << serializeLongString(getClientInitializationData(protocol_version)); + return os.str(); +} + +std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const video::SColor &color) const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_UPDATE_NAMETAG_ATTRIBUTES); + // parameters + writeU8(os, 1); // version for forward compatibility + writeARGB8(os, color); + return os.str(); +} + +void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) +{ + while (!m_messages_out.empty()) { + queue.push(m_messages_out.front()); + m_messages_out.pop(); + } +} \ No newline at end of file diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h new file mode 100644 index 000000000..2e013a6b6 --- /dev/null +++ b/src/server/serveractiveobject.h @@ -0,0 +1,265 @@ +/* +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 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 Lesser General Public License for more details. + +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. +*/ + +#pragma once + +#include +#include "irrlichttypes_bloated.h" +#include "activeobject.h" +#include "inventorymanager.h" +#include "itemgroup.h" +#include "util/container.h" + +/* + +Some planning +------------- + +* Server environment adds an active object, which gets the id 1 +* The active object list is scanned for each client once in a while, + and it finds out what objects have been added that are not known + by the client yet. This scan is initiated by the Server class and + the result ends up directly to the server. +* A network packet is created with the info and sent to the client. +* Environment converts objects to static data and static data to + objects, based on how close players are to them. + +*/ + +class ServerEnvironment; +struct ItemStack; +struct ToolCapabilities; +struct ObjectProperties; +struct PlayerHPChangeReason; + +class ServerActiveObject : public ActiveObject +{ +public: + /* + NOTE: m_env can be NULL, but step() isn't called if it is. + Prototypes are used that way. + */ + ServerActiveObject(ServerEnvironment *env, v3f pos); + virtual ~ServerActiveObject() = default; + + virtual ActiveObjectType getSendType() const + { return getType(); } + + // Called after id has been set and has been inserted in environment + virtual void addedToEnvironment(u32 dtime_s){}; + // Called before removing from environment + virtual void removingFromEnvironment(){}; + // Returns true if object's deletion is the job of the + // environment + virtual bool environmentDeletes() const + { return true; } + + // Create a certain type of ServerActiveObject + static ServerActiveObject* create(ActiveObjectType type, + ServerEnvironment *env, u16 id, v3f pos, + const std::string &data); + + /* + Some simple getters/setters + */ + v3f getBasePosition() const { return m_base_position; } + void setBasePosition(v3f pos){ m_base_position = pos; } + ServerEnvironment* getEnv(){ return m_env; } + + /* + Some more dynamic interface + */ + + virtual void setPos(const v3f &pos) + { setBasePosition(pos); } + // continuous: if true, object does not stop immediately at pos + virtual void moveTo(v3f pos, bool continuous) + { setBasePosition(pos); } + // If object has moved less than this and data has not changed, + // saving to disk may be omitted + virtual float getMinimumSavedMovement(); + + virtual std::string getDescription(){return "SAO";} + + /* + Step object in time. + Messages added to messages are sent to client over network. + + send_recommended: + True at around 5-10 times a second, same for all objects. + This is used to let objects send most of the data at the + same time so that the data can be combined in a single + packet. + */ + virtual void step(float dtime, bool send_recommended){} + + /* + The return value of this is passed to the client-side object + when it is created + */ + virtual std::string getClientInitializationData(u16 protocol_version) {return "";} + + /* + The return value of this is passed to the server-side object + when it is created (converted from static to active - actually + the data is the static form) + */ + virtual void getStaticData(std::string *result) const + { + assert(isStaticAllowed()); + *result = ""; + } + /* + Return false in here to never save and instead remove object + on unload. getStaticData() will not be called in that case. + */ + virtual bool isStaticAllowed() const + {return true;} + + // Returns tool wear + virtual u16 punch(v3f dir, + const ToolCapabilities *toolcap = nullptr, + ServerActiveObject *puncher = nullptr, + float time_from_last_punch = 1000000.0f) + { return 0; } + virtual void rightClick(ServerActiveObject *clicker) + {} + virtual void setHP(s32 hp, const PlayerHPChangeReason &reason) + {} + virtual u16 getHP() const + { return 0; } + + virtual void setArmorGroups(const ItemGroupList &armor_groups) + {} + virtual const ItemGroupList &getArmorGroups() const + { static ItemGroupList rv; return rv; } + virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity) + {} + virtual void setAnimation(v2f frames, float frame_speed, float frame_blend, bool frame_loop) + {} + virtual void getAnimation(v2f *frames, float *frame_speed, float *frame_blend, bool *frame_loop) + {} + virtual void setAnimationSpeed(float frame_speed) + {} + virtual void setBonePosition(const std::string &bone, v3f position, v3f rotation) + {} + virtual void getBonePosition(const std::string &bone, v3f *position, v3f *lotation) + {} + virtual const std::unordered_set &getAttachmentChildIds() const + { static std::unordered_set rv; return rv; } + virtual ServerActiveObject *getParent() const { return nullptr; } + virtual ObjectProperties* accessObjectProperties() + { return NULL; } + virtual void notifyObjectPropertiesModified() + {} + + // Inventory and wielded item + virtual Inventory *getInventory() const + { return NULL; } + virtual InventoryLocation getInventoryLocation() const + { return InventoryLocation(); } + virtual void setInventoryModified() + {} + virtual std::string getWieldList() const + { return ""; } + virtual u16 getWieldIndex() const + { return 0; } + virtual ItemStack getWieldedItem(ItemStack *selected, + ItemStack *hand = nullptr) const; + virtual bool setWieldedItem(const ItemStack &item); + inline void attachParticleSpawner(u32 id) + { + m_attached_particle_spawners.insert(id); + } + inline void detachParticleSpawner(u32 id) + { + m_attached_particle_spawners.erase(id); + } + + std::string generateUpdateInfantCommand(u16 infant_id, u16 protocol_version); + std::string generateUpdateNametagAttributesCommand(const video::SColor &color) const; + + void dumpAOMessagesToQueue(std::queue &queue); + + /* + Number of players which know about this object. Object won't be + deleted until this is 0 to keep the id preserved for the right + object. + */ + u16 m_known_by_count = 0; + + /* + - Whether this object is to be removed when nobody knows about + it anymore. + - Removal is delayed to preserve the id for the time during which + it could be confused to some other object by some client. + - This is usually set to true by the step() method when the object wants + to be deleted but can be set by anything else too. + */ + bool m_pending_removal = false; + + /* + Same purpose as m_pending_removal but for deactivation. + deactvation = save static data in block, remove active object + + If this is set alongside with m_pending_removal, removal takes + priority. + */ + bool m_pending_deactivation = false; + + /* + A getter that unifies the above to answer the question: + "Can the environment still interact with this object?" + */ + inline bool isGone() const + { return m_pending_removal || m_pending_deactivation; } + + /* + Whether the object's static data has been stored to a block + */ + bool m_static_exists = false; + /* + The block from which the object was loaded from, and in which + a copy of the static data resides. + */ + v3s16 m_static_block = v3s16(1337,1337,1337); + +protected: + virtual void onAttach(int parent_id) {} + virtual void onDetach(int parent_id) {} + + // Used for creating objects based on type + typedef ServerActiveObject* (*Factory) + (ServerEnvironment *env, v3f pos, + const std::string &data); + static void registerType(u16 type, Factory f); + + ServerEnvironment *m_env; + v3f m_base_position; + std::unordered_set m_attached_particle_spawners; + + /* + Queue of messages to be sent to the client + */ + std::queue m_messages_out; + +private: + // Used for creating objects based on type + static std::map m_types; +}; diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp new file mode 100644 index 000000000..66be67522 --- /dev/null +++ b/src/server/unit_sao.cpp @@ -0,0 +1,318 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#include "unit_sao.h" +#include "scripting_server.h" +#include "serverenvironment.h" + +/* + UnitSAO + */ + +UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos): + ServerActiveObject(env, pos) +{ + // Initialize something to armor groups + m_armor_groups["fleshy"] = 100; +} + +ServerActiveObject *UnitSAO::getParent() const +{ + if (!m_attachment_parent_id) + return nullptr; + // Check if the parent still exists + ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); + + return obj; +} + +void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups) +{ + m_armor_groups = armor_groups; + m_armor_groups_sent = false; +} + +const ItemGroupList &UnitSAO::getArmorGroups() const +{ + return m_armor_groups; +} + +void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) +{ + // 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_loop = frame_loop; + m_animation_sent = false; +} + +void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop) +{ + *frame_range = m_animation_range; + *frame_speed = m_animation_speed; + *frame_blend = m_animation_blend; + *frame_loop = m_animation_loop; +} + +void UnitSAO::setAnimationSpeed(float frame_speed) +{ + m_animation_speed = frame_speed; + m_animation_speed_sent = false; +} + +void UnitSAO::setBonePosition(const 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 UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) +{ + *position = m_bone_position[bone].X; + *rotation = m_bone_position[bone].Y; +} + +void UnitSAO::setAttachment(int parent_id, const 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. + + int old_parent = m_attachment_parent_id; + m_attachment_parent_id = parent_id; + m_attachment_bone = bone; + m_attachment_position = position; + m_attachment_rotation = rotation; + m_attachment_sent = false; + + if (parent_id != old_parent) { + onDetach(old_parent); + onAttach(parent_id); + } +} + +void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation) const +{ + *parent_id = m_attachment_parent_id; + *bone = m_attachment_bone; + *position = m_attachment_position; + *rotation = m_attachment_rotation; +} + +void UnitSAO::clearChildAttachments() +{ + for (int child_id : m_attachment_child_ids) { + // Child can be NULL if it was deleted earlier + if (ServerActiveObject *child = m_env->getActiveObject(child_id)) + child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + } + m_attachment_child_ids.clear(); +} + +void UnitSAO::clearParentAttachment() +{ + ServerActiveObject *parent = nullptr; + if (m_attachment_parent_id) { + parent = m_env->getActiveObject(m_attachment_parent_id); + setAttachment(0, "", m_attachment_position, m_attachment_rotation); + } else { + setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + } + // Do it + if (parent) + parent->removeAttachmentChild(m_id); +} + +void UnitSAO::addAttachmentChild(int child_id) +{ + m_attachment_child_ids.insert(child_id); +} + +void UnitSAO::removeAttachmentChild(int child_id) +{ + m_attachment_child_ids.erase(child_id); +} + +const std::unordered_set &UnitSAO::getAttachmentChildIds() const +{ + return m_attachment_child_ids; +} + +void UnitSAO::onAttach(int parent_id) +{ + if (!parent_id) + return; + + ServerActiveObject *parent = m_env->getActiveObject(parent_id); + + if (!parent || parent->isGone()) + return; // Do not try to notify soon gone parent + + if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + // Call parent's on_attach field + m_env->getScriptIface()->luaentity_on_attach_child(parent_id, this); + } +} + +void UnitSAO::onDetach(int parent_id) +{ + if (!parent_id) + return; + + ServerActiveObject *parent = m_env->getActiveObject(parent_id); + if (getType() == ACTIVEOBJECT_TYPE_LUAENTITY) + m_env->getScriptIface()->luaentity_on_detach(m_id, parent); + + if (!parent || parent->isGone()) + return; // Do not try to notify soon gone parent + + if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) + m_env->getScriptIface()->luaentity_on_detach_child(parent_id, this); +} + +ObjectProperties* UnitSAO::accessObjectProperties() +{ + return &m_prop; +} + +void UnitSAO::notifyObjectPropertiesModified() +{ + m_properties_sent = false; +} + +std::string UnitSAO::generateUpdateAttachmentCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_ATTACH_TO); + // parameters + writeS16(os, m_attachment_parent_id); + os << serializeString(m_attachment_bone); + writeV3F32(os, m_attachment_position); + writeV3F32(os, m_attachment_rotation); + return os.str(); +} + +std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_BONE_POSITION); + // parameters + os << serializeString(bone); + writeV3F32(os, position); + writeV3F32(os, rotation); + return os.str(); +} + + +std::string UnitSAO::generateUpdateAnimationSpeedCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_ANIMATION_SPEED); + // parameters + writeF32(os, m_animation_speed); + return os.str(); +} + +std::string UnitSAO::generateUpdateAnimationCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_ANIMATION); + // parameters + writeV2F32(os, m_animation_range); + writeF32(os, m_animation_speed); + writeF32(os, m_animation_blend); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_animation_loop); + return os.str(); +} + + +std::string UnitSAO::generateUpdateArmorGroupsCommand() const +{ + std::ostringstream os(std::ios::binary); + writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS); + writeU16(os, m_armor_groups.size()); + for (const auto &armor_group : m_armor_groups) { + os< +Copyright (C) 2013-2020 Minetest core developers & community + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#pragma once + +#include "object_properties.h" +#include "serveractiveobject.h" + +class UnitSAO: public ServerActiveObject +{ +public: + UnitSAO(ServerEnvironment *env, v3f pos); + virtual ~UnitSAO() = default; + + void setRotation(v3f rotation) { m_rotation = rotation; } + const v3f &getRotation() const { return m_rotation; } + v3f getRadRotation() { return m_rotation * core::DEGTORAD; } + + // Deprecated + f32 getRadYawDep() const { return (m_rotation.Y + 90.) * core::DEGTORAD; } + + u16 getHP() const { return m_hp; } + // Use a function, if isDead can be defined by other conditions + bool isDead() const { return m_hp == 0; } + + inline bool isAttached() const + { return getParent(); } + + inline bool isImmortal() const + { return itemgroup_get(getArmorGroups(), "immortal"); } + + void setArmorGroups(const ItemGroupList &armor_groups); + const ItemGroupList &getArmorGroups() const; + void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); + void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); + void setAnimationSpeed(float frame_speed); + void setBonePosition(const std::string &bone, v3f position, v3f rotation); + void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); + void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); + void getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation) const; + void clearChildAttachments(); + void clearParentAttachment(); + void addAttachmentChild(int child_id); + void removeAttachmentChild(int child_id); + const std::unordered_set &getAttachmentChildIds() const; + ServerActiveObject *getParent() const; + ObjectProperties* accessObjectProperties(); + void notifyObjectPropertiesModified(); + + std::string generateUpdateAttachmentCommand() const; + std::string generateUpdateAnimationSpeedCommand() const; + std::string generateUpdateAnimationCommand() const; + std::string generateUpdateArmorGroupsCommand() const; + static std::string generateUpdatePositionCommand(const v3f &position, const v3f &velocity, + const v3f &acceleration, const v3f &rotation, bool do_interpolate, + bool is_movement_end, f32 update_interval); + std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; + void sendPunchCommand(); + static std::string generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation); + +protected: + u16 m_hp = 1; + + v3f m_rotation; + + bool m_properties_sent = true; + ObjectProperties m_prop; + + ItemGroupList m_armor_groups; + bool m_armor_groups_sent = false; + + v2f m_animation_range; + float m_animation_speed = 0.0f; + float m_animation_blend = 0.0f; + bool m_animation_loop = true; + bool m_animation_sent = false; + bool m_animation_speed_sent = false; + + // Stores position and rotation for each bone name + std::unordered_map> m_bone_position; + bool m_bone_position_sent = false; + + int m_attachment_parent_id = 0; + std::unordered_set m_attachment_child_ids; + std::string m_attachment_bone = ""; + v3f m_attachment_position; + v3f m_attachment_rotation; + bool m_attachment_sent = false; +private: + void onAttach(int parent_id); + void onDetach(int parent_id); + + std::string generatePunchCommand(u16 result_hp) const; +}; diff --git a/src/serverobject.cpp b/src/serverobject.cpp deleted file mode 100644 index 119a41b7b..000000000 --- a/src/serverobject.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* -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 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 Lesser General Public License for more details. - -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. -*/ - -#include "serverobject.h" -#include -#include "inventory.h" -#include "constants.h" // BS -#include "log.h" - -ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos): - ActiveObject(0), - m_env(env), - m_base_position(pos) -{ -} - -ServerActiveObject* ServerActiveObject::create(ActiveObjectType type, - ServerEnvironment *env, u16 id, v3f pos, - const std::string &data) -{ - // Find factory function - std::map::iterator n; - n = m_types.find(type); - if(n == m_types.end()) { - // These are 0.3 entity types, return without error. - if (ACTIVEOBJECT_TYPE_ITEM <= type && type <= ACTIVEOBJECT_TYPE_MOBV2) { - return NULL; - } - - // If factory is not found, just return. - warningstream<<"ServerActiveObject: No factory for type=" - <second; - ServerActiveObject *object = (*f)(env, pos, data); - return object; -} - -void ServerActiveObject::registerType(u16 type, Factory f) -{ - std::map::iterator n; - n = m_types.find(type); - if(n != m_types.end()) - return; - m_types[type] = f; -} - -float ServerActiveObject::getMinimumSavedMovement() -{ - return 2.0*BS; -} - -ItemStack ServerActiveObject::getWieldedItem(ItemStack *selected, ItemStack *hand) const -{ - *selected = ItemStack(); - if (hand) - *hand = ItemStack(); - - return ItemStack(); -} - -bool ServerActiveObject::setWieldedItem(const ItemStack &item) -{ - return false; -} - -std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 protocol_version) -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SPAWN_INFANT); - // parameters - writeU16(os, infant_id); - writeU8(os, getSendType()); - os << serializeLongString(getClientInitializationData(protocol_version)); - return os.str(); -} - -std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const video::SColor &color) const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_UPDATE_NAMETAG_ATTRIBUTES); - // parameters - writeU8(os, 1); // version for forward compatibility - writeARGB8(os, color); - return os.str(); -} - -void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) -{ - while (!m_messages_out.empty()) { - queue.push(m_messages_out.front()); - m_messages_out.pop(); - } -} \ No newline at end of file diff --git a/src/serverobject.h b/src/serverobject.h deleted file mode 100644 index 2e013a6b6..000000000 --- a/src/serverobject.h +++ /dev/null @@ -1,265 +0,0 @@ -/* -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 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 Lesser General Public License for more details. - -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. -*/ - -#pragma once - -#include -#include "irrlichttypes_bloated.h" -#include "activeobject.h" -#include "inventorymanager.h" -#include "itemgroup.h" -#include "util/container.h" - -/* - -Some planning -------------- - -* Server environment adds an active object, which gets the id 1 -* The active object list is scanned for each client once in a while, - and it finds out what objects have been added that are not known - by the client yet. This scan is initiated by the Server class and - the result ends up directly to the server. -* A network packet is created with the info and sent to the client. -* Environment converts objects to static data and static data to - objects, based on how close players are to them. - -*/ - -class ServerEnvironment; -struct ItemStack; -struct ToolCapabilities; -struct ObjectProperties; -struct PlayerHPChangeReason; - -class ServerActiveObject : public ActiveObject -{ -public: - /* - NOTE: m_env can be NULL, but step() isn't called if it is. - Prototypes are used that way. - */ - ServerActiveObject(ServerEnvironment *env, v3f pos); - virtual ~ServerActiveObject() = default; - - virtual ActiveObjectType getSendType() const - { return getType(); } - - // Called after id has been set and has been inserted in environment - virtual void addedToEnvironment(u32 dtime_s){}; - // Called before removing from environment - virtual void removingFromEnvironment(){}; - // Returns true if object's deletion is the job of the - // environment - virtual bool environmentDeletes() const - { return true; } - - // Create a certain type of ServerActiveObject - static ServerActiveObject* create(ActiveObjectType type, - ServerEnvironment *env, u16 id, v3f pos, - const std::string &data); - - /* - Some simple getters/setters - */ - v3f getBasePosition() const { return m_base_position; } - void setBasePosition(v3f pos){ m_base_position = pos; } - ServerEnvironment* getEnv(){ return m_env; } - - /* - Some more dynamic interface - */ - - virtual void setPos(const v3f &pos) - { setBasePosition(pos); } - // continuous: if true, object does not stop immediately at pos - virtual void moveTo(v3f pos, bool continuous) - { setBasePosition(pos); } - // If object has moved less than this and data has not changed, - // saving to disk may be omitted - virtual float getMinimumSavedMovement(); - - virtual std::string getDescription(){return "SAO";} - - /* - Step object in time. - Messages added to messages are sent to client over network. - - send_recommended: - True at around 5-10 times a second, same for all objects. - This is used to let objects send most of the data at the - same time so that the data can be combined in a single - packet. - */ - virtual void step(float dtime, bool send_recommended){} - - /* - The return value of this is passed to the client-side object - when it is created - */ - virtual std::string getClientInitializationData(u16 protocol_version) {return "";} - - /* - The return value of this is passed to the server-side object - when it is created (converted from static to active - actually - the data is the static form) - */ - virtual void getStaticData(std::string *result) const - { - assert(isStaticAllowed()); - *result = ""; - } - /* - Return false in here to never save and instead remove object - on unload. getStaticData() will not be called in that case. - */ - virtual bool isStaticAllowed() const - {return true;} - - // Returns tool wear - virtual u16 punch(v3f dir, - const ToolCapabilities *toolcap = nullptr, - ServerActiveObject *puncher = nullptr, - float time_from_last_punch = 1000000.0f) - { return 0; } - virtual void rightClick(ServerActiveObject *clicker) - {} - virtual void setHP(s32 hp, const PlayerHPChangeReason &reason) - {} - virtual u16 getHP() const - { return 0; } - - virtual void setArmorGroups(const ItemGroupList &armor_groups) - {} - virtual const ItemGroupList &getArmorGroups() const - { static ItemGroupList rv; return rv; } - virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity) - {} - virtual void setAnimation(v2f frames, float frame_speed, float frame_blend, bool frame_loop) - {} - virtual void getAnimation(v2f *frames, float *frame_speed, float *frame_blend, bool *frame_loop) - {} - virtual void setAnimationSpeed(float frame_speed) - {} - virtual void setBonePosition(const std::string &bone, v3f position, v3f rotation) - {} - virtual void getBonePosition(const std::string &bone, v3f *position, v3f *lotation) - {} - virtual const std::unordered_set &getAttachmentChildIds() const - { static std::unordered_set rv; return rv; } - virtual ServerActiveObject *getParent() const { return nullptr; } - virtual ObjectProperties* accessObjectProperties() - { return NULL; } - virtual void notifyObjectPropertiesModified() - {} - - // Inventory and wielded item - virtual Inventory *getInventory() const - { return NULL; } - virtual InventoryLocation getInventoryLocation() const - { return InventoryLocation(); } - virtual void setInventoryModified() - {} - virtual std::string getWieldList() const - { return ""; } - virtual u16 getWieldIndex() const - { return 0; } - virtual ItemStack getWieldedItem(ItemStack *selected, - ItemStack *hand = nullptr) const; - virtual bool setWieldedItem(const ItemStack &item); - inline void attachParticleSpawner(u32 id) - { - m_attached_particle_spawners.insert(id); - } - inline void detachParticleSpawner(u32 id) - { - m_attached_particle_spawners.erase(id); - } - - std::string generateUpdateInfantCommand(u16 infant_id, u16 protocol_version); - std::string generateUpdateNametagAttributesCommand(const video::SColor &color) const; - - void dumpAOMessagesToQueue(std::queue &queue); - - /* - Number of players which know about this object. Object won't be - deleted until this is 0 to keep the id preserved for the right - object. - */ - u16 m_known_by_count = 0; - - /* - - Whether this object is to be removed when nobody knows about - it anymore. - - Removal is delayed to preserve the id for the time during which - it could be confused to some other object by some client. - - This is usually set to true by the step() method when the object wants - to be deleted but can be set by anything else too. - */ - bool m_pending_removal = false; - - /* - Same purpose as m_pending_removal but for deactivation. - deactvation = save static data in block, remove active object - - If this is set alongside with m_pending_removal, removal takes - priority. - */ - bool m_pending_deactivation = false; - - /* - A getter that unifies the above to answer the question: - "Can the environment still interact with this object?" - */ - inline bool isGone() const - { return m_pending_removal || m_pending_deactivation; } - - /* - Whether the object's static data has been stored to a block - */ - bool m_static_exists = false; - /* - The block from which the object was loaded from, and in which - a copy of the static data resides. - */ - v3s16 m_static_block = v3s16(1337,1337,1337); - -protected: - virtual void onAttach(int parent_id) {} - virtual void onDetach(int parent_id) {} - - // Used for creating objects based on type - typedef ServerActiveObject* (*Factory) - (ServerEnvironment *env, v3f pos, - const std::string &data); - static void registerType(u16 type, Factory f); - - ServerEnvironment *m_env; - v3f m_base_position; - std::unordered_set m_attached_particle_spawners; - - /* - Queue of messages to be sent to the client - */ - std::queue m_messages_out; - -private: - // Used for creating objects based on type - static std::map m_types; -}; diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index 7b2fd8236..816ec2c59 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -402,16 +402,14 @@ src/script/scripting_server.cpp src/script/scripting_server.h src/serialization.cpp src/serialization.h -src/serveractiveobjectmap.cpp -src/serveractiveobjectmap.h src/server.cpp src/serverenvironment.cpp src/serverenvironment.h src/server.h src/serverlist.cpp src/serverlist.h -src/serverobject.cpp -src/serverobject.h +src/server/serveractiveobject.cpp +src/server/serveractiveobject.h src/settings.cpp src/settings.h src/settings_translation_file.cpp -- cgit v1.2.3 From c99e8df07fe88c0b19363beca09e12f151bc13d0 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 11 Apr 2020 09:35:27 +0200 Subject: pass clang-format --- src/server/unit_sao.cpp | 44 +++++++++++++++++++++----------------------- src/server/unit_sao.h | 29 +++++++++++++++++------------ 2 files changed, 38 insertions(+), 35 deletions(-) (limited to 'src/server') diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index 66be67522..b30b7a76b 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -26,8 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., UnitSAO */ -UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos) +UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos) : ServerActiveObject(env, pos) { // Initialize something to armor groups m_armor_groups["fleshy"] = 100; @@ -54,7 +53,8 @@ const ItemGroupList &UnitSAO::getArmorGroups() const return m_armor_groups; } -void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) +void UnitSAO::setAnimation( + v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) { // store these so they can be updated to clients m_animation_range = frame_range; @@ -64,7 +64,8 @@ void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend m_animation_sent = false; } -void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop) +void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, + bool *frame_loop) { *frame_range = m_animation_range; *frame_speed = m_animation_speed; @@ -91,14 +92,15 @@ void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotat *rotation = m_bone_position[bone].Y; } -void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) +void UnitSAO::setAttachment( + int parent_id, const 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 + // 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. int old_parent = m_attachment_parent_id; @@ -114,8 +116,8 @@ void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position } } -void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const +void UnitSAO::getAttachment( + int *parent_id, std::string *bone, v3f *position, v3f *rotation) const { *parent_id = m_attachment_parent_id; *bone = m_attachment_bone; @@ -194,7 +196,7 @@ void UnitSAO::onDetach(int parent_id) m_env->getScriptIface()->luaentity_on_detach_child(parent_id, this); } -ObjectProperties* UnitSAO::accessObjectProperties() +ObjectProperties *UnitSAO::accessObjectProperties() { return &m_prop; } @@ -217,8 +219,8 @@ std::string UnitSAO::generateUpdateAttachmentCommand() const return os.str(); } -std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation) +std::string UnitSAO::generateUpdateBonePositionCommand( + const std::string &bone, const v3f &position, const v3f &rotation) { std::ostringstream os(std::ios::binary); // command @@ -230,7 +232,6 @@ std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, return os.str(); } - std::string UnitSAO::generateUpdateAnimationSpeedCommand() const { std::ostringstream os(std::ios::binary); @@ -255,23 +256,21 @@ std::string UnitSAO::generateUpdateAnimationCommand() const return os.str(); } - std::string UnitSAO::generateUpdateArmorGroupsCommand() const { std::ostringstream os(std::ios::binary); writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS); writeU16(os, m_armor_groups.size()); for (const auto &armor_group : m_armor_groups) { - os< &getAttachmentChildIds() const; ServerActiveObject *getParent() const; - ObjectProperties* accessObjectProperties(); + ObjectProperties *accessObjectProperties(); void notifyObjectPropertiesModified(); std::string generateUpdateAttachmentCommand() const; std::string generateUpdateAnimationSpeedCommand() const; std::string generateUpdateAnimationCommand() const; std::string generateUpdateArmorGroupsCommand() const; - static std::string generateUpdatePositionCommand(const v3f &position, const v3f &velocity, - const v3f &acceleration, const v3f &rotation, bool do_interpolate, - bool is_movement_end, f32 update_interval); + static std::string generateUpdatePositionCommand(const v3f &position, + const v3f &velocity, const v3f &acceleration, const v3f &rotation, + bool do_interpolate, bool is_movement_end, f32 update_interval); std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; void sendPunchCommand(); static std::string generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation); + const v3f &position, const v3f &rotation); protected: u16 m_hp = 1; @@ -105,6 +109,7 @@ protected: v3f m_attachment_position; v3f m_attachment_rotation; bool m_attachment_sent = false; + private: void onAttach(int parent_id); void onDetach(int parent_id); -- cgit v1.2.3 From 894a34aef48024a752a1ef151d046955d83858d0 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 11 Apr 2020 09:59:09 +0200 Subject: Move PlayerSAO to dedicated files --- src/clientiface.cpp | 3 +- src/content_sao.cpp | 676 +--------------------------------- src/content_sao.h | 299 --------------- src/database/database-files.cpp | 2 +- src/database/database-postgresql.cpp | 2 +- src/database/database-sqlite3.cpp | 2 +- src/network/serverpackethandler.cpp | 2 +- src/remoteplayer.cpp | 2 +- src/script/common/c_content.cpp | 3 +- src/script/cpp_api/s_base.cpp | 2 +- src/script/cpp_api/s_player.h | 1 + src/script/lua_api/l_env.cpp | 3 +- src/script/lua_api/l_object.cpp | 2 +- src/server.cpp | 2 +- src/server/CMakeLists.txt | 1 + src/server/player_sao.cpp | 695 +++++++++++++++++++++++++++++++++++ src/server/player_sao.h | 325 ++++++++++++++++ src/server/unit_sao.cpp | 4 - src/serverenvironment.cpp | 4 +- 19 files changed, 1038 insertions(+), 992 deletions(-) create mode 100644 src/server/player_sao.cpp create mode 100644 src/server/player_sao.h (limited to 'src/server') diff --git a/src/clientiface.cpp b/src/clientiface.cpp index dceaa64f2..17237f73e 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "clientiface.h" +#include "content_sao.h" #include "network/connection.h" #include "network/serveropcodes.h" #include "remoteplayer.h" @@ -27,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverenvironment.h" #include "map.h" #include "emerge.h" -#include "content_sao.h" // TODO this is used for cleanup of only +#include "server/player_sao.h" #include "log.h" #include "util/srp.h" #include "face_position_cache.h" diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 0d387b53a..7ec17aa82 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server.h" #include "scripting_server.h" +#include "server/player_sao.h" #include "settings.h" #include #include @@ -678,678 +679,3 @@ bool LuaEntitySAO::collideWithObjects() const { return m_prop.collideWithObjects; } - -/* - PlayerSAO -*/ - -// No prototype, PlayerSAO does not need to be deserialized - -PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, - bool is_singleplayer): - UnitSAO(env_, v3f(0,0,0)), - m_player(player_), - m_peer_id(peer_id_), - m_is_singleplayer(is_singleplayer) -{ - SANITY_CHECK(m_peer_id != PEER_ID_INEXISTENT); - - m_prop.hp_max = PLAYER_MAX_HP_DEFAULT; - m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT; - m_prop.physical = false; - m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); - m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); - m_prop.pointable = true; - // Start of default appearance, this should be overwritten by Lua - m_prop.visual = "upright_sprite"; - m_prop.visual_size = v3f(1, 2, 1); - m_prop.textures.clear(); - m_prop.textures.emplace_back("player.png"); - m_prop.textures.emplace_back("player_back.png"); - m_prop.colors.clear(); - m_prop.colors.emplace_back(255, 255, 255, 255); - m_prop.spritediv = v2s16(1,1); - m_prop.eye_height = 1.625f; - // End of default appearance - m_prop.is_visible = true; - m_prop.backface_culling = false; - m_prop.makes_footstep_sound = true; - m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; - m_hp = m_prop.hp_max; - m_breath = m_prop.breath_max; - // Disable zoom in survival mode using a value of 0 - m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f; - - if (!g_settings->getBool("enable_damage")) - m_armor_groups["immortal"] = 1; -} - -void PlayerSAO::finalize(RemotePlayer *player, const std::set &privs) -{ - assert(player); - m_player = player; - m_privs = privs; -} - -v3f PlayerSAO::getEyeOffset() const -{ - return v3f(0, BS * m_prop.eye_height, 0); -} - -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_base_position); - m_player->setPlayerSAO(this); - m_player->setPeerId(m_peer_id); - m_last_good_position = m_base_position; -} - -// Called before removing from environment -void PlayerSAO::removingFromEnvironment() -{ - ServerActiveObject::removingFromEnvironment(); - if (m_player->getPlayerSAO() == this) { - unlinkPlayerSessionAndSave(); - for (u32 attached_particle_spawner : m_attached_particle_spawners) { - m_env->deleteParticleSpawner(attached_particle_spawner, false); - } - } -} - -std::string PlayerSAO::getClientInitializationData(u16 protocol_version) -{ - std::ostringstream os(std::ios::binary); - - // Protocol >= 15 - writeU8(os, 1); // version - os << serializeString(m_player->getName()); // name - writeU8(os, 1); // is_player - writeS16(os, getId()); // id - writeV3F32(os, m_base_position); - writeV3F32(os, m_rotation); - writeU16(os, getHP()); - - std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size - } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 - msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 - // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. - msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 - int message_count = 6 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - ii != m_attachment_child_ids.end(); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { - message_count++; - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); - } - } - - writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); - - // return result - return os.str(); -} - -void PlayerSAO::getStaticData(std::string * result) const -{ - FATAL_ERROR("Obsolete function"); -} - -void PlayerSAO::step(float dtime, bool send_recommended) -{ - if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) { - // Get nose/mouth position, approximate with eye position - v3s16 p = floatToInt(getEyePosition(), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - // If node generates drown - if (c.drowning > 0 && m_hp > 0) { - if (m_breath > 0) - setBreath(m_breath - 1); - - // No more breath, damage player - if (m_breath == 0) { - PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING); - setHP(m_hp - c.drowning, reason); - m_env->getGameDef()->SendPlayerHPOrDie(this, reason); - } - } - } - - if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) { - // Get nose/mouth position, approximate with eye position - v3s16 p = floatToInt(getEyePosition(), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - // If player is alive & not drowning & not in ignore & not immortal, breathe - if (m_breath < m_prop.breath_max && c.drowning == 0 && - n.getContent() != CONTENT_IGNORE && m_hp > 0) - setBreath(m_breath + 1); - } - - if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) { - u32 damage_per_second = 0; - std::string nodename; - // Lowest and highest damage points are 0.1 within collisionbox - float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f; - - // Sequence of damage points, starting 0.1 above feet and progressing - // upwards in 1 node intervals, stopping below top damage point. - for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) { - v3s16 p = floatToInt(m_base_position + - v3f(0.0f, dam_height * BS, 0.0f), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - if (c.damage_per_second > damage_per_second) { - damage_per_second = c.damage_per_second; - nodename = c.name; - } - } - - // Top damage point - v3s16 ptop = floatToInt(m_base_position + - v3f(0.0f, dam_top * BS, 0.0f), BS); - MapNode ntop = m_env->getMap().getNode(ptop); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(ntop); - if (c.damage_per_second > damage_per_second) { - damage_per_second = c.damage_per_second; - nodename = c.name; - } - - if (damage_per_second != 0 && m_hp > 0) { - s32 newhp = (s32)m_hp - (s32)damage_per_second; - PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE, nodename); - setHP(newhp, reason); - m_env->getGameDef()->SendPlayerHPOrDie(this, reason); - } - } - - 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(aom); - m_env->getScriptIface()->player_event(this, "properties_changed"); - } - - // 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.0f, 0.0f, 0.0f); - m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); - setBasePosition(m_last_good_position); - m_env->getGameDef()->SendMovePlayer(m_peer_id); - } - - //dstream<<"PlayerSAO::step: dtime: "<getMaxLagEstimate() * 2.0f; - 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_teleport += dtime; - m_time_from_last_punch += dtime; - m_nocheat_dig_time += dtime; - m_max_speed_override_time = MYMAX(m_max_speed_override_time - dtime, 0.0f); - - // 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; - setBasePosition(pos); - } - - if (!send_recommended) - return; - - if (m_position_not_sent) { - m_position_not_sent = false; - float update_interval = m_env->getSendRecommendedInterval(); - v3f pos; - // When attached, the position is only sent to clients where the - // parent isn't known - if (isAttached()) - pos = m_last_good_position; - else - pos = m_base_position; - - std::string str = generateUpdatePositionCommand( - pos, - v3f(0.0f, 0.0f, 0.0f), - v3f(0.0f, 0.0f, 0.0f), - m_rotation, - true, - false, - update_interval - ); - // create message and add to list - m_messages_out.emplace(getId(), false, str); - } - - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - - if (!m_physics_override_sent) { - m_physics_override_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); - } - - if (!m_animation_sent) { - m_animation_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } -} - -std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); - // parameters - writeF32(os, m_physics_override_speed); - writeF32(os, m_physics_override_jump); - writeF32(os, m_physics_override_gravity); - // these are sent inverted so we get true when the server sends nothing - writeU8(os, !m_physics_override_sneak); - writeU8(os, !m_physics_override_sneak_glitch); - writeU8(os, !m_physics_override_new_move); - return os.str(); -} - -void PlayerSAO::setBasePosition(const v3f &position) -{ - if (m_player && position != m_base_position) - m_player->setDirty(true); - - // This needs to be ran for attachments too - ServerActiveObject::setBasePosition(position); - - // Updating is not wanted/required for player migration - if (m_env) { - m_position_not_sent = true; - } -} - -void PlayerSAO::setPos(const v3f &pos) -{ - if(isAttached()) - return; - - // Send mapblock of target location - v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE); - m_env->getGameDef()->SendBlock(m_peer_id, blockpos); - - setBasePosition(pos); - // Movement caused by this command is always valid - m_last_good_position = pos; - m_move_pool.empty(); - m_time_from_last_teleport = 0.0; - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::moveTo(v3f pos, bool continuous) -{ - if(isAttached()) - return; - - setBasePosition(pos); - // Movement caused by this command is always valid - m_last_good_position = pos; - m_move_pool.empty(); - m_time_from_last_teleport = 0.0; - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::setPlayerYaw(const float yaw) -{ - v3f rotation(0, yaw, 0); - if (m_player && yaw != m_rotation.Y) - m_player->setDirty(true); - - // Set player model yaw, not look view - UnitSAO::setRotation(rotation); -} - -void PlayerSAO::setFov(const float fov) -{ - if (m_player && fov != m_fov) - m_player->setDirty(true); - - m_fov = fov; -} - -void PlayerSAO::setWantedRange(const s16 range) -{ - if (m_player && range != m_wanted_range) - m_player->setDirty(true); - - m_wanted_range = range; -} - -void PlayerSAO::setPlayerYawAndSend(const float yaw) -{ - setPlayerYaw(yaw); - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::setLookPitch(const float pitch) -{ - if (m_player && pitch != m_pitch) - m_player->setDirty(true); - - m_pitch = pitch; -} - -void PlayerSAO::setLookPitchAndSend(const float pitch) -{ - setLookPitch(pitch); - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -u16 PlayerSAO::punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch) -{ - if (!toolcap) - return 0; - - FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); - - // No effect if PvP disabled or if immortal - if (isImmortal() || !g_settings->getBool("enable_pvp")) { - if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - // create message and add to list - sendPunchCommand(); - return 0; - } - } - - s32 old_hp = getHP(); - HitParams hitparams = getHitParams(m_armor_groups, toolcap, - time_from_last_punch); - - PlayerSAO *playersao = m_player->getPlayerSAO(); - - bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao, - puncher, time_from_last_punch, toolcap, dir, - hitparams.hp); - - if (!damage_handled) { - setHP((s32)getHP() - (s32)hitparams.hp, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); - } else { // override client prediction - if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - // create message and add to list - sendPunchCommand(); - } - } - - actionstream << puncher->getDescription() << " (id=" << puncher->getId() << - ", hp=" << puncher->getHP() << ") punched " << - getDescription() << " (id=" << m_id << ", hp=" << m_hp << - "), damage=" << (old_hp - (s32)getHP()) << - (damage_handled ? " (handled by Lua)" : "") << std::endl; - - return hitparams.wear; -} - -void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason) -{ - s32 oldhp = m_hp; - - hp = rangelim(hp, 0, m_prop.hp_max); - - if (oldhp != hp) { - s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason); - if (hp_change == 0) - return; - - hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max); - } - - if (hp < oldhp && isImmortal()) - return; - - m_hp = hp; - - // Update properties on death - if ((hp == 0) != (oldhp == 0)) - m_properties_sent = false; -} - -void PlayerSAO::setBreath(const u16 breath, bool send) -{ - if (m_player && breath != m_breath) - m_player->setDirty(true); - - m_breath = rangelim(breath, 0, m_prop.breath_max); - - if (send) - m_env->getGameDef()->SendPlayerBreath(this); -} - -Inventory *PlayerSAO::getInventory() const -{ - return m_player ? &m_player->inventory : nullptr; -} - -InventoryLocation PlayerSAO::getInventoryLocation() const -{ - InventoryLocation loc; - loc.setPlayer(m_player->getName()); - return loc; -} - -u16 PlayerSAO::getWieldIndex() const -{ - return m_player->getWieldIndex(); -} - -ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const -{ - return m_player->getWieldedItem(selected, hand); -} - -bool PlayerSAO::setWieldedItem(const ItemStack &item) -{ - InventoryList *mlist = m_player->inventory.getList(getWieldList()); - if (mlist) { - mlist->changeItem(m_player->getWieldIndex(), item); - return true; - } - return false; -} - -void PlayerSAO::disconnected() -{ - m_peer_id = PEER_ID_INEXISTENT; - m_pending_removal = true; -} - -void PlayerSAO::unlinkPlayerSessionAndSave() -{ - assert(m_player->getPlayerSAO() == this); - m_player->setPeerId(PEER_ID_INEXISTENT); - m_env->savePlayer(m_player); - m_player->setPlayerSAO(NULL); - m_env->removePlayer(m_player); -} - -std::string PlayerSAO::getPropertyPacket() -{ - m_prop.is_visible = (true); - return generateSetPropertiesCommand(m_prop); -} - -void PlayerSAO::setMaxSpeedOverride(const v3f &vel) -{ - if (m_max_speed_override_time == 0.0f) - m_max_speed_override = vel; - else - m_max_speed_override += vel; - if (m_player) { - float accel = MYMIN(m_player->movement_acceleration_default, - m_player->movement_acceleration_air); - m_max_speed_override_time = m_max_speed_override.getLength() / accel / BS; - } -} - -bool PlayerSAO::checkMovementCheat() -{ - if (isAttached() || m_is_singleplayer || - g_settings->getBool("disable_anticheat")) { - m_last_good_position = m_base_position; - return false; - } - - bool cheated = false; - /* - 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 override_max_H, override_max_V; - if (m_max_speed_override_time > 0.0f) { - override_max_H = MYMAX(fabs(m_max_speed_override.X), fabs(m_max_speed_override.Z)); - override_max_V = fabs(m_max_speed_override.Y); - } else { - override_max_H = override_max_V = 0.0f; - } - - float player_max_walk = 0; // horizontal movement - float player_max_jump = 0; // vertical upwards movement - - if (m_privs.count("fast") != 0) - player_max_walk = m_player->movement_speed_fast; // Fast speed - else - player_max_walk = m_player->movement_speed_walk; // Normal speed - player_max_walk *= m_physics_override_speed; - player_max_walk = MYMAX(player_max_walk, override_max_H); - - player_max_jump = m_player->movement_speed_jump * m_physics_override_jump; - // FIXME: Bouncy nodes cause practically unbound increase in Y speed, - // until this can be verified correctly, tolerate higher jumping speeds - player_max_jump *= 2.0; - player_max_jump = MYMAX(player_max_jump, override_max_V); - - // Don't divide by zero! - if (player_max_walk < 0.0001f) - player_max_walk = 0.0001f; - if (player_max_jump < 0.0001f) - player_max_jump = 0.0001f; - - v3f diff = (m_base_position - m_last_good_position); - float d_vert = diff.Y; - diff.Y = 0; - float d_horiz = diff.getLength(); - float required_time = d_horiz / player_max_walk; - - // FIXME: Checking downwards movement is not easily possible currently, - // the server could calculate speed differences to examine the gravity - if (d_vert > 0) { - // In certain cases (water, ladders) walking speed is applied vertically - float s = MYMAX(player_max_jump, player_max_walk); - required_time = MYMAX(required_time, d_vert / s); - } - - if (m_move_pool.grab(required_time)) { - m_last_good_position = m_base_position; - } else { - const float LAG_POOL_MIN = 5.0; - float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; - lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); - if (m_time_from_last_teleport > lag_pool_max) { - actionstream << "Player " << m_player->getName() - << " moved too fast; resetting position" - << std::endl; - cheated = true; - } - setBasePosition(m_last_good_position); - } - return cheated; -} - -bool PlayerSAO::getCollisionBox(aabb3f *toset) const -{ - //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; -} - -bool PlayerSAO::getSelectionBox(aabb3f *toset) const -{ - if (!m_prop.is_visible || !m_prop.pointable) { - return false; - } - - toset->MinEdge = m_prop.selectionbox.MinEdge * BS; - toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; - - return true; -} - -float PlayerSAO::getZoomFOV() const -{ - return m_prop.zoom_fov; -} diff --git a/src/content_sao.h b/src/content_sao.h index 32ab922d6..5387fd108 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -99,302 +99,3 @@ private: std::string m_current_texture_modifier = ""; }; -/* - PlayerSAO needs some internals exposed. -*/ - -class LagPool -{ - float m_pool = 15.0f; - float m_max = 15.0f; -public: - LagPool() = default; - - void setMax(float new_max) - { - m_max = new_max; - if(m_pool > new_max) - m_pool = new_max; - } - - void add(float dtime) - { - m_pool -= dtime; - if(m_pool < 0) - m_pool = 0; - } - - void empty() - { - m_pool = m_max; - } - - bool grab(float dtime) - { - if(dtime <= 0) - return true; - if(m_pool + dtime > m_max) - return false; - m_pool += dtime; - return true; - } -}; - -class RemotePlayer; - -class PlayerSAO : public UnitSAO -{ -public: - PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, - bool is_singleplayer); - - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_PLAYER; } - ActiveObjectType getSendType() const - { return ACTIVEOBJECT_TYPE_GENERIC; } - std::string getDescription(); - - /* - Active object <-> environment interface - */ - - void addedToEnvironment(u32 dtime_s); - void removingFromEnvironment(); - bool isStaticAllowed() const { return false; } - std::string getClientInitializationData(u16 protocol_version); - void getStaticData(std::string *result) const; - void step(float dtime, bool send_recommended); - void setBasePosition(const v3f &position); - void setPos(const v3f &pos); - void moveTo(v3f pos, bool continuous); - void setPlayerYaw(const float yaw); - // Data should not be sent at player initialization - void setPlayerYawAndSend(const float yaw); - void setLookPitch(const float pitch); - // Data should not be sent at player initialization - void setLookPitchAndSend(const float pitch); - f32 getLookPitch() const { return m_pitch; } - f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; } - // Deprecated - f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; } - void setFov(const float pitch); - f32 getFov() const { return m_fov; } - void setWantedRange(const s16 range); - s16 getWantedRange() const { return m_wanted_range; } - - /* - Interaction interface - */ - - u16 punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch); - void rightClick(ServerActiveObject *clicker) {} - void setHP(s32 hp, const PlayerHPChangeReason &reason); - void setHPRaw(u16 hp) { m_hp = hp; } - s16 readDamage(); - u16 getBreath() const { return m_breath; } - void setBreath(const u16 breath, bool send = true); - - /* - Inventory interface - */ - Inventory *getInventory() const; - InventoryLocation getInventoryLocation() const; - void setInventoryModified() {} - std::string getWieldList() const { return "main"; } - u16 getWieldIndex() const; - ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const; - bool setWieldedItem(const ItemStack &item); - - /* - PlayerSAO-specific - */ - - void disconnected(); - - RemotePlayer *getPlayer() { return m_player; } - session_t getPeerID() const { return m_peer_id; } - - // Cheat prevention - - v3f getLastGoodPosition() const - { - return m_last_good_position; - } - float resetTimeFromLastPunch() - { - float r = m_time_from_last_punch; - m_time_from_last_punch = 0.0; - return r; - } - void noCheatDigStart(const v3s16 &p) - { - m_nocheat_dig_pos = p; - m_nocheat_dig_time = 0; - } - v3s16 getNoCheatDigPos() - { - return m_nocheat_dig_pos; - } - float getNoCheatDigTime() - { - return m_nocheat_dig_time; - } - void noCheatDigEnd() - { - m_nocheat_dig_pos = v3s16(32767, 32767, 32767); - } - LagPool& getDigPool() - { - return m_dig_pool; - } - void setMaxSpeedOverride(const v3f &vel); - // Returns true if cheated - bool checkMovementCheat(); - - // Other - - void updatePrivileges(const std::set &privs, - bool is_singleplayer) - { - m_privs = privs; - m_is_singleplayer = is_singleplayer; - } - - bool getCollisionBox(aabb3f *toset) const; - bool getSelectionBox(aabb3f *toset) const; - bool collideWithObjects() const { return true; } - - void finalize(RemotePlayer *player, const std::set &privs); - - v3f getEyePosition() const { return m_base_position + getEyeOffset(); } - v3f getEyeOffset() const; - float getZoomFOV() const; - - inline Metadata &getMeta() { return m_meta; } - -private: - std::string getPropertyPacket(); - void unlinkPlayerSessionAndSave(); - std::string generateUpdatePhysicsOverrideCommand() const; - - RemotePlayer *m_player = nullptr; - session_t m_peer_id = 0; - - // Cheat prevention - LagPool m_dig_pool; - LagPool m_move_pool; - v3f m_last_good_position; - float m_time_from_last_teleport = 0.0f; - float m_time_from_last_punch = 0.0f; - v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767); - float m_nocheat_dig_time = 0.0f; - float m_max_speed_override_time = 0.0f; - v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f); - - // Timers - IntervalLimiter m_breathing_interval; - IntervalLimiter m_drowning_interval; - IntervalLimiter m_node_hurt_interval; - - bool m_position_not_sent = false; - - // Cached privileges for enforcement - std::set m_privs; - bool m_is_singleplayer; - - u16 m_breath = PLAYER_MAX_BREATH_DEFAULT; - f32 m_pitch = 0.0f; - f32 m_fov = 0.0f; - s16 m_wanted_range = 0.0f; - - Metadata m_meta; -public: - float m_physics_override_speed = 1.0f; - float m_physics_override_jump = 1.0f; - float m_physics_override_gravity = 1.0f; - bool m_physics_override_sneak = true; - bool m_physics_override_sneak_glitch = false; - bool m_physics_override_new_move = true; - bool m_physics_override_sent = false; -}; - - -struct PlayerHPChangeReason { - enum Type : u8 { - SET_HP, - PLAYER_PUNCH, - FALL, - NODE_DAMAGE, - DROWNING, - RESPAWN - }; - - Type type = SET_HP; - bool from_mod = false; - int lua_reference = -1; - - // For PLAYER_PUNCH - ServerActiveObject *object = nullptr; - // For NODE_DAMAGE - std::string node; - - inline bool hasLuaReference() const - { - return lua_reference >= 0; - } - - bool setTypeFromString(const std::string &typestr) - { - if (typestr == "set_hp") - type = SET_HP; - else if (typestr == "punch") - type = PLAYER_PUNCH; - else if (typestr == "fall") - type = FALL; - else if (typestr == "node_damage") - type = NODE_DAMAGE; - else if (typestr == "drown") - type = DROWNING; - else if (typestr == "respawn") - type = RESPAWN; - else - return false; - - return true; - } - - std::string getTypeAsString() const - { - switch (type) { - case PlayerHPChangeReason::SET_HP: - return "set_hp"; - case PlayerHPChangeReason::PLAYER_PUNCH: - return "punch"; - case PlayerHPChangeReason::FALL: - return "fall"; - case PlayerHPChangeReason::NODE_DAMAGE: - return "node_damage"; - case PlayerHPChangeReason::DROWNING: - return "drown"; - case PlayerHPChangeReason::RESPAWN: - return "respawn"; - default: - return "?"; - } - } - - PlayerHPChangeReason(Type type): - type(type) - {} - - PlayerHPChangeReason(Type type, ServerActiveObject *object): - type(type), object(object) - {} - - PlayerHPChangeReason(Type type, std::string node): - type(type), node(node) - {} -}; diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp index d09f1c074..d2b0b1543 100644 --- a/src/database/database-files.cpp +++ b/src/database/database-files.cpp @@ -20,11 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "database-files.h" -#include "content_sao.h" #include "remoteplayer.h" #include "settings.h" #include "porting.h" #include "filesys.h" +#include "server/player_sao.h" #include "util/string.h" // !!! WARNING !!! diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp index d7c94ff15..77385e240 100644 --- a/src/database/database-postgresql.cpp +++ b/src/database/database-postgresql.cpp @@ -36,8 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "exceptions.h" #include "settings.h" -#include "content_sao.h" #include "remoteplayer.h" +#include "server/player_sao.h" Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) : m_connect_string(connect_string) diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 1bacdfe6c..4560743b9 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -33,8 +33,8 @@ SQLite format specification: #include "settings.h" #include "porting.h" #include "util/string.h" -#include "content_sao.h" #include "remoteplayer.h" +#include "server/player_sao.h" #include diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 23bcc867f..b2fdb2a22 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chatmessage.h" #include "server.h" #include "log.h" -#include "content_sao.h" #include "emerge.h" #include "mapblock.h" #include "modchannels.h" @@ -34,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/connection.h" #include "network/networkprotocol.h" #include "network/serveropcodes.h" +#include "server/player_sao.h" #include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 1a8fec68c..7a603d53e 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -20,13 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include -#include "content_sao.h" #include "filesys.h" #include "gamedef.h" #include "porting.h" // strlcpy #include "server.h" #include "settings.h" #include "convert_json.h" +#include "server/player_sao.h" /* RemotePlayer diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 60f12052f..c8cd7539f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_types.h" #include "nodedef.h" #include "object_properties.h" -#include "content_sao.h" #include "cpp_api/s_node.h" #include "lua_api/l_object.h" #include "lua_api/l_item.h" @@ -29,10 +28,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "log.h" #include "tool.h" -#include "server/serveractiveobject.h" #include "porting.h" #include "mapgen/mg_schematic.h" #include "noise.h" +#include "server/player_sao.h" #include "util/pointedthing.h" #include "debug.h" // For FATAL_ERROR #include diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 16c20eeae..150baf77e 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_security.h" #include "lua_api/l_object.h" #include "common/c_converter.h" -#include "server/serveractiveobject.h" +#include "server/player_sao.h" #include "filesys.h" #include "content/mods.h" #include "porting.h" diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h index cf24ddc73..7ca3d8f30 100644 --- a/src/script/cpp_api/s_player.h +++ b/src/script/cpp_api/s_player.h @@ -28,6 +28,7 @@ struct InventoryLocation; struct ItemStack; struct ToolCapabilities; struct PlayerHPChangeReason; +class ServerActiveObject; class ScriptApiPlayer : virtual public ScriptApiBase { diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 31e582d3d..438669feb 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "lua_api/l_env.h" #include "lua_api/l_internal.h" #include "lua_api/l_nodemeta.h" @@ -25,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" -#include #include "scripting_server.h" #include "environment.h" #include "mapblock.h" @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "pathfinder.h" #include "face_position_cache.h" #include "remoteplayer.h" +#include "server/player_sao.h" #ifndef SERVER #include "client/client.h" #endif diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 1ea144a1c..95c96235e 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -27,12 +27,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" #include "tool.h" -#include "server/serveractiveobject.h" #include "content_sao.h" #include "remoteplayer.h" #include "server.h" #include "hud.h" #include "scripting_server.h" +#include "server/player_sao.h" /* ObjectRef diff --git a/src/server.cpp b/src/server.cpp index 529466f6b..85d07fbc4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -47,7 +47,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen/mg_biome.h" #include "content_mapnode.h" #include "content_nodemeta.h" -#include "content_sao.h" #include "content/mods.h" #include "modchannels.h" #include "serverlist.h" @@ -64,6 +63,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chatmessage.h" #include "chat_interface.h" #include "remoteplayer.h" +#include "server/player_sao.h" class ClientNotFoundException : public BaseException { diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 9fa5ed9fa..26eaed5ac 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,6 +1,7 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp new file mode 100644 index 000000000..58fcea5fe --- /dev/null +++ b/src/server/player_sao.cpp @@ -0,0 +1,695 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#include "player_sao.h" +#include "nodedef.h" +#include "remoteplayer.h" +#include "scripting_server.h" +#include "server.h" +#include "serverenvironment.h" + +PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, + bool is_singleplayer): + UnitSAO(env_, v3f(0,0,0)), + m_player(player_), + m_peer_id(peer_id_), + m_is_singleplayer(is_singleplayer) +{ + SANITY_CHECK(m_peer_id != PEER_ID_INEXISTENT); + + m_prop.hp_max = PLAYER_MAX_HP_DEFAULT; + m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT; + m_prop.physical = false; + m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); + m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); + m_prop.pointable = true; + // Start of default appearance, this should be overwritten by Lua + m_prop.visual = "upright_sprite"; + m_prop.visual_size = v3f(1, 2, 1); + m_prop.textures.clear(); + m_prop.textures.emplace_back("player.png"); + m_prop.textures.emplace_back("player_back.png"); + m_prop.colors.clear(); + m_prop.colors.emplace_back(255, 255, 255, 255); + m_prop.spritediv = v2s16(1,1); + m_prop.eye_height = 1.625f; + // End of default appearance + m_prop.is_visible = true; + m_prop.backface_culling = false; + m_prop.makes_footstep_sound = true; + m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; + m_hp = m_prop.hp_max; + m_breath = m_prop.breath_max; + // Disable zoom in survival mode using a value of 0 + m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f; + + if (!g_settings->getBool("enable_damage")) + m_armor_groups["immortal"] = 1; +} + +void PlayerSAO::finalize(RemotePlayer *player, const std::set &privs) +{ + assert(player); + m_player = player; + m_privs = privs; +} + +v3f PlayerSAO::getEyeOffset() const +{ + return v3f(0, BS * m_prop.eye_height, 0); +} + +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_base_position); + m_player->setPlayerSAO(this); + m_player->setPeerId(m_peer_id); + m_last_good_position = m_base_position; +} + +// Called before removing from environment +void PlayerSAO::removingFromEnvironment() +{ + ServerActiveObject::removingFromEnvironment(); + if (m_player->getPlayerSAO() == this) { + unlinkPlayerSessionAndSave(); + for (u32 attached_particle_spawner : m_attached_particle_spawners) { + m_env->deleteParticleSpawner(attached_particle_spawner, false); + } + } +} + +std::string PlayerSAO::getClientInitializationData(u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + + // Protocol >= 15 + writeU8(os, 1); // version + os << serializeString(m_player->getName()); // name + writeU8(os, 1); // is_player + writeS16(os, getId()); // id + writeV3F32(os, m_base_position); + writeV3F32(os, m_rotation); + writeU16(os, getHP()); + + std::ostringstream msg_os(std::ios::binary); + msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + } + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. + msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 + int message_count = 6 + m_bone_position.size(); + for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); + ii != m_attachment_child_ids.end(); ++ii) { + if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + message_count++; + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + } + } + + writeU8(os, message_count); + os.write(msg_os.str().c_str(), msg_os.str().size()); + + // return result + return os.str(); +} + +void PlayerSAO::getStaticData(std::string * result) const +{ + FATAL_ERROR("Obsolete function"); +} + +void PlayerSAO::step(float dtime, bool send_recommended) +{ + if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) { + // Get nose/mouth position, approximate with eye position + v3s16 p = floatToInt(getEyePosition(), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + // If node generates drown + if (c.drowning > 0 && m_hp > 0) { + if (m_breath > 0) + setBreath(m_breath - 1); + + // No more breath, damage player + if (m_breath == 0) { + PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING); + setHP(m_hp - c.drowning, reason); + m_env->getGameDef()->SendPlayerHPOrDie(this, reason); + } + } + } + + if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) { + // Get nose/mouth position, approximate with eye position + v3s16 p = floatToInt(getEyePosition(), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + // If player is alive & not drowning & not in ignore & not immortal, breathe + if (m_breath < m_prop.breath_max && c.drowning == 0 && + n.getContent() != CONTENT_IGNORE && m_hp > 0) + setBreath(m_breath + 1); + } + + if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) { + u32 damage_per_second = 0; + std::string nodename; + // Lowest and highest damage points are 0.1 within collisionbox + float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f; + + // Sequence of damage points, starting 0.1 above feet and progressing + // upwards in 1 node intervals, stopping below top damage point. + for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) { + v3s16 p = floatToInt(m_base_position + + v3f(0.0f, dam_height * BS, 0.0f), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + if (c.damage_per_second > damage_per_second) { + damage_per_second = c.damage_per_second; + nodename = c.name; + } + } + + // Top damage point + v3s16 ptop = floatToInt(m_base_position + + v3f(0.0f, dam_top * BS, 0.0f), BS); + MapNode ntop = m_env->getMap().getNode(ptop); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(ntop); + if (c.damage_per_second > damage_per_second) { + damage_per_second = c.damage_per_second; + nodename = c.name; + } + + if (damage_per_second != 0 && m_hp > 0) { + s32 newhp = (s32)m_hp - (s32)damage_per_second; + PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE, nodename); + setHP(newhp, reason); + m_env->getGameDef()->SendPlayerHPOrDie(this, reason); + } + } + + 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(aom); + m_env->getScriptIface()->player_event(this, "properties_changed"); + } + + // 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.0f, 0.0f, 0.0f); + m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); + setBasePosition(m_last_good_position); + m_env->getGameDef()->SendMovePlayer(m_peer_id); + } + + //dstream<<"PlayerSAO::step: dtime: "<getMaxLagEstimate() * 2.0f; + 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_teleport += dtime; + m_time_from_last_punch += dtime; + m_nocheat_dig_time += dtime; + m_max_speed_override_time = MYMAX(m_max_speed_override_time - dtime, 0.0f); + + // 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; + setBasePosition(pos); + } + + if (!send_recommended) + return; + + if (m_position_not_sent) { + m_position_not_sent = false; + float update_interval = m_env->getSendRecommendedInterval(); + v3f pos; + // When attached, the position is only sent to clients where the + // parent isn't known + if (isAttached()) + pos = m_last_good_position; + else + pos = m_base_position; + + std::string str = generateUpdatePositionCommand( + pos, + v3f(0.0f, 0.0f, 0.0f), + v3f(0.0f, 0.0f, 0.0f), + m_rotation, + true, + false, + update_interval + ); + // create message and add to list + m_messages_out.emplace(getId(), false, str); + } + + if (!m_armor_groups_sent) { + m_armor_groups_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); + } + + if (!m_physics_override_sent) { + m_physics_override_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); + } + + if (!m_animation_sent) { + m_animation_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); + } + + if (!m_bone_position_sent) { + m_bone_position_sent = true; + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + std::string str = generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y); + // create message and add to list + m_messages_out.emplace(getId(), true, str); + } + } + + if (!m_attachment_sent) { + m_attachment_sent = true; + std::string str = generateUpdateAttachmentCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } +} + +std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); + // parameters + writeF32(os, m_physics_override_speed); + writeF32(os, m_physics_override_jump); + writeF32(os, m_physics_override_gravity); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_physics_override_sneak); + writeU8(os, !m_physics_override_sneak_glitch); + writeU8(os, !m_physics_override_new_move); + return os.str(); +} + +void PlayerSAO::setBasePosition(const v3f &position) +{ + if (m_player && position != m_base_position) + m_player->setDirty(true); + + // This needs to be ran for attachments too + ServerActiveObject::setBasePosition(position); + + // Updating is not wanted/required for player migration + if (m_env) { + m_position_not_sent = true; + } +} + +void PlayerSAO::setPos(const v3f &pos) +{ + if(isAttached()) + return; + + // Send mapblock of target location + v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE); + m_env->getGameDef()->SendBlock(m_peer_id, blockpos); + + setBasePosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::moveTo(v3f pos, bool continuous) +{ + if(isAttached()) + return; + + setBasePosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::setPlayerYaw(const float yaw) +{ + v3f rotation(0, yaw, 0); + if (m_player && yaw != m_rotation.Y) + m_player->setDirty(true); + + // Set player model yaw, not look view + UnitSAO::setRotation(rotation); +} + +void PlayerSAO::setFov(const float fov) +{ + if (m_player && fov != m_fov) + m_player->setDirty(true); + + m_fov = fov; +} + +void PlayerSAO::setWantedRange(const s16 range) +{ + if (m_player && range != m_wanted_range) + m_player->setDirty(true); + + m_wanted_range = range; +} + +void PlayerSAO::setPlayerYawAndSend(const float yaw) +{ + setPlayerYaw(yaw); + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::setLookPitch(const float pitch) +{ + if (m_player && pitch != m_pitch) + m_player->setDirty(true); + + m_pitch = pitch; +} + +void PlayerSAO::setLookPitchAndSend(const float pitch) +{ + setLookPitch(pitch); + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +u16 PlayerSAO::punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch) +{ + if (!toolcap) + return 0; + + FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); + + // No effect if PvP disabled or if immortal + if (isImmortal() || !g_settings->getBool("enable_pvp")) { + if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + // create message and add to list + sendPunchCommand(); + return 0; + } + } + + s32 old_hp = getHP(); + HitParams hitparams = getHitParams(m_armor_groups, toolcap, + time_from_last_punch); + + PlayerSAO *playersao = m_player->getPlayerSAO(); + + bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao, + puncher, time_from_last_punch, toolcap, dir, + hitparams.hp); + + if (!damage_handled) { + setHP((s32)getHP() - (s32)hitparams.hp, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); + } else { // override client prediction + if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + // create message and add to list + sendPunchCommand(); + } + } + + actionstream << puncher->getDescription() << " (id=" << puncher->getId() << + ", hp=" << puncher->getHP() << ") punched " << + getDescription() << " (id=" << m_id << ", hp=" << m_hp << + "), damage=" << (old_hp - (s32)getHP()) << + (damage_handled ? " (handled by Lua)" : "") << std::endl; + + return hitparams.wear; +} + +void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason) +{ + s32 oldhp = m_hp; + + hp = rangelim(hp, 0, m_prop.hp_max); + + if (oldhp != hp) { + s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason); + if (hp_change == 0) + return; + + hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max); + } + + if (hp < oldhp && isImmortal()) + return; + + m_hp = hp; + + // Update properties on death + if ((hp == 0) != (oldhp == 0)) + m_properties_sent = false; +} + +void PlayerSAO::setBreath(const u16 breath, bool send) +{ + if (m_player && breath != m_breath) + m_player->setDirty(true); + + m_breath = rangelim(breath, 0, m_prop.breath_max); + + if (send) + m_env->getGameDef()->SendPlayerBreath(this); +} + +Inventory *PlayerSAO::getInventory() const +{ + return m_player ? &m_player->inventory : nullptr; +} + +InventoryLocation PlayerSAO::getInventoryLocation() const +{ + InventoryLocation loc; + loc.setPlayer(m_player->getName()); + return loc; +} + +u16 PlayerSAO::getWieldIndex() const +{ + return m_player->getWieldIndex(); +} + +ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const +{ + return m_player->getWieldedItem(selected, hand); +} + +bool PlayerSAO::setWieldedItem(const ItemStack &item) +{ + InventoryList *mlist = m_player->inventory.getList(getWieldList()); + if (mlist) { + mlist->changeItem(m_player->getWieldIndex(), item); + return true; + } + return false; +} + +void PlayerSAO::disconnected() +{ + m_peer_id = PEER_ID_INEXISTENT; + m_pending_removal = true; +} + +void PlayerSAO::unlinkPlayerSessionAndSave() +{ + assert(m_player->getPlayerSAO() == this); + m_player->setPeerId(PEER_ID_INEXISTENT); + m_env->savePlayer(m_player); + m_player->setPlayerSAO(NULL); + m_env->removePlayer(m_player); +} + +std::string PlayerSAO::getPropertyPacket() +{ + m_prop.is_visible = (true); + return generateSetPropertiesCommand(m_prop); +} + +void PlayerSAO::setMaxSpeedOverride(const v3f &vel) +{ + if (m_max_speed_override_time == 0.0f) + m_max_speed_override = vel; + else + m_max_speed_override += vel; + if (m_player) { + float accel = MYMIN(m_player->movement_acceleration_default, + m_player->movement_acceleration_air); + m_max_speed_override_time = m_max_speed_override.getLength() / accel / BS; + } +} + +bool PlayerSAO::checkMovementCheat() +{ + if (isAttached() || m_is_singleplayer || + g_settings->getBool("disable_anticheat")) { + m_last_good_position = m_base_position; + return false; + } + + bool cheated = false; + /* + 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 override_max_H, override_max_V; + if (m_max_speed_override_time > 0.0f) { + override_max_H = MYMAX(fabs(m_max_speed_override.X), fabs(m_max_speed_override.Z)); + override_max_V = fabs(m_max_speed_override.Y); + } else { + override_max_H = override_max_V = 0.0f; + } + + float player_max_walk = 0; // horizontal movement + float player_max_jump = 0; // vertical upwards movement + + if (m_privs.count("fast") != 0) + player_max_walk = m_player->movement_speed_fast; // Fast speed + else + player_max_walk = m_player->movement_speed_walk; // Normal speed + player_max_walk *= m_physics_override_speed; + player_max_walk = MYMAX(player_max_walk, override_max_H); + + player_max_jump = m_player->movement_speed_jump * m_physics_override_jump; + // FIXME: Bouncy nodes cause practically unbound increase in Y speed, + // until this can be verified correctly, tolerate higher jumping speeds + player_max_jump *= 2.0; + player_max_jump = MYMAX(player_max_jump, override_max_V); + + // Don't divide by zero! + if (player_max_walk < 0.0001f) + player_max_walk = 0.0001f; + if (player_max_jump < 0.0001f) + player_max_jump = 0.0001f; + + v3f diff = (m_base_position - m_last_good_position); + float d_vert = diff.Y; + diff.Y = 0; + float d_horiz = diff.getLength(); + float required_time = d_horiz / player_max_walk; + + // FIXME: Checking downwards movement is not easily possible currently, + // the server could calculate speed differences to examine the gravity + if (d_vert > 0) { + // In certain cases (water, ladders) walking speed is applied vertically + float s = MYMAX(player_max_jump, player_max_walk); + required_time = MYMAX(required_time, d_vert / s); + } + + if (m_move_pool.grab(required_time)) { + m_last_good_position = m_base_position; + } else { + const float LAG_POOL_MIN = 5.0; + float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; + lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); + if (m_time_from_last_teleport > lag_pool_max) { + actionstream << "Player " << m_player->getName() + << " moved too fast; resetting position" + << std::endl; + cheated = true; + } + setBasePosition(m_last_good_position); + } + return cheated; +} + +bool PlayerSAO::getCollisionBox(aabb3f *toset) const +{ + //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; +} + +bool PlayerSAO::getSelectionBox(aabb3f *toset) const +{ + if (!m_prop.is_visible || !m_prop.pointable) { + return false; + } + + toset->MinEdge = m_prop.selectionbox.MinEdge * BS; + toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; + + return true; +} + +float PlayerSAO::getZoomFOV() const +{ + return m_prop.zoom_fov; +} diff --git a/src/server/player_sao.h b/src/server/player_sao.h new file mode 100644 index 000000000..ce1cb1677 --- /dev/null +++ b/src/server/player_sao.h @@ -0,0 +1,325 @@ + +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#include "constants.h" +#include "network/networkprotocol.h" +#include "unit_sao.h" +#include "util/numeric.h" + +/* + PlayerSAO needs some internals exposed. +*/ + +class LagPool +{ + float m_pool = 15.0f; + float m_max = 15.0f; +public: + LagPool() = default; + + void setMax(float new_max) + { + m_max = new_max; + if(m_pool > new_max) + m_pool = new_max; + } + + void add(float dtime) + { + m_pool -= dtime; + if(m_pool < 0) + m_pool = 0; + } + + void empty() + { + m_pool = m_max; + } + + bool grab(float dtime) + { + if(dtime <= 0) + return true; + if(m_pool + dtime > m_max) + return false; + m_pool += dtime; + return true; + } +}; + +class RemotePlayer; + +class PlayerSAO : public UnitSAO +{ +public: + PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, + bool is_singleplayer); + + ActiveObjectType getType() const + { return ACTIVEOBJECT_TYPE_PLAYER; } + ActiveObjectType getSendType() const + { return ACTIVEOBJECT_TYPE_GENERIC; } + std::string getDescription(); + + /* + Active object <-> environment interface + */ + + void addedToEnvironment(u32 dtime_s); + void removingFromEnvironment(); + bool isStaticAllowed() const { return false; } + std::string getClientInitializationData(u16 protocol_version); + void getStaticData(std::string *result) const; + void step(float dtime, bool send_recommended); + void setBasePosition(const v3f &position); + void setPos(const v3f &pos); + void moveTo(v3f pos, bool continuous); + void setPlayerYaw(const float yaw); + // Data should not be sent at player initialization + void setPlayerYawAndSend(const float yaw); + void setLookPitch(const float pitch); + // Data should not be sent at player initialization + void setLookPitchAndSend(const float pitch); + f32 getLookPitch() const { return m_pitch; } + f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; } + // Deprecated + f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; } + void setFov(const float pitch); + f32 getFov() const { return m_fov; } + void setWantedRange(const s16 range); + s16 getWantedRange() const { return m_wanted_range; } + + /* + Interaction interface + */ + + u16 punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch); + void rightClick(ServerActiveObject *clicker) {} + void setHP(s32 hp, const PlayerHPChangeReason &reason); + void setHPRaw(u16 hp) { m_hp = hp; } + s16 readDamage(); + u16 getBreath() const { return m_breath; } + void setBreath(const u16 breath, bool send = true); + + /* + Inventory interface + */ + Inventory *getInventory() const; + InventoryLocation getInventoryLocation() const; + void setInventoryModified() {} + std::string getWieldList() const { return "main"; } + u16 getWieldIndex() const; + ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const; + bool setWieldedItem(const ItemStack &item); + + /* + PlayerSAO-specific + */ + + void disconnected(); + + RemotePlayer *getPlayer() { return m_player; } + session_t getPeerID() const { return m_peer_id; } + + // Cheat prevention + + v3f getLastGoodPosition() const + { + return m_last_good_position; + } + float resetTimeFromLastPunch() + { + float r = m_time_from_last_punch; + m_time_from_last_punch = 0.0; + return r; + } + void noCheatDigStart(const v3s16 &p) + { + m_nocheat_dig_pos = p; + m_nocheat_dig_time = 0; + } + v3s16 getNoCheatDigPos() + { + return m_nocheat_dig_pos; + } + float getNoCheatDigTime() + { + return m_nocheat_dig_time; + } + void noCheatDigEnd() + { + m_nocheat_dig_pos = v3s16(32767, 32767, 32767); + } + LagPool& getDigPool() + { + return m_dig_pool; + } + void setMaxSpeedOverride(const v3f &vel); + // Returns true if cheated + bool checkMovementCheat(); + + // Other + + void updatePrivileges(const std::set &privs, + bool is_singleplayer) + { + m_privs = privs; + m_is_singleplayer = is_singleplayer; + } + + bool getCollisionBox(aabb3f *toset) const; + bool getSelectionBox(aabb3f *toset) const; + bool collideWithObjects() const { return true; } + + void finalize(RemotePlayer *player, const std::set &privs); + + v3f getEyePosition() const { return m_base_position + getEyeOffset(); } + v3f getEyeOffset() const; + float getZoomFOV() const; + + inline Metadata &getMeta() { return m_meta; } + +private: + std::string getPropertyPacket(); + void unlinkPlayerSessionAndSave(); + std::string generateUpdatePhysicsOverrideCommand() const; + + RemotePlayer *m_player = nullptr; + session_t m_peer_id = 0; + + // Cheat prevention + LagPool m_dig_pool; + LagPool m_move_pool; + v3f m_last_good_position; + float m_time_from_last_teleport = 0.0f; + float m_time_from_last_punch = 0.0f; + v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767); + float m_nocheat_dig_time = 0.0f; + float m_max_speed_override_time = 0.0f; + v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f); + + // Timers + IntervalLimiter m_breathing_interval; + IntervalLimiter m_drowning_interval; + IntervalLimiter m_node_hurt_interval; + + bool m_position_not_sent = false; + + // Cached privileges for enforcement + std::set m_privs; + bool m_is_singleplayer; + + u16 m_breath = PLAYER_MAX_BREATH_DEFAULT; + f32 m_pitch = 0.0f; + f32 m_fov = 0.0f; + s16 m_wanted_range = 0.0f; + + Metadata m_meta; +public: + float m_physics_override_speed = 1.0f; + float m_physics_override_jump = 1.0f; + float m_physics_override_gravity = 1.0f; + bool m_physics_override_sneak = true; + bool m_physics_override_sneak_glitch = false; + bool m_physics_override_new_move = true; + bool m_physics_override_sent = false; +}; + + +struct PlayerHPChangeReason { + enum Type : u8 { + SET_HP, + PLAYER_PUNCH, + FALL, + NODE_DAMAGE, + DROWNING, + RESPAWN + }; + + Type type = SET_HP; + bool from_mod = false; + int lua_reference = -1; + + // For PLAYER_PUNCH + ServerActiveObject *object = nullptr; + // For NODE_DAMAGE + std::string node; + + inline bool hasLuaReference() const + { + return lua_reference >= 0; + } + + bool setTypeFromString(const std::string &typestr) + { + if (typestr == "set_hp") + type = SET_HP; + else if (typestr == "punch") + type = PLAYER_PUNCH; + else if (typestr == "fall") + type = FALL; + else if (typestr == "node_damage") + type = NODE_DAMAGE; + else if (typestr == "drown") + type = DROWNING; + else if (typestr == "respawn") + type = RESPAWN; + else + return false; + + return true; + } + + std::string getTypeAsString() const + { + switch (type) { + case PlayerHPChangeReason::SET_HP: + return "set_hp"; + case PlayerHPChangeReason::PLAYER_PUNCH: + return "punch"; + case PlayerHPChangeReason::FALL: + return "fall"; + case PlayerHPChangeReason::NODE_DAMAGE: + return "node_damage"; + case PlayerHPChangeReason::DROWNING: + return "drown"; + case PlayerHPChangeReason::RESPAWN: + return "respawn"; + default: + return "?"; + } + } + + PlayerHPChangeReason(Type type): + type(type) + {} + + PlayerHPChangeReason(Type type, ServerActiveObject *object): + type(type), object(object) + {} + + PlayerHPChangeReason(Type type, std::string node): + type(type), node(node) + {} +}; diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index b30b7a76b..74b0508b8 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -22,10 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_server.h" #include "serverenvironment.h" -/* - UnitSAO - */ - UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos) : ServerActiveObject(env, pos) { // Initialize something to armor groups diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 0ccbd772b..c2ab5c07d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "serverenvironment.h" -#include "content_sao.h" #include "settings.h" #include "log.h" #include "mapblock.h" @@ -44,7 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_POSTGRESQL #include "database/database-postgresql.h" #endif -#include +#include "server/player_sao.h" #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" -- cgit v1.2.3 From 5146086a64d5eeb480948d612a008a2ec81455d4 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 11 Apr 2020 11:22:15 +0200 Subject: Drop content_sao.{cpp,h} Move LuaEntitySAO to a new dedicated file Drop TestSAO (useless object) Drop the old static startup initialized SAO factory, which was pretty useless. This factory was using a std::map for 2 elements, now just use a simple condition owned by ServerEnvironment, which will be lightweight, that will also drop a one time useful test on each LuaEntitySAO creation. This should reduce server load on massive SAO creation --- build/android/jni/Android.mk | 4 +- src/CMakeLists.txt | 1 - src/clientiface.cpp | 2 +- src/content_sao.cpp | 681 --------------------------------- src/content_sao.h | 101 ----- src/mapgen/mapgen.cpp | 1 - src/mapgen/mapgen_carpathian.cpp | 1 - src/mapgen/mapgen_flat.cpp | 1 - src/mapgen/mapgen_fractal.cpp | 1 - src/mapgen/mapgen_v5.cpp | 1 - src/mapgen/mapgen_v6.cpp | 1 - src/mapgen/mapgen_v7.cpp | 1 - src/script/cpp_api/s_base.cpp | 1 - src/script/lua_api/l_env.cpp | 2 +- src/script/lua_api/l_item.cpp | 1 - src/script/lua_api/l_object.cpp | 2 +- src/server/CMakeLists.txt | 1 + src/server/luaentity_sao.cpp | 581 ++++++++++++++++++++++++++++ src/server/luaentity_sao.h | 93 +++++ src/server/player_sao.h | 83 ++-- src/server/serveractiveobject.cpp | 33 -- src/server/serveractiveobject.h | 10 - src/serverenvironment.cpp | 19 +- src/serverenvironment.h | 2 + src/staticobject.cpp | 2 +- src/unittest/test_player.cpp | 1 - util/travis/clang-format-whitelist.txt | 4 +- 27 files changed, 731 insertions(+), 900 deletions(-) delete mode 100644 src/content_sao.cpp delete mode 100644 src/content_sao.h create mode 100644 src/server/luaentity_sao.cpp create mode 100644 src/server/luaentity_sao.h (limited to 'src/server') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 32a16c174..fdbfba9bc 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -161,7 +161,6 @@ LOCAL_SRC_FILES := \ jni/src/content/mods.cpp \ jni/src/content_nodemeta.cpp \ jni/src/content/packages.cpp \ - jni/src/content_sao.cpp \ jni/src/content/subgames.cpp \ jni/src/convert_json.cpp \ jni/src/craftdef.cpp \ @@ -258,8 +257,11 @@ LOCAL_SRC_FILES := \ jni/src/server.cpp \ jni/src/serverenvironment.cpp \ jni/src/serverlist.cpp \ + jni/src/server/luaentity_sao.cpp \ jni/src/server/mods.cpp \ + jni/src/server/player_sao.cpp \ jni/src/server/serveractiveobject.cpp \ + jni/src/server/unit_sao.cpp \ jni/src/settings.cpp \ jni/src/staticobject.cpp \ jni/src/tileanimation.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index faa117d41..0ebbd628c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -375,7 +375,6 @@ set(common_SRCS collision.cpp content_mapnode.cpp content_nodemeta.cpp - content_sao.cpp convert_json.cpp craftdef.cpp debug.cpp diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 17237f73e..4f954342a 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "clientiface.h" -#include "content_sao.h" #include "network/connection.h" #include "network/serveropcodes.h" #include "remoteplayer.h" @@ -28,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverenvironment.h" #include "map.h" #include "emerge.h" +#include "server/luaentity_sao.h" #include "server/player_sao.h" #include "log.h" #include "util/srp.h" diff --git a/src/content_sao.cpp b/src/content_sao.cpp deleted file mode 100644 index 7ec17aa82..000000000 --- a/src/content_sao.cpp +++ /dev/null @@ -1,681 +0,0 @@ -/* -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 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 Lesser General Public License for more details. - -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. -*/ - -#include "content_sao.h" -#include "util/serialize.h" -#include "collision.h" -#include "environment.h" -#include "tool.h" // For ToolCapabilities -#include "gamedef.h" -#include "nodedef.h" -#include "remoteplayer.h" -#include "server.h" -#include "scripting_server.h" -#include "server/player_sao.h" -#include "settings.h" -#include -#include - -std::map ServerActiveObject::m_types; - -/* - TestSAO -*/ - -class TestSAO : public ServerActiveObject -{ -public: - TestSAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos), - m_timer1(0), - m_age(0) - { - ServerActiveObject::registerType(getType(), create); - } - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_TEST; } - - static ServerActiveObject* create(ServerEnvironment *env, v3f pos, - const std::string &data) - { - return new TestSAO(env, pos); - } - - void step(float dtime, bool send_recommended) - { - m_age += dtime; - if(m_age > 10) - { - m_pending_removal = true; - return; - } - - m_base_position.Y += dtime * BS * 2; - if(m_base_position.Y > 8*BS) - m_base_position.Y = 2*BS; - - if (!send_recommended) - return; - - m_timer1 -= dtime; - if(m_timer1 < 0.0) - { - m_timer1 += 0.125; - - std::string data; - - data += itos(0); // 0 = position - data += " "; - data += itos(m_base_position.X); - data += " "; - data += itos(m_base_position.Y); - data += " "; - data += itos(m_base_position.Z); - - ActiveObjectMessage aom(getId(), false, data); - m_messages_out.push(aom); - } - } - - bool getCollisionBox(aabb3f *toset) const { return false; } - - virtual bool getSelectionBox(aabb3f *toset) const { return false; } - - bool collideWithObjects() const { return false; } - -private: - float m_timer1; - float m_age; -}; - -// Prototype (registers item for deserialization) -TestSAO proto_TestSAO(NULL, v3f(0,0,0)); - -/* - LuaEntitySAO -*/ - -// Prototype (registers item for deserialization) -LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", ""); - -LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, - const std::string &name, const std::string &state): - UnitSAO(env, pos), - m_init_name(name), - m_init_state(state) -{ - // Only register type if no environment supplied - if(env == NULL){ - ServerActiveObject::registerType(getType(), create); - return; - } -} - -LuaEntitySAO::~LuaEntitySAO() -{ - if(m_registered){ - m_env->getScriptIface()->luaentity_Remove(m_id); - } - - for (u32 attached_particle_spawner : m_attached_particle_spawners) { - m_env->deleteParticleSpawner(attached_particle_spawner, false); - } -} - -void LuaEntitySAO::addedToEnvironment(u32 dtime_s) -{ - ServerActiveObject::addedToEnvironment(dtime_s); - - // Create entity from name - m_registered = m_env->getScriptIface()-> - luaentity_Add(m_id, m_init_name.c_str()); - - if(m_registered){ - // Get properties - m_env->getScriptIface()-> - luaentity_GetProperties(m_id, this, &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, dtime_s); - } else { - m_prop.infotext = m_init_name; - } -} - -ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos, - const std::string &data) -{ - std::string name; - std::string state; - u16 hp = 1; - v3f velocity; - v3f rotation; - - while (!data.empty()) { // breakable, run for one iteration - std::istringstream is(data, std::ios::binary); - // 'version' does not allow to incrementally extend the parameter list thus - // we need another variable to build on top of 'version=1'. Ugly hack but worksâ„¢ - u8 version2 = 0; - u8 version = readU8(is); - - name = deSerializeString(is); - state = deSerializeLongString(is); - - if (version < 1) - break; - - hp = readU16(is); - velocity = readV3F1000(is); - // yaw must be yaw to be backwards-compatible - rotation.Y = readF1000(is); - - if (is.good()) // EOF for old formats - version2 = readU8(is); - - if (version2 < 1) // PROTOCOL_VERSION < 37 - break; - - // version2 >= 1 - rotation.X = readF1000(is); - rotation.Z = readF1000(is); - - // if (version2 < 2) - // break; - // - break; - } - // create object - infostream << "LuaEntitySAO::create(name=\"" << name << "\" state=\"" - << state << "\")" << std::endl; - LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state); - sao->m_hp = hp; - sao->m_velocity = velocity; - sao->m_rotation = rotation; - return sao; -} - -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(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; - - // 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){ - aabb3f 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)) { - float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI - + m_prop.automatic_face_movement_dir_offset; - float max_rotation_per_sec = - m_prop.automatic_face_movement_max_rotation_per_sec; - - if (max_rotation_per_sec > 0) { - m_rotation.Y = wrapDegrees_0_360(m_rotation.Y); - wrappedApproachShortest(m_rotation.Y, target_yaw, - dtime * max_rotation_per_sec, 360.f); - } else { - // Negative values of max_rotation_per_sec mean disabled. - m_rotation.Y = target_yaw; - } - } - } - - if(m_registered){ - m_env->getScriptIface()->luaentity_Step(m_id, dtime); - } - - if (!send_recommended) - return; - - 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 || - std::fabs(m_rotation.X - m_last_sent_rotation.X) > 1.0f || - std::fabs(m_rotation.Y - m_last_sent_rotation.Y) > 1.0f || - std::fabs(m_rotation.Z - m_last_sent_rotation.Z) > 1.0f) { - - sendPosition(true, false); - } - } - - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - - if (!m_animation_sent) { - m_animation_sent = true; - std::string str = generateUpdateAnimationCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } - - if (!m_animation_speed_sent) { - m_animation_speed_sent = true; - std::string str = generateUpdateAnimationSpeedCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } -} - -std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) -{ - std::ostringstream os(std::ios::binary); - - // PROTOCOL_VERSION >= 37 - writeU8(os, 1); // version - os << serializeString(""); // name - writeU8(os, 0); // is_player - writeU16(os, getId()); //id - writeV3F32(os, m_base_position); - writeV3F32(os, m_rotation); - writeU16(os, m_hp); - - std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size - } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 - int message_count = 4 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - (ii != m_attachment_child_ids.end()); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { - message_count++; - // TODO after a protocol bump: only send the object initialization data - // to older clients (superfluous since this message exists) - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); - } - } - - msg_os << serializeLongString(generateSetTextureModCommand()); - message_count++; - - writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); - - // return result - return os.str(); -} - -void LuaEntitySAO::getStaticData(std::string *result) const -{ - verbosestream<getScriptIface()-> - luaentity_GetStaticdata(m_id); - os<= 37 - - writeF1000(os, m_rotation.X); - writeF1000(os, m_rotation.Z); - - // - - *result = os.str(); -} - -u16 LuaEntitySAO::punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch) -{ - if (!m_registered) { - // Delete unknown LuaEntities when punched - m_pending_removal = true; - return 0; - } - - FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); - - s32 old_hp = getHP(); - ItemStack selected_item, hand_item; - ItemStack tool_item = puncher->getWieldedItem(&selected_item, &hand_item); - - PunchDamageResult result = getPunchDamage( - m_armor_groups, - toolcap, - &tool_item, - time_from_last_punch); - - bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher, - time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0); - - if (!damage_handled) { - if (result.did_punch) { - setHP((s32)getHP() - result.damage, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); - - // create message and add to list - sendPunchCommand(); - } - } - - if (getHP() == 0 && !isGone()) { - clearParentAttachment(); - clearChildAttachments(); - m_env->getScriptIface()->luaentity_on_death(m_id, puncher); - m_pending_removal = true; - } - - actionstream << puncher->getDescription() << " (id=" << puncher->getId() << - ", hp=" << puncher->getHP() << ") punched " << - getDescription() << " (id=" << m_id << ", hp=" << m_hp << - "), damage=" << (old_hp - (s32)getHP()) << - (damage_handled ? " (handled by Lua)" : "") << std::endl; - - // TODO: give Lua control over wear - return result.wear; -} - -void LuaEntitySAO::rightClick(ServerActiveObject *clicker) -{ - if (!m_registered) - return; - - m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker); -} - -void LuaEntitySAO::setPos(const 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); -} - -float LuaEntitySAO::getMinimumSavedMovement() -{ - return 0.1 * BS; -} - -std::string LuaEntitySAO::getDescription() -{ - std::ostringstream oss; - oss << "LuaEntitySAO \"" << m_init_name << "\" "; - auto pos = floatToInt(m_base_position, BS); - oss << "at " << PP(pos); - return oss.str(); -} - -void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason) -{ - m_hp = rangelim(hp, 0, U16_MAX); -} - -u16 LuaEntitySAO::getHP() const -{ - return m_hp; -} - -void LuaEntitySAO::setVelocity(v3f velocity) -{ - m_velocity = velocity; -} - -v3f LuaEntitySAO::getVelocity() -{ - return m_velocity; -} - -void LuaEntitySAO::setAcceleration(v3f acceleration) -{ - m_acceleration = acceleration; -} - -v3f LuaEntitySAO::getAcceleration() -{ - return m_acceleration; -} - -void LuaEntitySAO::setTextureMod(const std::string &mod) -{ - m_current_texture_modifier = mod; - // create message and add to list - m_messages_out.emplace(getId(), true, generateSetTextureModCommand()); -} - -std::string LuaEntitySAO::getTextureMod() const -{ - return m_current_texture_modifier; -} - - -std::string LuaEntitySAO::generateSetTextureModCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_TEXTURE_MOD); - // parameters - os << serializeString(m_current_texture_modifier); - return os.str(); -} - -std::string LuaEntitySAO::generateSetSpriteCommand(v2s16 p, u16 num_frames, - f32 framelength, bool select_horiz_by_yawpitch) -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_SPRITE); - // parameters - writeV2S16(os, p); - writeU16(os, num_frames); - writeF32(os, framelength); - writeU8(os, select_horiz_by_yawpitch); - return os.str(); -} - -void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength, - bool select_horiz_by_yawpitch) -{ - std::string str = generateSetSpriteCommand( - p, - num_frames, - framelength, - select_horiz_by_yawpitch - ); - // create message and add to list - m_messages_out.emplace(getId(), true, str); -} - -std::string LuaEntitySAO::getName() -{ - return m_init_name; -} - -std::string LuaEntitySAO::getPropertyPacket() -{ - return generateSetPropertiesCommand(m_prop); -} - -void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) -{ - // If the object is attached client-side, don't waste bandwidth sending its position to clients - if(isAttached()) - return; - - m_last_sent_move_precision = m_base_position.getDistanceFrom( - m_last_sent_position); - m_last_sent_position_timer = 0; - m_last_sent_position = m_base_position; - m_last_sent_velocity = m_velocity; - //m_last_sent_acceleration = m_acceleration; - m_last_sent_rotation = m_rotation; - - float update_interval = m_env->getSendRecommendedInterval(); - - std::string str = generateUpdatePositionCommand( - m_base_position, - m_velocity, - m_acceleration, - m_rotation, - do_interpolate, - is_movement_end, - update_interval - ); - // create message and add to list - m_messages_out.emplace(getId(), false, str); -} - -bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const -{ - 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::getSelectionBox(aabb3f *toset) const -{ - if (!m_prop.is_visible || !m_prop.pointable) { - return false; - } - - toset->MinEdge = m_prop.selectionbox.MinEdge * BS; - toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; - - return true; -} - -bool LuaEntitySAO::collideWithObjects() const -{ - return m_prop.collideWithObjects; -} diff --git a/src/content_sao.h b/src/content_sao.h deleted file mode 100644 index 5387fd108..000000000 --- a/src/content_sao.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 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 Lesser General Public License for more details. - -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. -*/ - -#pragma once - -#include "network/networkprotocol.h" -#include "util/numeric.h" -#include "server/unit_sao.h" -#include "itemgroup.h" -#include "constants.h" - -/* - LuaEntitySAO needs some internals exposed. -*/ - -class LuaEntitySAO : public UnitSAO -{ -public: - LuaEntitySAO(ServerEnvironment *env, v3f pos, - const std::string &name, const std::string &state); - ~LuaEntitySAO(); - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_LUAENTITY; } - ActiveObjectType getSendType() const - { return ACTIVEOBJECT_TYPE_GENERIC; } - virtual void addedToEnvironment(u32 dtime_s); - static ServerActiveObject* create(ServerEnvironment *env, v3f pos, - const std::string &data); - void step(float dtime, bool send_recommended); - std::string getClientInitializationData(u16 protocol_version); - bool isStaticAllowed() const - { return m_prop.static_save; } - void getStaticData(std::string *result) const; - u16 punch(v3f dir, - const ToolCapabilities *toolcap = nullptr, - ServerActiveObject *puncher = nullptr, - float time_from_last_punch = 1000000.0f); - void rightClick(ServerActiveObject *clicker); - void setPos(const v3f &pos); - void moveTo(v3f pos, bool continuous); - float getMinimumSavedMovement(); - std::string getDescription(); - void setHP(s32 hp, const PlayerHPChangeReason &reason); - u16 getHP() const; - - /* LuaEntitySAO-specific */ - void setVelocity(v3f velocity); - void addVelocity(v3f velocity) - { - m_velocity += velocity; - } - v3f getVelocity(); - void setAcceleration(v3f acceleration); - v3f getAcceleration(); - - void setTextureMod(const std::string &mod); - std::string getTextureMod() const; - void setSprite(v2s16 p, int num_frames, float framelength, - bool select_horiz_by_yawpitch); - std::string getName(); - bool getCollisionBox(aabb3f *toset) const; - bool getSelectionBox(aabb3f *toset) const; - bool collideWithObjects() const; -private: - std::string getPropertyPacket(); - void sendPosition(bool do_interpolate, bool is_movement_end); - std::string generateSetTextureModCommand() const; - static std::string generateSetSpriteCommand(v2s16 p, u16 num_frames, f32 framelength, - bool select_horiz_by_yawpitch); - - std::string m_init_name; - std::string m_init_state; - bool m_registered = false; - - v3f m_velocity; - v3f m_acceleration; - - v3f m_last_sent_position; - v3f m_last_sent_velocity; - v3f m_last_sent_rotation; - float m_last_sent_position_timer = 0.0f; - float m_last_sent_move_precision = 0.0f; - std::string m_current_texture_modifier = ""; -}; - diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index 6c426ba41..0094608ec 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "emerge.h" #include "voxelalgorithms.h" diff --git a/src/mapgen/mapgen_carpathian.cpp b/src/mapgen/mapgen_carpathian.cpp index 0dc1d33be..bd7ae5e7c 100644 --- a/src/mapgen/mapgen_carpathian.cpp +++ b/src/mapgen/mapgen_carpathian.cpp @@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_flat.cpp b/src/mapgen/mapgen_flat.cpp index 879435948..272964b51 100644 --- a/src/mapgen/mapgen_flat.cpp +++ b/src/mapgen/mapgen_flat.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_fractal.cpp b/src/mapgen/mapgen_fractal.cpp index 96febb4f4..97f77d947 100644 --- a/src/mapgen/mapgen_fractal.cpp +++ b/src/mapgen/mapgen_fractal.cpp @@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_v5.cpp b/src/mapgen/mapgen_v5.cpp index 447fe8c50..3bd7615c4 100644 --- a/src/mapgen/mapgen_v5.cpp +++ b/src/mapgen/mapgen_v5.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp index f473f725d..da9ae1428 100644 --- a/src/mapgen/mapgen_v6.cpp +++ b/src/mapgen/mapgen_v6.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp index 325c4957a..82556cc4f 100644 --- a/src/mapgen/mapgen_v7.cpp +++ b/src/mapgen/mapgen_v7.cpp @@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 150baf77e..df07206d7 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -43,7 +43,6 @@ extern "C" { #include #include #include "script/common/c_content.h" -#include "content_sao.h" #include diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 438669feb..40e50e64a 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -33,12 +33,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "daynightratio.h" #include "util/pointedthing.h" -#include "content_sao.h" #include "mapgen/treegen.h" #include "emerge.h" #include "pathfinder.h" #include "face_position_cache.h" #include "remoteplayer.h" +#include "server/luaentity_sao.h" #include "server/player_sao.h" #ifndef SERVER #include "client/client.h" diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index ff77cba32..9f12d3ac7 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" #include "nodedef.h" #include "server.h" -#include "content_sao.h" #include "inventory.h" #include "log.h" diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 95c96235e..fa34260bf 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -27,11 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" #include "tool.h" -#include "content_sao.h" #include "remoteplayer.h" #include "server.h" #include "hud.h" #include "scripting_server.h" +#include "server/luaentity_sao.h" #include "server/player_sao.h" /* diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 26eaed5ac..4d94504f6 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,5 +1,6 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/luaentity_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp new file mode 100644 index 000000000..125939241 --- /dev/null +++ b/src/server/luaentity_sao.cpp @@ -0,0 +1,581 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#include "luaentity_sao.h" +#include "collision.h" +#include "constants.h" +#include "player_sao.h" +#include "scripting_server.h" +#include "server.h" +#include "serverenvironment.h" + +LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &data) + : UnitSAO(env, pos) +{ + std::string name; + std::string state; + u16 hp = 1; + v3f velocity; + v3f rotation; + + while (!data.empty()) { // breakable, run for one iteration + std::istringstream is(data, std::ios::binary); + // 'version' does not allow to incrementally extend the parameter list thus + // we need another variable to build on top of 'version=1'. Ugly hack but worksâ„¢ + u8 version2 = 0; + u8 version = readU8(is); + + name = deSerializeString(is); + state = deSerializeLongString(is); + + if (version < 1) + break; + + hp = readU16(is); + velocity = readV3F1000(is); + // yaw must be yaw to be backwards-compatible + rotation.Y = readF1000(is); + + if (is.good()) // EOF for old formats + version2 = readU8(is); + + if (version2 < 1) // PROTOCOL_VERSION < 37 + break; + + // version2 >= 1 + rotation.X = readF1000(is); + rotation.Z = readF1000(is); + + // if (version2 < 2) + // break; + // + break; + } + // create object + infostream << "LuaEntitySAO::create(name=\"" << name << "\" state=\"" + << state << "\")" << std::endl; + + m_init_name = name; + m_init_state = state; + m_hp = hp; + m_velocity = velocity; + m_rotation = rotation; +} + +LuaEntitySAO::~LuaEntitySAO() +{ + if(m_registered){ + m_env->getScriptIface()->luaentity_Remove(m_id); + } + + for (u32 attached_particle_spawner : m_attached_particle_spawners) { + m_env->deleteParticleSpawner(attached_particle_spawner, false); + } +} + +void LuaEntitySAO::addedToEnvironment(u32 dtime_s) +{ + ServerActiveObject::addedToEnvironment(dtime_s); + + // Create entity from name + m_registered = m_env->getScriptIface()-> + luaentity_Add(m_id, m_init_name.c_str()); + + if(m_registered){ + // Get properties + m_env->getScriptIface()-> + luaentity_GetProperties(m_id, this, &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, dtime_s); + } else { + m_prop.infotext = m_init_name; + } +} + +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(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; + + // 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){ + aabb3f 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)) { + float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI + + m_prop.automatic_face_movement_dir_offset; + float max_rotation_per_sec = + m_prop.automatic_face_movement_max_rotation_per_sec; + + if (max_rotation_per_sec > 0) { + m_rotation.Y = wrapDegrees_0_360(m_rotation.Y); + wrappedApproachShortest(m_rotation.Y, target_yaw, + dtime * max_rotation_per_sec, 360.f); + } else { + // Negative values of max_rotation_per_sec mean disabled. + m_rotation.Y = target_yaw; + } + } + } + + if(m_registered){ + m_env->getScriptIface()->luaentity_Step(m_id, dtime); + } + + if (!send_recommended) + return; + + 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 || + std::fabs(m_rotation.X - m_last_sent_rotation.X) > 1.0f || + std::fabs(m_rotation.Y - m_last_sent_rotation.Y) > 1.0f || + std::fabs(m_rotation.Z - m_last_sent_rotation.Z) > 1.0f) { + + sendPosition(true, false); + } + } + + if (!m_armor_groups_sent) { + m_armor_groups_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); + } + + if (!m_animation_sent) { + m_animation_sent = true; + std::string str = generateUpdateAnimationCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } + + if (!m_animation_speed_sent) { + m_animation_speed_sent = true; + std::string str = generateUpdateAnimationSpeedCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } + + if (!m_bone_position_sent) { + m_bone_position_sent = true; + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + std::string str = generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } + } + + if (!m_attachment_sent) { + m_attachment_sent = true; + std::string str = generateUpdateAttachmentCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } +} + +std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + + // PROTOCOL_VERSION >= 37 + writeU8(os, 1); // version + os << serializeString(""); // name + writeU8(os, 0); // is_player + writeU16(os, getId()); //id + writeV3F32(os, m_base_position); + writeV3F32(os, m_rotation); + writeU16(os, m_hp); + + std::ostringstream msg_os(std::ios::binary); + msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + } + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + int message_count = 4 + m_bone_position.size(); + for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); + (ii != m_attachment_child_ids.end()); ++ii) { + if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + message_count++; + // TODO after a protocol bump: only send the object initialization data + // to older clients (superfluous since this message exists) + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + } + } + + msg_os << serializeLongString(generateSetTextureModCommand()); + message_count++; + + writeU8(os, message_count); + os.write(msg_os.str().c_str(), msg_os.str().size()); + + // return result + return os.str(); +} + +void LuaEntitySAO::getStaticData(std::string *result) const +{ + verbosestream<getScriptIface()-> + luaentity_GetStaticdata(m_id); + os<= 37 + + writeF1000(os, m_rotation.X); + writeF1000(os, m_rotation.Z); + + // + + *result = os.str(); +} + +u16 LuaEntitySAO::punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch) +{ + if (!m_registered) { + // Delete unknown LuaEntities when punched + m_pending_removal = true; + return 0; + } + + FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); + + s32 old_hp = getHP(); + ItemStack selected_item, hand_item; + ItemStack tool_item = puncher->getWieldedItem(&selected_item, &hand_item); + + PunchDamageResult result = getPunchDamage( + m_armor_groups, + toolcap, + &tool_item, + time_from_last_punch); + + bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher, + time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0); + + if (!damage_handled) { + if (result.did_punch) { + setHP((s32)getHP() - result.damage, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); + + // create message and add to list + sendPunchCommand(); + } + } + + if (getHP() == 0 && !isGone()) { + clearParentAttachment(); + clearChildAttachments(); + m_env->getScriptIface()->luaentity_on_death(m_id, puncher); + m_pending_removal = true; + } + + actionstream << puncher->getDescription() << " (id=" << puncher->getId() << + ", hp=" << puncher->getHP() << ") punched " << + getDescription() << " (id=" << m_id << ", hp=" << m_hp << + "), damage=" << (old_hp - (s32)getHP()) << + (damage_handled ? " (handled by Lua)" : "") << std::endl; + + // TODO: give Lua control over wear + return result.wear; +} + +void LuaEntitySAO::rightClick(ServerActiveObject *clicker) +{ + if (!m_registered) + return; + + m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker); +} + +void LuaEntitySAO::setPos(const 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); +} + +float LuaEntitySAO::getMinimumSavedMovement() +{ + return 0.1 * BS; +} + +std::string LuaEntitySAO::getDescription() +{ + std::ostringstream oss; + oss << "LuaEntitySAO \"" << m_init_name << "\" "; + auto pos = floatToInt(m_base_position, BS); + oss << "at " << PP(pos); + return oss.str(); +} + +void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason) +{ + m_hp = rangelim(hp, 0, U16_MAX); +} + +u16 LuaEntitySAO::getHP() const +{ + return m_hp; +} + +void LuaEntitySAO::setVelocity(v3f velocity) +{ + m_velocity = velocity; +} + +v3f LuaEntitySAO::getVelocity() +{ + return m_velocity; +} + +void LuaEntitySAO::setAcceleration(v3f acceleration) +{ + m_acceleration = acceleration; +} + +v3f LuaEntitySAO::getAcceleration() +{ + return m_acceleration; +} + +void LuaEntitySAO::setTextureMod(const std::string &mod) +{ + m_current_texture_modifier = mod; + // create message and add to list + m_messages_out.emplace(getId(), true, generateSetTextureModCommand()); +} + +std::string LuaEntitySAO::getTextureMod() const +{ + return m_current_texture_modifier; +} + + +std::string LuaEntitySAO::generateSetTextureModCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_TEXTURE_MOD); + // parameters + os << serializeString(m_current_texture_modifier); + return os.str(); +} + +std::string LuaEntitySAO::generateSetSpriteCommand(v2s16 p, u16 num_frames, + f32 framelength, bool select_horiz_by_yawpitch) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_SPRITE); + // parameters + writeV2S16(os, p); + writeU16(os, num_frames); + writeF32(os, framelength); + writeU8(os, select_horiz_by_yawpitch); + return os.str(); +} + +void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength, + bool select_horiz_by_yawpitch) +{ + std::string str = generateSetSpriteCommand( + p, + num_frames, + framelength, + select_horiz_by_yawpitch + ); + // create message and add to list + m_messages_out.emplace(getId(), true, str); +} + +std::string LuaEntitySAO::getName() +{ + return m_init_name; +} + +std::string LuaEntitySAO::getPropertyPacket() +{ + return generateSetPropertiesCommand(m_prop); +} + +void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) +{ + // If the object is attached client-side, don't waste bandwidth sending its position to clients + if(isAttached()) + return; + + m_last_sent_move_precision = m_base_position.getDistanceFrom( + m_last_sent_position); + m_last_sent_position_timer = 0; + m_last_sent_position = m_base_position; + m_last_sent_velocity = m_velocity; + //m_last_sent_acceleration = m_acceleration; + m_last_sent_rotation = m_rotation; + + float update_interval = m_env->getSendRecommendedInterval(); + + std::string str = generateUpdatePositionCommand( + m_base_position, + m_velocity, + m_acceleration, + m_rotation, + do_interpolate, + is_movement_end, + update_interval + ); + // create message and add to list + m_messages_out.emplace(getId(), false, str); +} + +bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const +{ + 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::getSelectionBox(aabb3f *toset) const +{ + if (!m_prop.is_visible || !m_prop.pointable) { + return false; + } + + toset->MinEdge = m_prop.selectionbox.MinEdge * BS; + toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; + + return true; +} + +bool LuaEntitySAO::collideWithObjects() const +{ + return m_prop.collideWithObjects; +} diff --git a/src/server/luaentity_sao.h b/src/server/luaentity_sao.h new file mode 100644 index 000000000..2520c8f5d --- /dev/null +++ b/src/server/luaentity_sao.h @@ -0,0 +1,93 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#pragma once + +#include "unit_sao.h" + +class LuaEntitySAO : public UnitSAO +{ +public: + LuaEntitySAO() = delete; + // Used by the environment to load SAO + LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &data); + // Used by the Lua API + LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &name, + const std::string &state) : + UnitSAO(env, pos), + m_init_name(name), m_init_state(state) + { + } + ~LuaEntitySAO(); + ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_LUAENTITY; } + ActiveObjectType getSendType() const { return ACTIVEOBJECT_TYPE_GENERIC; } + virtual void addedToEnvironment(u32 dtime_s); + void step(float dtime, bool send_recommended); + std::string getClientInitializationData(u16 protocol_version); + bool isStaticAllowed() const { return m_prop.static_save; } + void getStaticData(std::string *result) const; + u16 punch(v3f dir, const ToolCapabilities *toolcap = nullptr, + ServerActiveObject *puncher = nullptr, + float time_from_last_punch = 1000000.0f); + void rightClick(ServerActiveObject *clicker); + void setPos(const v3f &pos); + void moveTo(v3f pos, bool continuous); + float getMinimumSavedMovement(); + std::string getDescription(); + void setHP(s32 hp, const PlayerHPChangeReason &reason); + u16 getHP() const; + + /* LuaEntitySAO-specific */ + void setVelocity(v3f velocity); + void addVelocity(v3f velocity) { m_velocity += velocity; } + v3f getVelocity(); + void setAcceleration(v3f acceleration); + v3f getAcceleration(); + + void setTextureMod(const std::string &mod); + std::string getTextureMod() const; + void setSprite(v2s16 p, int num_frames, float framelength, + bool select_horiz_by_yawpitch); + std::string getName(); + bool getCollisionBox(aabb3f *toset) const; + bool getSelectionBox(aabb3f *toset) const; + bool collideWithObjects() const; + +private: + std::string getPropertyPacket(); + void sendPosition(bool do_interpolate, bool is_movement_end); + std::string generateSetTextureModCommand() const; + static std::string generateSetSpriteCommand(v2s16 p, u16 num_frames, + f32 framelength, bool select_horiz_by_yawpitch); + + std::string m_init_name; + std::string m_init_state; + bool m_registered = false; + + v3f m_velocity; + v3f m_acceleration; + + v3f m_last_sent_position; + v3f m_last_sent_velocity; + v3f m_last_sent_rotation; + float m_last_sent_position_timer = 0.0f; + float m_last_sent_move_precision = 0.0f; + std::string m_current_texture_modifier = ""; +}; diff --git a/src/server/player_sao.h b/src/server/player_sao.h index ce1cb1677..8571bd4f9 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -1,4 +1,3 @@ - /* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola @@ -19,6 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "constants.h" #include "network/networkprotocol.h" #include "unit_sao.h" @@ -32,33 +33,31 @@ class LagPool { float m_pool = 15.0f; float m_max = 15.0f; + public: LagPool() = default; void setMax(float new_max) { m_max = new_max; - if(m_pool > new_max) + if (m_pool > new_max) m_pool = new_max; } void add(float dtime) { m_pool -= dtime; - if(m_pool < 0) + if (m_pool < 0) m_pool = 0; } - void empty() - { - m_pool = m_max; - } + void empty() { m_pool = m_max; } bool grab(float dtime) { - if(dtime <= 0) + if (dtime <= 0) return true; - if(m_pool + dtime > m_max) + if (m_pool + dtime > m_max) return false; m_pool += dtime; return true; @@ -73,10 +72,8 @@ public: PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, bool is_singleplayer); - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_PLAYER; } - ActiveObjectType getSendType() const - { return ACTIVEOBJECT_TYPE_GENERIC; } + ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_PLAYER; } + ActiveObjectType getSendType() const { return ACTIVEOBJECT_TYPE_GENERIC; } std::string getDescription(); /* @@ -111,10 +108,8 @@ public: Interaction interface */ - u16 punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch); + u16 punch(v3f dir, const ToolCapabilities *toolcap, ServerActiveObject *puncher, + float time_from_last_punch); void rightClick(ServerActiveObject *clicker) {} void setHP(s32 hp, const PlayerHPChangeReason &reason); void setHPRaw(u16 hp) { m_hp = hp; } @@ -144,10 +139,7 @@ public: // Cheat prevention - v3f getLastGoodPosition() const - { - return m_last_good_position; - } + v3f getLastGoodPosition() const { return m_last_good_position; } float resetTimeFromLastPunch() { float r = m_time_from_last_punch; @@ -159,30 +151,17 @@ public: m_nocheat_dig_pos = p; m_nocheat_dig_time = 0; } - v3s16 getNoCheatDigPos() - { - return m_nocheat_dig_pos; - } - float getNoCheatDigTime() - { - return m_nocheat_dig_time; - } - void noCheatDigEnd() - { - m_nocheat_dig_pos = v3s16(32767, 32767, 32767); - } - LagPool& getDigPool() - { - return m_dig_pool; - } + v3s16 getNoCheatDigPos() { return m_nocheat_dig_pos; } + float getNoCheatDigTime() { return m_nocheat_dig_time; } + void noCheatDigEnd() { m_nocheat_dig_pos = v3s16(32767, 32767, 32767); } + LagPool &getDigPool() { return m_dig_pool; } void setMaxSpeedOverride(const v3f &vel); // Returns true if cheated bool checkMovementCheat(); // Other - void updatePrivileges(const std::set &privs, - bool is_singleplayer) + void updatePrivileges(const std::set &privs, bool is_singleplayer) { m_privs = privs; m_is_singleplayer = is_singleplayer; @@ -236,6 +215,7 @@ private: s16 m_wanted_range = 0.0f; Metadata m_meta; + public: float m_physics_override_speed = 1.0f; float m_physics_override_jump = 1.0f; @@ -246,9 +226,10 @@ public: bool m_physics_override_sent = false; }; - -struct PlayerHPChangeReason { - enum Type : u8 { +struct PlayerHPChangeReason +{ + enum Type : u8 + { SET_HP, PLAYER_PUNCH, FALL, @@ -266,10 +247,7 @@ struct PlayerHPChangeReason { // For NODE_DAMAGE std::string node; - inline bool hasLuaReference() const - { - return lua_reference >= 0; - } + inline bool hasLuaReference() const { return lua_reference >= 0; } bool setTypeFromString(const std::string &typestr) { @@ -311,15 +289,12 @@ struct PlayerHPChangeReason { } } - PlayerHPChangeReason(Type type): - type(type) - {} + PlayerHPChangeReason(Type type) : type(type) {} - PlayerHPChangeReason(Type type, ServerActiveObject *object): + PlayerHPChangeReason(Type type, ServerActiveObject *object) : type(type), object(object) - {} + { + } - PlayerHPChangeReason(Type type, std::string node): - type(type), node(node) - {} + PlayerHPChangeReason(Type type, std::string node) : type(type), node(node) {} }; diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index 3aa78c7d5..8345ebd47 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -30,39 +30,6 @@ ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos): { } -ServerActiveObject* ServerActiveObject::create(ActiveObjectType type, - ServerEnvironment *env, u16 id, v3f pos, - const std::string &data) -{ - // Find factory function - std::map::iterator n; - n = m_types.find(type); - if(n == m_types.end()) { - // These are 0.3 entity types, return without error. - if (ACTIVEOBJECT_TYPE_ITEM <= type && type <= ACTIVEOBJECT_TYPE_MOBV2) { - return NULL; - } - - // If factory is not found, just return. - warningstream<<"ServerActiveObject: No factory for type=" - <second; - ServerActiveObject *object = (*f)(env, pos, data); - return object; -} - -void ServerActiveObject::registerType(u16 type, Factory f) -{ - std::map::iterator n; - n = m_types.find(type); - if(n != m_types.end()) - return; - m_types[type] = f; -} - float ServerActiveObject::getMinimumSavedMovement() { return 2.0*BS; diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index 2e013a6b6..927009aef 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -244,12 +244,6 @@ protected: virtual void onAttach(int parent_id) {} virtual void onDetach(int parent_id) {} - // Used for creating objects based on type - typedef ServerActiveObject* (*Factory) - (ServerEnvironment *env, v3f pos, - const std::string &data); - static void registerType(u16 type, Factory f); - ServerEnvironment *m_env; v3f m_base_position; std::unordered_set m_attached_particle_spawners; @@ -258,8 +252,4 @@ protected: Queue of messages to be sent to the client */ std::queue m_messages_out; - -private: - // Used for creating objects based on type - static std::map m_types; }; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index c2ab5c07d..32d10f8c0 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -44,6 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_POSTGRESQL #include "database/database-postgresql.h" #endif +#include "server/luaentity_sao.h" #include "server/player_sao.h" #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" @@ -1778,6 +1779,18 @@ static void print_hexdump(std::ostream &o, const std::string &data) } } +ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos, + const std::string &data) +{ + switch (type) { + case ACTIVEOBJECT_TYPE_LUAENTITY: + return new LuaEntitySAO(this, pos, data); + default: + warningstream << "ServerActiveObject: No factory for type=" << type << std::endl; + } + return nullptr; +} + /* Convert stored objects from blocks near the players to active. */ @@ -1811,10 +1824,10 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) std::vector new_stored; for (const StaticObject &s_obj : block->m_static_objects.m_stored) { // Create an active object from the data - ServerActiveObject *obj = ServerActiveObject::create - ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data); + ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos, + s_obj.data); // If couldn't create object, store static data back. - if(obj == NULL) { + if (!obj) { errorstream<<"ServerEnvironment::activateObjects(): " <<"failed to create active object from static object " <<"in block "< m_particle_spawners; std::unordered_map m_particle_spawner_attachments; + + ServerActiveObject* createSAO(ActiveObjectType type, v3f pos, const std::string &data); }; diff --git a/src/staticobject.cpp b/src/staticobject.cpp index bebca12ec..5ccb7baf5 100644 --- a/src/staticobject.cpp +++ b/src/staticobject.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "staticobject.h" #include "util/serialize.h" -#include "content_sao.h" +#include "server/serveractiveobject.h" StaticObject::StaticObject(const ServerActiveObject *s_obj, const v3f &pos_): type(s_obj->getType()), diff --git a/src/unittest/test_player.cpp b/src/unittest/test_player.cpp index e2b1cd855..6990b4016 100644 --- a/src/unittest/test_player.cpp +++ b/src/unittest/test_player.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "exceptions.h" #include "remoteplayer.h" -#include "content_sao.h" #include "server.h" class TestPlayer : public TestBase diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index 816ec2c59..bb97da7b5 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -118,8 +118,6 @@ src/content_mapblock.h src/content_mapnode.cpp src/content_nodemeta.cpp src/content_nodemeta.h -src/content_sao.cpp -src/content_sao.h src/convert_json.cpp src/convert_json.h src/craftdef.cpp @@ -408,6 +406,8 @@ src/serverenvironment.h src/server.h src/serverlist.cpp src/serverlist.h +src/server/luaentity_sao.cpp +src/server/player_sao.cpp src/server/serveractiveobject.cpp src/server/serveractiveobject.h src/settings.cpp -- cgit v1.2.3 From e8ac5a31cf12afcfddf8e3ed31e8038930edb06f Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Thu, 16 Apr 2020 08:25:48 +0200 Subject: Optimize get_objects_inside_radius calls (#9671) * Optimize getObjectsInsideRadius calls our previous implementation calls the ActiveObjectMgr to return ids and then lookup those ids in the same map and test each object Instead now we call the global map to return the pointers directly and we ask filtering when building the list using lamba. This drop double looping over ranges of active objects (and then filtered one) and drop x lookups on the map regarding the first call results --- src/collision.cpp | 20 +++++++++++--------- src/script/lua_api/l_env.cpp | 24 ++++++++++++------------ src/server/activeobjectmgr.cpp | 10 ++++++---- src/server/activeobjectmgr.h | 5 +++-- src/serverenvironment.cpp | 12 +++++------- src/serverenvironment.h | 5 +++-- src/unittest/test_serveractiveobjectmgr.cpp | 18 +++++++++++++++--- 7 files changed, 55 insertions(+), 39 deletions(-) (limited to 'src/server') diff --git a/src/collision.cpp b/src/collision.cpp index d9fbd3202..6d24bc699 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -360,17 +360,19 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Calculate distance by speed, add own extent and 1.5m of tolerance f32 distance = speed_f->getLength() * dtime + box_0.getExtent().getLength() + 1.5f * BS; - std::vector s_objects; - s_env->getObjectsInsideRadius(s_objects, *pos_f, distance); - for (u16 obj_id : s_objects) { - ServerActiveObject *current = s_env->getActiveObject(obj_id); - - if (!self || (self != current && - self != current->getParent())) { - objects.push_back((ActiveObject*)current); + // search for objects which are not us, or we are not its parent + // we directly use the callback to populate the result to prevent + // a useless result loop here + auto include_obj_cb = [self, &objects] (ServerActiveObject *obj) { + if (!self || (self != obj && self != obj->getParent())) { + objects.push_back((ActiveObject *)obj); } - } + return false; + }; + + std::vector s_objects; + s_env->getObjectsInsideRadius(s_objects, *pos_f, distance, include_obj_cb); } } diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 8c45a1510..831464d3b 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -681,22 +681,22 @@ int ModApiEnvMod::l_get_player_by_name(lua_State *L) int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L) { GET_ENV_PTR; + ScriptApiBase *script = getScriptApiBase(L); // Do it v3f pos = checkFloatPos(L, 1); float radius = readParam(L, 2) * BS; - std::vector ids; - env->getObjectsInsideRadius(ids, pos, radius); - ScriptApiBase *script = getScriptApiBase(L); - lua_createtable(L, ids.size(), 0); - std::vector::const_iterator iter = ids.begin(); - for(u32 i = 0; iter != ids.end(); ++iter) { - ServerActiveObject *obj = env->getActiveObject(*iter); - if (!obj->isGone()) { - // Insert object reference into table - script->objectrefGetOrCreate(L, obj); - lua_rawseti(L, -2, ++i); - } + std::vector objs; + + auto include_obj_cb = [](ServerActiveObject *obj){ return !obj->isGone(); }; + env->getObjectsInsideRadius(objs, pos, radius, include_obj_cb); + + int i = 0; + lua_createtable(L, objs.size(), 0); + for (const auto obj : objs) { + // Insert object reference into table + script->objectrefGetOrCreate(L, obj); + lua_rawseti(L, -2, ++i); } return 1; } diff --git a/src/server/activeobjectmgr.cpp b/src/server/activeobjectmgr.cpp index 984ae7794..1b8e31409 100644 --- a/src/server/activeobjectmgr.cpp +++ b/src/server/activeobjectmgr.cpp @@ -111,17 +111,19 @@ void ActiveObjectMgr::removeObject(u16 id) } // clang-format on -void ActiveObjectMgr::getObjectsInsideRadius( - const v3f &pos, float radius, std::vector &result) +void ActiveObjectMgr::getObjectsInsideRadius(const v3f &pos, float radius, + std::vector &result, + std::function include_obj_cb) { float r2 = radius * radius; for (auto &activeObject : m_active_objects) { ServerActiveObject *obj = activeObject.second; - u16 id = activeObject.first; const v3f &objectpos = obj->getBasePosition(); if (objectpos.getDistanceFromSQ(pos) > r2) continue; - result.push_back(id); + + if (!include_obj_cb || include_obj_cb(obj)) + result.push_back(obj); } } diff --git a/src/server/activeobjectmgr.h b/src/server/activeobjectmgr.h index 5fea1bea6..bc2085499 100644 --- a/src/server/activeobjectmgr.h +++ b/src/server/activeobjectmgr.h @@ -35,8 +35,9 @@ public: bool registerObject(ServerActiveObject *obj) override; void removeObject(u16 id) override; - void getObjectsInsideRadius( - const v3f &pos, float radius, std::vector &result); + void getObjectsInsideRadius(const v3f &pos, float radius, + std::vector &result, + std::function include_obj_cb); void getAddedActiveObjectsAroundPos(const v3f &player_pos, f32 radius, f32 player_radius, std::set ¤t_objects, diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 739384673..27f0c1e3d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1608,14 +1608,12 @@ void ServerEnvironment::getSelectedActiveObjects( const core::line3d &shootline_on_map, std::vector &objects) { - std::vector objectIds; - getObjectsInsideRadius(objectIds, shootline_on_map.start, - shootline_on_map.getLength() + 10.0f); + std::vector objs; + getObjectsInsideRadius(objs, shootline_on_map.start, + shootline_on_map.getLength() + 10.0f, nullptr); const v3f line_vector = shootline_on_map.getVector(); - for (u16 objectId : objectIds) { - ServerActiveObject* obj = getActiveObject(objectId); - + for (auto obj : objs) { aabb3f selection_box; if (!obj->getSelectionBox(&selection_box)) continue; @@ -1630,7 +1628,7 @@ void ServerEnvironment::getSelectedActiveObjects( if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, ¤t_intersection, ¤t_normal)) { objects.emplace_back( - (s16) objectId, current_intersection, current_normal, + (s16) obj->getId(), current_intersection, current_normal, (current_intersection - shootline_on_map.start).getLengthSQ()); } } diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 55ecbd05f..f814b95c0 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -323,9 +323,10 @@ public: bool swapNode(v3s16 p, const MapNode &n); // Find all active objects inside a radius around a point - void getObjectsInsideRadius(std::vector &objects, const v3f &pos, float radius) + void getObjectsInsideRadius(std::vector &objects, const v3f &pos, float radius, + std::function include_obj_cb) { - return m_ao_manager.getObjectsInsideRadius(pos, radius, objects); + return m_ao_manager.getObjectsInsideRadius(pos, radius, objects, include_obj_cb); } // Clear objects, loading and going through every MapBlock diff --git a/src/unittest/test_serveractiveobjectmgr.cpp b/src/unittest/test_serveractiveobjectmgr.cpp index 0806972ab..aa0047400 100644 --- a/src/unittest/test_serveractiveobjectmgr.cpp +++ b/src/unittest/test_serveractiveobjectmgr.cpp @@ -148,14 +148,26 @@ void TestServerActiveObjectMgr::testGetObjectsInsideRadius() saomgr.registerObject(new TestServerActiveObject(p)); } - std::vector result; - saomgr.getObjectsInsideRadius(v3f(), 50, result); + std::vector result; + saomgr.getObjectsInsideRadius(v3f(), 50, result, nullptr); UASSERTCMP(int, ==, result.size(), 1); result.clear(); - saomgr.getObjectsInsideRadius(v3f(), 750, result); + saomgr.getObjectsInsideRadius(v3f(), 750, result, nullptr); UASSERTCMP(int, ==, result.size(), 2); + result.clear(); + saomgr.getObjectsInsideRadius(v3f(), 750000, result, nullptr); + UASSERTCMP(int, ==, result.size(), 5); + + result.clear(); + auto include_obj_cb = [](ServerActiveObject *obj) { + return (obj->getBasePosition().X != 10); + }; + + saomgr.getObjectsInsideRadius(v3f(), 750000, result, include_obj_cb); + UASSERTCMP(int, ==, result.size(), 4); + clearSAOMgr(&saomgr); } -- cgit v1.2.3 From 3475759d1adbd4a64c6250fd87981f783e64f69c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 Apr 2020 14:11:33 +0200 Subject: Expose collision information to LuaEntity on_step --- builtin/game/features.lua | 1 + doc/lua_api.txt | 25 ++++++++++++++++++- src/script/common/c_content.cpp | 54 +++++++++++++++++++++++++++++++++++++++++ src/script/common/c_content.h | 4 ++- src/script/cpp_api/s_entity.cpp | 12 ++++++--- src/script/cpp_api/s_entity.h | 4 ++- src/server/luaentity_sao.cpp | 8 +++--- 7 files changed, 98 insertions(+), 10 deletions(-) (limited to 'src/server') diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 623f8183b..a15475333 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -16,6 +16,7 @@ core.features = { formspec_version_element = true, area_store_persistent_ids = true, pathfinder_works = true, + object_step_has_moveresult = true, } function core.has_feature(arg) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 3ca32649a..5e4e18b62 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4147,6 +4147,8 @@ Utilities area_store_persistent_ids = true, -- Whether minetest.find_path is functional (5.2.0) pathfinder_works = true, + -- Whether Collision info is available to an objects' on_step (5.3.0) + object_step_has_moveresult = true, } * `minetest.has_feature(arg)`: returns `boolean, missing_features` @@ -6579,7 +6581,10 @@ Used by `minetest.register_entity`. on_activate = function(self, staticdata, dtime_s), - on_step = function(self, dtime), + on_step = function(self, dtime, moveresult), + -- Called every server step + -- dtime: Elapsed time + -- moveresult: Table with collision info (only available if physical=true) on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir), @@ -6594,6 +6599,24 @@ Used by `minetest.register_entity`. -- for more info) by using a '_' prefix } +Collision info passed to `on_step`: + + { + touching_ground = boolean, + collides = boolean, + standing_on_object = boolean, + collisions = { + { + type = string, -- "node" or "object", + axis = string, -- "x", "y" or "z" + node_pos = vector, -- if type is "node" + old_speed = vector, + new_speed = vector, + }, + ... + } + } + ABM (ActiveBlockModifier) definition ------------------------------------ diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 8335fccb5..6ff642738 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_types.h" #include "nodedef.h" #include "object_properties.h" +#include "collision.h" #include "cpp_api/s_node.h" #include "lua_api/l_object.h" #include "lua_api/l_item.h" @@ -2002,3 +2003,56 @@ HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) } return stat; } + +/******************************************************************************/ + +// Indices must match values in `enum CollisionType` exactly!! +static const char *collision_type_str[] = { + "node", + "object", +}; + +// Indices must match values in `enum CollisionAxis` exactly!! +static const char *collision_axis_str[] = { + "x", + "y", + "z", +}; + +void push_collision_move_result(lua_State *L, const collisionMoveResult &res) +{ + lua_createtable(L, 0, 4); + + setboolfield(L, -1, "touching_ground", res.touching_ground); + setboolfield(L, -1, "collides", res.collides); + setboolfield(L, -1, "standing_on_object", res.standing_on_object); + + /* collisions */ + lua_createtable(L, res.collisions.size(), 0); + int i = 1; + for (const auto &c : res.collisions) { + lua_createtable(L, 0, 5); + + lua_pushstring(L, collision_type_str[c.type]); + lua_setfield(L, -2, "type"); + + assert(c.axis != COLLISION_AXIS_NONE); + lua_pushstring(L, collision_axis_str[c.axis]); + lua_setfield(L, -2, "axis"); + + if (c.type == COLLISION_NODE) { + push_v3s16(L, c.node_p); + lua_setfield(L, -2, "node_pos"); + } + + push_v3f(L, c.old_speed / BS); + lua_setfield(L, -2, "old_speed"); + + push_v3f(L, c.new_speed / BS); + lua_setfield(L, -2, "new_speed"); + + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "collisions"); + /**/ +} diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 9e755682f..8f32e58eb 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -63,7 +63,9 @@ struct EnumString; struct NoiseParams; class Schematic; class ServerActiveObject; +struct collisionMoveResult; +extern struct EnumString es_TileAnimationType[]; ContentFeatures read_content_features (lua_State *L, int index); void push_content_features (lua_State *L, @@ -196,4 +198,4 @@ void push_hud_element (lua_State *L, HudElement *elem); HudElementStat read_hud_change (lua_State *L, HudElement *elem, void **value); -extern struct EnumString es_TileAnimationType[]; +void push_collision_move_result(lua_State *L, const collisionMoveResult &res); diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index 26c7e8cd4..ea9320051 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -178,12 +178,11 @@ void ScriptApiEntity::luaentity_GetProperties(u16 id, lua_pop(L, 1); } -void ScriptApiEntity::luaentity_Step(u16 id, float dtime) +void ScriptApiEntity::luaentity_Step(u16 id, float dtime, + const collisionMoveResult *moveresult) { SCRIPTAPI_PRECHECKHEADER - //infostream<<"scriptapi_luaentity_step: id="<getScriptIface()->luaentity_Step(m_id, dtime); + if(m_registered) { + m_env->getScriptIface()->luaentity_Step(m_id, dtime, moveresult_p); } if (!send_recommended) -- cgit v1.2.3 From 56bababcdfce097a4e08cc3d1de8d798e7999ce7 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 27 Apr 2020 08:31:37 +0200 Subject: Add MetricsBackend with prometheus counter support --- Dockerfile | 22 +++++-- README.md | 1 + builtin/settingtypes.txt | 6 ++ src/CMakeLists.txt | 26 ++++++++ src/cmake_config.h.in | 1 + src/defaultsettings.cpp | 3 + src/map.cpp | 11 +++- src/map.h | 6 +- src/server.cpp | 57 ++++++++++++---- src/server.h | 21 ++++-- src/server/mods.cpp | 1 + src/server/mods.h | 3 + src/util/CMakeLists.txt | 1 + src/util/metricsbackend.cpp | 140 ++++++++++++++++++++++++++++++++++++++++ src/util/metricsbackend.h | 140 ++++++++++++++++++++++++++++++++++++++++ util/ci/build_prometheus_cpp.sh | 13 ++++ 16 files changed, 427 insertions(+), 25 deletions(-) create mode 100644 src/util/metricsbackend.cpp create mode 100644 src/util/metricsbackend.h create mode 100755 util/ci/build_prometheus_cpp.sh (limited to 'src/server') diff --git a/Dockerfile b/Dockerfile index 7c1107288..72343ab9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,15 +21,29 @@ WORKDIR /usr/src/minetest RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \ jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \ libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \ - gmp-dev jsoncpp-dev postgresql-dev && \ + gmp-dev jsoncpp-dev postgresql-dev ca-certificates && \ git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \ - rm -fr ./games/minetest_game/.git && \ - mkdir build && \ + rm -fr ./games/minetest_game/.git + +WORKDIR /usr/src/ +RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \ + mkdir prometheus-cpp/build && \ + cd prometheus-cpp/build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DCMAKE_BUILD_TYPE=Release \ + -DENABLE_TESTING=0 && \ + make -j2 && \ + make install + +WORKDIR /usr/src/minetest +RUN mkdir build && \ cd build && \ cmake .. \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_SERVER=TRUE \ + -DENABLE_PROMETHEUS=TRUE \ -DBUILD_UNITTESTS=FALSE \ -DBUILD_CLIENT=FALSE && \ make -j2 && \ @@ -49,6 +63,6 @@ COPY --from=0 /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/ USER minetest:minetest -EXPOSE 30000/udp +EXPOSE 30000/udp 30000/tcp CMD ["/usr/local/bin/minetestserver", "--config", "/etc/minetest/minetest.conf"] diff --git a/README.md b/README.md index b3b2b863e..024e7b691 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,7 @@ General options and their default values: ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua) + ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default) ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp) ENABLE_SYSTEM_JSONCPP=OFF - Use JsonCPP from system OPENGL_GL_PREFERENCE=LEGACY - Linux client build only; See CMake Policy CMP0072 for reference diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index b9228f384..165ed8c06 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -955,6 +955,12 @@ address (Server address) string # Note that the port field in the main menu overrides this setting. remote_port (Remote port) int 30000 1 65535 +# Prometheus listener address. +# If minetest is compiled with ENABLE_PROMETHEUS option enabled, +# enable metrics listener for Prometheus on that address. +# Metrics can be fetch on http://127.0.0.1:30000/metrics +prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000 + # Save the map received by the client on disk. enable_local_map_saving (Saving map received from server) bool false diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b416faaf3..710d9e13e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -217,6 +217,26 @@ endif(ENABLE_REDIS) find_package(SQLite3 REQUIRED) +OPTION(ENABLE_PROMETHEUS "Enable prometheus client support" FALSE) +set(USE_PROMETHEUS FALSE) + +if(ENABLE_PROMETHEUS) + find_path(PROMETHEUS_CPP_INCLUDE_DIR NAMES prometheus/counter.h) + find_library(PROMETHEUS_PULL_LIBRARY NAMES prometheus-cpp-pull) + find_library(PROMETHEUS_CORE_LIBRARY NAMES prometheus-cpp-core) + if(PROMETHEUS_CPP_INCLUDE_DIR AND PROMETHEUS_PULL_LIBRARY AND PROMETHEUS_CORE_LIBRARY) + set(PROMETHEUS_LIBRARIES ${PROMETHEUS_PULL_LIBRARY} ${PROMETHEUS_CORE_LIBRARY}) + set(USE_PROMETHEUS TRUE) + include_directories(${PROMETHEUS_CPP_INCLUDE_DIR}) + endif(PROMETHEUS_CPP_INCLUDE_DIR AND PROMETHEUS_PULL_LIBRARY AND PROMETHEUS_CORE_LIBRARY) +endif(ENABLE_PROMETHEUS) + +if(USE_PROMETHEUS) + message(STATUS "Prometheus client enabled.") +else(USE_PROMETHEUS) + message(STATUS "Prometheus client disabled.") +endif(USE_PROMETHEUS) + OPTION(ENABLE_SPATIAL "Enable SpatialIndex AreaStore backend" TRUE) set(USE_SPATIAL FALSE) @@ -597,6 +617,9 @@ if(BUILD_CLIENT) if (USE_REDIS) target_link_libraries(${PROJECT_NAME} ${REDIS_LIBRARY}) endif() + if (USE_PROMETHEUS) + target_link_libraries(${PROJECT_NAME} ${PROMETHEUS_LIBRARIES}) + endif() if (USE_SPATIAL) target_link_libraries(${PROJECT_NAME} ${SPATIAL_LIBRARY}) endif() @@ -632,6 +655,9 @@ if(BUILD_SERVER) if (USE_REDIS) target_link_libraries(${PROJECT_NAME}server ${REDIS_LIBRARY}) endif() + if (USE_PROMETHEUS) + target_link_libraries(${PROJECT_NAME}server ${PROMETHEUS_LIBRARIES}) + endif() if (USE_SPATIAL) target_link_libraries(${PROJECT_NAME}server ${SPATIAL_LIBRARY}) endif() diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index cac6335d4..cfcee4b58 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -23,6 +23,7 @@ #cmakedefine01 USE_LEVELDB #cmakedefine01 USE_LUAJIT #cmakedefine01 USE_POSTGRESQL +#cmakedefine01 USE_PROMETHEUS #cmakedefine01 USE_SPATIAL #cmakedefine01 USE_SYSTEM_GMP #cmakedefine01 USE_REDIS diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index b6b1ce1f2..06daa3b94 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -334,6 +334,9 @@ void set_default_settings(Settings *settings) // Server settings->setDefault("disable_escape_sequences", "false"); settings->setDefault("strip_color_codes", "false"); +#if USE_PROMETHEUS + settings->setDefault("prometheus_listener_address", "127.0.0.1:30000"); +#endif // Network settings->setDefault("enable_ipv6", "true"); diff --git a/src/map.cpp b/src/map.cpp index 12e122124..5f1b984a4 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -144,7 +144,7 @@ bool Map::isNodeUnderground(v3s16 p) { v3s16 blockpos = getNodeBlockPos(p); MapBlock *block = getBlockNoCreateNoEx(blockpos); - return block && block->getIsUnderground(); + return block && block->getIsUnderground(); } bool Map::isValidPosition(v3s16 p) @@ -1187,7 +1187,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) ServerMap */ ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, - EmergeManager *emerge): + EmergeManager *emerge, MetricsBackend *mb): Map(dout_server, gamedef), settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"), m_emerge(emerge) @@ -1221,6 +1221,8 @@ ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, m_savedir = savedir; m_map_saving_enabled = false; + m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)"); + try { // If directory exists, check contents and load if possible if (fs::PathExists(m_savedir)) { @@ -1777,6 +1779,8 @@ void ServerMap::save(ModifiedState save_level) return; } + u64 start_time = porting::getTimeNs(); + if(save_level == MOD_STATE_CLEAN) infostream<<"ServerMap: Saving whole map, this can take time." <increment(end_time - start_time); } void ServerMap::listAllLoadableBlocks(std::vector &dst) diff --git a/src/map.h b/src/map.h index ff6b20c4f..77ee4da9e 100644 --- a/src/map.h +++ b/src/map.h @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "voxel.h" #include "modifiedstate.h" #include "util/container.h" +#include "util/metricsbackend.h" #include "nodetimer.h" #include "map_settings_manager.h" #include "debug.h" @@ -45,6 +46,7 @@ class NodeMetadata; class IGameDef; class IRollbackManager; class EmergeManager; +class MetricsBackend; class ServerEnvironment; struct BlockMakeData; @@ -324,7 +326,7 @@ public: /* savedir: directory to which map data should be saved */ - ServerMap(const std::string &savedir, IGameDef *gamedef, EmergeManager *emerge); + ServerMap(const std::string &savedir, IGameDef *gamedef, EmergeManager *emerge, MetricsBackend *mb); ~ServerMap(); s32 mapType() const @@ -449,6 +451,8 @@ private: bool m_map_metadata_changed = true; MapDatabase *dbase = nullptr; MapDatabase *dbase_ro = nullptr; + + MetricCounterPtr m_save_time_counter; }; diff --git a/src/server.cpp b/src/server.cpp index af6d3e40d..a7eb52837 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -229,18 +229,46 @@ Server::Server( m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), m_thread(new ServerThread(this)), - m_uptime(0), m_clients(m_con), m_admin_chat(iface), m_modchannel_mgr(new ModChannelMgr()) { - m_lag = g_settings->getFloat("dedicated_server_step"); - if (m_path_world.empty()) throw ServerError("Supplied empty world path"); if (!gamespec.isValid()) throw ServerError("Supplied invalid gamespec"); + +#if USE_PROMETHEUS + m_metrics_backend = std::unique_ptr(createPrometheusMetricsBackend()); +#else + m_metrics_backend = std::unique_ptr(new MetricsBackend()); +#endif + + m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)"); + m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players"); + + m_timeofday_gauge = m_metrics_backend->addGauge( + "minetest_core_timeofday", + "Time of day value"); + + m_lag_gauge = m_metrics_backend->addGauge( + "minetest_core_latency", + "Latency value (in seconds)"); + + m_aom_buffer_counter = m_metrics_backend->addCounter( + "minetest_core_aom_generated_count", + "Number of active object messages generated"); + + m_packet_recv_counter = m_metrics_backend->addCounter( + "minetest_core_server_packet_recv", + "Processable packets received"); + + m_packet_recv_processed_counter = m_metrics_backend->addCounter( + "minetest_core_server_packet_recv_processed", + "Valid received packets processed"); + + m_lag_gauge->set(g_settings->getFloat("dedicated_server_step")); } Server::~Server() @@ -353,7 +381,7 @@ void Server::init() MutexAutoLock envlock(m_env_mutex); // Create the Map (loads map_meta.txt, overriding configured mapgen params) - ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge); + ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get()); // Initialize scripting infostream << "Server: Initializing Lua" << std::endl; @@ -511,9 +539,7 @@ void Server::AsyncRunStep(bool initial_step) /* Update uptime */ - { - m_uptime.set(m_uptime.get() + dtime); - } + m_uptime_counter->increment(dtime); handlePeerChanges(); @@ -527,11 +553,13 @@ void Server::AsyncRunStep(bool initial_step) */ m_time_of_day_send_timer -= dtime; - if(m_time_of_day_send_timer < 0.0) { + if (m_time_of_day_send_timer < 0.0) { m_time_of_day_send_timer = g_settings->getFloat("time_send_interval"); u16 time = m_env->getTimeOfDay(); float time_speed = g_settings->getFloat("time_speed"); SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed); + + m_timeofday_gauge->set(time); } { @@ -603,7 +631,7 @@ void Server::AsyncRunStep(bool initial_step) } m_clients.step(dtime); - m_lag += (m_lag > dtime ? -1 : 1) * dtime/100; + m_lag_gauge->increment((m_lag_gauge->get() > dtime ? -1 : 1) * dtime/100); #if USE_CURL // send masterserver announce { @@ -614,9 +642,9 @@ void Server::AsyncRunStep(bool initial_step) ServerList::AA_START, m_bind_addr.getPort(), m_clients.getPlayerNames(), - m_uptime.get(), + m_uptime_counter->get(), m_env->getGameTime(), - m_lag, + m_lag_gauge->get(), m_gamespec.id, Mapgen::getMapgenName(m_emerge->mgparams->mgtype), m_modmgr->getMods(), @@ -638,6 +666,7 @@ void Server::AsyncRunStep(bool initial_step) const RemoteClientMap &clients = m_clients.getClientList(); ScopeProfiler sp(g_profiler, "Server: update objects within range"); + m_player_gauge->set(clients.size()); for (const auto &client_it : clients) { RemoteClient *client = client_it.second; @@ -703,6 +732,8 @@ void Server::AsyncRunStep(bool initial_step) message_list->push_back(aom); } + m_aom_buffer_counter->increment(buffered_messages.size()); + m_clients.lock(); const RemoteClientMap &clients = m_clients.getClientList(); // Route data to every client @@ -943,7 +974,9 @@ void Server::Receive() } peer_id = pkt.getPeerId(); + m_packet_recv_counter->increment(); ProcessData(&pkt); + m_packet_recv_processed_counter->increment(); } catch (const con::InvalidIncomingDataException &e) { infostream << "Server::Receive(): InvalidIncomingDataException: what()=" << e.what() << std::endl; @@ -3127,7 +3160,7 @@ std::wstring Server::getStatusString() // Version os << L"version=" << narrow_to_wide(g_version_string); // Uptime - os << L", uptime=" << m_uptime.get(); + os << L", uptime=" << m_uptime_counter->get(); // Max lag estimate os << L", max_lag=" << (m_env ? m_env->getMaxLagEstimate() : 0); diff --git a/src/server.h b/src/server.h index b995aba28..71059dd30 100644 --- a/src/server.h +++ b/src/server.h @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "util/thread.h" #include "util/basic_macros.h" +#include "util/metricsbackend.h" #include "serverenvironment.h" #include "clientiface.h" #include "chatmessage.h" @@ -203,7 +204,7 @@ public: // Connection must be locked when called std::wstring getStatusString(); - inline double getUptime() const { return m_uptime.m_value; } + inline double getUptime() const { return m_uptime_counter->get(); } // read shutdown state inline bool isShutdownRequested() const { return m_shutdown_state.is_requested; } @@ -591,9 +592,6 @@ private: float m_step_dtime = 0.0f; std::mutex m_step_dtime_mutex; - // current server step lag counter - float m_lag; - // The server mainly operates in this thread ServerThread *m_thread = nullptr; @@ -602,8 +600,6 @@ private: */ // Timer for sending time of day over network float m_time_of_day_send_timer = 0.0f; - // Uptime of server in seconds - MutexedVariable m_uptime; /* Client interface @@ -677,6 +673,19 @@ private: // ModChannel manager std::unique_ptr m_modchannel_mgr; + + // Global server metrics backend + std::unique_ptr m_metrics_backend; + + // Server metrics + MetricCounterPtr m_uptime_counter; + MetricGaugePtr m_player_gauge; + MetricGaugePtr m_timeofday_gauge; + // current server step lag + MetricGaugePtr m_lag_gauge; + MetricCounterPtr m_aom_buffer_counter; + MetricCounterPtr m_packet_recv_counter; + MetricCounterPtr m_packet_recv_processed_counter; }; /* diff --git a/src/server/mods.cpp b/src/server/mods.cpp index c8d8a28e2..6ac530739 100644 --- a/src/server/mods.cpp +++ b/src/server/mods.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_server.h" #include "content/subgames.h" #include "porting.h" +#include "util/metricsbackend.h" /** * Manage server mods diff --git a/src/server/mods.h b/src/server/mods.h index 2bc1aa22f..54774bd86 100644 --- a/src/server/mods.h +++ b/src/server/mods.h @@ -20,7 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "content/mods.h" +#include +class MetricsBackend; +class MetricCounter; class ServerScripting; /** diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 199d3aeaa..cd2e468d1 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -5,6 +5,7 @@ set(UTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ieee_float.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/metricsbackend.cpp ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/quicktune.cpp diff --git a/src/util/metricsbackend.cpp b/src/util/metricsbackend.cpp new file mode 100644 index 000000000..4454557a3 --- /dev/null +++ b/src/util/metricsbackend.cpp @@ -0,0 +1,140 @@ +/* +Minetest +Copyright (C) 2013-2020 Minetest core developers team + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#include "metricsbackend.h" +#if USE_PROMETHEUS +#include +#include +#include +#include +#include "log.h" +#include "settings.h" +#endif + +MetricCounterPtr MetricsBackend::addCounter( + const std::string &name, const std::string &help_str) +{ + return std::make_shared(name, help_str); +} + +MetricGaugePtr MetricsBackend::addGauge( + const std::string &name, const std::string &help_str) +{ + return std::make_shared(name, help_str); +} + +#if USE_PROMETHEUS + +class PrometheusMetricCounter : public MetricCounter +{ +public: + PrometheusMetricCounter() = delete; + + PrometheusMetricCounter(const std::string &name, const std::string &help_str, + std::shared_ptr registry) : + MetricCounter(), + m_family(prometheus::BuildCounter() + .Name(name) + .Help(help_str) + .Register(*registry)), + m_counter(m_family.Add({})) + { + } + + virtual ~PrometheusMetricCounter() {} + + virtual void increment(double number) { m_counter.Increment(number); } + virtual double get() const { return m_counter.Value(); } + +private: + prometheus::Family &m_family; + prometheus::Counter &m_counter; +}; + +class PrometheusMetricGauge : public MetricGauge +{ +public: + PrometheusMetricGauge() = delete; + + PrometheusMetricGauge(const std::string &name, const std::string &help_str, + std::shared_ptr registry) : + MetricGauge(), + m_family(prometheus::BuildGauge() + .Name(name) + .Help(help_str) + .Register(*registry)), + m_gauge(m_family.Add({})) + { + } + + virtual ~PrometheusMetricGauge() {} + + virtual void increment(double number) { m_gauge.Increment(number); } + virtual void decrement(double number) { m_gauge.Decrement(number); } + virtual void set(double number) { m_gauge.Set(number); } + virtual double get() const { return m_gauge.Value(); } + +private: + prometheus::Family &m_family; + prometheus::Gauge &m_gauge; +}; + +class PrometheusMetricsBackend : public MetricsBackend +{ +public: + PrometheusMetricsBackend(const std::string &addr) : + MetricsBackend(), m_exposer(std::unique_ptr( + new prometheus::Exposer(addr))), + m_registry(std::make_shared()) + { + m_exposer->RegisterCollectable(m_registry); + } + + virtual ~PrometheusMetricsBackend() {} + + virtual MetricCounterPtr addCounter( + const std::string &name, const std::string &help_str); + virtual MetricGaugePtr addGauge( + const std::string &name, const std::string &help_str); + +private: + std::unique_ptr m_exposer; + std::shared_ptr m_registry; +}; + +MetricCounterPtr PrometheusMetricsBackend::addCounter( + const std::string &name, const std::string &help_str) +{ + return std::make_shared(name, help_str, m_registry); +} + +MetricGaugePtr PrometheusMetricsBackend::addGauge( + const std::string &name, const std::string &help_str) +{ + return std::make_shared(name, help_str, m_registry); +} + +MetricsBackend *createPrometheusMetricsBackend() +{ + std::string addr; + g_settings->getNoEx("prometheus_listener_address", addr); + return new PrometheusMetricsBackend(addr); +} + +#endif diff --git a/src/util/metricsbackend.h b/src/util/metricsbackend.h new file mode 100644 index 000000000..c37306392 --- /dev/null +++ b/src/util/metricsbackend.h @@ -0,0 +1,140 @@ +/* +Minetest +Copyright (C) 2013-2020 Minetest core developers team + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#pragma once +#include +#include "config.h" +#include "util/thread.h" + +class MetricCounter +{ +public: + MetricCounter() = default; + + virtual ~MetricCounter() {} + + virtual void increment(double number = 1.0) = 0; + virtual double get() const = 0; +}; + +typedef std::shared_ptr MetricCounterPtr; + +class SimpleMetricCounter : public MetricCounter +{ +public: + SimpleMetricCounter() = delete; + + virtual ~SimpleMetricCounter() {} + + SimpleMetricCounter(const std::string &name, const std::string &help_str) : + MetricCounter(), m_name(name), m_help_str(help_str), + m_counter(0.0) + { + } + + virtual void increment(double number) + { + MutexAutoLock lock(m_mutex); + m_counter += number; + } + virtual double get() const + { + MutexAutoLock lock(m_mutex); + return m_counter; + } + +private: + std::string m_name; + std::string m_help_str; + + mutable std::mutex m_mutex; + double m_counter; +}; + +class MetricGauge +{ +public: + MetricGauge() = default; + virtual ~MetricGauge() {} + + virtual void increment(double number = 1.0) = 0; + virtual void decrement(double number = 1.0) = 0; + virtual void set(double number) = 0; + virtual double get() const = 0; +}; + +typedef std::shared_ptr MetricGaugePtr; + +class SimpleMetricGauge : public MetricGauge +{ +public: + SimpleMetricGauge() = delete; + + SimpleMetricGauge(const std::string &name, const std::string &help_str) : + MetricGauge(), m_name(name), m_help_str(help_str), m_gauge(0.0) + { + } + + virtual ~SimpleMetricGauge() {} + + virtual void increment(double number) + { + MutexAutoLock lock(m_mutex); + m_gauge += number; + } + virtual void decrement(double number) + { + MutexAutoLock lock(m_mutex); + m_gauge -= number; + } + virtual void set(double number) + { + MutexAutoLock lock(m_mutex); + m_gauge = number; + } + virtual double get() const + { + MutexAutoLock lock(m_mutex); + return m_gauge; + } + +private: + std::string m_name; + std::string m_help_str; + + mutable std::mutex m_mutex; + double m_gauge; +}; + +class MetricsBackend +{ +public: + MetricsBackend() = default; + + virtual ~MetricsBackend() {} + + virtual MetricCounterPtr addCounter( + const std::string &name, const std::string &help_str); + virtual MetricGaugePtr addGauge( + const std::string &name, const std::string &help_str); +}; + +#if USE_PROMETHEUS +MetricsBackend *createPrometheusMetricsBackend(); +#endif diff --git a/util/ci/build_prometheus_cpp.sh b/util/ci/build_prometheus_cpp.sh new file mode 100755 index 000000000..edfd574cd --- /dev/null +++ b/util/ci/build_prometheus_cpp.sh @@ -0,0 +1,13 @@ +#! /bin/bash -eu + +cd /tmp +git clone --recursive https://github.com/jupp0r/prometheus-cpp +mkdir prometheus-cpp/build +cd prometheus-cpp/build +cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DCMAKE_BUILD_TYPE=Release \ + -DENABLE_TESTING=0 +make -j2 +sudo make install + -- cgit v1.2.3 From d1c6cc72cce7e02dcaca7797b463d071d67d27db Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 5 May 2020 17:05:11 +0200 Subject: Server: Improve some log messages (#9820) --- src/network/serverpackethandler.cpp | 16 +++++++--------- src/server/player_sao.cpp | 6 +++--- 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'src/server') diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 5136eb0ec..adaa9a965 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -111,8 +111,6 @@ void Server::handleCommand_Init(NetworkPacket* pkt) if (depl_serial_v == SER_FMT_VER_INVALID) { actionstream << "Server: A mismatched client tried to connect from " << - addr_s << std::endl; - infostream << "Server: Cannot negotiate serialization version with " << addr_s << " client_max=" << (int)client_max << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION); return; @@ -902,8 +900,8 @@ bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std: actionstream << "Player " << player->getName() << " tried to access " << what << " from too far: " - << "d=" << d <<", max_d=" << max_d - << ". ignoring." << std::endl; + << "d=" << d << ", max_d=" << max_d + << "; ignoring." << std::endl; // Call callbacks m_script->on_cheat(player->getPlayerSAO(), "interacted_too_far"); return false; @@ -956,7 +954,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } if (playersao->isDead()) { - actionstream << "Server: NoCheat: " << player->getName() + actionstream << "Server: " << player->getName() << " tried to interact while dead; ignoring." << std::endl; if (pointed.type == POINTEDTHING_NODE) { // Re-send block to revert change on client-side @@ -1145,7 +1143,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) playersao->noCheatDigEnd(); // If player didn't start digging this, ignore dig if (nocheat_p != p_under) { - infostream << "Server: NoCheat: " << player->getName() + infostream << "Server: " << player->getName() << " started digging " << PP(nocheat_p) << " and completed digging " << PP(p_under) << "; not digging." << std::endl; @@ -1169,9 +1167,9 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } // If can't dig, ignore dig if (!params.diggable) { - infostream << "Server: NoCheat: " << player->getName() + infostream << "Server: " << player->getName() << " completed digging " << PP(p_under) - << ", which is not diggable with tool. not digging." + << ", which is not diggable with tool; not digging." << std::endl; is_valid_dig = false; // Call callbacks @@ -1195,7 +1193,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } // Dig not possible else { - infostream << "Server: NoCheat: " << player->getName() + infostream << "Server: " << player->getName() << " completed digging " << PP(p_under) << "too fast; not digging." << std::endl; is_valid_dig = false; diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 58fcea5fe..a4d0f4ce7 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -656,9 +656,9 @@ bool PlayerSAO::checkMovementCheat() float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); if (m_time_from_last_teleport > lag_pool_max) { - actionstream << "Player " << m_player->getName() - << " moved too fast; resetting position" - << std::endl; + actionstream << "Server: " << m_player->getName() + << " moved too fast: V=" << d_vert << ", H=" << d_horiz + << "; resetting position." << std::endl; cheated = true; } setBasePosition(m_last_good_position); -- cgit v1.2.3 From 454dbf83a9bf292910c1495a2aa49fd8b960c28f Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Thu, 7 May 2020 22:38:41 +0200 Subject: Server class code cleanups (#9769) * Server::overrideDayNightRatio doesn't require to return bool There is no sense to sending null player, the caller should send a valid object * Server::init: make private & cleanup This function is always called before start() and loads some variables which can be loaded in constructor directly. Make it private and call it directly in start * Split Server inventory responsibility to a dedicated object This splits permit to found various historical issues: * duplicate lookups on player connection * sending inventory to non related player when a player connects * non friendly lookups on detached inventories ownership This reduce the detached inventory complexity and also increased the lookup performance in a quite interesting way for servers with thousands of inventories. --- src/client/game.cpp | 1 - src/main.cpp | 2 - src/network/serverpackethandler.cpp | 11 ++- src/script/lua_api/l_base.cpp | 5 + src/script/lua_api/l_base.h | 2 + src/script/lua_api/l_inventory.cpp | 13 +-- src/script/lua_api/l_object.cpp | 7 +- src/server.cpp | 192 +++++------------------------------- src/server.h | 33 ++----- src/server/CMakeLists.txt | 1 + src/server/serverinventorymgr.cpp | 192 ++++++++++++++++++++++++++++++++++++ src/server/serverinventorymgr.h | 60 +++++++++++ 12 files changed, 312 insertions(+), 207 deletions(-) create mode 100644 src/server/serverinventorymgr.cpp create mode 100644 src/server/serverinventorymgr.h (limited to 'src/server') diff --git a/src/client/game.cpp b/src/client/game.cpp index 1577a37db..4d7a85526 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1302,7 +1302,6 @@ bool Game::createSingleplayerServer(const std::string &map_dir, } server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false); - server->init(); server->start(); return true; diff --git a/src/main.cpp b/src/main.cpp index 147f686ed..b3b17c2d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -887,7 +887,6 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Create server Server server(game_params.world_path, game_params.game_spec, false, bind_addr, true, &iface); - server.init(); g_term_console.setup(&iface, &kill, admin_nick); @@ -922,7 +921,6 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Create server Server server(game_params.world_path, game_params.game_spec, false, bind_addr, true); - server.init(); server.start(); // Run server diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index adaa9a965..39a912827 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/networkprotocol.h" #include "network/serveropcodes.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" #include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" @@ -620,9 +621,9 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) ma->from_inv.applyCurrentPlayer(player->getName()); ma->to_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(ma->from_inv); + m_inventory_mgr->setInventoryModified(ma->from_inv); if (ma->from_inv != ma->to_inv) - setInventoryModified(ma->to_inv); + m_inventory_mgr->setInventoryModified(ma->to_inv); bool from_inv_is_current_player = (ma->from_inv.type == InventoryLocation::PLAYER) && @@ -687,7 +688,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) da->from_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(da->from_inv); + m_inventory_mgr->setInventoryModified(da->from_inv); /* Disable dropping items out of craftpreview @@ -723,7 +724,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) ca->craft_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(ca->craft_inv); + m_inventory_mgr->setInventoryModified(ca->craft_inv); //bool craft_inv_is_current_player = // (ca->craft_inv.type == InventoryLocation::PLAYER) && @@ -739,7 +740,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) } // Do the action - a->apply(this, playersao, this); + a->apply(m_inventory_mgr.get(), playersao, this); // Eat the action delete a; } diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index b8658f62b..2bee09436 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -45,6 +45,11 @@ Server *ModApiBase::getServer(lua_State *L) return getScriptApiBase(L)->getServer(); } +ServerInventoryManager *ModApiBase::getServerInventoryMgr(lua_State *L) +{ + return getScriptApiBase(L)->getServer()->getInventoryMgr(); +} + #ifndef SERVER Client *ModApiBase::getClient(lua_State *L) { diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index e32647628..65fce8481 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -38,12 +38,14 @@ class GUIEngine; class ScriptApiBase; class Server; class Environment; +class ServerInventoryManager; class ModApiBase : protected LuaHelper { public: static ScriptApiBase* getScriptApiBase(lua_State *L); static Server* getServer(lua_State *L); + static ServerInventoryManager *getServerInventoryMgr(lua_State *L); #ifndef SERVER static Client* getClient(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L); diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 4c8977898..e41b5cb41 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" +#include "server/serverinventorymgr.h" #include "remoteplayer.h" /* @@ -38,7 +39,7 @@ InvRef* InvRef::checkobject(lua_State *L, int narg) Inventory* InvRef::getinv(lua_State *L, InvRef *ref) { - return getServer(L)->getInventory(ref->m_loc); + return getServerInventoryMgr(L)->getInventory(ref->m_loc); } InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, @@ -54,7 +55,7 @@ InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, void InvRef::reportInventoryChange(lua_State *L, InvRef *ref) { // Inform other things that the inventory has changed - getServer(L)->setInventoryModified(ref->m_loc); + getServerInventoryMgr(L)->setInventoryModified(ref->m_loc); } // Exported functions @@ -497,7 +498,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) v3s16 pos = check_v3s16(L, -1); loc.setNodeMeta(pos); - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); @@ -515,7 +516,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) lua_pop(L, 1); } - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); @@ -530,7 +531,7 @@ int ModApiInventory::l_create_detached_inventory_raw(lua_State *L) NO_MAP_LOCK_REQUIRED; const char *name = luaL_checkstring(L, 1); std::string player = readParam(L, 2, ""); - if (getServer(L)->createDetachedInventory(name, player) != NULL) { + if (getServerInventoryMgr(L)->createDetachedInventory(name, getServer(L)->idef(), player) != NULL) { InventoryLocation loc; loc.setDetached(name); InvRef::create(L, loc); @@ -545,7 +546,7 @@ int ModApiInventory::l_remove_detached_inventory_raw(lua_State *L) { NO_MAP_LOCK_REQUIRED; const std::string &name = luaL_checkstring(L, 1); - lua_pushboolean(L, getServer(L)->removeDetachedInventory(name)); + lua_pushboolean(L, getServerInventoryMgr(L)->removeDetachedInventory(name)); return 1; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index f71130378..0a9f3117b 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_server.h" #include "server/luaentity_sao.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" /* ObjectRef @@ -289,7 +290,7 @@ int ObjectRef::l_get_inventory(lua_State *L) if (co == NULL) return 0; // Do it InventoryLocation loc = co->getInventoryLocation(); - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); // An object may have no inventory (nil) @@ -2172,9 +2173,7 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L) ratio = readParam(L, 2); } - if (!getServer(L)->overrideDayNightRatio(player, do_override, ratio)) - return 0; - + getServer(L)->overrideDayNightRatio(player, do_override, ratio); lua_pushboolean(L, true); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 05584be2d..16e026ce2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chat_interface.h" #include "remoteplayer.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" #include "translation.h" class ClientNotFoundException : public BaseException @@ -338,11 +339,6 @@ Server::~Server() infostream << "Server: Deinitializing scripting" << std::endl; delete m_script; - // Delete detached inventories - for (auto &detached_inventory : m_detached_inventories) { - delete detached_inventory.second; - } - while (!m_unsent_map_edit_queue.empty()) { delete m_unsent_map_edit_queue.front(); m_unsent_map_edit_queue.pop(); @@ -388,6 +384,9 @@ void Server::init() m_script = new ServerScripting(this); + // Must be created before mod loading because we have some inventory creation + m_inventory_mgr = std::unique_ptr(new ServerInventoryManager()); + m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); m_modmgr->loadMods(m_script); @@ -422,6 +421,7 @@ void Server::init() // Initialize Environment m_env = new ServerEnvironment(servermap, m_script, this, m_path_world); + m_inventory_mgr->setEnv(m_env); m_clients.setEnv(m_env); if (!servermap->settings_mgr.makeMapgenParams()) @@ -443,6 +443,8 @@ void Server::init() m_env->loadMeta(); + // Those settings can be overwritten in world.mt, they are + // intended to be cached after environment loading. m_liquid_transform_every = g_settings->getFloat("liquid_update"); m_max_chatmessage_length = g_settings->getU16("chat_message_max_size"); m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags"); @@ -451,6 +453,8 @@ void Server::init() void Server::start() { + init(); + infostream << "Starting server on " << m_bind_addr.serializeString() << "..." << std::endl; @@ -1180,82 +1184,6 @@ void Server::onMapEditEvent(const MapEditEvent &event) m_unsent_map_edit_queue.push(new MapEditEvent(event)); } -Inventory* Server::getInventory(const InventoryLocation &loc) -{ - switch (loc.type) { - case InventoryLocation::UNDEFINED: - case InventoryLocation::CURRENT_PLAYER: - break; - case InventoryLocation::PLAYER: - { - RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); - if(!player) - return NULL; - PlayerSAO *playersao = player->getPlayerSAO(); - if(!playersao) - return NULL; - return playersao->getInventory(); - } - break; - case InventoryLocation::NODEMETA: - { - NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); - if(!meta) - return NULL; - return meta->getInventory(); - } - break; - case InventoryLocation::DETACHED: - { - if(m_detached_inventories.count(loc.name) == 0) - return NULL; - return m_detached_inventories[loc.name]; - } - break; - default: - sanity_check(false); // abort - break; - } - return NULL; -} - -void Server::setInventoryModified(const InventoryLocation &loc) -{ - switch(loc.type){ - case InventoryLocation::UNDEFINED: - break; - case InventoryLocation::PLAYER: - { - - RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); - - if (!player) - return; - - player->setModified(true); - player->inventory.setModified(true); - // Updates are sent in ServerEnvironment::step() - } - break; - case InventoryLocation::NODEMETA: - { - MapEditEvent event; - event.type = MEET_BLOCK_NODE_METADATA_CHANGED; - event.p = loc.p; - m_env->getMap().dispatchEvent(event); - } - break; - case InventoryLocation::DETACHED: - { - // Updates are sent in ServerEnvironment::step() - } - break; - default: - sanity_check(false); // abort - break; - } -} - void Server::SetBlocksNotSent(std::map& block) { std::vector clients = m_clients.getClientIDs(); @@ -2712,40 +2640,20 @@ void Server::sendRequestedMedia(session_t peer_id, } } -void Server::sendDetachedInventory(const std::string &name, session_t peer_id) +void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id) { - const auto &inv_it = m_detached_inventories.find(name); - const auto &player_it = m_detached_inventories_player.find(name); - - if (player_it == m_detached_inventories_player.end() || - player_it->second.empty()) { - // OK. Send to everyone - } else { - if (!m_env) - return; // Mods are not done loading - - RemotePlayer *p = m_env->getPlayer(player_it->second.c_str()); - if (!p) - return; // Player is offline - - if (peer_id != PEER_ID_INEXISTENT && peer_id != p->getPeerId()) - return; // Caller requested send to a different player, so don't send. - - peer_id = p->getPeerId(); - } - NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id); pkt << name; - if (inv_it == m_detached_inventories.end()) { + if (!inventory) { pkt << false; // Remove inventory } else { pkt << true; // Update inventory // Serialization & NetworkPacket isn't a love story std::ostringstream os(std::ios_base::binary); - inv_it->second->serialize(os); - inv_it->second->setModified(false); + inventory->serialize(os); + inventory->setModified(false); const std::string &os_str = os.str(); pkt << static_cast(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients @@ -2760,16 +2668,17 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id) void Server::sendDetachedInventories(session_t peer_id, bool incremental) { - for (const auto &detached_inventory : m_detached_inventories) { - const std::string &name = detached_inventory.first; - if (incremental) { - Inventory *inv = detached_inventory.second; - if (!inv || !inv->checkModified()) - continue; - } - - sendDetachedInventory(name, peer_id); + // Lookup player name, to filter detached inventories just after + std::string peer_name; + if (peer_id != PEER_ID_INEXISTENT) { + peer_name = getClient(peer_id, CS_Created)->getName(); } + + auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) { + sendDetachedInventory(inv, name, peer_id); + }; + + m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb); } /* @@ -3442,15 +3351,12 @@ void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms) SendCloudParams(player->getPeerId(), params); } -bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, +void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, float ratio) { - if (!player) - return false; - + sanity_check(player); player->overrideDayNightRatio(do_override, ratio); SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio); - return true; } void Server::notifyPlayers(const std::wstring &msg) @@ -3541,52 +3447,6 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } -Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player) -{ - if(m_detached_inventories.count(name) > 0){ - infostream<<"Server clearing detached inventory \""<second; - m_detached_inventories.erase(inv_it); - - if (!m_env) // Mods are not done loading - return true; - - const auto &player_it = m_detached_inventories_player.find(name); - if (player_it != m_detached_inventories_player.end()) { - RemotePlayer *player = m_env->getPlayer(player_it->second.c_str()); - - if (player && player->getPeerId() != PEER_ID_INEXISTENT) - sendDetachedInventory(name, player->getPeerId()); - - m_detached_inventories_player.erase(player_it); - } else { - // Notify all players about the change - sendDetachedInventory(name, PEER_ID_INEXISTENT); - } - return true; -} - // actions: time-reversed list // Return value: success/failure bool Server::rollbackRevertActions(const std::list &actions, @@ -3607,7 +3467,7 @@ bool Server::rollbackRevertActions(const std::list &actions, for (const RollbackAction &action : actions) { num_tried++; - bool success = action.applyRevert(map, this, this); + bool success = action.applyRevert(map, m_inventory_mgr.get(), this); if(!success){ num_failed++; std::ostringstream os; diff --git a/src/server.h b/src/server.h index 71059dd30..7a1de9370 100644 --- a/src/server.h +++ b/src/server.h @@ -68,6 +68,7 @@ struct MoonParams; struct StarParams; class ServerThread; class ServerModManager; +class ServerInventoryManager; enum ClientDeletionReason { CDR_LEAVE, @@ -116,7 +117,7 @@ struct ServerPlayingSound }; class Server : public con::PeerHandler, public MapEventReceiver, - public InventoryManager, public IGameDef + public IGameDef { public: /* @@ -134,7 +135,6 @@ public: ~Server(); DISABLE_CLASS_COPY(Server); - void init(); void start(); void stop(); // This is mainly a way to pass the time to the server. @@ -196,12 +196,6 @@ public: */ void onMapEditEvent(const MapEditEvent &event); - /* - Shall be called with the environment and the connection locked. - */ - Inventory* getInventory(const InventoryLocation &loc); - void setInventoryModified(const InventoryLocation &loc); - // Connection must be locked when called std::wstring getStatusString(); inline double getUptime() const { return m_uptime_counter->get(); } @@ -253,10 +247,8 @@ public: void deleteParticleSpawner(const std::string &playername, u32 id); - // Creates or resets inventory - Inventory *createDetachedInventory(const std::string &name, - const std::string &player = ""); - bool removeDetachedInventory(const std::string &name); + ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); } + void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id); // Envlock and conlock should be locked when using scriptapi ServerScripting *getScriptIface(){ return m_script; } @@ -318,7 +310,7 @@ public: void setClouds(RemotePlayer *player, const CloudParams ¶ms); - bool overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness); + void overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness); /* con::PeerHandler implementation. */ void peerAdded(con::Peer *peer); @@ -389,6 +381,8 @@ private: float m_timer = 0.0f; }; + void init(); + void SendMovement(session_t peer_id); void SendHP(session_t peer_id, u16 hp); void SendBreath(session_t peer_id, u16 breath); @@ -457,8 +451,6 @@ private: void sendRequestedMedia(session_t peer_id, const std::vector &tosend); - void sendDetachedInventory(const std::string &name, session_t peer_id); - // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) void SendAddParticleSpawner(session_t peer_id, u16 protocol_version, u16 amount, float spawntime, @@ -656,14 +648,6 @@ private: s32 m_next_sound_id = 0; // positive values only s32 nextSoundId(); - /* - Detached inventories (behind m_env_mutex) - */ - // key = name - std::map m_detached_inventories; - // value = "" (visible to all players) or player name - std::map m_detached_inventories_player; - std::unordered_map m_mod_storages; float m_mod_storage_save_timer = 10.0f; @@ -674,6 +658,9 @@ private: // ModChannel manager std::unique_ptr m_modchannel_mgr; + // Inventory manager + std::unique_ptr m_inventory_mgr; + // Global server metrics backend std::unique_ptr m_metrics_backend; diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 4d94504f6..0a5a8f3a7 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -4,5 +4,6 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/serverinventorymgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/serverinventorymgr.cpp b/src/server/serverinventorymgr.cpp new file mode 100644 index 000000000..555e01ec6 --- /dev/null +++ b/src/server/serverinventorymgr.cpp @@ -0,0 +1,192 @@ +/* +Minetest +Copyright (C) 2010-2020 Minetest core development team + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#include "serverinventorymgr.h" +#include "map.h" +#include "nodemetadata.h" +#include "player_sao.h" +#include "remoteplayer.h" +#include "server.h" +#include "serverenvironment.h" + +ServerInventoryManager::ServerInventoryManager() : InventoryManager() +{ +} + +ServerInventoryManager::~ServerInventoryManager() +{ + // Delete detached inventories + for (auto &detached_inventory : m_detached_inventories) { + delete detached_inventory.second.inventory; + } +} + +Inventory *ServerInventoryManager::getInventory(const InventoryLocation &loc) +{ + switch (loc.type) { + case InventoryLocation::UNDEFINED: + case InventoryLocation::CURRENT_PLAYER: + break; + case InventoryLocation::PLAYER: { + RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); + if (!player) + return NULL; + PlayerSAO *playersao = player->getPlayerSAO(); + if (!playersao) + return NULL; + return playersao->getInventory(); + } break; + case InventoryLocation::NODEMETA: { + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); + if (!meta) + return NULL; + return meta->getInventory(); + } break; + case InventoryLocation::DETACHED: { + auto it = m_detached_inventories.find(loc.name); + if (it == m_detached_inventories.end()) + return nullptr; + return it->second.inventory; + } break; + default: + sanity_check(false); // abort + break; + } + return NULL; +} + +void ServerInventoryManager::setInventoryModified(const InventoryLocation &loc) +{ + switch (loc.type) { + case InventoryLocation::UNDEFINED: + break; + case InventoryLocation::PLAYER: { + + RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); + + if (!player) + return; + + player->setModified(true); + player->inventory.setModified(true); + // Updates are sent in ServerEnvironment::step() + } break; + case InventoryLocation::NODEMETA: { + MapEditEvent event; + event.type = MEET_BLOCK_NODE_METADATA_CHANGED; + event.p = loc.p; + m_env->getMap().dispatchEvent(event); + } break; + case InventoryLocation::DETACHED: { + // Updates are sent in ServerEnvironment::step() + } break; + default: + sanity_check(false); // abort + break; + } +} + +Inventory *ServerInventoryManager::createDetachedInventory( + const std::string &name, IItemDefManager *idef, const std::string &player) +{ + if (m_detached_inventories.count(name) > 0) { + infostream << "Server clearing detached inventory \"" << name << "\"" + << std::endl; + delete m_detached_inventories[name].inventory; + } else { + infostream << "Server creating detached inventory \"" << name << "\"" + << std::endl; + } + + Inventory *inv = new Inventory(idef); + sanity_check(inv); + m_detached_inventories[name].inventory = inv; + if (!player.empty()) { + m_detached_inventories[name].owner = player; + + if (!m_env) + return inv; // Mods are not loaded yet, ignore + + RemotePlayer *p = m_env->getPlayer(name.c_str()); + + // if player is connected, send him the inventory + if (p && p->getPeerId() != PEER_ID_INEXISTENT) { + m_env->getGameDef()->sendDetachedInventory( + inv, name, p->getPeerId()); + } + } else { + if (!m_env) + return inv; // Mods are not loaded yet, don't send + + // Inventory is for everybody, broadcast + m_env->getGameDef()->sendDetachedInventory(inv, name, PEER_ID_INEXISTENT); + } + + return inv; +} + +bool ServerInventoryManager::removeDetachedInventory(const std::string &name) +{ + const auto &inv_it = m_detached_inventories.find(name); + if (inv_it == m_detached_inventories.end()) + return false; + + delete inv_it->second.inventory; + const std::string &owner = inv_it->second.owner; + + if (!owner.empty()) { + RemotePlayer *player = m_env->getPlayer(owner.c_str()); + + if (player && player->getPeerId() != PEER_ID_INEXISTENT) + m_env->getGameDef()->sendDetachedInventory( + nullptr, name, player->getPeerId()); + + } else { + // Notify all players about the change + m_env->getGameDef()->sendDetachedInventory( + nullptr, name, PEER_ID_INEXISTENT); + } + + m_detached_inventories.erase(inv_it); + + return true; +} + +void ServerInventoryManager::sendDetachedInventories(const std::string &peer_name, + bool incremental, + std::function apply_cb) +{ + for (const auto &detached_inventory : m_detached_inventories) { + const DetachedInventory &dinv = detached_inventory.second; + if (incremental) { + if (!dinv.inventory || !dinv.inventory->checkModified()) + continue; + } + + // if we are pushing inventories to a specific player + // we should filter to send only the right inventories + if (!peer_name.empty()) { + const std::string &attached_player = dinv.owner; + if (!attached_player.empty() && peer_name != attached_player) + continue; + } + + apply_cb(detached_inventory.first, detached_inventory.second.inventory); + } +} diff --git a/src/server/serverinventorymgr.h b/src/server/serverinventorymgr.h new file mode 100644 index 000000000..d0aac4dae --- /dev/null +++ b/src/server/serverinventorymgr.h @@ -0,0 +1,60 @@ +/* +Minetest +Copyright (C) 2010-2020 Minetest core development team + +This program is free software; you can redistribute it and/or modify +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 Lesser General Public License for more details. + +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. +*/ + +#pragma once + +#include "inventorymanager.h" +#include + +class ServerEnvironment; + +class ServerInventoryManager : public InventoryManager +{ +public: + ServerInventoryManager(); + virtual ~ServerInventoryManager(); + + void setEnv(ServerEnvironment *env) + { + assert(!m_env); + m_env = env; + } + + Inventory *getInventory(const InventoryLocation &loc); + void setInventoryModified(const InventoryLocation &loc); + + // Creates or resets inventory + Inventory *createDetachedInventory(const std::string &name, IItemDefManager *idef, + const std::string &player = ""); + bool removeDetachedInventory(const std::string &name); + + void sendDetachedInventories(const std::string &peer_name, bool incremental, + std::function apply_cb); + +private: + struct DetachedInventory + { + Inventory *inventory; + std::string owner; + }; + + ServerEnvironment *m_env = nullptr; + + std::unordered_map m_detached_inventories; +}; \ No newline at end of file -- cgit v1.2.3 From 471e567657dfd75a994a1b54d7a23cf4541a6bed Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 26 May 2020 17:38:31 +0200 Subject: Value copy / allocation optimizations mostly in server, SAO and serialize code --- src/client/game.cpp | 2 +- src/client/sky.cpp | 4 ++-- src/client/sky.h | 2 +- src/content/mods.cpp | 2 +- src/content/mods.h | 2 +- src/script/cpp_api/s_node.cpp | 2 +- src/script/cpp_api/s_node.h | 2 +- src/server.cpp | 43 ++++++++++++++++++--------------------- src/server/luaentity_sao.cpp | 15 +++++--------- src/server/player_sao.cpp | 7 ++----- src/server/serveractiveobject.cpp | 2 +- src/server/serverinventorymgr.h | 2 +- src/serverenvironment.cpp | 8 ++++---- src/serverenvironment.h | 4 ++-- src/tool.cpp | 2 +- src/util/serialize.cpp | 17 ++++++++-------- 16 files changed, 52 insertions(+), 64 deletions(-) (limited to 'src/server') diff --git a/src/client/game.cpp b/src/client/game.cpp index e7663a113..cdf4da21e 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2806,7 +2806,7 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) // Shows the mesh skybox sky->setVisible(true); // Update mesh based skybox colours if applicable. - sky->setSkyColors(*event->set_sky); + sky->setSkyColors(event->set_sky->sky_color); sky->setHorizonTint( event->set_sky->fog_sun_tint, event->set_sky->fog_moon_tint, diff --git a/src/client/sky.cpp b/src/client/sky.cpp index d21b56fcc..2e0cbca86 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -907,9 +907,9 @@ void Sky::setStarCount(u16 star_count, bool force_update) } } -void Sky::setSkyColors(const SkyboxParams sky) +void Sky::setSkyColors(const SkyColor &sky_color) { - m_sky_params.sky_color = sky.sky_color; + m_sky_params.sky_color = sky_color; } void Sky::setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, diff --git a/src/client/sky.h b/src/client/sky.h index 8637f96d4..3227e8f59 100644 --- a/src/client/sky.h +++ b/src/client/sky.h @@ -94,7 +94,7 @@ public: m_bgcolor = bgcolor; m_skycolor = skycolor; } - void setSkyColors(const SkyboxParams sky); + void setSkyColors(const SkyColor &sky_color); void setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, std::string use_sun_tint); void setInClouds(bool clouds) { m_in_clouds = clouds; } diff --git a/src/content/mods.cpp b/src/content/mods.cpp index 676666f78..95ab0290a 100644 --- a/src/content/mods.cpp +++ b/src/content/mods.cpp @@ -167,7 +167,7 @@ std::map getModsInPath( return result; } -std::vector flattenMods(std::map mods) +std::vector flattenMods(const std::map &mods) { std::vector result; for (const auto &it : mods) { diff --git a/src/content/mods.h b/src/content/mods.h index 6e2506dbf..b3500fbc8 100644 --- a/src/content/mods.h +++ b/src/content/mods.h @@ -68,7 +68,7 @@ std::map getModsInPath( const std::string &path, bool part_of_modpack = false); // replaces modpack Modspecs with their content -std::vector flattenMods(std::map mods); +std::vector flattenMods(const std::map &mods); // a ModConfiguration is a subset of installed mods, expected to have // all dependencies fullfilled, so it can be used as a list of mods to diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index d93a4c3ad..e0f9bcd78 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -94,7 +94,7 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] = }; bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node, - ServerActiveObject *puncher, PointedThing pointed) + ServerActiveObject *puncher, const PointedThing &pointed) { SCRIPTAPI_PRECHECKHEADER diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h index e7c0c01d1..81b44f0f0 100644 --- a/src/script/cpp_api/s_node.h +++ b/src/script/cpp_api/s_node.h @@ -36,7 +36,7 @@ public: virtual ~ScriptApiNode() = default; bool node_on_punch(v3s16 p, MapNode node, - ServerActiveObject *puncher, PointedThing pointed); + ServerActiveObject *puncher, const PointedThing &pointed); bool node_on_dig(v3s16 p, MapNode node, ServerActiveObject *digger); void node_on_construct(v3s16 p, MapNode node); diff --git a/src/server.cpp b/src/server.cpp index 8c62584c8..6ecbd7097 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -718,34 +718,35 @@ void Server::AsyncRunStep(bool initial_step) std::unordered_map*> buffered_messages; // Get active object messages from environment + ActiveObjectMessage aom(0); + u32 aom_count = 0; for(;;) { - ActiveObjectMessage aom = m_env->getActiveObjectMessage(); - if (aom.id == 0) + if (!m_env->getActiveObjectMessage(&aom)) break; std::vector* message_list = nullptr; - std::unordered_map* >::iterator n; - n = buffered_messages.find(aom.id); + auto n = buffered_messages.find(aom.id); if (n == buffered_messages.end()) { message_list = new std::vector; buffered_messages[aom.id] = message_list; - } - else { + } else { message_list = n->second; } - message_list->push_back(aom); + message_list->push_back(std::move(aom)); + aom_count++; } - m_aom_buffer_counter->increment(buffered_messages.size()); + m_aom_buffer_counter->increment(aom_count); m_clients.lock(); const RemoteClientMap &clients = m_clients.getClientList(); // Route data to every client + std::string reliable_data, unreliable_data; for (const auto &client_it : clients) { + reliable_data.clear(); + unreliable_data.clear(); RemoteClient *client = client_it.second; PlayerSAO *player = getPlayerSAO(client->peer_id); - std::string reliable_data; - std::string unreliable_data; // Go through all objects in message buffer for (const auto &buffered_message : buffered_messages) { // If object does not exist or is not known by client, skip it @@ -770,19 +771,15 @@ void Server::AsyncRunStep(bool initial_step) client->m_known_objects.end()) continue; } - // Compose the full new data with header - std::string new_data; - // Add object id - char buf[2]; - writeU16((u8*)&buf[0], aom.id); - new_data.append(buf, 2); - // Add data - new_data += serializeString(aom.datastring); - // Add data to buffer - if (aom.reliable) - reliable_data += new_data; - else - unreliable_data += new_data; + + // Add full new data to appropriate buffer + std::string &buffer = aom.reliable ? reliable_data : unreliable_data; + char idbuf[2]; + writeU16((u8*) idbuf, aom.id); + // u16 id + // std::string data + buffer.append(idbuf, sizeof(idbuf)); + buffer.append(serializeString(aom.datastring)); } } /* diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 51e1ca90e..8174da265 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -119,8 +119,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) m_properties_sent = true; std::string str = getPropertyPacket(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } // If attached, check that our parent is still there. If it isn't, detach. @@ -228,16 +227,14 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) m_animation_sent = true; std::string str = generateUpdateAnimationCommand(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } if (!m_animation_speed_sent) { m_animation_speed_sent = true; std::string str = generateUpdateAnimationSpeedCommand(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } if (!m_bone_position_sent) { @@ -247,8 +244,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) std::string str = generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } } @@ -256,8 +252,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) m_attachment_sent = true; std::string str = generateUpdateAttachmentCommand(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } } diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index a4d0f4ce7..3ea3536e2 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -223,8 +223,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_properties_sent = true; std::string str = getPropertyPacket(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); m_env->getScriptIface()->player_event(this, "properties_changed"); } @@ -324,10 +323,8 @@ void PlayerSAO::step(float dtime, bool send_recommended) if (!m_attachment_sent) { m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand()); } } diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index 8345ebd47..fdcb13bd8 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -75,7 +75,7 @@ std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const vid void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) { while (!m_messages_out.empty()) { - queue.push(m_messages_out.front()); + queue.push(std::move(m_messages_out.front())); m_messages_out.pop(); } } \ No newline at end of file diff --git a/src/server/serverinventorymgr.h b/src/server/serverinventorymgr.h index d0aac4dae..ccf6d3b2e 100644 --- a/src/server/serverinventorymgr.h +++ b/src/server/serverinventorymgr.h @@ -57,4 +57,4 @@ private: ServerEnvironment *m_env = nullptr; std::unordered_map m_detached_inventories; -}; \ No newline at end of file +}; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index d485c32e8..222b4d203 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1603,14 +1603,14 @@ void ServerEnvironment::setStaticForActiveObjectsInBlock( } } -ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() +bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest) { if(m_active_object_messages.empty()) - return ActiveObjectMessage(0); + return false; - ActiveObjectMessage message = m_active_object_messages.front(); + *dest = std::move(m_active_object_messages.front()); m_active_object_messages.pop(); - return message; + return true; } void ServerEnvironment::getSelectedActiveObjects( diff --git a/src/serverenvironment.h b/src/serverenvironment.h index e2f1a3784..4b453d39a 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -289,9 +289,9 @@ public: /* Get the next message emitted by some active object. - Returns a message with id=0 if no messages are available. + Returns false if no messages are available, true otherwise. */ - ActiveObjectMessage getActiveObjectMessage(); + bool getActiveObjectMessage(ActiveObjectMessage *dest); virtual void getSelectedActiveObjects( const core::line3d &shootline_on_map, diff --git a/src/tool.cpp b/src/tool.cpp index d911c518f..22e41d28e 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -130,7 +130,7 @@ void ToolCapabilities::serializeJson(std::ostream &os) const root["punch_attack_uses"] = punch_attack_uses; Json::Value groupcaps_object; - for (auto groupcap : groupcaps) { + for (const auto &groupcap : groupcaps) { groupcap.second.toJson(groupcaps_object[groupcap.first]); } root["groupcaps"] = groupcaps_object; diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp index f0e177d57..5b276668d 100644 --- a/src/util/serialize.cpp +++ b/src/util/serialize.cpp @@ -110,6 +110,7 @@ std::string serializeString(const std::string &plain) if (plain.size() > STRING_MAX_LEN) throw SerializationError("String too long for serializeString"); + s.reserve(2 + plain.size()); writeU16((u8 *)&buf[0], plain.size()); s.append(buf, 2); @@ -131,13 +132,11 @@ std::string deSerializeString(std::istream &is) if (s_size == 0) return s; - Buffer buf2(s_size); - is.read(&buf2[0], s_size); + s.resize(s_size); + is.read(&s[0], s_size); if (is.gcount() != s_size) throw SerializationError("deSerializeString: couldn't read all chars"); - s.reserve(s_size); - s.append(&buf2[0], s_size); return s; } @@ -152,6 +151,7 @@ std::string serializeWideString(const std::wstring &plain) if (plain.size() > WIDE_STRING_MAX_LEN) throw SerializationError("String too long for serializeWideString"); + s.reserve(2 + 2 * plain.size()); writeU16((u8 *)buf, plain.size()); s.append(buf, 2); @@ -196,13 +196,14 @@ std::wstring deSerializeWideString(std::istream &is) std::string serializeLongString(const std::string &plain) { + std::string s; char buf[4]; if (plain.size() > LONG_STRING_MAX_LEN) throw SerializationError("String too long for serializeLongString"); + s.reserve(4 + plain.size()); writeU32((u8*)&buf[0], plain.size()); - std::string s; s.append(buf, 4); s.append(plain); return s; @@ -227,13 +228,11 @@ std::string deSerializeLongString(std::istream &is) "string too long: " + itos(s_size) + " bytes"); } - Buffer buf2(s_size); - is.read(&buf2[0], s_size); + s.resize(s_size); + is.read(&s[0], s_size); if ((u32)is.gcount() != s_size) throw SerializationError("deSerializeLongString: couldn't read all chars"); - s.reserve(s_size); - s.append(&buf2[0], s_size); return s; } -- cgit v1.2.3 From 4c8e1c320054ee0dc5d8ec821a6b4cd69002aa09 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 26 May 2020 16:05:06 +0200 Subject: Clean up CAO nametag handling and remove deprecated AO_CMD AO_CMD_UPDATE_NAMETAG_ATTRIBUTES was deprecated in 9eee3c3f465c071bb9908749cf48be3c131a1bdf (0.4.14) --- src/activeobject.h | 3 ++- src/client/content_cao.cpp | 57 ++++++++++++++++++++++++--------------- src/client/content_cao.h | 2 ++ src/server/player_sao.cpp | 4 +-- src/server/serveractiveobject.cpp | 13 +-------- 5 files changed, 42 insertions(+), 37 deletions(-) (limited to 'src/server') diff --git a/src/activeobject.h b/src/activeobject.h index c83243f86..85e160d10 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -66,7 +66,8 @@ enum ActiveObjectCommand { AO_CMD_SET_BONE_POSITION, AO_CMD_ATTACH_TO, AO_CMD_SET_PHYSICS_OVERRIDE, - AO_CMD_UPDATE_NAMETAG_ATTRIBUTES, + AO_CMD_OBSOLETE1, + // ^ UPDATE_NAMETAG_ATTRIBUTES deprecated since 0.4.14, removed in 5.3.0 AO_CMD_SPAWN_INFANT, AO_CMD_SET_ANIMATION_SPEED }; diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 947c1a279..dde31899b 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -776,15 +776,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc) if (node && m_matrixnode) node->setParent(m_matrixnode); - if (node && !m_prop.nametag.empty() && !m_is_local_player) { - // Add nametag - v3f pos; - pos.Y = m_prop.selectionbox.MaxEdge.Y + 0.3f; - m_nametag = m_client->getCamera()->addNametag(node, - m_prop.nametag, m_prop.nametag_color, - pos); - } - + updateNametag(); updateNodePos(); updateAnimation(); updateBonePosition(); @@ -872,6 +864,38 @@ v3s16 GenericCAO::getLightPosition() return floatToInt(m_position, BS); } +void GenericCAO::updateNametag() +{ + if (m_is_local_player) // No nametag for local player + return; + + if (m_prop.nametag.empty()) { + // Delete nametag + if (m_nametag) { + m_client->getCamera()->removeNametag(m_nametag); + m_nametag = nullptr; + } + return; + } + + scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + + v3f pos; + pos.Y = m_prop.selectionbox.MaxEdge.Y + 0.3f; + if (!m_nametag) { + // Add nametag + m_nametag = m_client->getCamera()->addNametag(node, + m_prop.nametag, m_prop.nametag_color, pos); + } else { + // Update nametag + m_nametag->nametag_text = m_prop.nametag; + m_nametag->nametag_color = m_prop.nametag_color; + m_nametag->nametag_pos = pos; + } +} + void GenericCAO::updateNodePos() { if (getParent() != NULL) @@ -1465,8 +1489,6 @@ bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const old.initial_sprite_basepos != new_.initial_sprite_basepos || old.is_visible != new_.is_visible || old.mesh != new_.mesh || - old.nametag != new_.nametag || - old.nametag_color != new_.nametag_color || old.spritediv != new_.spritediv || old.use_texture_alpha != new_.use_texture_alpha || old.visual != new_.visual || @@ -1516,6 +1538,7 @@ void GenericCAO::processMessage(const std::string &data) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) m_prop.nametag = m_name; + updateNametag(); if (expire_visuals) { expireVisuals(); @@ -1694,22 +1717,14 @@ void GenericCAO::processMessage(const std::string &data) int rating = readS16(is); m_armor_groups[name] = rating; } - } else if (cmd == AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) { - // Deprecated, for backwards compatibility only. - readU8(is); // version - m_prop.nametag_color = readARGB8(is); - if (m_nametag != NULL) { - m_nametag->nametag_color = m_prop.nametag_color; - v3f pos; - pos.Y = m_prop.collisionbox.MaxEdge.Y + 0.3f; - m_nametag->nametag_pos = pos; - } } else if (cmd == AO_CMD_SPAWN_INFANT) { u16 child_id = readU16(is); u8 type = readU8(is); // maybe this will be useful later (void)type; addAttachmentChild(child_id); + } else if (cmd == AO_CMD_OBSOLETE1) { + // Don't do anything and also don't log a warning } else { warningstream << FUNCTION_NAME << ": unknown command or outdated client \"" diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 03a355204..8e2a13ea8 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -244,6 +244,8 @@ public: v3s16 getLightPosition(); + void updateNametag(); + void updateNodePos(); void step(float dtime, ClientEnvironment *env); diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 3ea3536e2..9ea0743f7 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -127,9 +127,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) } msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 - // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. - msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 - int message_count = 6 + m_bone_position.size(); + int message_count = 5 + m_bone_position.size(); for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); ii != m_attachment_child_ids.end(); ++ii) { if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index fdcb13bd8..dbf25e3bc 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -61,21 +61,10 @@ std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 p return os.str(); } -std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const video::SColor &color) const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_UPDATE_NAMETAG_ATTRIBUTES); - // parameters - writeU8(os, 1); // version for forward compatibility - writeARGB8(os, color); - return os.str(); -} - void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) { while (!m_messages_out.empty()) { queue.push(std::move(m_messages_out.front())); m_messages_out.pop(); } -} \ No newline at end of file +} -- cgit v1.2.3 From c1e01bc638637efa788b5698238a465406bc3f5e Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 4 Jun 2020 19:31:46 +0200 Subject: Move shared parameters sending to UnitSAO (#9968) Better header sorting by topic Make UnitSAO-specific parameters private Skip redundant recursive entity sending code (since ~5.2.0) --- src/client/content_cao.cpp | 1 + src/server/luaentity_sao.cpp | 71 ++++++++++----------------------------- src/server/player_sao.cpp | 57 +++++++++---------------------- src/server/serveractiveobject.cpp | 7 +++- src/server/unit_sao.cpp | 33 ++++++++++++++++++ src/server/unit_sao.h | 56 +++++++++++++++++++----------- 6 files changed, 110 insertions(+), 115 deletions(-) (limited to 'src/server') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index d0682e51e..702d089af 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -475,6 +475,7 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit parent->addAttachmentChild(m_id); } + updateAttachments(); } diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 8174da265..d504c42ca 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -123,12 +123,11 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) } // 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); + if (m_attachment_parent_id && !isAttached()) { + // This is handled when objects are removed from the map + warningstream << "LuaEntitySAO::step() id=" << m_id << + " is attached to nonexistent parent. This is a bug." << std::endl; + clearParentAttachment(); sendPosition(false, true); } @@ -217,43 +216,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) } } - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - - if (!m_animation_sent) { - m_animation_sent = true; - std::string str = generateUpdateAnimationCommand(); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - - if (!m_animation_speed_sent) { - m_animation_speed_sent = true; - std::string str = generateUpdateAnimationSpeedCommand(); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } + sendOutdatedData(); } std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) @@ -273,20 +236,19 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) msg_os << serializeLongString(getPropertyPacket()); // message 1 msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + for (const auto &bone_pos : m_bone_position) { + msg_os << serializeLongString(generateUpdateBonePositionCommand( + bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size } msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + int message_count = 4 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - (ii != m_attachment_child_ids.end()); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + + for (const auto &id : getAttachmentChildIds()) { + if (ServerActiveObject *obj = m_env->getActiveObject(id)) { message_count++; - // TODO after a protocol bump: only send the object initialization data - // to older clients (superfluous since this message exists) - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + msg_os << serializeLongString(obj->generateUpdateInfantCommand( + id, protocol_version)); } } @@ -294,7 +256,8 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) message_count++; writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); + std::string serialized = msg_os.str(); + os.write(serialized.c_str(), serialized.size()); // return result return os.str(); diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 9ea0743f7..67efed210 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -120,24 +120,26 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) msg_os << serializeLongString(getPropertyPacket()); // message 1 msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + for (const auto &bone_pos : m_bone_position) { + msg_os << serializeLongString(generateUpdateBonePositionCommand( + bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size } msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + int message_count = 5 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - ii != m_attachment_child_ids.end(); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + + for (const auto &id : getAttachmentChildIds()) { + if (ServerActiveObject *obj = m_env->getActiveObject(id)) { message_count++; - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + msg_os << serializeLongString(obj->generateUpdateInfantCommand( + id, protocol_version)); } } writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); + std::string serialized = msg_os.str(); + os.write(serialized.c_str(), serialized.size()); // return result return os.str(); @@ -227,10 +229,10 @@ void PlayerSAO::step(float dtime, bool send_recommended) // 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.0f, 0.0f, 0.0f); - m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); + // This is handled when objects are removed from the map + warningstream << "PlayerSAO::step() id=" << m_id << + " is attached to nonexistent parent. This is a bug." << std::endl; + clearParentAttachment(); setBasePosition(m_last_good_position); m_env->getGameDef()->SendMovePlayer(m_peer_id); } @@ -290,40 +292,13 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_messages_out.emplace(getId(), false, str); } - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - if (!m_physics_override_sent) { m_physics_override_sent = true; // create message and add to list m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); } - if (!m_animation_sent) { - m_animation_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand()); - } + sendOutdatedData(); } std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index dbf25e3bc..3341dc008 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -57,7 +57,12 @@ std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 p // parameters writeU16(os, infant_id); writeU8(os, getSendType()); - os << serializeLongString(getClientInitializationData(protocol_version)); + if (protocol_version < 38) { + // Clients since 4aa9a66 so no longer need this data + // Version 38 is the first bump after that commit. + // See also: ClientEnvironment::addActiveObject + os << serializeLongString(getClientInitializationData(protocol_version)); + } return os.str(); } diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index 74b0508b8..ef0e87f2c 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -88,6 +88,39 @@ void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotat *rotation = m_bone_position[bone].Y; } +// clang-format off +void UnitSAO::sendOutdatedData() +{ + if (!m_armor_groups_sent) { + m_armor_groups_sent = true; + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); + } + + if (!m_animation_sent) { + m_animation_sent = true; + m_animation_speed_sent = true; + m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); + } else if (!m_animation_speed_sent) { + // Animation speed is also sent when 'm_animation_sent == false' + m_animation_speed_sent = true; + m_messages_out.emplace(getId(), true, generateUpdateAnimationSpeedCommand()); + } + + if (!m_bone_position_sent) { + m_bone_position_sent = true; + for (const auto &bone_pos : m_bone_position) { + m_messages_out.emplace(getId(), true, generateUpdateBonePositionCommand( + bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); + } + } + + if (!m_attachment_sent) { + m_attachment_sent = true; + m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand()); + } +} +// clang-format on + void UnitSAO::setAttachment( int parent_id, const std::string &bone, v3f position, v3f rotation) { diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index c73115855..3cb7f0ad5 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -29,6 +29,11 @@ public: UnitSAO(ServerEnvironment *env, v3f pos); virtual ~UnitSAO() = default; + u16 getHP() const { return m_hp; } + // Use a function, if isDead can be defined by other conditions + bool isDead() const { return m_hp == 0; } + + // Rotation void setRotation(v3f rotation) { m_rotation = rotation; } const v3f &getRotation() const { return m_rotation; } v3f getRadRotation() { return m_rotation * core::DEGTORAD; } @@ -36,26 +41,28 @@ public: // Deprecated f32 getRadYawDep() const { return (m_rotation.Y + 90.) * core::DEGTORAD; } - u16 getHP() const { return m_hp; } - // Use a function, if isDead can be defined by other conditions - bool isDead() const { return m_hp == 0; } - - inline bool isAttached() const { return getParent(); } - + // Armor groups inline bool isImmortal() const { return itemgroup_get(getArmorGroups(), "immortal"); } - void setArmorGroups(const ItemGroupList &armor_groups); const ItemGroupList &getArmorGroups() const; + + // Animation void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); void setAnimationSpeed(float frame_speed); + + // Bone position void setBonePosition(const std::string &bone, v3f position, v3f rotation); void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); + + // Attachments + ServerActiveObject *getParent() const; + inline bool isAttached() const { return getParent(); } void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); void getAttachment(int *parent_id, std::string *bone, v3f *position, @@ -65,10 +72,13 @@ public: void addAttachmentChild(int child_id); void removeAttachmentChild(int child_id); const std::unordered_set &getAttachmentChildIds() const; - ServerActiveObject *getParent() const; + + // Object properties ObjectProperties *accessObjectProperties(); void notifyObjectPropertiesModified(); + void sendOutdatedData(); + // Update packets std::string generateUpdateAttachmentCommand() const; std::string generateUpdateAnimationSpeedCommand() const; std::string generateUpdateAnimationCommand() const; @@ -77,21 +87,36 @@ public: const v3f &velocity, const v3f &acceleration, const v3f &rotation, bool do_interpolate, bool is_movement_end, f32 update_interval); std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; - void sendPunchCommand(); static std::string generateUpdateBonePositionCommand(const std::string &bone, const v3f &position, const v3f &rotation); + void sendPunchCommand(); protected: u16 m_hp = 1; v3f m_rotation; + ItemGroupList m_armor_groups; + + // Object properties bool m_properties_sent = true; ObjectProperties m_prop; - ItemGroupList m_armor_groups; + // Stores position and rotation for each bone name + std::unordered_map> m_bone_position; + + int m_attachment_parent_id = 0; + +private: + void onAttach(int parent_id); + void onDetach(int parent_id); + + std::string generatePunchCommand(u16 result_hp) const; + + // Armor groups bool m_armor_groups_sent = false; + // Animation v2f m_animation_range; float m_animation_speed = 0.0f; float m_animation_blend = 0.0f; @@ -99,20 +124,13 @@ protected: bool m_animation_sent = false; bool m_animation_speed_sent = false; - // Stores position and rotation for each bone name - std::unordered_map> m_bone_position; + // Bone positions bool m_bone_position_sent = false; - int m_attachment_parent_id = 0; + // Attachments std::unordered_set m_attachment_child_ids; std::string m_attachment_bone = ""; v3f m_attachment_position; v3f m_attachment_rotation; bool m_attachment_sent = false; - -private: - void onAttach(int parent_id); - void onDetach(int parent_id); - - std::string generatePunchCommand(u16 result_hp) const; }; -- cgit v1.2.3