Add an SSCSM controller and environment skeleton
This commit is contained in:
parent
f54d209bc8
commit
d46fc495ff
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
65
src/script/sscsm_controller.cpp
Normal file
65
src/script/sscsm_controller.cpp
Normal 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));
|
||||
}
|
37
src/script/sscsm_controller.h
Normal file
37
src/script/sscsm_controller.h
Normal 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);
|
||||
};
|
47
src/script/sscsm_environment.cpp
Normal file
47
src/script/sscsm_environment.cpp
Normal 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;
|
||||
}
|
32
src/script/sscsm_environment.h
Normal file
32
src/script/sscsm_environment.h
Normal 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
34
src/script/sscsm_events.h
Normal 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
39
src/script/sscsm_ievent.h
Normal 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;
|
||||
}
|
67
src/script/sscsm_irequest.h
Normal file
67
src/script/sscsm_irequest.h
Normal 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;
|
||||
}
|
50
src/script/sscsm_requests.h
Normal file
50
src/script/sscsm_requests.h
Normal 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});
|
||||
}
|
||||
};
|
84
src/script/sscsm_stupid_channel.h
Normal file
84
src/script/sscsm_stupid_channel.h
Normal 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();
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user