Server class code cleanups (#9769)
authorLoïc Blot <nerzhul@users.noreply.github.com>
Thu, 7 May 2020 20:38:41 +0000 (22:38 +0200)
committerGitHub <noreply@github.com>
Thu, 7 May 2020 20:38:41 +0000 (22:38 +0200)
* 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.

12 files changed:
src/client/game.cpp
src/main.cpp
src/network/serverpackethandler.cpp
src/script/lua_api/l_base.cpp
src/script/lua_api/l_base.h
src/script/lua_api/l_inventory.cpp
src/script/lua_api/l_object.cpp
src/server.cpp
src/server.h
src/server/CMakeLists.txt
src/server/serverinventorymgr.cpp [new file with mode: 0644]
src/server/serverinventorymgr.h [new file with mode: 0644]

index 1577a37db33b911310ec3645c0aafa7b4040953c..4d7a85526b466c9d1a11b23bc1ecdf1947ff523f 100644 (file)
@@ -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;
index 147f686edd494e93b71cf9c70cd9baa581f23088..b3b17c2d1e4c04b99ace9bc2d22dfc94e65680fb 100644 (file)
@@ -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
index adaa9a965dc61da5301fa1482e6983efb34336ae..39a912827a18563ee18aadc48cfb425c84678f0a 100644 (file)
@@ -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;
 }
index b8658f62bfbdb7b262a21467093b830179b7cb0b..2bee094364c36c2307c26f58fd25c868bf57c567 100644 (file)
@@ -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)
 {
index e326476285cf028a796132ffb38190588b3b7c0e..65fce848134e46858a972b565ff49bc7743fd2e1 100644 (file)
@@ -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);
index 4c8977898a6b1baef81da3717644c9576471bd4f..e41b5cb41b4e0247077df30e4240422d9c6d1070 100644 (file)
@@ -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<std::string>(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;
 }
 
index f711303789e5ac8c95512c7d88319a0b2797ed65..0a9f3117b3d7a41df71535b3b637d82f6bd66a75 100644 (file)
@@ -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<float>(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;
 }
index 05584be2d4114bb3bf2de9ae98770514f3db1f44..16e026ce25fea65b8f84463ea6e7fcf40f1cc121 100644 (file)
@@ -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<ServerInventoryManager>(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<v3s16, MapBlock *>& block)
 {
        std::vector<session_t> 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<u16>(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 &params)
        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 \""<<name<<"\""<<std::endl;
-               delete m_detached_inventories[name];
-       } else {
-               infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
-       }
-       Inventory *inv = new Inventory(m_itemdef);
-       sanity_check(inv);
-       m_detached_inventories[name] = inv;
-       if (!player.empty())
-               m_detached_inventories_player[name] = player;
-
-       //TODO find a better way to do this
-       sendDetachedInventory(name,PEER_ID_INEXISTENT);
-       return inv;
-}
-
-bool Server::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;
-       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<RollbackAction> &actions,
@@ -3607,7 +3467,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &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;
index 71059dd307c6b89cc1a967aefcff9022093f5d55..7a1de9370d13ac53e4d02a268e42337666cf2d4a 100644 (file)
@@ -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 &params);
 
-       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<std::string> &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<std::string, Inventory*> m_detached_inventories;
-       // value = "" (visible to all players) or player name
-       std::map<std::string, std::string> m_detached_inventories_player;
-
        std::unordered_map<std::string, ModMetadata *> m_mod_storages;
        float m_mod_storage_save_timer = 10.0f;
 
@@ -674,6 +658,9 @@ private:
        // ModChannel manager
        std::unique_ptr<ModChannelMgr> m_modchannel_mgr;
 
+       // Inventory manager
+       std::unique_ptr<ServerInventoryManager> m_inventory_mgr;
+
        // Global server metrics backend
        std::unique_ptr<MetricsBackend> m_metrics_backend;
 
index 4d94504f649f504fd4fdd8d376eaa8259cd07a0a..0a5a8f3a754ccf7e6eed51144e84ea70a68bf02f 100644 (file)
@@ -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 (file)
index 0000000..555e01e
--- /dev/null
@@ -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<void(const std::string &, Inventory *)> 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 (file)
index 0000000..d0aac4d
--- /dev/null
@@ -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 <functional>
+
+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<void(const std::string &, Inventory *)> apply_cb);
+
+private:
+       struct DetachedInventory
+       {
+               Inventory *inventory;
+               std::string owner;
+       };
+
+       ServerEnvironment *m_env = nullptr;
+
+       std::unordered_map<std::string, DetachedInventory> m_detached_inventories;
+};
\ No newline at end of file