mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-01 17:38:41 +00:00
Add generic IPC mechanism between Lua envs
This commit is contained in:
parent
06907aa99b
commit
f1a436619f
12 changed files with 191 additions and 19 deletions
|
@ -34,19 +34,19 @@ class Camera;
|
|||
class ModChannel;
|
||||
class ModStorage;
|
||||
class ModStorageDatabase;
|
||||
struct SubgameSpec;
|
||||
struct ModSpec;
|
||||
struct ModIPCStore;
|
||||
|
||||
namespace irr::scene {
|
||||
class IAnimatedMesh;
|
||||
class ISceneManager;
|
||||
}
|
||||
|
||||
struct SubgameSpec;
|
||||
struct ModSpec;
|
||||
/*
|
||||
An interface for fetching game-global definitions like tool and
|
||||
mapnode properties
|
||||
*/
|
||||
|
||||
class IGameDef
|
||||
{
|
||||
public:
|
||||
|
@ -63,6 +63,9 @@ public:
|
|||
// environment thread.
|
||||
virtual IRollbackManager* getRollbackManager() { return NULL; }
|
||||
|
||||
// Only usable on server.
|
||||
virtual ModIPCStore *getModIPCStore() { return nullptr; }
|
||||
|
||||
// Shorthands
|
||||
// TODO: these should be made const-safe so that a const IGameDef* is
|
||||
// actually usable
|
||||
|
|
|
@ -50,11 +50,12 @@ AsyncEngine::~AsyncEngine()
|
|||
}
|
||||
|
||||
// Wait for threads to finish
|
||||
infostream << "AsyncEngine: Waiting for " << workerThreads.size()
|
||||
<< " threads" << std::endl;
|
||||
for (AsyncWorkerThread *workerThread : workerThreads) {
|
||||
workerThread->wait();
|
||||
}
|
||||
|
||||
// Force kill all threads
|
||||
for (AsyncWorkerThread *workerThread : workerThreads) {
|
||||
delete workerThread;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ set(common_SCRIPT_LUA_API_SRCS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_http.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_ipc.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_itemstackmeta.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_mapgen.cpp
|
||||
|
|
68
src/script/lua_api/l_ipc.cpp
Normal file
68
src/script/lua_api/l_ipc.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Minetest
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "lua_api/l_ipc.h"
|
||||
#include "lua_api/l_internal.h"
|
||||
#include "common/c_packer.h"
|
||||
#include "server.h"
|
||||
#include "debug.h"
|
||||
|
||||
typedef std::shared_lock<std::shared_mutex> SharedReadLock;
|
||||
typedef std::unique_lock<std::shared_mutex> SharedWriteLock;
|
||||
|
||||
int ModApiIPC::l_ipc_get(lua_State *L)
|
||||
{
|
||||
auto *store = getGameDef(L)->getModIPCStore();
|
||||
|
||||
auto key = readParam<std::string>(L, 1);
|
||||
|
||||
{
|
||||
SharedReadLock autolock(store->mutex);
|
||||
auto it = store->map.find(key);
|
||||
if (it == store->map.end())
|
||||
lua_pushnil(L);
|
||||
else
|
||||
script_unpack(L, it->second.get());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ModApiIPC::l_ipc_set(lua_State *L)
|
||||
{
|
||||
auto *store = getGameDef(L)->getModIPCStore();
|
||||
|
||||
auto key = readParam<std::string>(L, 1);
|
||||
|
||||
luaL_checkany(L, 2);
|
||||
std::unique_ptr<PackedValue> pv;
|
||||
if (!lua_isnil(L, 2)) {
|
||||
pv.reset(script_pack(L, 2));
|
||||
if (pv->contains_userdata)
|
||||
throw LuaError("Userdata not allowed");
|
||||
}
|
||||
|
||||
{
|
||||
SharedWriteLock autolock(store->mutex);
|
||||
if (pv)
|
||||
store->map[key] = std::move(pv);
|
||||
else
|
||||
store->map.erase(key); // delete the map value for nil
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation note:
|
||||
* Iterating over the IPC table is intentionally not supported.
|
||||
* Mods should know what they have set.
|
||||
* This has the nice side effect that mods are able to use a randomly generated key
|
||||
* if they really *really* want to avoid other code touching their data.
|
||||
*/
|
||||
|
||||
void ModApiIPC::Initialize(lua_State *L, int top)
|
||||
{
|
||||
FATAL_ERROR_IF(!getGameDef(L)->getModIPCStore(), "ModIPCStore missing from gamedef");
|
||||
|
||||
API_FCT(ipc_get);
|
||||
API_FCT(ipc_set);
|
||||
}
|
15
src/script/lua_api/l_ipc.h
Normal file
15
src/script/lua_api/l_ipc.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Minetest
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lua_api/l_base.h"
|
||||
|
||||
class ModApiIPC : public ModApiBase {
|
||||
private:
|
||||
static int l_ipc_get(lua_State *L);
|
||||
static int l_ipc_set(lua_State *L);
|
||||
|
||||
public:
|
||||
static void Initialize(lua_State *L, int top);
|
||||
};
|
|
@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "lua_api/l_util.h"
|
||||
#include "lua_api/l_vmanip.h"
|
||||
#include "lua_api/l_settings.h"
|
||||
#include "lua_api/l_ipc.h"
|
||||
|
||||
extern "C" {
|
||||
#include <lualib.h>
|
||||
|
@ -89,5 +90,6 @@ void EmergeScripting::InitializeModApi(lua_State *L, int top)
|
|||
ModApiMapgen::InitializeEmerge(L, top);
|
||||
ModApiServer::InitializeAsync(L, top);
|
||||
ModApiUtil::InitializeAsync(L, top);
|
||||
ModApiIPC::Initialize(L, top);
|
||||
// TODO ^ these should also be renamed to InitializeRO or such
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "lua_api/l_settings.h"
|
||||
#include "lua_api/l_http.h"
|
||||
#include "lua_api/l_storage.h"
|
||||
#include "lua_api/l_ipc.h"
|
||||
|
||||
extern "C" {
|
||||
#include <lualib.h>
|
||||
|
@ -121,6 +122,7 @@ void ServerScripting::initAsync()
|
|||
asyncEngine.registerStateInitializer(ModApiCraft::InitializeAsync);
|
||||
asyncEngine.registerStateInitializer(ModApiItem::InitializeAsync);
|
||||
asyncEngine.registerStateInitializer(ModApiServer::InitializeAsync);
|
||||
asyncEngine.registerStateInitializer(ModApiIPC::Initialize);
|
||||
// not added: ModApiMapgen is a minefield for thread safety
|
||||
// not added: ModApiHttp async api can't really work together with our jobs
|
||||
// not added: ModApiStorage is probably not thread safe(?)
|
||||
|
@ -176,6 +178,7 @@ void ServerScripting::InitializeModApi(lua_State *L, int top)
|
|||
ModApiHttp::Initialize(L, top);
|
||||
ModApiStorage::Initialize(L, top);
|
||||
ModApiChannels::Initialize(L, top);
|
||||
ModApiIPC::Initialize(L, top);
|
||||
}
|
||||
|
||||
void ServerScripting::InitializeAsync(lua_State *L, int top)
|
||||
|
|
|
@ -86,6 +86,15 @@ public:
|
|||
{}
|
||||
};
|
||||
|
||||
ModIPCStore::~ModIPCStore()
|
||||
{
|
||||
// we don't have to do this, it's pure debugging aid
|
||||
if (!std::unique_lock(mutex, std::try_to_lock).owns_lock()) {
|
||||
errorstream << FUNCTION_NAME << ": lock is still in use!" << std::endl;
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
class ServerThread : public Thread
|
||||
{
|
||||
public:
|
||||
|
|
19
src/server.h
19
src/server.h
|
@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <unordered_set>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <shared_mutex>
|
||||
|
||||
class ChatEvent;
|
||||
struct ChatEventChat;
|
||||
|
@ -142,6 +143,20 @@ struct ClientInfo {
|
|||
std::string vers_string, lang_code;
|
||||
};
|
||||
|
||||
struct ModIPCStore {
|
||||
ModIPCStore() = default;
|
||||
~ModIPCStore();
|
||||
|
||||
/// RW lock for this entire structure
|
||||
std::shared_mutex mutex;
|
||||
/**
|
||||
* Map storing the data
|
||||
*
|
||||
* @note Do not store `nil` data in this map, instead remove the whole key.
|
||||
*/
|
||||
std::unordered_map<std::string, std::unique_ptr<PackedValue>> map;
|
||||
};
|
||||
|
||||
class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
public IGameDef
|
||||
{
|
||||
|
@ -301,12 +316,14 @@ public:
|
|||
NodeDefManager* getWritableNodeDefManager();
|
||||
IWritableCraftDefManager* getWritableCraftDefManager();
|
||||
|
||||
// Not under envlock
|
||||
virtual const std::vector<ModSpec> &getMods() const;
|
||||
virtual const ModSpec* getModSpec(const std::string &modname) const;
|
||||
virtual const SubgameSpec* getGameSpec() const { return &m_gamespec; }
|
||||
static std::string getBuiltinLuaPath();
|
||||
virtual std::string getWorldPath() const { return m_path_world; }
|
||||
virtual std::string getModDataPath() const { return m_path_mod_data; }
|
||||
virtual ModIPCStore *getModIPCStore() { return &m_ipcstore; }
|
||||
|
||||
inline bool isSingleplayer() const
|
||||
{ return m_simple_singleplayer_mode; }
|
||||
|
@ -666,6 +683,8 @@ private:
|
|||
|
||||
std::unordered_map<std::string, Translations> server_translations;
|
||||
|
||||
ModIPCStore m_ipcstore;
|
||||
|
||||
/*
|
||||
Threads
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue