This commit is contained in:
DS 2025-01-02 17:32:26 +01:00 committed by GitHub
commit 0d7a7a3ae5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 467 additions and 2 deletions

View File

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

View File

@ -55,6 +55,8 @@ struct MeshMakeData;
struct MinimapMapblock;
struct PlayerControl;
struct PointedThing;
class ClientScripting;
class SSCSMController;
namespace con {
class IConnection;
@ -98,8 +100,6 @@ private:
std::map<u16, u32> m_packets;
};
class ClientScripting;
class Client : public con::PeerHandler, public InventoryManager, public IGameDef
{
public:
@ -582,6 +582,9 @@ private:
std::vector<ModSpec> m_mods;
StringMap m_mod_vfs;
// SSCSM
std::unique_ptr<SSCSMController> m_sscsm_controller;
bool m_shutdown = false;
// CSM restrictions byteflag

View File

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

View File

@ -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> SSCSMController::create()
{
auto channel = std::make_shared<StupidChannel>();
auto thread = std::make_unique<SSCSMEnvironment>(channel);
thread->start();
// Wait for thread to finish initializing.
auto req0 = deserializeSSCSMRequest(channel->recvB());
FATAL_ERROR_IF(!dynamic_cast<SSCSMRequestPollNextEvent *>(req0.get()),
"First request must be pollEvent.");
return std::make_unique<SSCSMController>(std::move(thread), channel);
}
SSCSMController::SSCSMController(std::unique_ptr<SSCSMEnvironment> thread,
std::shared_ptr<StupidChannel> channel) :
m_thread(std::move(thread)), m_channel(std::move(channel))
{
}
SSCSMController::~SSCSMController()
{
// send tear-down
m_channel->sendB(serializeSSCSMAnswer(SSCSMAnswerPollNextEvent{
std::make_unique<SSCSMEventTearDown>()
}));
// 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<ISSCSMEvent> event)
{
auto answer = serializeSSCSMAnswer(SSCSMAnswerPollNextEvent{std::move(event)});
while (true) {
auto request = deserializeSSCSMRequest(m_channel->exchangeB(std::move(answer)));
if (dynamic_cast<SSCSMRequestPollNextEvent *>(request.get()) != nullptr) {
break;
}
answer = handleRequest(client, request.get());
}
}
void SSCSMController::eventOnStep(Client *client, f32 dtime)
{
runEvent(client, std::make_unique<SSCSMEventOnStep>(dtime));
}

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2024 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <memory>
#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<SSCSMEnvironment> m_thread;
std::shared_ptr<StupidChannel> m_channel;
SerializedSSCSMAnswer handleRequest(Client *client, ISSCSMRequest *req);
public:
static std::unique_ptr<SSCSMController> create();
SSCSMController(std::unique_ptr<SSCSMEnvironment> thread,
std::shared_ptr<StupidChannel> channel);
~SSCSMController();
DISABLE_CLASS_COPY(SSCSMController);
// Handles requests until the next event is polled
void runEvent(Client *client, std::unique_ptr<ISSCSMEvent> event);
void eventOnStep(Client *client, f32 dtime);
};

View File

@ -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<SSCSMEventTearDown *>(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<ISSCSMEvent> SSCSMEnvironment::cmdPollNextEvent()
{
auto request = SSCSMRequestPollNextEvent{};
auto answer = deserializeSSCSMAnswer<SSCSMAnswerPollNextEvent>(
exchange(serializeSSCSMRequest(request))
);
return std::move(answer.next_event);
}
MapNode SSCSMEnvironment::cmdGetNode(v3s16 pos)
{
auto request = SSCSMRequestGetNode{pos};
auto answer = deserializeSSCSMAnswer<SSCSMAnswerGetNode>(
exchange(serializeSSCSMRequest(request))
);
return answer.node;
}

View File

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2024 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <memory>
#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<StupidChannel> m_channel;
void *run() override;
SerializedSSCSMAnswer exchange(SerializedSSCSMRequest req);
public:
SSCSMEnvironment(std::shared_ptr<StupidChannel> channel) :
Thread("SSCSMEnvironment-thread"),
m_channel(std::move(channel))
{
}
std::unique_ptr<ISSCSMEvent> cmdPollNextEvent();
MapNode cmdGetNode(v3s16 pos);
};

34
src/script/sscsm_events.h Normal file
View File

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

39
src/script/sscsm_ievent.h Normal file
View File

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2024 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <memory>
#include <type_traits>
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<ISSCSMEvent>;
template <typename T>
inline SerializedSSCSMEvent serializeSSCSMEvent(const T &event)
{
static_assert(std::is_base_of_v<ISSCSMEvent, T>);
return std::make_unique<T>(event);
}
inline std::unique_ptr<ISSCSMEvent> deserializeSSCSMEvent(SerializedSSCSMEvent event_serialized)
{
// The actual deserialization will have to use a type tag, and then choose
// the appropriate deserializer.
return event_serialized;
}

View File

@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: 2024 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "exceptions.h"
#include <memory>
#include <type_traits>
class SSCSMController;
class Client;
struct ISSCSMAnswer
{
virtual ~ISSCSMAnswer() = default;
};
// FIXME: actually serialize, and replace this with a string
using SerializedSSCSMAnswer = std::unique_ptr<ISSCSMAnswer>;
// 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<ISSCSMRequest>;
template <typename T>
inline SerializedSSCSMRequest serializeSSCSMRequest(const T &request)
{
static_assert(std::is_base_of_v<ISSCSMRequest, T>);
return std::make_unique<T>(request);
}
template <typename T>
inline T deserializeSSCSMAnswer(SerializedSSCSMAnswer answer_serialized)
{
static_assert(std::is_base_of_v<ISSCSMAnswer, T>);
// dynamic cast in place of actual deserialization
auto ptr = dynamic_cast<T *>(answer_serialized.get());
if (!ptr) {
throw SerializationError("deserializeSSCSMAnswer failed");
}
return std::move(*ptr);
}
template <typename T>
inline SerializedSSCSMAnswer serializeSSCSMAnswer(T answer)
{
static_assert(std::is_base_of_v<ISSCSMAnswer, T>);
return std::make_unique<T>(std::move(answer));
}
inline std::unique_ptr<ISSCSMRequest> deserializeSSCSMRequest(SerializedSSCSMRequest request_serialized)
{
// The actual deserialization will have to use a type tag, and then choose
// the appropriate deserializer.
return request_serialized;
}

View File

@ -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<ISSCSMEvent> next_event;
SSCSMAnswerPollNextEvent(std::unique_ptr<ISSCSMEvent> 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});
}
};

View File

@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: 2024 Luanti authors
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <memory>
#include <mutex>
#include <condition_variable>
#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();
}
};