minetest/src/server/serverinventorymgr.cpp
Loïc Blot 454dbf83a9
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.
2020-05-07 22:38:41 +02:00

193 lines
5.3 KiB
C++

/*
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);
}
}