1
0
Fork 0
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:
sfan5 2024-05-14 22:24:05 +02:00
parent 06907aa99b
commit f1a436619f
12 changed files with 191 additions and 19 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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

View 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);
}

View 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);
};

View file

@ -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
}

View file

@ -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)

View file

@ -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:

View file

@ -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
*/