1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-09-30 19:22:14 +00:00
This commit is contained in:
Desour 2025-01-27 14:12:40 +01:00
parent 7935a63ed4
commit 0fb8e1b398
19 changed files with 284 additions and 17 deletions

View file

@ -1883,6 +1883,10 @@ mgvalleys_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500),
# This support is experimental and API can change. # This support is experimental and API can change.
enable_client_modding (Client modding) [client] bool false enable_client_modding (Client modding) [client] bool false
# Where to enable server-sent client-side modding (SSCSM).
# Warning: Experimental.
enable_sscsm (Client modding) enum off off,singleplayer,localhost,lan,worldwide
# Replaces the default main menu with a custom one. # Replaces the default main menu with a custom one.
main_menu_script (Main menu script) [client] string main_menu_script (Main menu script) [client] string

View file

@ -55,6 +55,7 @@
#include "mapnode.h" #include "mapnode.h"
#include "item_visuals_manager.h" #include "item_visuals_manager.h"
#include "script/sscsm/sscsm_controller.h" #include "script/sscsm/sscsm_controller.h"
#include "script/sscsm/sscsm_events.h"
extern gui::IGUIEnvironment* guienv; extern gui::IGUIEnvironment* guienv;
@ -139,6 +140,42 @@ Client::Client(
m_mesh_grid = { g_settings->getU16("client_mesh_chunk") }; m_mesh_grid = { g_settings->getU16("client_mesh_chunk") };
m_sscsm_controller = SSCSMController::create(); m_sscsm_controller = SSCSMController::create();
{
auto event1 = std::make_unique<SSCSMEventUpdateVFSFiles>();
//TODO: read files
event1->files.emplace_back("/client_builtin/sscsm_client/init.lua",
R"=+=(
print("client builtin: loading")
)=+=");
//TODO: checksum
m_sscsm_controller->runEvent(this, std::move(event1));
// load client builtin immediately
auto event2 = std::make_unique<SSCSMEventLoadMods>();
event2->init_paths.emplace_back("/client_builtin/sscsm_client/init.lua");
m_sscsm_controller->runEvent(this, std::move(event2));
}
{
//TODO: network packets
std::string enable_sscsm = g_settings->get("enable_sscsm");
if (enable_sscsm == "singleplayer") {
auto event1 = std::make_unique<SSCSMEventUpdateVFSFiles>();
event1->files.emplace_back("/mods/sscsm_test0/init.lua",
R"=+=(
print("sscsm_test0: loading")
)=+=");
m_sscsm_controller->runEvent(this, std::move(event1));
auto event2 = std::make_unique<SSCSMEventLoadMods>();
event2->init_paths.emplace_back("/client_builtin/sscsm_client/init.lua");
m_sscsm_controller->runEvent(this, std::move(event2));
}
}
} }
void Client::migrateModStorage() void Client::migrateModStorage()

View file

@ -121,6 +121,7 @@ void set_default_settings()
settings->setDefault("curl_verify_cert", "true"); settings->setDefault("curl_verify_cert", "true");
settings->setDefault("enable_remote_media_server", "true"); settings->setDefault("enable_remote_media_server", "true");
settings->setDefault("enable_client_modding", "false"); settings->setDefault("enable_client_modding", "false");
settings->setDefault("enable_sscsm", "off");
settings->setDefault("max_out_chat_queue_size", "20"); settings->setDefault("max_out_chat_queue_size", "20");
settings->setDefault("pause_on_lost_focus", "false"); settings->setDefault("pause_on_lost_focus", "false");
settings->setDefault("enable_split_login_register", "true"); settings->setDefault("enable_split_login_register", "true");

View file

@ -21,6 +21,7 @@ set(client_SCRIPT_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/scripting_client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/scripting_client.cpp
${CMAKE_CURRENT_SOURCE_DIR}/scripting_pause_menu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/scripting_pause_menu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/scripting_sscsm.cpp
${client_SCRIPT_COMMON_SRCS} ${client_SCRIPT_COMMON_SRCS}
${client_SCRIPT_CPP_API_SRCS} ${client_SCRIPT_CPP_API_SRCS}

View file

@ -22,5 +22,6 @@ set(client_SCRIPT_CPP_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/s_client_common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_client_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_pause_menu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_pause_menu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_sscsm.cpp
PARENT_SCOPE) PARENT_SCOPE)

View file

@ -43,11 +43,12 @@ extern "C" {
enum class ScriptingType: u8 { enum class ScriptingType: u8 {
Async, // either mainmenu (client) or ingame (server) Async, // either mainmenu (client) or ingame (server)
Client, Client, // CPCSM
MainMenu, MainMenu,
Server, Server,
Emerge, Emerge,
PauseMenu, PauseMenu,
SSCSM,
}; };
class Server; class Server;
@ -58,6 +59,7 @@ class EmergeThread;
class IGameDef; class IGameDef;
class Environment; class Environment;
class GUIEngine; class GUIEngine;
class SSCSMEnvironment;
class ServerActiveObject; class ServerActiveObject;
struct PlayerHPChangeReason; struct PlayerHPChangeReason;
@ -158,6 +160,9 @@ protected:
#if CHECK_CLIENT_BUILD() #if CHECK_CLIENT_BUILD()
GUIEngine* getGuiEngine() { return m_guiengine; } GUIEngine* getGuiEngine() { return m_guiengine; }
void setGuiEngine(GUIEngine* guiengine) { m_guiengine = guiengine; } void setGuiEngine(GUIEngine* guiengine) { m_guiengine = guiengine; }
SSCSMEnvironment *getSSCSMEnv() { return m_sscsm_environment; }
void setSSCSMEnv(SSCSMEnvironment *env) { m_sscsm_environment = env; }
#endif #endif
EmergeThread* getEmergeThread() { return m_emerge; } EmergeThread* getEmergeThread() { return m_emerge; }
@ -184,6 +189,7 @@ private:
Environment *m_environment = nullptr; Environment *m_environment = nullptr;
#if CHECK_CLIENT_BUILD() #if CHECK_CLIENT_BUILD()
GUIEngine *m_guiengine = nullptr; GUIEngine *m_guiengine = nullptr;
SSCSMEnvironment *m_sscsm_environment = nullptr;
#endif #endif
EmergeThread *m_emerge = nullptr; EmergeThread *m_emerge = nullptr;

View file

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2025 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "s_sscsm.h"
#include "s_internal.h"
#include "script/sscsm/sscsm_environment.h"
void ScriptApiSSCSM::load_mods(const std::vector<std::string> &init_paths)
{
//TODO
}
void ScriptApiSSCSM::environment_step(float dtime)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_globalsteps
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_globalsteps");
// Call callbacks
lua_pushnumber(L, dtime);
try {
runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
getSSCSMEnv()->setFatalError(e);
}
}

View file

@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2025 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "cpp_api/s_base.h"
class ScriptApiSSCSM : virtual public ScriptApiBase
{
public:
void load_mods(const std::vector<std::string> &init_paths);
void environment_step(float dtime);
};

View file

@ -43,4 +43,5 @@ set(client_SCRIPT_LUA_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/l_particles_local.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_particles_local.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_pause_menu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_pause_menu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_sscsm.cpp
PARENT_SCOPE) PARENT_SCOPE)

View file

@ -58,6 +58,11 @@ GUIEngine *ModApiBase::getGuiEngine(lua_State *L)
{ {
return getScriptApiBase(L)->getGuiEngine(); return getScriptApiBase(L)->getGuiEngine();
} }
SSCSMEnvironment *ModApiBase::getSSCSMEnv(lua_State *L)
{
return getScriptApiBase(L)->getSSCSMEnv();
}
#endif #endif
EmergeThread *ModApiBase::getEmergeThread(lua_State *L) EmergeThread *ModApiBase::getEmergeThread(lua_State *L)

View file

@ -23,6 +23,7 @@ class EmergeThread;
class ScriptApiBase; class ScriptApiBase;
class Server; class Server;
class Environment; class Environment;
class SSCSMEnvironment;
class ServerInventoryManager; class ServerInventoryManager;
class ModApiBase : protected LuaHelper { class ModApiBase : protected LuaHelper {
@ -33,6 +34,7 @@ public:
#if CHECK_CLIENT_BUILD() #if CHECK_CLIENT_BUILD()
static Client* getClient(lua_State *L); static Client* getClient(lua_State *L);
static GUIEngine* getGuiEngine(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L);
static SSCSMEnvironment *getSSCSMEnv(lua_State *L);
#endif // !SERVER #endif // !SERVER
static EmergeThread* getEmergeThread(lua_State *L); static EmergeThread* getEmergeThread(lua_State *L);

View file

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2025 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "l_sscsm.h"
#include "common/c_content.h"
#include "common/c_converter.h"
#include "l_internal.h"
#include "log.h"
#include "script/sscsm/sscsm_environment.h"
#include "mapnode.h"
// print(text)
int ModApiSSCSM::l_print(lua_State *L)
{
// TODO: send request to main process
std::string text = luaL_checkstring(L, 1);
rawstream << text << std::endl;
return 0;
}
// get_node_or_nil(pos)
// pos = {x=num, y=num, z=num}
int ModApiSSCSM::l_get_node_or_nil(lua_State *L)
{
// pos
v3s16 pos = read_v3s16(L, 1);
// Do it
bool pos_ok = true;
MapNode n = getSSCSMEnv(L)->requestGetNode(pos); //TODO: add pos_ok to request
if (pos_ok) {
// Return node
pushnode(L, n);
} else {
lua_pushnil(L);
}
return 1;
}
void ModApiSSCSM::Initialize(lua_State *L, int top)
{
API_FCT(print);
API_FCT(get_node_or_nil);
}

View file

@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2025 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "lua_api/l_base.h"
class ModApiSSCSM : public ModApiBase
{
private:
// print(text)
static int l_print(lua_State *L);
// get_node_or_nil(pos)
static int l_get_node_or_nil(lua_State *L);
public:
static void Initialize(lua_State *L, int top);
};

View file

@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: 2025 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "scripting_sscsm.h"
SSCSMScripting::SSCSMScripting(SSCSMEnvironment *env)
{
setSSCSMEnv(env);
//TODO
}
void SSCSMScripting::initializeModApi(lua_State *L, int top)
{
//TODO
}

View file

@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2025 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "cpp_api/s_base.h"
#include "cpp_api/s_sscsm.h"
#include "cpp_api/s_security.h"
class SSCSMScripting :
virtual public ScriptApiBase,
public ScriptApiSSCSM,
public ScriptApiSecurity
{
public:
SSCSMScripting(SSCSMEnvironment *env);
protected:
bool checkPathInternal(const std::string &abs_path, bool write_required,
bool *write_allowed) { return false; };
private:
void initializeModApi(lua_State *L, int top);
};

View file

@ -1,5 +1,7 @@
file(GLOB client_SCRIPT_SSCSM_HDRS "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
set(client_SCRIPT_SSCSM_SRCS set(client_SCRIPT_SSCSM_SRCS
${client_SCRIPT_SSCSM_HDRS}
${CMAKE_CURRENT_SOURCE_DIR}/sscsm_controller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sscsm_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sscsm_environment.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sscsm_environment.cpp
PARENT_SCOPE) PARENT_SCOPE)

View file

@ -8,6 +8,13 @@
#include "sscsm_stupid_channel.h" #include "sscsm_stupid_channel.h"
SSCSMEnvironment::SSCSMEnvironment(std::shared_ptr<StupidChannel> channel) :
Thread("SSCSMEnvironment-thread"),
m_channel(std::move(channel)),
m_script(std::make_unique<SSCSMScripting>(this))
{
}
void *SSCSMEnvironment::run() void *SSCSMEnvironment::run()
{ {
while (true) { while (true) {
@ -28,6 +35,21 @@ SerializedSSCSMAnswer SSCSMEnvironment::exchange(SerializedSSCSMRequest req)
return m_channel->exchangeA(std::move(req)); return m_channel->exchangeA(std::move(req));
} }
void SSCSMEnvironment::updateVFSFiles(std::vector<std::pair<std::string, std::string>> &&files)
{
for (auto &&p : files) {
m_vfs.emplace(std::move(p.first), std::move(p.second));
}
}
void SSCSMEnvironment::setFatalError(const std::string &reason)
{
//TODO
// what to do on error?
// probably send a request
errorstream << "SSCSMEnvironment::setFatalError() reason: " << reason << std::endl;
}
std::unique_ptr<ISSCSMEvent> SSCSMEnvironment::requestPollNextEvent() std::unique_ptr<ISSCSMEvent> SSCSMEnvironment::requestPollNextEvent()
{ {
auto request = SSCSMRequestPollNextEvent{}; auto request = SSCSMRequestPollNextEvent{};

View file

@ -9,22 +9,36 @@
#include "threading/thread.h" #include "threading/thread.h"
#include "sscsm_controller.h" #include "sscsm_controller.h"
#include "sscsm_irequest.h" #include "sscsm_irequest.h"
#include "../scripting_sscsm.h"
// The thread that runs SSCSM code. // The thread that runs SSCSM code.
// Meant to be replaced by a sandboxed process. // Meant to be replaced by a sandboxed process.
class SSCSMEnvironment : public Thread class SSCSMEnvironment : public Thread
{ {
std::shared_ptr<StupidChannel> m_channel; std::shared_ptr<StupidChannel> m_channel;
std::unique_ptr<SSCSMScripting> m_script;
// virtual file system.
// TODO: decide and doc how paths look like, maybe:
// /client_builtin/subdir/foo.lua
// /server_builtin/subdir/foo.lua
// /mods/modname/subdir/foo.lua
std::unordered_map<std::string, std::string> m_vfs;
void *run() override; void *run() override;
SerializedSSCSMAnswer exchange(SerializedSSCSMRequest req); SerializedSSCSMAnswer exchange(SerializedSSCSMRequest req);
public: public:
SSCSMEnvironment(std::shared_ptr<StupidChannel> channel) : SSCSMEnvironment(std::shared_ptr<StupidChannel> channel);
Thread("SSCSMEnvironment-thread"),
m_channel(std::move(channel)) SSCSMScripting *getScript() { return m_script.get(); }
void updateVFSFiles(std::vector<std::pair<std::string, std::string>> &&files);
void setFatalError(const std::string &reason);
void setFatalError(const LuaError &e)
{ {
setFatalError(std::string("Lua: ") + e.what());
} }
std::unique_ptr<ISSCSMEvent> requestPollNextEvent(); std::unique_ptr<ISSCSMEvent> requestPollNextEvent();

View file

@ -7,9 +7,7 @@
#include "sscsm_ievent.h" #include "sscsm_ievent.h"
#include "debug.h" #include "debug.h"
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include "irr_v3d.h"
#include "sscsm_environment.h" #include "sscsm_environment.h"
#include "mapnode.h"
struct SSCSMEventTearDown : public ISSCSMEvent struct SSCSMEventTearDown : public ISSCSMEvent
{ {
@ -19,14 +17,35 @@ struct SSCSMEventTearDown : public ISSCSMEvent
} }
}; };
struct SSCSMEventOnStep final : public ISSCSMEvent struct SSCSMEventUpdateVFSFiles : public ISSCSMEvent
{
// pairs are virtual path and file content
std::vector<std::pair<std::string, std::string>> files;
void exec(SSCSMEnvironment *env) override
{
env->updateVFSFiles(std::move(files));
}
};
struct SSCSMEventLoadMods : public ISSCSMEvent
{
// paths to init.lua files, in load order
std::vector<std::string> init_paths;
void exec(SSCSMEnvironment *env) override
{
env->getScript()->load_mods(init_paths);
}
};
struct SSCSMEventOnStep : public ISSCSMEvent
{ {
f32 dtime; f32 dtime;
void exec(SSCSMEnvironment *env) override void exec(SSCSMEnvironment *env) override
{ {
// example env->getScript()->environment_step(dtime);
env->requestGetNode(v3s16(0, 0, 0));
} }
}; };