From 054c5dfaa35dd79560a465ccc0ef214cb5f34c88 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 10 Apr 2020 22:06:24 +0200 Subject: scriptapi: Sort out ServerEnvironment / Environment distinction properly The API implementation is shared between CSM and SSM. Functions should retrieve a plain env when they do not need any server-specific functions. --- src/script/lua_api/l_object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/lua_api/l_object.cpp') diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 23ed1ffe0..5cd978d73 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -533,7 +533,7 @@ int ObjectRef::l_set_local_animation(lua_State *L) // get_local_animation(self) int ObjectRef::l_get_local_animation(lua_State *L) { - NO_MAP_LOCK_REQUIRED + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); if (player == NULL) -- 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/script/lua_api/l_object.cpp') 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 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/script/lua_api/l_object.cpp') 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/script/lua_api/l_object.cpp') 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 2d5bd3bf794672285cfc796edebab2f672f2cab0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 Apr 2020 20:44:18 +0200 Subject: scriptapi: Some small optimizations to value pushing (#9669) --- src/script/common/c_content.cpp | 34 ++++++++++++++++----------------- src/script/common/c_converter.cpp | 40 +++++++++++++++++++-------------------- src/script/lua_api/l_env.cpp | 8 ++++---- src/script/lua_api/l_mapgen.cpp | 15 +++++++-------- src/script/lua_api/l_noise.cpp | 14 +++++++------- src/script/lua_api/l_object.cpp | 9 +-------- src/script/lua_api/l_vmanip.cpp | 6 +++--- 7 files changed, 59 insertions(+), 67 deletions(-) (limited to 'src/script/lua_api/l_object.cpp') diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index ff9ceec6d..d73ecedab 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -350,7 +350,7 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) push_v3f(L, prop->visual_size); lua_setfield(L, -2, "visual_size"); - lua_newtable(L); + lua_createtable(L, prop->textures.size(), 0); u16 i = 1; for (const std::string &texture : prop->textures) { lua_pushlstring(L, texture.c_str(), texture.size()); @@ -358,7 +358,7 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) } lua_setfield(L, -2, "textures"); - lua_newtable(L); + lua_createtable(L, prop->colors.size(), 0); i = 1; for (const video::SColor &color : prop->colors) { push_ARGB8(L, color); @@ -840,7 +840,7 @@ void push_content_features(lua_State *L, const ContentFeatures &c) lua_pushnumber(L, c.connect_sides); lua_setfield(L, -2, "connect_sides"); - lua_newtable(L); + lua_createtable(L, c.connects_to.size(), 0); u16 i = 1; for (const std::string &it : c.connects_to) { lua_pushlstring(L, it.c_str(), it.size()); @@ -963,7 +963,7 @@ void push_nodebox(lua_State *L, const NodeBox &box) void push_box(lua_State *L, const std::vector &box) { - lua_newtable(L); + lua_createtable(L, box.size(), 0); u8 i = 1; for (const aabb3f &it : box) { push_aabb3f(L, it); @@ -1040,7 +1040,7 @@ void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec) void push_soundspec(lua_State *L, const SimpleSoundSpec &spec) { - lua_newtable(L); + lua_createtable(L, 0, 3); lua_pushstring(L, spec.name.c_str()); lua_setfield(L, -2, "name"); lua_pushnumber(L, spec.gain); @@ -1125,12 +1125,12 @@ MapNode readnode(lua_State *L, int index, const NodeDefManager *ndef) /******************************************************************************/ void pushnode(lua_State *L, const MapNode &n, const NodeDefManager *ndef) { - lua_newtable(L); + lua_createtable(L, 0, 3); lua_pushstring(L, ndef->get(n).name.c_str()); lua_setfield(L, -2, "name"); - lua_pushnumber(L, n.getParam1()); + lua_pushinteger(L, n.getParam1()); lua_setfield(L, -2, "param1"); - lua_pushnumber(L, n.getParam2()); + lua_pushinteger(L, n.getParam2()); lua_setfield(L, -2, "param2"); } @@ -1163,7 +1163,7 @@ bool string_to_enum(const EnumString *spec, int &result, { const EnumString *esp = spec; while(esp->str){ - if(str == std::string(esp->str)){ + if (!strcmp(str.c_str(), esp->str)) { result = esp->num; return true; } @@ -1438,7 +1438,7 @@ ToolCapabilities read_tool_capabilities( /******************************************************************************/ void push_dig_params(lua_State *L,const DigParams ¶ms) { - lua_newtable(L); + lua_createtable(L, 0, 3); setboolfield(L, -1, "diggable", params.diggable); setfloatfield(L, -1, "time", params.time); setintfield(L, -1, "wear", params.wear); @@ -1447,7 +1447,7 @@ void push_dig_params(lua_State *L,const DigParams ¶ms) /******************************************************************************/ void push_hit_params(lua_State *L,const HitParams ¶ms) { - lua_newtable(L); + lua_createtable(L, 0, 3); setintfield(L, -1, "hp", params.hp); setintfield(L, -1, "wear", params.wear); } @@ -1540,9 +1540,9 @@ void read_groups(lua_State *L, int index, ItemGroupList &result) /******************************************************************************/ void push_groups(lua_State *L, const ItemGroupList &groups) { - lua_newtable(L); + lua_createtable(L, 0, groups.size()); for (const auto &group : groups) { - lua_pushnumber(L, group.second); + lua_pushinteger(L, group.second); lua_setfield(L, -2, group.first.c_str()); } } @@ -1587,7 +1587,7 @@ void luaentity_get(lua_State *L, u16 id) lua_getglobal(L, "core"); lua_getfield(L, -1, "luaentities"); luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); + lua_pushinteger(L, id); lua_gettable(L, -2); lua_remove(L, -2); // Remove luaentities lua_remove(L, -2); // Remove core @@ -1689,7 +1689,7 @@ static bool push_json_value_helper(lua_State *L, const Json::Value &value, lua_pushboolean(L, value.asInt()); break; case Json::arrayValue: - lua_newtable(L); + lua_createtable(L, value.size(), 0); for (Json::Value::const_iterator it = value.begin(); it != value.end(); ++it) { push_json_value_helper(L, *it, nullindex); @@ -1697,7 +1697,7 @@ static bool push_json_value_helper(lua_State *L, const Json::Value &value, } break; case Json::objectValue: - lua_newtable(L); + lua_createtable(L, 0, value.size()); for (Json::Value::const_iterator it = value.begin(); it != value.end(); ++it) { #ifndef JSONCPP_STRING @@ -1824,7 +1824,7 @@ void push_objectRef(lua_State *L, const u16 id) lua_getglobal(L, "core"); lua_getfield(L, -1, "object_refs"); luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); + lua_pushinteger(L, id); lua_gettable(L, -2); lua_remove(L, -2); // object_refs lua_remove(L, -2); // core diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 3c2f75641..334af61c3 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -62,7 +62,7 @@ void push_float_string(lua_State *L, float value) void push_v3f(lua_State *L, v3f p) { - lua_newtable(L); + lua_createtable(L, 0, 3); lua_pushnumber(L, p.X); lua_setfield(L, -2, "x"); lua_pushnumber(L, p.Y); @@ -73,7 +73,7 @@ void push_v3f(lua_State *L, v3f p) void push_v2f(lua_State *L, v2f p) { - lua_newtable(L); + lua_createtable(L, 0, 2); lua_pushnumber(L, p.X); lua_setfield(L, -2, "x"); lua_pushnumber(L, p.Y); @@ -82,7 +82,7 @@ void push_v2f(lua_State *L, v2f p) void push_v3_float_string(lua_State *L, v3f p) { - lua_newtable(L); + lua_createtable(L, 0, 3); push_float_string(L, p.X); lua_setfield(L, -2, "x"); push_float_string(L, p.Y); @@ -93,7 +93,7 @@ void push_v3_float_string(lua_State *L, v3f p) void push_v2_float_string(lua_State *L, v2f p) { - lua_newtable(L); + lua_createtable(L, 0, 2); push_float_string(L, p.X); lua_setfield(L, -2, "x"); push_float_string(L, p.Y); @@ -115,19 +115,19 @@ v2s16 read_v2s16(lua_State *L, int index) void push_v2s16(lua_State *L, v2s16 p) { - lua_newtable(L); - lua_pushnumber(L, p.X); + lua_createtable(L, 0, 2); + lua_pushinteger(L, p.X); lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); + lua_pushinteger(L, p.Y); lua_setfield(L, -2, "y"); } void push_v2s32(lua_State *L, v2s32 p) { - lua_newtable(L); - lua_pushnumber(L, p.X); + lua_createtable(L, 0, 2); + lua_pushinteger(L, p.X); lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); + lua_pushinteger(L, p.Y); lua_setfield(L, -2, "y"); } @@ -250,14 +250,14 @@ v3d check_v3d(lua_State *L, int index) void push_ARGB8(lua_State *L, video::SColor color) { - lua_newtable(L); - lua_pushnumber(L, color.getAlpha()); + lua_createtable(L, 0, 4); + lua_pushinteger(L, color.getAlpha()); lua_setfield(L, -2, "a"); - lua_pushnumber(L, color.getRed()); + lua_pushinteger(L, color.getRed()); lua_setfield(L, -2, "r"); - lua_pushnumber(L, color.getGreen()); + lua_pushinteger(L, color.getGreen()); lua_setfield(L, -2, "g"); - lua_pushnumber(L, color.getBlue()); + lua_pushinteger(L, color.getBlue()); lua_setfield(L, -2, "b"); } @@ -274,12 +274,12 @@ v3f checkFloatPos(lua_State *L, int index) void push_v3s16(lua_State *L, v3s16 p) { - lua_newtable(L); - lua_pushnumber(L, p.X); + lua_createtable(L, 0, 3); + lua_pushinteger(L, p.X); lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); + lua_pushinteger(L, p.Y); lua_setfield(L, -2, "y"); - lua_pushnumber(L, p.Z); + lua_pushinteger(L, p.Z); lua_setfield(L, -2, "z"); } @@ -386,7 +386,7 @@ aabb3f read_aabb3f(lua_State *L, int index, f32 scale) void push_aabb3f(lua_State *L, aabb3f box) { - lua_newtable(L); + lua_createtable(L, 6, 0); lua_pushnumber(L, box.MinEdge.X); lua_rawseti(L, -2, 1); lua_pushnumber(L, box.MinEdge.Y); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index e3afe1862..8c45a1510 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -73,7 +73,7 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, lua_remove(L, -2); // Remove core // Get registered_abms[m_id] - lua_pushnumber(L, m_id); + lua_pushinteger(L, m_id); lua_gettable(L, -2); if(lua_isnil(L, -1)) FATAL_ERROR(""); @@ -116,7 +116,7 @@ void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) lua_remove(L, -2); // Remove core // Get registered_lbms[m_id] - lua_pushnumber(L, m_id); + lua_pushinteger(L, m_id); lua_gettable(L, -2); FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table"); lua_remove(L, -2); // Remove registered_lbms @@ -550,7 +550,7 @@ int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L) std::vector positions = env->getMap().findNodesWithMetadata( check_v3s16(L, 1), check_v3s16(L, 2)); - lua_newtable(L); + lua_createtable(L, positions.size(), 0); for (size_t i = 0; i != positions.size(); i++) { push_v3s16(L, positions[i]); lua_rawseti(L, -2, i + 1); @@ -1197,7 +1197,7 @@ int ModApiEnvMod::l_find_path(lua_State *L) searchdistance, max_jump, max_drop, algo); if (!path.empty()) { - lua_newtable(L); + lua_createtable(L, path.size(), 0); int top = lua_gettop(L); unsigned int index = 1; for (const v3s16 &i : path) { diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index cb0d6ac95..afe77826a 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -710,7 +710,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) if (!mg->heightmap) return 0; - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushinteger(L, mg->heightmap[i]); lua_rawseti(L, -2, i + 1); @@ -722,7 +722,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) if (!mg->biomegen) return 0; - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushinteger(L, mg->biomegen->biomemap[i]); lua_rawseti(L, -2, i + 1); @@ -736,7 +736,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen; - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, bg->heatmap[i]); lua_rawseti(L, -2, i + 1); @@ -751,7 +751,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen; - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, bg->humidmap[i]); lua_rawseti(L, -2, i + 1); @@ -761,13 +761,12 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) } case MGOBJ_GENNOTIFY: { std::map >event_map; - std::map >::iterator it; mg->gennotify.getEvents(event_map); - lua_newtable(L); - for (it = event_map.begin(); it != event_map.end(); ++it) { - lua_newtable(L); + lua_createtable(L, 0, event_map.size()); + for (auto it = event_map.begin(); it != event_map.end(); ++it) { + lua_createtable(L, it->second.size(), 0); for (size_t j = 0; j != it->second.size(); j++) { push_v3s16(L, it->second[j]); diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index e38d319f4..9aeb15709 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -171,9 +171,9 @@ int LuaPerlinNoiseMap::l_get_2d_map(lua_State *L) Noise *n = o->noise; n->perlinMap2D(p.X, p.Y); - lua_newtable(L); + lua_createtable(L, n->sy, 0); for (u32 y = 0; y != n->sy; y++) { - lua_newtable(L); + lua_createtable(L, n->sx, 0); for (u32 x = 0; x != n->sx; x++) { lua_pushnumber(L, n->result[i++]); lua_rawseti(L, -2, x + 1); @@ -200,7 +200,7 @@ int LuaPerlinNoiseMap::l_get_2d_map_flat(lua_State *L) if (use_buffer) lua_pushvalue(L, 3); else - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, n->result[i]); @@ -224,11 +224,11 @@ int LuaPerlinNoiseMap::l_get_3d_map(lua_State *L) Noise *n = o->noise; n->perlinMap3D(p.X, p.Y, p.Z); - lua_newtable(L); + lua_createtable(L, n->sz, 0); for (u32 z = 0; z != n->sz; z++) { - lua_newtable(L); + lua_createtable(L, n->sy, 0); for (u32 y = 0; y != n->sy; y++) { - lua_newtable(L); + lua_createtable(L, n->sx, 0); for (u32 x = 0; x != n->sx; x++) { lua_pushnumber(L, n->result[i++]); lua_rawseti(L, -2, x + 1); @@ -260,7 +260,7 @@ int LuaPerlinNoiseMap::l_get_3d_map_flat(lua_State *L) if (use_buffer) lua_pushvalue(L, 3); else - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, n->result[i]); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index fa34260bf..bb1456ac9 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -123,14 +123,7 @@ int ObjectRef::l_get_pos(lua_State *L) ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; - v3f pos = co->getBasePosition() / BS; - lua_newtable(L); - lua_pushnumber(L, pos.X); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, pos.Y); - lua_setfield(L, -2, "y"); - lua_pushnumber(L, pos.Z); - lua_setfield(L, -2, "z"); + push_v3f(L, co->getBasePosition() / BS); return 1; } diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index fd73d21d1..b99b1d98c 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -72,7 +72,7 @@ int LuaVoxelManip::l_get_data(lua_State *L) if (use_buffer) lua_pushvalue(L, 2); else - lua_newtable(L); + lua_createtable(L, volume, 0); for (u32 i = 0; i != volume; i++) { lua_Integer cid = vm->m_data[i].getContent(); @@ -261,7 +261,7 @@ int LuaVoxelManip::l_get_light_data(lua_State *L) u32 volume = vm->m_area.getVolume(); - lua_newtable(L); + lua_createtable(L, volume, 0); for (u32 i = 0; i != volume; i++) { lua_Integer light = vm->m_data[i].param1; lua_pushinteger(L, light); @@ -309,7 +309,7 @@ int LuaVoxelManip::l_get_param2_data(lua_State *L) if (use_buffer) lua_pushvalue(L, 2); else - lua_newtable(L); + lua_createtable(L, volume, 0); for (u32 i = 0; i != volume; i++) { lua_Integer param2 = vm->m_data[i].param2; -- cgit v1.2.3 From 87829cd7446dd13d1dfd27d96e0b4aeb2f234e33 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 18 Apr 2020 17:21:58 +0200 Subject: script: Move SAO usability check so that it covers all functions (#9698) see also 91eef646a59575bd9ae792e257bb6ad12fafc0b1 --- src/script/lua_api/l_object.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'src/script/lua_api/l_object.cpp') diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index bb1456ac9..d7afb84da 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -50,6 +50,8 @@ ObjectRef* ObjectRef::checkobject(lua_State *L, int narg) ServerActiveObject* ObjectRef::getobject(ObjectRef *ref) { ServerActiveObject *co = ref->m_object; + if (co && co->isGone()) + return NULL; return co; } @@ -60,8 +62,6 @@ LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref) return NULL; if (obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) return NULL; - if (obj->isGone()) - return NULL; return (LuaEntitySAO*)obj; } @@ -72,8 +72,6 @@ PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref) return NULL; if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) return NULL; - if (obj->isGone()) - return NULL; return (PlayerSAO*)obj; } @@ -132,7 +130,6 @@ int ObjectRef::l_set_pos(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - //LuaEntitySAO *co = getluaobject(ref); ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; // pos @@ -147,7 +144,6 @@ int ObjectRef::l_move_to(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - //LuaEntitySAO *co = getluaobject(ref); ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; // pos @@ -1102,17 +1098,13 @@ int ObjectRef::l_add_player_velocity(lua_State *L) ObjectRef *ref = checkobject(L, 1); v3f vel = checkFloatPos(L, 2); - RemotePlayer *player = getplayer(ref); PlayerSAO *co = getplayersao(ref); - if (!player || !co) + if (!co) return 0; - session_t peer_id = player->getPeerId(); - if (peer_id == PEER_ID_INEXISTENT) - return 0; // Do it co->setMaxSpeedOverride(vel); - getServer(L)->SendPlayerSpeed(peer_id, vel); + getServer(L)->SendPlayerSpeed(co->getPeerID(), vel); return 0; } -- cgit v1.2.3 From 91c4f7f0ea0b653d15bfabc05f5474f8fa1a8806 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Mon, 27 Apr 2020 11:27:27 +0200 Subject: Forbid object:attach(obj, ...) (#9762) Fixes #9761 --- src/script/lua_api/l_object.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/script/lua_api/l_object.cpp') diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index d7afb84da..77e1e7dc2 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -674,8 +674,13 @@ int ObjectRef::l_set_attach(lua_State *L) ServerActiveObject *parent = getobject(parent_ref); if (co == NULL) return 0; + if (parent == NULL) return 0; + + if (co == parent) + throw LuaError("ObjectRef::set_attach: attaching object to itself is not allowed."); + // Do it int parent_id = 0; std::string bone; -- cgit v1.2.3 From e0ea87f1f32273dba2eb5421c2a8c890479ba078 Mon Sep 17 00:00:00 2001 From: ANAND Date: Sat, 2 May 2020 16:22:11 +0530 Subject: set_fov: Add support for time-based transitions (#9705) --- doc/lua_api.txt | 15 ++++--- src/client/camera.cpp | 84 +++++++++++++++++++++++++++++-------- src/client/camera.h | 15 ++++++- src/network/clientpackethandler.cpp | 15 ++++++- src/network/networkprotocol.h | 3 +- src/player.h | 12 ++++-- src/script/lua_api/l_object.cpp | 11 +++-- src/server.cpp | 4 +- 8 files changed, 124 insertions(+), 35 deletions(-) (limited to 'src/script/lua_api/l_object.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f9107b623..44f62f7a7 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5998,15 +5998,18 @@ object you are working with still exists. * max: bubbles bar is not shown * See [Object properties] for more information * Is limited to range 0 ... 65535 (2^16 - 1) -* `set_fov(fov, is_multiplier)`: Sets player's FOV +* `set_fov(fov, is_multiplier, transition_time)`: Sets player's FOV * `fov`: FOV value. * `is_multiplier`: Set to `true` if the FOV value is a multiplier. Defaults to `false`. - * Set to 0 to clear FOV override. -* `get_fov()`: - * Returns player's FOV override in degrees, and a boolean depending on whether - the value is a multiplier. - * Returns 0 as first value if player's FOV hasn't been overridden. + * `transition_time`: If defined, enables smooth FOV transition. + Interpreted as the time (in seconds) to reach target FOV. + If set to 0, FOV change is instantaneous. Defaults to 0. + * Set `fov` to 0 to clear FOV override. +* `get_fov()`: Returns the following: + * Server-sent FOV value. Returns 0 if an FOV override doesn't exist. + * Boolean indicating whether the FOV value is a multiplier. + * Time (in seconds) taken for the FOV transition. Set by `set_fov`. * `set_attribute(attribute, value)`: DEPRECATED, use get_meta() instead * Sets an extra attribute with value on player. * `value` must be a string, or a number which will be converted to a diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 69bd82a47..1a5253db4 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -86,6 +86,51 @@ Camera::~Camera() m_wieldmgr->drop(); } +void Camera::notifyFovChange() +{ + LocalPlayer *player = m_client->getEnv().getLocalPlayer(); + assert(player); + + PlayerFovSpec spec = player->getFov(); + + /* + * Update m_old_fov_degrees first - it serves as the starting point of the + * upcoming transition. + * + * If an FOV transition is already active, mark current FOV as the start of + * the new transition. If not, set it to the previous transition's target FOV. + */ + if (m_fov_transition_active) + m_old_fov_degrees = m_curr_fov_degrees; + else + m_old_fov_degrees = m_server_sent_fov ? m_target_fov_degrees : m_cache_fov; + + /* + * Update m_server_sent_fov next - it corresponds to the target FOV of the + * upcoming transition. + * + * Set it to m_cache_fov, if server-sent FOV is 0. Otherwise check if + * server-sent FOV is a multiplier, and multiply it with m_cache_fov instead + * of overriding. + */ + if (spec.fov == 0.0f) { + m_server_sent_fov = false; + m_target_fov_degrees = m_cache_fov; + } else { + m_server_sent_fov = true; + m_target_fov_degrees = spec.is_multiplier ? m_cache_fov * spec.fov : spec.fov; + } + + if (spec.transition_time > 0.0f) + m_fov_transition_active = true; + + // If FOV smooth transition is active, initialize required variables + if (m_fov_transition_active) { + m_transition_time = spec.transition_time; + m_fov_diff = m_target_fov_degrees - m_old_fov_degrees; + } +} + bool Camera::successfullyCreated(std::string &error_message) { if (!m_playernode) { @@ -462,33 +507,38 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r m_camera_position = my_cp; /* - * Apply server-sent FOV. If server doesn't enforce FOV, - * check for zoom and set to zoom FOV. - * Otherwise, default to m_cache_fov + * Apply server-sent FOV, instantaneous or smooth transition. + * If not, check for zoom and set to zoom FOV. + * Otherwise, default to m_cache_fov. */ - - f32 fov_degrees; - PlayerFovSpec fov_spec = player->getFov(); - if (fov_spec.fov > 0.0f) { - // If server-sent FOV is a multiplier, multiply - // it with m_cache_fov instead of overriding - if (fov_spec.is_multiplier) - fov_degrees = m_cache_fov * fov_spec.fov; - else - fov_degrees = fov_spec.fov; + if (m_fov_transition_active) { + // Smooth FOV transition + // Dynamically calculate FOV delta based on frametimes + f32 delta = (frametime / m_transition_time) * m_fov_diff; + m_curr_fov_degrees += delta; + + // Mark transition as complete if target FOV has been reached + if ((m_fov_diff > 0.0f && m_curr_fov_degrees >= m_target_fov_degrees) || + (m_fov_diff < 0.0f && m_curr_fov_degrees <= m_target_fov_degrees)) { + m_fov_transition_active = false; + m_curr_fov_degrees = m_target_fov_degrees; + } + } else if (m_server_sent_fov) { + // Instantaneous FOV change + m_curr_fov_degrees = m_target_fov_degrees; } else if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) { // Player requests zoom, apply zoom FOV - fov_degrees = player->getZoomFOV(); + m_curr_fov_degrees = player->getZoomFOV(); } else { // Set to client's selected FOV - fov_degrees = m_cache_fov; + m_curr_fov_degrees = m_cache_fov; } - fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f); + m_curr_fov_degrees = rangelim(m_curr_fov_degrees, 1.0f, 160.0f); // FOV and aspect ratio const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); m_aspect = (f32) window_size.X / (f32) window_size.Y; - m_fov_y = fov_degrees * M_PI / 180.0; + m_fov_y = m_curr_fov_degrees * M_PI / 180.0; // Increase vertical FOV on lower aspect ratios (<16:10) m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect))); m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y)); diff --git a/src/client/camera.h b/src/client/camera.h index 6ec37fe10..3a59637bc 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -112,6 +112,9 @@ public: return MYMAX(m_fov_x, m_fov_y); } + // Notify about new server-sent FOV and initialize smooth FOV transition + void notifyFovChange(); + // Checks if the constructor was able to create the scene nodes bool successfullyCreated(std::string &error_message); @@ -186,6 +189,9 @@ private: Client *m_client; + // Default Client FOV (as defined by the "fov" setting) + f32 m_cache_fov; + // Absolute camera position v3f m_camera_position; // Absolute camera direction @@ -193,6 +199,14 @@ private: // Camera offset v3s16 m_camera_offset; + // Server-sent FOV variables + bool m_server_sent_fov = false; + f32 m_curr_fov_degrees, m_old_fov_degrees, m_target_fov_degrees; + + // FOV transition variables + bool m_fov_transition_active = false; + f32 m_fov_diff, m_transition_time; + v2f m_wieldmesh_offset = v2f(55.0f, -35.0f); v2f m_arm_dir; v2f m_cam_vel; @@ -230,7 +244,6 @@ private: f32 m_cache_fall_bobbing_amount; f32 m_cache_view_bobbing_amount; - f32 m_cache_fov; bool m_arm_inertia; std::list m_nametags; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index d19dc3818..6428ed752 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/client.h" #include "util/base64.h" +#include "client/camera.h" #include "chatmessage.h" #include "client/clientmedia.h" #include "log.h" @@ -530,11 +531,21 @@ void Client::handleCommand_Movement(NetworkPacket* pkt) void Client::handleCommand_Fov(NetworkPacket *pkt) { f32 fov; - bool is_multiplier; + bool is_multiplier = false; + f32 transition_time = 0.0f; + *pkt >> fov >> is_multiplier; + // Wrap transition_time extraction within a + // try-catch to preserve backwards compat + try { + *pkt >> transition_time; + } catch (PacketError &e) {}; + LocalPlayer *player = m_env.getLocalPlayer(); - player->setFov({ fov, is_multiplier }); + assert(player); + player->setFov({ fov, is_multiplier, transition_time }); + m_camera->notifyFovChange(); } void Client::handleCommand_HP(NetworkPacket *pkt) diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 4b7345b15..73523ea42 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -384,8 +384,9 @@ enum ToClientCommand /* Sends an FOV override/multiplier to client. - float fov + f32 fov bool is_multiplier + f32 transition_time */ TOCLIENT_DEATHSCREEN = 0x37, diff --git a/src/player.h b/src/player.h index de7f427e9..3bc7762fa 100644 --- a/src/player.h +++ b/src/player.h @@ -35,7 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc., struct PlayerFovSpec { f32 fov; + + // Whether to multiply the client's FOV or to override it bool is_multiplier; + + // The time to be take to trasition to the new FOV value. + // Transition is instantaneous if omitted. Omitted by default. + f32 transition_time; }; struct PlayerControl @@ -186,12 +192,12 @@ public: void setFov(const PlayerFovSpec &spec) { - m_fov_spec = spec; + m_fov_override_spec = spec; } const PlayerFovSpec &getFov() const { - return m_fov_spec; + return m_fov_override_spec; } u32 keyPressed = 0; @@ -208,7 +214,7 @@ protected: char m_name[PLAYERNAME_SIZE]; v3f m_speed; u16 m_wield_index = 0; - PlayerFovSpec m_fov_spec = { 0.0f, false }; + PlayerFovSpec m_fov_override_spec = { 0.0f, false, 0.0f }; std::vector hud; private: diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 77e1e7dc2..dcaee10b2 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1249,7 +1249,7 @@ int ObjectRef::l_set_look_yaw(lua_State *L) return 1; } -// set_fov(self, degrees[, is_multiplier]) +// set_fov(self, degrees[, is_multiplier, transition_time]) int ObjectRef::l_set_fov(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -1258,7 +1258,11 @@ int ObjectRef::l_set_fov(lua_State *L) if (!player) return 0; - player->setFov({ static_cast(luaL_checknumber(L, 2)), readParam(L, 3) }); + player->setFov({ + static_cast(luaL_checknumber(L, 2)), + readParam(L, 3, false), + lua_isnumber(L, 4) ? static_cast(luaL_checknumber(L, 4)) : 0.0f + }); getServer(L)->SendPlayerFov(player->getPeerId()); return 0; @@ -1276,8 +1280,9 @@ int ObjectRef::l_get_fov(lua_State *L) PlayerFovSpec fov_spec = player->getFov(); lua_pushnumber(L, fov_spec.fov); lua_pushboolean(L, fov_spec.is_multiplier); + lua_pushnumber(L, fov_spec.transition_time); - return 2; + return 3; } // set_breath(self, breath) diff --git a/src/server.cpp b/src/server.cpp index a7eb52837..0346d197d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1892,10 +1892,10 @@ void Server::SendMovePlayer(session_t peer_id) void Server::SendPlayerFov(session_t peer_id) { - NetworkPacket pkt(TOCLIENT_FOV, 4 + 1, peer_id); + NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id); PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov(); - pkt << fov_spec.fov << fov_spec.is_multiplier; + pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time; Send(&pkt); } -- cgit v1.2.3 From cad5b987ad4720bd6a49d3604be9e81ea348f799 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 4 May 2020 20:19:12 +0200 Subject: Sky API: Rename *_tint to fog_*_tint for consistency --- src/client/game.cpp | 12 ++++++------ src/client/sky.cpp | 12 ++++++------ src/network/clientpackethandler.cpp | 8 ++++---- src/network/networkprotocol.h | 6 +++--- src/remoteplayer.cpp | 6 +++--- src/script/lua_api/l_object.cpp | 22 +++++++++++----------- src/server.cpp | 4 ++-- src/skyparams.h | 6 +++--- 8 files changed, 38 insertions(+), 38 deletions(-) (limited to 'src/script/lua_api/l_object.cpp') diff --git a/src/client/game.cpp b/src/client/game.cpp index 3bdac786c..d1eb3bba2 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2799,9 +2799,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) // Update mesh based skybox colours if applicable. sky->setSkyColors(*event->set_sky); sky->setHorizonTint( - event->set_sky->sun_tint, - event->set_sky->moon_tint, - event->set_sky->tint_type + event->set_sky->fog_sun_tint, + event->set_sky->fog_moon_tint, + event->set_sky->fog_tint_type ); } else if (event->set_sky->type == "skybox" && event->set_sky->textures.size() == 6) { @@ -2811,9 +2811,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) sky->setFallbackBgColor(event->set_sky->bgcolor); // Set sunrise and sunset fog tinting: sky->setHorizonTint( - event->set_sky->sun_tint, - event->set_sky->moon_tint, - event->set_sky->tint_type + event->set_sky->fog_sun_tint, + event->set_sky->fog_moon_tint, + event->set_sky->fog_tint_type ); // Add textures to skybox. for (int i = 0; i < 6; i++) diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 7a7b188ce..ce33b96ae 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -529,7 +529,7 @@ void Sky::update(float time_of_day, float time_brightness, pointcolor_sun_f.g = pointcolor_light * (float)m_materials[3].EmissiveColor.getGreen() / 255; } else if (!m_default_tint) { - pointcolor_sun_f = m_sky_params.sun_tint; + pointcolor_sun_f = m_sky_params.fog_sun_tint; } else { pointcolor_sun_f.r = pointcolor_light * 1; pointcolor_sun_f.b = pointcolor_light * @@ -548,9 +548,9 @@ void Sky::update(float time_of_day, float time_brightness, ); } else { pointcolor_moon_f = video::SColorf( - (m_sky_params.moon_tint.getRed() / 255) * pointcolor_light, - (m_sky_params.moon_tint.getGreen() / 255) * pointcolor_light, - (m_sky_params.moon_tint.getBlue() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getRed() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getGreen() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getBlue() / 255) * pointcolor_light, 1 ); } @@ -941,8 +941,8 @@ void Sky::setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, std::string use_sun_tint) { // Change sun and moon tinting: - m_sky_params.sun_tint = sun_tint; - m_sky_params.moon_tint = moon_tint; + m_sky_params.fog_sun_tint = sun_tint; + m_sky_params.fog_moon_tint = moon_tint; // Faster than comparing strings every rendering frame if (use_sun_tint == "default") m_default_tint = true; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 6428ed752..8d0225a3d 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1276,9 +1276,9 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) // Fix for "regular" skies, as color isn't kept: if (skybox.type == "regular") { skybox.sky_color = sky_defaults.getSkyColorDefaults(); - skybox.tint_type = "default"; - skybox.moon_tint = video::SColor(255, 255, 255, 255); - skybox.sun_tint = video::SColor(255, 255, 255, 255); + skybox.fog_tint_type = "default"; + skybox.fog_moon_tint = video::SColor(255, 255, 255, 255); + skybox.fog_sun_tint = video::SColor(255, 255, 255, 255); } else { sun.visible = false; @@ -1313,7 +1313,7 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) std::string texture; *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >> - skybox.sun_tint >> skybox.moon_tint >> skybox.tint_type; + skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type; if (skybox.type == "skybox") { *pkt >> texture_count; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 73523ea42..527ebba7c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -634,9 +634,9 @@ enum ToClientCommand u8[4] night_sky (ARGB) u8[4] night_horizon (ARGB) u8[4] indoors (ARGB) - u8[4] sun_tint (ARGB) - u8[4] moon_tint (ARGB) - std::string tint_type + u8[4] fog_sun_tint (ARGB) + u8[4] fog_moon_tint (ARGB) + std::string fog_tint_type */ TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50, diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 7a603d53e..bef60c792 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -74,9 +74,9 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef): m_skybox_params.sky_color = sky_defaults.getSkyColorDefaults(); m_skybox_params.type = "regular"; m_skybox_params.clouds = true; - m_skybox_params.sun_tint = video::SColor(255, 244, 125, 29); - m_skybox_params.moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor(); - m_skybox_params.tint_type = "default"; + m_skybox_params.fog_sun_tint = video::SColor(255, 244, 125, 29); + m_skybox_params.fog_moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor(); + m_skybox_params.fog_tint_type = "default"; m_sun_params = sky_defaults.getSunDefaults(); m_moon_params = sky_defaults.getMoonDefaults(); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index dcaee10b2..f71130378 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1782,19 +1782,19 @@ int ObjectRef::l_set_sky(lua_State *L) lua_pop(L, 1); // Prevent flickering clouds at dawn/dusk: - skybox_params.sun_tint = video::SColor(255, 255, 255, 255); + skybox_params.fog_sun_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_sun_tint"); - read_color(L, -1, &skybox_params.sun_tint); + read_color(L, -1, &skybox_params.fog_sun_tint); lua_pop(L, 1); - skybox_params.moon_tint = video::SColor(255, 255, 255, 255); + skybox_params.fog_moon_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_moon_tint"); - read_color(L, -1, &skybox_params.moon_tint); + read_color(L, -1, &skybox_params.fog_moon_tint); lua_pop(L, 1); lua_getfield(L, -1, "fog_tint_type"); if (!lua_isnil(L, -1)) - skybox_params.tint_type = luaL_checkstring(L, -1); + skybox_params.fog_tint_type = luaL_checkstring(L, -1); lua_pop(L, 1); // Because we need to leave the "sky_color" table. @@ -1912,12 +1912,12 @@ int ObjectRef::l_get_sky_color(lua_State *L) push_ARGB8(L, skybox_params.sky_color.indoors); lua_setfield(L, -2, "indoors"); } - push_ARGB8(L, skybox_params.sun_tint); - lua_setfield(L, -2, "sun_tint"); - push_ARGB8(L, skybox_params.moon_tint); - lua_setfield(L, -2, "moon_tint"); - lua_pushstring(L, skybox_params.tint_type.c_str()); - lua_setfield(L, -2, "tint_type"); + push_ARGB8(L, skybox_params.fog_sun_tint); + lua_setfield(L, -2, "fog_sun_tint"); + push_ARGB8(L, skybox_params.fog_moon_tint); + lua_setfield(L, -2, "fog_moon_tint"); + lua_pushstring(L, skybox_params.fog_tint_type.c_str()); + lua_setfield(L, -2, "fog_tint_type"); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 0346d197d..05584be2d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1770,8 +1770,8 @@ void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms) pkt << params.clouds; } else { // Handle current clients and future clients pkt << params.bgcolor << params.type - << params.clouds << params.sun_tint - << params.moon_tint << params.tint_type; + << params.clouds << params.fog_sun_tint + << params.fog_moon_tint << params.fog_tint_type; if (params.type == "skybox") { pkt << (u16) params.textures.size(); diff --git a/src/skyparams.h b/src/skyparams.h index 9fdfd89da..c362ef8f3 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -37,9 +37,9 @@ struct SkyboxParams std::vector textures; bool clouds; SkyColor sky_color; - video::SColor sun_tint; - video::SColor moon_tint; - std::string tint_type; + video::SColor fog_sun_tint; + video::SColor fog_moon_tint; + std::string fog_tint_type; }; struct SunParams -- 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/script/lua_api/l_object.cpp') 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 e7e065f553b430173e9112ad55c7046cfc02f2c5 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Sat, 13 Jun 2020 22:46:20 +0200 Subject: Exposing the zoom key to Lua API (#9903) Co-authored-by: Raul Ferriz --- doc/lua_api.txt | 7 ++++--- src/client/game.cpp | 5 +++-- src/network/serverpackethandler.cpp | 1 + src/script/lua_api/l_object.cpp | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/script/lua_api/l_object.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index cb968958f..07e9698e8 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6106,13 +6106,14 @@ object you are working with still exists. * `get_formspec_prepend(formspec)`: returns a formspec string. * `get_player_control()`: returns table with player pressed keys * The table consists of fields with boolean value representing the pressed - keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up. + keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up, zoom. * example: `{jump=false, right=true, left=false, LMB=false, RMB=false, - sneak=true, aux1=false, down=false, up=false}` + sneak=true, aux1=false, down=false, up=false, zoom=false}` + * The `zoom` field is available since 5.3 * `get_player_control_bits()`: returns integer with bit packed player pressed keys. * bit nr/meaning: 0/up, 1/down, 2/left, 3/right, 4/jump, 5/aux1, 6/sneak, - 7/LMB, 8/RMB + 7/LMB, 8/RMB, 9/zoom (zoom available since 5.3) * `set_physics_override(override_table)` * `override_table` is a table with the following fields: * `speed`: multiplier to default walking speed value (default: `1`) diff --git a/src/client/game.cpp b/src/client/game.cpp index 139742cec..069c482ca 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2490,7 +2490,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) ); - u32 keypress_bits = + u32 keypress_bits = ( ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) | ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) | ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | @@ -2499,7 +2499,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8 + ( (u32)(input->getRightState() & 0x1) << 8) | + ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9) ); #ifdef ANDROID diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index fed3b6f85..b3008bb50 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -501,6 +501,7 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, player->control.sneak = (keyPressed & 64); player->control.LMB = (keyPressed & 128); player->control.RMB = (keyPressed & 256); + player->control.zoom = (keyPressed & 512); if (playersao->checkMovementCheat()) { // Call callbacks diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 0a9f3117b..e7394133a 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1459,6 +1459,8 @@ int ObjectRef::l_get_player_control(lua_State *L) lua_setfield(L, -2, "LMB"); lua_pushboolean(L, control.RMB); lua_setfield(L, -2, "RMB"); + lua_pushboolean(L, control.zoom); + lua_setfield(L, -2, "zoom"); return 1; } -- cgit v1.2.3