]> git.lizzy.rs Git - minetest.git/blob - src/server/serverinventorymgr.cpp
Make MapEditEvent more complete
[minetest.git] / src / server / serverinventorymgr.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2020 Minetest core development team
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "serverinventorymgr.h"
21 #include "map.h"
22 #include "nodemetadata.h"
23 #include "player_sao.h"
24 #include "remoteplayer.h"
25 #include "server.h"
26 #include "serverenvironment.h"
27
28 ServerInventoryManager::ServerInventoryManager() : InventoryManager()
29 {
30 }
31
32 ServerInventoryManager::~ServerInventoryManager()
33 {
34         // Delete detached inventories
35         for (auto &detached_inventory : m_detached_inventories) {
36                 delete detached_inventory.second.inventory;
37         }
38 }
39
40 Inventory *ServerInventoryManager::getInventory(const InventoryLocation &loc)
41 {
42         // No m_env check here: allow creation and modification of detached inventories
43
44         switch (loc.type) {
45         case InventoryLocation::UNDEFINED:
46         case InventoryLocation::CURRENT_PLAYER:
47                 break;
48         case InventoryLocation::PLAYER: {
49                 if (!m_env)
50                         return nullptr;
51
52                 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
53                 if (!player)
54                         return NULL;
55
56                 PlayerSAO *playersao = player->getPlayerSAO();
57                 return playersao ? playersao->getInventory() : nullptr;
58         } break;
59         case InventoryLocation::NODEMETA: {
60                 if (!m_env)
61                         return nullptr;
62
63                 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
64                 return meta ? meta->getInventory() : nullptr;
65         } break;
66         case InventoryLocation::DETACHED: {
67                 auto it = m_detached_inventories.find(loc.name);
68                 if (it == m_detached_inventories.end())
69                         return nullptr;
70                 return it->second.inventory;
71         } break;
72         default:
73                 sanity_check(false); // abort
74                 break;
75         }
76         return NULL;
77 }
78
79 void ServerInventoryManager::setInventoryModified(const InventoryLocation &loc)
80 {
81         switch (loc.type) {
82         case InventoryLocation::UNDEFINED:
83                 break;
84         case InventoryLocation::PLAYER: {
85
86                 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
87
88                 if (!player)
89                         return;
90
91                 player->setModified(true);
92                 player->inventory.setModified(true);
93                 // Updates are sent in ServerEnvironment::step()
94         } break;
95         case InventoryLocation::NODEMETA: {
96                 MapEditEvent event;
97                 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
98                 event.setPositionModified(loc.p);
99                 m_env->getMap().dispatchEvent(event);
100         } break;
101         case InventoryLocation::DETACHED: {
102                 // Updates are sent in ServerEnvironment::step()
103         } break;
104         default:
105                 sanity_check(false); // abort
106                 break;
107         }
108 }
109
110 Inventory *ServerInventoryManager::createDetachedInventory(
111                 const std::string &name, IItemDefManager *idef, const std::string &player)
112 {
113         if (m_detached_inventories.count(name) > 0) {
114                 infostream << "Server clearing detached inventory \"" << name << "\""
115                            << std::endl;
116                 delete m_detached_inventories[name].inventory;
117         } else {
118                 infostream << "Server creating detached inventory \"" << name << "\""
119                            << std::endl;
120         }
121
122         Inventory *inv = new Inventory(idef);
123         sanity_check(inv);
124         m_detached_inventories[name].inventory = inv;
125         if (!player.empty()) {
126                 m_detached_inventories[name].owner = player;
127
128                 if (!m_env)
129                         return inv; // Mods are not loaded yet, ignore
130
131                 RemotePlayer *p = m_env->getPlayer(name.c_str());
132
133                 // if player is connected, send him the inventory
134                 if (p && p->getPeerId() != PEER_ID_INEXISTENT) {
135                         m_env->getGameDef()->sendDetachedInventory(
136                                         inv, name, p->getPeerId());
137                 }
138         } else {
139                 if (!m_env)
140                         return inv; // Mods are not loaded yet, don't send
141
142                 // Inventory is for everybody, broadcast
143                 m_env->getGameDef()->sendDetachedInventory(inv, name, PEER_ID_INEXISTENT);
144         }
145
146         return inv;
147 }
148
149 bool ServerInventoryManager::removeDetachedInventory(const std::string &name)
150 {
151         const auto &inv_it = m_detached_inventories.find(name);
152         if (inv_it == m_detached_inventories.end())
153                 return false;
154
155         delete inv_it->second.inventory;
156         const std::string &owner = inv_it->second.owner;
157
158         if (!owner.empty()) {
159                 if (m_env) {
160                         RemotePlayer *player = m_env->getPlayer(owner.c_str());
161
162                         if (player && player->getPeerId() != PEER_ID_INEXISTENT)
163                                 m_env->getGameDef()->sendDetachedInventory(
164                                                 nullptr, name, player->getPeerId());
165                 }
166         } else if (m_env) {
167                 // Notify all players about the change as soon ServerEnv exists
168                 m_env->getGameDef()->sendDetachedInventory(
169                                 nullptr, name, PEER_ID_INEXISTENT);
170         }
171
172         m_detached_inventories.erase(inv_it);
173
174         return true;
175 }
176
177 bool ServerInventoryManager::checkDetachedInventoryAccess(
178                 const InventoryLocation &loc, const std::string &player) const
179 {
180         SANITY_CHECK(loc.type == InventoryLocation::DETACHED);
181
182         const auto &inv_it = m_detached_inventories.find(loc.name);
183         if (inv_it == m_detached_inventories.end())
184                 return false;
185
186         return inv_it->second.owner.empty() || inv_it->second.owner == player;
187 }
188
189 void ServerInventoryManager::sendDetachedInventories(const std::string &peer_name,
190                 bool incremental,
191                 std::function<void(const std::string &, Inventory *)> apply_cb)
192 {
193         for (const auto &detached_inventory : m_detached_inventories) {
194                 const DetachedInventory &dinv = detached_inventory.second;
195                 if (incremental) {
196                         if (!dinv.inventory || !dinv.inventory->checkModified())
197                                 continue;
198                 }
199
200                 // if we are pushing inventories to a specific player
201                 // we should filter to send only the right inventories
202                 if (!peer_name.empty()) {
203                         const std::string &attached_player = dinv.owner;
204                         if (!attached_player.empty() && peer_name != attached_player)
205                                 continue;
206                 }
207
208                 apply_cb(detached_inventory.first, detached_inventory.second.inventory);
209         }
210 }