From d46fc495ff01dedaeb453964b3e38f6d6aa2a17a Mon Sep 17 00:00:00 2001 From: Desour Date: Tue, 17 Dec 2024 11:30:13 +0100 Subject: [PATCH] Add an SSCSM controller and environment skeleton --- src/client/client.cpp | 5 ++ src/client/client.h | 7 ++- src/script/CMakeLists.txt | 2 + src/script/sscsm_controller.cpp | 65 ++++++++++++++++++++++++ src/script/sscsm_controller.h | 37 ++++++++++++++ src/script/sscsm_environment.cpp | 47 +++++++++++++++++ src/script/sscsm_environment.h | 32 ++++++++++++ src/script/sscsm_events.h | 34 +++++++++++++ src/script/sscsm_ievent.h | 39 ++++++++++++++ src/script/sscsm_irequest.h | 67 ++++++++++++++++++++++++ src/script/sscsm_requests.h | 50 ++++++++++++++++++ src/script/sscsm_stupid_channel.h | 84 +++++++++++++++++++++++++++++++ 12 files changed, 467 insertions(+), 2 deletions(-) create mode 100644 src/script/sscsm_controller.cpp create mode 100644 src/script/sscsm_controller.h create mode 100644 src/script/sscsm_environment.cpp create mode 100644 src/script/sscsm_environment.h create mode 100644 src/script/sscsm_events.h create mode 100644 src/script/sscsm_ievent.h create mode 100644 src/script/sscsm_irequest.h create mode 100644 src/script/sscsm_requests.h create mode 100644 src/script/sscsm_stupid_channel.h diff --git a/src/client/client.cpp b/src/client/client.cpp index 222c1a2acbe99..33a002b393c4a 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -51,6 +51,7 @@ #include "translation.h" #include "content/mod_configuration.h" #include "mapnode.h" +#include "script/sscsm_controller.h" extern gui::IGUIEnvironment* guienv; @@ -131,6 +132,8 @@ Client::Client( m_cache_save_interval = g_settings->getU16("server_map_save_interval"); m_mesh_grid = { g_settings->getU16("client_mesh_chunk") }; + + m_sscsm_controller = SSCSMController::create(); } void Client::migrateModStorage() @@ -498,6 +501,8 @@ void Client::step(float dtime) */ LocalPlayer *player = m_env.getLocalPlayer(); + m_sscsm_controller->eventOnStep(this, dtime); + // Step environment (also handles player controls) m_env.step(dtime); m_sound->step(dtime); diff --git a/src/client/client.h b/src/client/client.h index e4bb77ab2260a..c378f3d99f019 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -55,6 +55,8 @@ struct MeshMakeData; struct MinimapMapblock; struct PlayerControl; struct PointedThing; +class ClientScripting; +class SSCSMController; namespace con { class IConnection; @@ -98,8 +100,6 @@ class PacketCounter std::map m_packets; }; -class ClientScripting; - class Client : public con::PeerHandler, public InventoryManager, public IGameDef { public: @@ -582,6 +582,9 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef std::vector m_mods; StringMap m_mod_vfs; + // SSCSM + std::unique_ptr m_sscsm_controller; + bool m_shutdown = false; // CSM restrictions byteflag diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index ccb5785bdaf36..45ad4268a075e 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -15,6 +15,8 @@ set(common_SCRIPT_SRCS set(client_SCRIPT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/scripting_client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sscsm_controller.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sscsm_environment.cpp ${client_SCRIPT_COMMON_SRCS} ${client_SCRIPT_CPP_API_SRCS} ${client_SCRIPT_LUA_API_SRCS} diff --git a/src/script/sscsm_controller.cpp b/src/script/sscsm_controller.cpp new file mode 100644 index 0000000000000..012a3d7667e82 --- /dev/null +++ b/src/script/sscsm_controller.cpp @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "sscsm_controller.h" +#include "sscsm_environment.h" +#include "sscsm_requests.h" +#include "sscsm_events.h" +#include "sscsm_stupid_channel.h" + +std::unique_ptr SSCSMController::create() +{ + auto channel = std::make_shared(); + auto thread = std::make_unique(channel); + thread->start(); + + // Wait for thread to finish initializing. + auto req0 = deserializeSSCSMRequest(channel->recvB()); + FATAL_ERROR_IF(!dynamic_cast(req0.get()), + "First request must be pollEvent."); + + return std::make_unique(std::move(thread), channel); +} + +SSCSMController::SSCSMController(std::unique_ptr thread, + std::shared_ptr channel) : + m_thread(std::move(thread)), m_channel(std::move(channel)) +{ +} + +SSCSMController::~SSCSMController() +{ + // send tear-down + m_channel->sendB(serializeSSCSMAnswer(SSCSMAnswerPollNextEvent{ + std::make_unique() + })); + // wait for death + m_thread->stop(); + m_thread->wait(); +} + +SerializedSSCSMAnswer SSCSMController::handleRequest(Client *client, ISSCSMRequest *req) +{ + return req->exec(client); +} + +void SSCSMController::runEvent(Client *client, std::unique_ptr event) +{ + auto answer = serializeSSCSMAnswer(SSCSMAnswerPollNextEvent{std::move(event)}); + + while (true) { + auto request = deserializeSSCSMRequest(m_channel->exchangeB(std::move(answer))); + + if (dynamic_cast(request.get()) != nullptr) { + break; + } + + answer = handleRequest(client, request.get()); + } +} + +void SSCSMController::eventOnStep(Client *client, f32 dtime) +{ + runEvent(client, std::make_unique(dtime)); +} diff --git a/src/script/sscsm_controller.h b/src/script/sscsm_controller.h new file mode 100644 index 0000000000000..77a16aa75d1ef --- /dev/null +++ b/src/script/sscsm_controller.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include "irrlichttypes.h" +#include "sscsm_irequest.h" +#include "sscsm_ievent.h" +#include "util/basic_macros.h" + +class SSCSMEnvironment; +class StupidChannel; + +class SSCSMController +{ + std::unique_ptr m_thread; + std::shared_ptr m_channel; + + SerializedSSCSMAnswer handleRequest(Client *client, ISSCSMRequest *req); + +public: + static std::unique_ptr create(); + + SSCSMController(std::unique_ptr thread, + std::shared_ptr channel); + + ~SSCSMController(); + + DISABLE_CLASS_COPY(SSCSMController); + + // Handles requests until the next event is polled + void runEvent(Client *client, std::unique_ptr event); + + void eventOnStep(Client *client, f32 dtime); +}; diff --git a/src/script/sscsm_environment.cpp b/src/script/sscsm_environment.cpp new file mode 100644 index 0000000000000..e05f7987553cd --- /dev/null +++ b/src/script/sscsm_environment.cpp @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "sscsm_environment.h" +#include "sscsm_requests.h" +#include "sscsm_events.h" +#include "sscsm_stupid_channel.h" + + +void *SSCSMEnvironment::run() +{ + while (true) { + auto next_event = cmdPollNextEvent(); + + if (dynamic_cast(next_event.get())) { + break; + } + + next_event->exec(this); + } + + return nullptr; +} + +SerializedSSCSMAnswer SSCSMEnvironment::exchange(SerializedSSCSMRequest req) +{ + return m_channel->exchangeA(std::move(req)); +} + +std::unique_ptr SSCSMEnvironment::cmdPollNextEvent() +{ + auto request = SSCSMRequestPollNextEvent{}; + auto answer = deserializeSSCSMAnswer( + exchange(serializeSSCSMRequest(request)) + ); + return std::move(answer.next_event); +} + +MapNode SSCSMEnvironment::cmdGetNode(v3s16 pos) +{ + auto request = SSCSMRequestGetNode{pos}; + auto answer = deserializeSSCSMAnswer( + exchange(serializeSSCSMRequest(request)) + ); + return answer.node; +} diff --git a/src/script/sscsm_environment.h b/src/script/sscsm_environment.h new file mode 100644 index 0000000000000..3fa3423a85e0b --- /dev/null +++ b/src/script/sscsm_environment.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include "client/client.h" +#include "threading/thread.h" +#include "sscsm_controller.h" +#include "sscsm_irequest.h" + +// The thread that runs SSCSM code. +// Meant to be replaced by a sandboxed process. +class SSCSMEnvironment : public Thread +{ + std::shared_ptr m_channel; + + void *run() override; + + SerializedSSCSMAnswer exchange(SerializedSSCSMRequest req); + +public: + SSCSMEnvironment(std::shared_ptr channel) : + Thread("SSCSMEnvironment-thread"), + m_channel(std::move(channel)) + { + } + + std::unique_ptr cmdPollNextEvent(); + MapNode cmdGetNode(v3s16 pos); +}; diff --git a/src/script/sscsm_events.h b/src/script/sscsm_events.h new file mode 100644 index 0000000000000..f3379006e12c8 --- /dev/null +++ b/src/script/sscsm_events.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "sscsm_ievent.h" +#include "debug.h" +#include "irrlichttypes.h" +#include "irr_v3d.h" +#include "sscsm_environment.h" +#include "mapnode.h" + +struct SSCSMEventTearDown : public ISSCSMEvent +{ + void exec(SSCSMEnvironment *env) override + { + FATAL_ERROR("SSCSMEventTearDown needs to be handled by SSCSMEnvironment::run()"); + } +}; + +struct SSCSMEventOnStep : public ISSCSMEvent +{ + f32 dtime; + + SSCSMEventOnStep(f32 dtime_) : dtime(dtime_) {} + + void exec(SSCSMEnvironment *env) override + { + // example + env->cmdGetNode(v3s16(0, 0, 0)); + } +}; + diff --git a/src/script/sscsm_ievent.h b/src/script/sscsm_ievent.h new file mode 100644 index 0000000000000..8742a3302d287 --- /dev/null +++ b/src/script/sscsm_ievent.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include + +class SSCSMEnvironment; + +// Event triggered from the main env for the SSCSM env. +struct ISSCSMEvent +{ + virtual ~ISSCSMEvent() = default; + + // Note: No return value (difference to ISSCSMRequest). These are not callbacks + // that you can run at arbitrary locations, because the untrusted code could + // then clobber your local variables. + virtual void exec(SSCSMEnvironment *cntrl) = 0; +}; + +// FIXME: actually serialize, and replace this with a string +using SerializedSSCSMEvent = std::unique_ptr; + +template +inline SerializedSSCSMEvent serializeSSCSMEvent(const T &event) +{ + static_assert(std::is_base_of_v); + + return std::make_unique(event); +} + +inline std::unique_ptr deserializeSSCSMEvent(SerializedSSCSMEvent event_serialized) +{ + // The actual deserialization will have to use a type tag, and then choose + // the appropriate deserializer. + return event_serialized; +} diff --git a/src/script/sscsm_irequest.h b/src/script/sscsm_irequest.h new file mode 100644 index 0000000000000..28140e5eb56a3 --- /dev/null +++ b/src/script/sscsm_irequest.h @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "exceptions.h" +#include +#include + +class SSCSMController; +class Client; + +struct ISSCSMAnswer +{ + virtual ~ISSCSMAnswer() = default; +}; + +// FIXME: actually serialize, and replace this with a string +using SerializedSSCSMAnswer = std::unique_ptr; + +// Request made by the sscsm env to the main env. +struct ISSCSMRequest +{ + virtual ~ISSCSMRequest() = default; + + virtual SerializedSSCSMAnswer exec(Client *client) = 0; +}; + +// FIXME: actually serialize, and replace this with a string +using SerializedSSCSMRequest = std::unique_ptr; + +template +inline SerializedSSCSMRequest serializeSSCSMRequest(const T &request) +{ + static_assert(std::is_base_of_v); + + return std::make_unique(request); +} + +template +inline T deserializeSSCSMAnswer(SerializedSSCSMAnswer answer_serialized) +{ + static_assert(std::is_base_of_v); + + // dynamic cast in place of actual deserialization + auto ptr = dynamic_cast(answer_serialized.get()); + if (!ptr) { + throw SerializationError("deserializeSSCSMAnswer failed"); + } + return std::move(*ptr); +} + +template +inline SerializedSSCSMAnswer serializeSSCSMAnswer(T answer) +{ + static_assert(std::is_base_of_v); + + return std::make_unique(std::move(answer)); +} + +inline std::unique_ptr deserializeSSCSMRequest(SerializedSSCSMRequest request_serialized) +{ + // The actual deserialization will have to use a type tag, and then choose + // the appropriate deserializer. + return request_serialized; +} diff --git a/src/script/sscsm_requests.h b/src/script/sscsm_requests.h new file mode 100644 index 0000000000000..3cdde64a1d3c6 --- /dev/null +++ b/src/script/sscsm_requests.h @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "sscsm_irequest.h" +#include "sscsm_ievent.h" +#include "mapnode.h" +#include "map.h" +#include "client/client.h" + +struct SSCSMAnswerPollNextEvent : public ISSCSMAnswer +{ + std::unique_ptr next_event; + + SSCSMAnswerPollNextEvent(std::unique_ptr next_event_) : + next_event(std::move(next_event_)) + { + } +}; + +struct SSCSMRequestPollNextEvent : public ISSCSMRequest +{ + SerializedSSCSMAnswer exec(Client *client) override + { + FATAL_ERROR("SSCSMRequestPollNextEvent needs to be handled by SSCSMControler::runEvent()"); + } +}; + +struct SSCSMAnswerGetNode : public ISSCSMAnswer +{ + MapNode node; + + SSCSMAnswerGetNode(MapNode node_) : node(node_) {} +}; + +struct SSCSMRequestGetNode : public ISSCSMRequest +{ + v3s16 pos; + + SSCSMRequestGetNode(v3s16 pos_) : pos(pos_) {} + + SerializedSSCSMAnswer exec(Client *client) override + { + MapNode node = client->getEnv().getMap().getNode(pos); + + return serializeSSCSMAnswer(SSCSMAnswerGetNode{node}); + } +}; diff --git a/src/script/sscsm_stupid_channel.h b/src/script/sscsm_stupid_channel.h new file mode 100644 index 0000000000000..cb82d3ca128b2 --- /dev/null +++ b/src/script/sscsm_stupid_channel.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: 2024 Luanti authors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include +#include "sscsm_irequest.h" + +// FIXME: replace this with an ipc channel +class StupidChannel +{ + std::mutex m_mutex; + std::condition_variable m_condvar; + SerializedSSCSMRequest m_request; + SerializedSSCSMAnswer m_answer; + +public: + void sendA(SerializedSSCSMRequest request) + { + { + auto lock = std::lock_guard(m_mutex); + + m_request = std::move(request); + } + + m_condvar.notify_one(); + } + + SerializedSSCSMAnswer recvA() + { + auto lock = std::unique_lock(m_mutex); + + while (!m_answer) { + m_condvar.wait(lock); + } + + auto answer = std::move(m_answer); + m_answer = nullptr; + + return answer; + } + + SerializedSSCSMAnswer exchangeA(SerializedSSCSMRequest request) + { + sendA(std::move(request)); + + return recvA(); + } + + void sendB(SerializedSSCSMAnswer answer) + { + { + auto lock = std::lock_guard(m_mutex); + + m_answer = std::move(answer); + } + + m_condvar.notify_one(); + } + + SerializedSSCSMRequest recvB() + { + auto lock = std::unique_lock(m_mutex); + + while (!m_request) { + m_condvar.wait(lock); + } + + auto request = std::move(m_request); + m_request = nullptr; + + return request; + } + + SerializedSSCSMRequest exchangeB(SerializedSSCSMAnswer answer) + { + sendB(std::move(answer)); + + return recvB(); + } +};