mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-02 16:38:41 +00:00
Add an SSCSM controller and environment skeleton
This commit is contained in:
parent
5c2599315c
commit
93b53d1a0d
13 changed files with 468 additions and 2 deletions
|
@ -54,6 +54,7 @@
|
|||
#include "content/mod_configuration.h"
|
||||
#include "mapnode.h"
|
||||
#include "item_visuals_manager.h"
|
||||
#include "script/sscsm/sscsm_controller.h"
|
||||
|
||||
extern gui::IGUIEnvironment* guienv;
|
||||
|
||||
|
@ -136,6 +137,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()
|
||||
|
@ -523,6 +526,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);
|
||||
|
|
|
@ -56,6 +56,8 @@ struct MinimapMapblock;
|
|||
struct PlayerControl;
|
||||
struct PointedThing;
|
||||
struct ItemVisualsManager;
|
||||
class ClientScripting;
|
||||
class SSCSMController;
|
||||
|
||||
namespace con {
|
||||
class IConnection;
|
||||
|
@ -99,8 +101,6 @@ private:
|
|||
std::map<u16, u32> m_packets;
|
||||
};
|
||||
|
||||
class ClientScripting;
|
||||
|
||||
class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
{
|
||||
public:
|
||||
|
@ -585,6 +585,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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
add_subdirectory(common)
|
||||
add_subdirectory(cpp_api)
|
||||
add_subdirectory(lua_api)
|
||||
add_subdirectory(sscsm)
|
||||
|
||||
# Used by server and client
|
||||
set(common_SCRIPT_SRCS
|
||||
|
@ -19,5 +20,6 @@ set(client_SCRIPT_SRCS
|
|||
${client_SCRIPT_COMMON_SRCS}
|
||||
${client_SCRIPT_CPP_API_SRCS}
|
||||
${client_SCRIPT_LUA_API_SRCS}
|
||||
${client_SCRIPT_SSCSM_SRCS}
|
||||
PARENT_SCOPE)
|
||||
|
||||
|
|
5
src/script/sscsm/CMakeLists.txt
Normal file
5
src/script/sscsm/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
set(client_SCRIPT_SSCSM_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sscsm_controller.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sscsm_environment.cpp
|
||||
PARENT_SCOPE)
|
69
src/script/sscsm/sscsm_controller.cpp
Normal file
69
src/script/sscsm/sscsm_controller.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
// 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
|
||||
auto answer = SSCSMRequestPollNextEvent::Answer{};
|
||||
answer.next_event = std::make_unique<SSCSMEventTearDown>();
|
||||
m_channel->sendB(serializeSSCSMAnswer(std::move(answer)));
|
||||
// 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 answer0 = SSCSMRequestPollNextEvent::Answer{};
|
||||
answer0.next_event = std::move(event);
|
||||
auto answer = serializeSSCSMAnswer(std::move(answer0));
|
||||
|
||||
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)
|
||||
{
|
||||
auto event = std::make_unique<SSCSMEventOnStep>();
|
||||
event->dtime = dtime;
|
||||
runEvent(client, std::move(event));
|
||||
}
|
37
src/script/sscsm/sscsm_controller.h
Normal file
37
src/script/sscsm/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);
|
||||
};
|
48
src/script/sscsm/sscsm_environment.cpp
Normal file
48
src/script/sscsm/sscsm_environment.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
// 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 = requestPollNextEvent();
|
||||
|
||||
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::requestPollNextEvent()
|
||||
{
|
||||
auto request = SSCSMRequestPollNextEvent{};
|
||||
auto answer = deserializeSSCSMAnswer<SSCSMRequestPollNextEvent::Answer>(
|
||||
exchange(serializeSSCSMRequest(request))
|
||||
);
|
||||
return std::move(answer.next_event);
|
||||
}
|
||||
|
||||
MapNode SSCSMEnvironment::requestGetNode(v3s16 pos)
|
||||
{
|
||||
auto request = SSCSMRequestGetNode{};
|
||||
request.pos = pos;
|
||||
auto answer = deserializeSSCSMAnswer<SSCSMRequestGetNode::Answer>(
|
||||
exchange(serializeSSCSMRequest(request))
|
||||
);
|
||||
return answer.node;
|
||||
}
|
32
src/script/sscsm/sscsm_environment.h
Normal file
32
src/script/sscsm/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> requestPollNextEvent();
|
||||
MapNode requestGetNode(v3s16 pos);
|
||||
};
|
32
src/script/sscsm/sscsm_events.h
Normal file
32
src/script/sscsm/sscsm_events.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
// 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 final : public ISSCSMEvent
|
||||
{
|
||||
f32 dtime;
|
||||
|
||||
void exec(SSCSMEnvironment *env) override
|
||||
{
|
||||
// example
|
||||
env->requestGetNode(v3s16(0, 0, 0));
|
||||
}
|
||||
};
|
||||
|
39
src/script/sscsm/sscsm_ievent.h
Normal file
39
src/script/sscsm/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/sscsm_irequest.h
Normal file
67
src/script/sscsm/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;
|
||||
}
|
43
src/script/sscsm/sscsm_requests.h
Normal file
43
src/script/sscsm/sscsm_requests.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
// 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 SSCSMRequestPollNextEvent : public ISSCSMRequest
|
||||
{
|
||||
struct Answer : public ISSCSMAnswer
|
||||
{
|
||||
std::unique_ptr<ISSCSMEvent> next_event;
|
||||
};
|
||||
|
||||
SerializedSSCSMAnswer exec(Client *client) override
|
||||
{
|
||||
FATAL_ERROR("SSCSMRequestPollNextEvent needs to be handled by SSCSMControler::runEvent()");
|
||||
}
|
||||
};
|
||||
|
||||
struct SSCSMRequestGetNode : public ISSCSMRequest
|
||||
{
|
||||
struct Answer : public ISSCSMAnswer
|
||||
{
|
||||
MapNode node;
|
||||
};
|
||||
|
||||
v3s16 pos;
|
||||
|
||||
SerializedSSCSMAnswer exec(Client *client) override
|
||||
{
|
||||
MapNode node = client->getEnv().getMap().getNode(pos);
|
||||
|
||||
Answer answer{};
|
||||
answer.node = node;
|
||||
return serializeSSCSMAnswer(std::move(answer));
|
||||
}
|
||||
};
|
84
src/script/sscsm/sscsm_stupid_channel.h
Normal file
84
src/script/sscsm/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…
Add table
Add a link
Reference in a new issue