Skip to content

Commit

Permalink
Add an SSCSM controller and environment skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
Desour committed Dec 31, 2024
1 parent f54d209 commit d46fc49
Show file tree
Hide file tree
Showing 12 changed files with 467 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/client/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "translation.h"
#include "content/mod_configuration.h"
#include "mapnode.h"
#include "script/sscsm_controller.h"

extern gui::IGUIEnvironment* guienv;

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions src/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ struct MeshMakeData;
struct MinimapMapblock;
struct PlayerControl;
struct PointedThing;
class ClientScripting;
class SSCSMController;

namespace con {
class IConnection;
Expand Down Expand Up @@ -98,8 +100,6 @@ class PacketCounter
std::map<u16, u32> m_packets;
};

class ClientScripting;

class Client : public con::PeerHandler, public InventoryManager, public IGameDef
{
public:
Expand Down Expand Up @@ -582,6 +582,9 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
std::vector<ModSpec> m_mods;
StringMap m_mod_vfs;

// SSCSM
std::unique_ptr<SSCSMController> m_sscsm_controller;

bool m_shutdown = false;

// CSM restrictions byteflag
Expand Down
2 changes: 2 additions & 0 deletions src/script/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
65 changes: 65 additions & 0 deletions src/script/sscsm_controller.cpp
Original file line number Diff line number Diff line change
@@ -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));
}
37 changes: 37 additions & 0 deletions src/script/sscsm_controller.h
Original file line number Diff line number Diff line change
@@ -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);
};
47 changes: 47 additions & 0 deletions src/script/sscsm_environment.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
32 changes: 32 additions & 0 deletions src/script/sscsm_environment.h
Original file line number Diff line number Diff line change
@@ -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 changes: 34 additions & 0 deletions src/script/sscsm_events.h
Original file line number Diff line number Diff line change
@@ -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 changes: 39 additions & 0 deletions src/script/sscsm_ievent.h
Original file line number Diff line number Diff line change
@@ -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;
}
67 changes: 67 additions & 0 deletions src/script/sscsm_irequest.h
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit d46fc49

Please sign in to comment.