X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.h;h=bc7829f748cafcda5bf29d35b382a4b1a85615c7;hb=27947d85f7543a16a6c70aff4e9dcdca814c40ba;hp=9cbefa6440295bc87c0d19747cc98637ba892b9c;hpb=56ba193c77109687b9115be0ccb1730922caef65;p=minetest.git diff --git a/src/server.h b/src/server.h index 9cbefa644..bc7829f74 100644 --- a/src/server.h +++ b/src/server.h @@ -1,18 +1,18 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola +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 General Public License as published by -the Free Software Foundation; either version 2 of the License, or +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 General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +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. */ @@ -21,26 +21,39 @@ with this program; if not, write to the Free Software Foundation, Inc., #define SERVER_HEADER #include "connection.h" -#include "environment.h" -#include "common_irrlicht.h" -#include -#include "porting.h" +#include "irr_v3d.h" #include "map.h" -#include "inventory.h" -#include "ban.h" +#include "hud.h" #include "gamedef.h" #include "serialization.h" // For SER_FMT_VER_INVALID #include "mods.h" #include "inventorymanager.h" #include "subgame.h" -#include "sound.h" -struct LuaState; -typedef struct lua_State lua_State; +#include "rollback_interface.h" // Needed for rollbackRevertActions() +#include "util/numeric.h" +#include "util/thread.h" +#include "environment.h" +#include +#include +#include +#include + +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" + class IWritableItemDefManager; class IWritableNodeDefManager; class IWritableCraftDefManager; +class BanManager; class EventManager; +class Inventory; +class Player; class PlayerSAO; +class IRollbackManager; +class EmergeManager; +class GameScripting; +class ServerEnvironment; +struct SimpleSoundSpec; + class ServerError : public std::exception { @@ -64,175 +77,63 @@ class ServerError : public std::exception */ v3f findSpawnPos(ServerMap &map); -/* - A structure containing the data needed for queueing the fetching - of blocks. -*/ -struct QueuedBlockEmerge -{ - v3s16 pos; - // key = peer_id, value = flags - core::map peer_ids; -}; -/* - This is a thread-safe class. -*/ -class BlockEmergeQueue +class MapEditEventIgnorer { public: - BlockEmergeQueue() - { - m_mutex.Init(); - } - - ~BlockEmergeQueue() - { - JMutexAutoLock lock(m_mutex); - - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) - { - QueuedBlockEmerge *q = *i; - delete q; - } - } - - /* - peer_id=0 adds with nobody to send to - */ - void addBlock(u16 peer_id, v3s16 pos, u8 flags) - { - DSTACK(__FUNCTION_NAME); - - JMutexAutoLock lock(m_mutex); - - if(peer_id != 0) - { - /* - Find if block is already in queue. - If it is, update the peer to it and quit. - */ - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) - { - QueuedBlockEmerge *q = *i; - if(q->pos == pos) - { - q->peer_ids[peer_id] = flags; - return; - } - } - } - - /* - Add the block - */ - QueuedBlockEmerge *q = new QueuedBlockEmerge; - q->pos = pos; - if(peer_id != 0) - q->peer_ids[peer_id] = flags; - m_queue.push_back(q); - } - - // Returned pointer must be deleted - // Returns NULL if queue is empty - QueuedBlockEmerge * pop() + MapEditEventIgnorer(bool *flag): + m_flag(flag) { - JMutexAutoLock lock(m_mutex); - - core::list::Iterator i = m_queue.begin(); - if(i == m_queue.end()) - return NULL; - QueuedBlockEmerge *q = *i; - m_queue.erase(i); - return q; + if(*m_flag == false) + *m_flag = true; + else + m_flag = NULL; } - u32 size() + ~MapEditEventIgnorer() { - JMutexAutoLock lock(m_mutex); - return m_queue.size(); - } - - u32 peerItemCount(u16 peer_id) - { - JMutexAutoLock lock(m_mutex); - - u32 count = 0; - - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) + if(m_flag) { - QueuedBlockEmerge *q = *i; - if(q->peer_ids.find(peer_id) != NULL) - count++; + assert(*m_flag); + *m_flag = false; } - - return count; } private: - core::list m_queue; - JMutex m_mutex; -}; - -class Server; - -class ServerThread : public SimpleThread -{ - Server *m_server; - -public: - - ServerThread(Server *server): - SimpleThread(), - m_server(server) - { - } - - void * Thread(); + bool *m_flag; }; -class EmergeThread : public SimpleThread +class MapEditEventAreaIgnorer { - Server *m_server; - public: - - EmergeThread(Server *server): - SimpleThread(), - m_server(server) + MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a): + m_ignorevariable(ignorevariable) { + if(m_ignorevariable->getVolume() == 0) + *m_ignorevariable = a; + else + m_ignorevariable = NULL; } - void * Thread(); - - void trigger() + ~MapEditEventAreaIgnorer() { - setRun(true); - if(IsRunning() == false) + if(m_ignorevariable) { - Start(); + assert(m_ignorevariable->getVolume() != 0); + *m_ignorevariable = VoxelArea(); } } -}; -struct PlayerInfo -{ - u16 id; - char name[PLAYERNAME_SIZE]; - v3f position; - Address address; - float avg_rtt; - - PlayerInfo(); - void PrintLine(std::ostream *s); +private: + VoxelArea *m_ignorevariable; }; +class Server; +class ServerThread; + /* Used for queueing and sorting block transfers in containers - + Lower priority number means higher priority. */ struct PrioritySortedBlockTransfer @@ -243,7 +144,7 @@ struct PrioritySortedBlockTransfer pos = a_pos; peer_id = a_peer_id; } - bool operator < (PrioritySortedBlockTransfer &other) + bool operator < (const PrioritySortedBlockTransfer &other) const { return priority < other.priority; } @@ -297,7 +198,7 @@ struct ServerSoundParams max_hear_distance(32*BS), loop(false) {} - + v3f getPos(ServerEnvironment *env, bool *pos_exists) const; }; @@ -324,6 +225,8 @@ class RemoteClient bool definitions_sent; + bool denied; + RemoteClient(): m_time_from_building(9999), m_excess_gotblocks(0) @@ -333,6 +236,7 @@ class RemoteClient net_proto_version = 0; pending_serialization_version = SER_FMT_VER_INVALID; definitions_sent = false; + denied = false; m_nearest_unsent_d = 0; m_nearest_unsent_reset_timer = 0.0; m_nothing_to_send_counter = 0; @@ -341,27 +245,27 @@ class RemoteClient ~RemoteClient() { } - + /* Finds block that should be sent next to the client. Environment should be locked when this is called. dtime is used for resetting send radius at slow interval */ void GetNextBlocks(Server *server, float dtime, - core::array &dest); + std::vector &dest); void GotBlock(v3s16 p); void SentBlock(v3s16 p); void SetBlockNotSent(v3s16 p); - void SetBlocksNotSent(core::map &blocks); + void SetBlocksNotSent(std::map &blocks); s32 SendingCount() { return m_blocks_sending.size(); } - + // Increments timeouts and removes timed-out blocks from list // NOTE: This doesn't fix the server-not-sending-block bug // because it is related to emerging, not sending. @@ -380,18 +284,18 @@ class RemoteClient // Time from last placing or removing blocks float m_time_from_building; - + /*JMutex m_dig_mutex; float m_dig_time_remaining; // -1 = not digging s16 m_dig_tool_item; v3s16 m_dig_position;*/ - + /* List of active objects that the client knows of. Value is dummy. */ - core::map m_known_objects; + std::set m_known_objects; private: /* @@ -399,15 +303,15 @@ class RemoteClient - These don't have to be sent again. - A block is cleared from here when client says it has deleted it from it's memory - + Key is position, value is dummy. No MapBlock* is stored here because the blocks can get deleted. */ - core::map m_blocks_sent; + std::set m_blocks_sent; s16 m_nearest_unsent_d; v3s16 m_last_center; float m_nearest_unsent_reset_timer; - + /* Blocks that are currently on the line. This is used for throttling the sending of blocks. @@ -416,7 +320,7 @@ class RemoteClient Block is removed when GOTBLOCKS is received. Value is time from sending. (not used at the moment) */ - core::map m_blocks_sending; + std::map m_blocks_sending; /* Count of excess GotBlocks(). @@ -426,24 +330,22 @@ class RemoteClient This is resetted by PrintInfo() */ u32 m_excess_gotblocks; - + // CPU usage optimization u32 m_nothing_to_send_counter; float m_nothing_to_send_pause_timer; }; class Server : public con::PeerHandler, public MapEventReceiver, - public InventoryManager, public IGameDef, - public IBackgroundBlockEmerger + public InventoryManager, public IGameDef { public: /* NOTE: Every public method should be thread-safe */ - + Server( const std::string &path_world, - const std::string &path_config, const SubgameSpec &gamespec, bool simple_singleplayer_mode ); @@ -458,20 +360,14 @@ class Server : public con::PeerHandler, public MapEventReceiver, void Receive(); void ProcessData(u8 *data, u32 datasize, u16 peer_id); - core::list getPlayerInfo(); - // Environment must be locked when called - void setTimeOfDay(u32 time) - { - m_env->setTimeOfDay(time); - m_time_of_day_send_timer = 0; - } + void setTimeOfDay(u32 time); bool getShutdownRequested() { return m_shutdown_requested; } - + /* Shall be called with the environment locked. This is accessed by the map, which is inside the environment, @@ -483,7 +379,6 @@ class Server : public con::PeerHandler, public MapEventReceiver, Shall be called with the environment and the connection locked. */ Inventory* getInventory(const InventoryLocation &loc); - std::string getInventoryOwner(const InventoryLocation &loc); void setInventoryModified(const InventoryLocation &loc); // Connection must be locked when called @@ -498,63 +393,91 @@ class Server : public con::PeerHandler, public MapEventReceiver, // Envlock + conlock s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams ¶ms); void stopSound(s32 handle); - + // Envlock + conlock std::set getPlayerEffectivePrivs(const std::string &name); bool checkPriv(const std::string &name, const std::string &priv); void reportPrivsModified(const std::string &name=""); // ""=all + void reportInventoryFormspecModified(const std::string &name); - // Saves g_settings to configpath given at initialization - void saveConfig(); - - void setIpBanned(const std::string &ip, const std::string &name) - { - m_banmanager.add(ip, name); - return; - } - - void unsetIpBanned(const std::string &ip_or_name) - { - m_banmanager.remove(ip_or_name); - return; - } - - std::string getBanDescription(const std::string &ip_or_name) - { - return m_banmanager.getBanDescription(ip_or_name); - } + void setIpBanned(const std::string &ip, const std::string &name); + void unsetIpBanned(const std::string &ip_or_name); + std::string getBanDescription(const std::string &ip_or_name); Address getPeerAddress(u16 peer_id) { return m_con.GetPeerAddress(peer_id); } - + // Envlock and conlock should be locked when calling this - void notifyPlayer(const char *name, const std::wstring msg); + void notifyPlayer(const char *name, const std::wstring msg, const bool prepend); void notifyPlayers(const std::wstring msg); + void spawnParticle(const char *playername, + v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + void spawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + u32 addParticleSpawner(const char *playername, + u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture); + + u32 addParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture); + + void deleteParticleSpawner(const char *playername, u32 id); + void deleteParticleSpawnerAll(u32 id); + + // Creates or resets inventory + Inventory* createDetachedInventory(const std::string &name); + + // Envlock and conlock should be locked when using scriptapi + GameScripting *getScriptIface(){ return m_script; } + + // Envlock should be locked when using the rollback manager + IRollbackManager *getRollbackManager(){ return m_rollback; } + + //TODO: determine what (if anything) should be locked to access EmergeManager + EmergeManager *getEmergeManager(){ return m_emerge; } + + // actions: time-reversed list + // Return value: success/failure + bool rollbackRevertActions(const std::list &actions, + std::list *log); - void queueBlockEmerge(v3s16 blockpos, bool allow_generate); - - // Envlock and conlock should be locked when using Lua - lua_State *getLua(){ return m_lua; } - // IGameDef interface // Under envlock virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); virtual ITextureSource* getTextureSource(); + virtual IShaderSource* getShaderSource(); virtual u16 allocateUnknownNodeId(const std::string &name); virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); - + virtual IRollbackReportSink* getRollbackReportSink(); + IWritableItemDefManager* getWritableItemDefManager(); IWritableNodeDefManager* getWritableNodeDefManager(); IWritableCraftDefManager* getWritableCraftDefManager(); const ModSpec* getModSpec(const std::string &modname); + void getModNames(std::list &modlist); std::string getBuiltinLuaPath(); - + std::string getWorldPath(){ return m_path_world; } bool isSingleplayer(){ return m_simple_singleplayer_mode; } @@ -564,6 +487,18 @@ class Server : public con::PeerHandler, public MapEventReceiver, m_async_fatal_error.set(error); } + bool showFormspec(const char *name, const std::string &formspec, const std::string &formname); + Map & getMap() { return m_env->getMap(); } + ServerEnvironment & getEnv() { return *m_env; } + + u32 hudAdd(Player *player, HudElement *element); + bool hudRemove(Player *player, u32 id); + bool hudChange(Player *player, u32 id, HudElementStat stat, void *value); + bool hudSetFlags(Player *player, u32 flags, u32 mask); + bool hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount); + void hudSetHotbarImage(Player *player, std::string name); + void hudSetHotbarSelectedImage(Player *player, std::string name); + private: // con::PeerHandler implementation. @@ -571,21 +506,23 @@ class Server : public con::PeerHandler, public MapEventReceiver, // As of now, these create and remove clients and players. void peerAdded(con::Peer *peer); void deletingPeer(con::Peer *peer, bool timeout); - + /* Static send methods */ - + + static void SendMovement(con::Connection &con, u16 peer_id); static void SendHP(con::Connection &con, u16 peer_id, u8 hp); + static void SendBreath(con::Connection &con, u16 peer_id, u16 breath); static void SendAccessDenied(con::Connection &con, u16 peer_id, const std::wstring &reason); static void SendDeathscreen(con::Connection &con, u16 peer_id, bool set_camera_point_target, v3f camera_point_target); static void SendItemDef(con::Connection &con, u16 peer_id, - IItemDefManager *itemdef); + IItemDefManager *itemdef, u16 protocol_version); static void SendNodeDef(con::Connection &con, u16 peer_id, - INodeDefManager *nodedef); - + INodeDefManager *nodedef, u16 protocol_version); + /* Non-static send methods. Conlock should be always used. @@ -597,9 +534,19 @@ class Server : public con::PeerHandler, public MapEventReceiver, void SendInventory(u16 peer_id); void SendChatMessage(u16 peer_id, const std::wstring &message); void BroadcastChatMessage(const std::wstring &message); + void SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed); void SendPlayerHP(u16 peer_id); + void SendPlayerBreath(u16 peer_id); void SendMovePlayer(u16 peer_id); void SendPlayerPrivileges(u16 peer_id); + void SendPlayerInventoryFormspec(u16 peer_id); + void SendShowFormspecMessage(u16 peer_id, const std::string formspec, const std::string formname); + void SendHUDAdd(u16 peer_id, u32 id, HudElement *form); + void SendHUDRemove(u16 peer_id, u32 id); + void SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value); + void SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask); + void SendHUDSetParam(u16 peer_id, u16 param, const std::string &value); + /* Send a node removal/addition event to all clients except ignore_id. Additionally, if far_players!=NULL, players further away than @@ -607,51 +554,85 @@ class Server : public con::PeerHandler, public MapEventReceiver, */ // Envlock and conlock should be locked when calling these void sendRemoveNode(v3s16 p, u16 ignore_id=0, - core::list *far_players=NULL, float far_d_nodes=100); + std::list *far_players=NULL, float far_d_nodes=100); void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0, - core::list *far_players=NULL, float far_d_nodes=100); + std::list *far_players=NULL, float far_d_nodes=100); void setBlockNotSent(v3s16 p); - + // Environment and Connection must be locked when called - void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver); - + void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version); + // Sends blocks to clients (locks env and con on its own) void SendBlocks(float dtime); - + void fillMediaCache(); void sendMediaAnnouncement(u16 peer_id); void sendRequestedMedia(u16 peer_id, - const core::list &tosend); + const std::list &tosend); + + void sendDetachedInventory(const std::string &name, u16 peer_id); + void sendDetachedInventoryToAll(const std::string &name); + void sendDetachedInventories(u16 peer_id); + + // Adds a ParticleSpawner on peer with peer_id + void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture, u32 id); + + // Adds a ParticleSpawner on all peers + void SendAddParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture, u32 id); + + // Deletes ParticleSpawner on a single client + void SendDeleteParticleSpawner(u16 peer_id, u32 id); + + // Deletes ParticleSpawner on all clients + void SendDeleteParticleSpawnerAll(u32 id); + + // Spawns particle on single client + void SendSpawnParticle(u16 peer_id, + v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + // Spawns particle on all clients + void SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); /* Something random */ - + void DiePlayer(u16 peer_id); void RespawnPlayer(u16 peer_id); - + void DenyAccess(u16 peer_id, const std::wstring &reason); + + enum ClientDeletionReason { + CDR_LEAVE, + CDR_TIMEOUT, + CDR_DENY + }; + void DeleteClient(u16 peer_id, ClientDeletionReason reason); + void UpdateCrafting(u16 peer_id); - + // When called, connection mutex should be locked RemoteClient* getClient(u16 peer_id); - - // When called, environment mutex should be locked - std::string getPlayerName(u16 peer_id) - { - Player *player = m_env->getPlayer(peer_id); - if(player == NULL) - return "[id="+itos(peer_id)+"]"; - return player->getName(); - } + RemoteClient* getClientNoEx(u16 peer_id); // When called, environment mutex should be locked - PlayerSAO* getPlayerSAO(u16 peer_id) - { - Player *player = m_env->getPlayer(peer_id); - if(player == NULL) - return NULL; - return player->getPlayerSAO(); - } + std::string getPlayerName(u16 peer_id); + PlayerSAO* getPlayerSAO(u16 peer_id); /* Get a player from memory or creates one. @@ -661,7 +642,7 @@ class Server : public con::PeerHandler, public MapEventReceiver, Call with env and con locked. */ PlayerSAO *emergePlayer(const char *name, u16 peer_id); - + // Locks environment and connection by its own struct PeerChange; void handlePeerChange(PeerChange &c); @@ -670,11 +651,9 @@ class Server : public con::PeerHandler, public MapEventReceiver, /* Variables */ - + // World directory std::string m_path_world; - // Path to user's configuration file ("" = no configuration file) - std::string m_path_config; // Subgame specification SubgameSpec m_gamespec; // If true, do not allow multiple players and hide some multiplayer @@ -683,66 +662,73 @@ class Server : public con::PeerHandler, public MapEventReceiver, // Thread can set; step() will throw as ServerError MutexedVariable m_async_fatal_error; - + // Some timers float m_liquid_transform_timer; + float m_liquid_transform_every; float m_print_info_timer; + float m_masterserver_timer; float m_objectdata_timer; float m_emergethread_trigger_timer; float m_savemap_timer; IntervalLimiter m_map_timer_and_unload_interval; - + // NOTE: If connection and environment are both to be locked, // environment shall be locked first. // Environment ServerEnvironment *m_env; JMutex m_env_mutex; - + // Connection con::Connection m_con; JMutex m_con_mutex; // Connected clients (behind the con mutex) - core::map m_clients; + std::map m_clients; + u16 m_clients_number; //for announcing masterserver + + // Ban checking + BanManager *m_banmanager; - // Bann checking - BanManager m_banmanager; + // Rollback manager (behind m_env_mutex) + IRollbackManager *m_rollback; + bool m_rollback_sink_enabled; + bool m_enable_rollback_recording; // Updated once in a while + + // Emerge manager + EmergeManager *m_emerge; // Scripting // Envlock and conlock should be locked when using Lua - lua_State *m_lua; + GameScripting *m_script; // Item definition manager IWritableItemDefManager *m_itemdef; - + // Node definition manager IWritableNodeDefManager *m_nodedef; - + // Craft definition manager IWritableCraftDefManager *m_craftdef; - + // Event manager EventManager *m_event; - + // Mods - core::list m_mods; - + std::vector m_mods; + /* Threads */ - + // A buffer for time steps // step() increments and AsyncRunStep() run by m_thread reads it. float m_step_dtime; JMutex m_step_dtime_mutex; // The server mainly operates in this thread - ServerThread m_thread; - // This thread fetches and generates map - EmergeThread m_emergethread; - // Queue of block coordinates to be processed by the emerge thread - BlockEmergeQueue m_emerge_queue; - + ServerThread *m_thread; + /* Time related stuff */ @@ -751,7 +737,7 @@ class Server : public con::PeerHandler, public MapEventReceiver, float m_time_of_day_send_timer; // Uptime of server in seconds MutexedVariable m_uptime; - + /* Peer change queue. Queues stuff from peerAdded() and deletingPeer() to @@ -773,9 +759,9 @@ class Server : public con::PeerHandler, public MapEventReceiver, /* Random stuff */ - + // Mod parent directory paths - core::list m_modspaths; + std::list m_modspaths; bool m_shutdown_requested; @@ -823,6 +809,17 @@ class Server : public con::PeerHandler, public MapEventReceiver, */ std::map m_playing_sounds; s32 m_next_sound_id; + + /* + Detached inventories (behind m_env_mutex) + */ + // key = name + std::map m_detached_inventories; + + /* + Particles + */ + std::vector m_particlespawner_ids; }; /*