forked from Mirrorlandia_minetest/minetest
454dbf83a9
* 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.
193 lines
5.3 KiB
C++
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);
|
|
}
|
|
}
|