Merge pull request #730 from MihailRis/project-permissions

project permissions
This commit is contained in:
MihailRis 2025-12-07 15:09:22 +03:00 • committed by GitHub
commit d457b2ee7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 128 additions and 41 deletions

View file

@ -1,3 +1,6 @@
# default project
name = "default"
base_packs = ["base"]
permissions = [
"network"
]

View file

@ -182,6 +182,9 @@ local function clean(iterable, checkFun, ...)
end
network.__process_events = function()
if not network.is_available() then
return
end
local CLIENT_CONNECTED = 1
local CONNECTED_TO_SERVER = 2
local DATAGRAM = 3

View file

@ -94,12 +94,17 @@ bool ClientConnection::alive() const {
static network::Server& create_tcp_server(
DebuggingServer& dbgServer, Engine& engine, int port
) {
auto& network = engine.getNetwork();
u64id_t serverId = network.openTcpServer(
auto network = engine.getNetwork();
if (network == nullptr) {
throw std::runtime_error(
"unable to create tcp server: project has no network permission"
);
}
u64id_t serverId = network->openTcpServer(
port,
[&network, &dbgServer](u64id_t sid, u64id_t id) {
auto& connection = dynamic_cast<network::ReadableConnection&>(
*network.getConnection(id, true)
*network->getConnection(id, true)
);
connection.setPrivate(true);
logger.info() << "connected client " << id << ": "
@ -108,7 +113,7 @@ static network::Server& create_tcp_server(
dbgServer.setClient(id);
}
);
auto& server = *network.getServer(serverId, true);
auto& server = *network->getServer(serverId, true);
server.setPrivate(true);
auto& tcpServer = dynamic_cast<network::TcpServer&>(server);
@ -302,8 +307,10 @@ void DebuggingServer::sendValue(
}
void DebuggingServer::setClient(u64id_t client) {
auto network = engine.getNetwork();
assert (network != nullptr);
connection =
std::make_unique<ClientConnection>(engine.getNetwork(), client);
std::make_unique<ClientConnection>(*network, client);
connectionEstablished = false;
}

View file

@ -11,10 +11,15 @@ static debug::Logger logger("project");
Project::~Project() = default;
dv::value Project::serialize() const {
auto permissionsList = dv::list();
for (const auto& perm : permissions.permissions) {
permissionsList.add(perm);
}
return dv::object({
{"name", name},
{"title", title},
{"base_packs", dv::to_value(basePacks)},
{"permissions", std::move(permissionsList)}
});
}
@ -22,6 +27,17 @@ void Project::deserialize(const dv::value& src) {
src.at("name").get(name);
src.at("title").get(title);
dv::get(src, "base_packs", basePacks);
if (src.has("permissions")) {
std::vector<std::string> perms;
dv::get(src, "permissions", perms);
permissions.permissions =
std::set<std::string>(perms.begin(), perms.end());
}
logger.info() << "permissions: ";
for (const auto& perm : permissions.permissions) {
logger.info() << " - " << perm;
}
}
void Project::loadProjectClientScript() {
@ -43,3 +59,7 @@ void Project::loadProjectStartScript() {
logger.warning() << "project start script does not exists";
}
}
bool Permissions::has(const std::string& name) const {
return permissions.find(name) != permissions.end();
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <set>
#include <string>
#include <vector>
#include <memory>
@ -11,12 +12,22 @@ namespace scripting {
class IClientProjectScript;
}
struct Permissions {
static inline std::string WRITE_TO_USER = "write-to-user";
static inline std::string NETWORK = "network";
std::set<std::string> permissions;
bool has(const std::string& name) const;
};
struct Project : Serializable {
std::string name;
std::string title;
std::vector<std::string> basePacks;
std::unique_ptr<scripting::IClientProjectScript> clientScript;
std::unique_ptr<Process> setupCoroutine;
Permissions permissions;
~Project();

View file

@ -134,10 +134,14 @@ void Engine::initialize(CoreParameters coreParameters) {
}
paths = std::make_unique<EnginePaths>(params);
loadProject();
paths->setupProject(*project);
editor = std::make_unique<devtools::Editor>(*this);
cmd = std::make_unique<cmd::CommandsInterpreter>();
network = network::Network::create(settings.network);
if (project->permissions.has(Permissions::NETWORK)) {
network = network::Network::create(settings.network);
}
if (!params.debugServerString.empty()) {
try {
@ -232,7 +236,9 @@ void Engine::run() {
}
void Engine::postUpdate() {
network->update();
if (network) {
network->update();
}
postRunnables.run();
scripting::process_post_runnables();
@ -265,6 +271,8 @@ void Engine::nextFrame(bool waitForRefresh) {
}
void Engine::startPauseLoop() {
assert (network != nullptr);
bool initialCursorLocked = false;
if (!isHeadless()) {
initialCursorLocked = input->isCursorLocked();

View file

@ -3,9 +3,7 @@
#include "CoreParameters.hpp"
#include "PostRunnables.hpp"
#include "Time.hpp"
#include "delegates.hpp"
#include "settings.hpp"
#include "typedefs.hpp"
#include "util/ObjectsKeeper.hpp"
#include <memory>
@ -161,8 +159,8 @@ public:
return *window;
}
network::Network& getNetwork() {
return *network;
network::Network* getNetwork() {
return network.get();
}
cmd::CommandsInterpreter& getCmd() {

View file

@ -6,6 +6,7 @@
#include "io/devices/ZipFileDevice.hpp"
#include "maths/util.hpp"
#include "typedefs.hpp"
#include "devtools/Project.hpp"
#include "util/platform.hpp"
#include "util/random.hpp"
#include "util/stringutil.hpp"
@ -43,7 +44,10 @@ static std::string generate_random_base64() {
EnginePaths::EnginePaths(CoreParameters& params)
: resourcesFolder(params.resFolder),
userFilesFolder(params.userFolder),
projectFolder(params.projectFolder) {
projectFolder(params.projectFolder),
initiallyWriteables({
"world", "export", "config"
}) {
if (!params.scriptFile.empty()) {
scriptFolder = params.scriptFile.parent_path();
io::set_device("script", std::make_shared<io::StdfsDevice>(*scriptFolder));
@ -239,6 +243,22 @@ void EnginePaths::setEntryPoints(std::vector<PathsRoot> entryPoints) {
this->entryPoints = std::move(entryPoints);
}
void EnginePaths::setupProject(const Project& project) {
if (project.permissions.has(Permissions::WRITE_TO_USER)) {
initiallyWriteables.insert("user");
}
}
bool EnginePaths::isWriteable(const std::string& entryPoint) const {
if (entryPoint.length() < 2) {
return false;
}
if (entryPoint.substr(0, 2) == "W.") {
return true;
}
return initiallyWriteables.find(entryPoint) != initiallyWriteables.end();
}
std::tuple<std::string, std::string> EnginePaths::parsePath(std::string_view path) {
size_t separator = path.find(':');
if (separator == std::string::npos) {

View file

@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include <tuple>
#include <set>
struct PathsRoot {
std::string name;
@ -42,6 +43,8 @@ private:
std::vector<PathsRoot> roots;
};
struct Project;
class EnginePaths {
public:
ResPaths resPaths;
@ -65,6 +68,10 @@ public:
void setEntryPoints(std::vector<PathsRoot> entryPoints);
void setupProject(const Project& project);
bool isWriteable(const std::string& entryPoint) const;
std::vector<io::path> scanForWorlds() const;
static std::tuple<std::string, std::string> parsePath(std::string_view view);
@ -81,6 +88,7 @@ private:
std::vector<PathsRoot> entryPoints;
std::unordered_map<std::string, std::string> writeables;
std::vector<std::string> mounted;
std::set<std::string> initiallyWriteables;
void cleanup();
};

View file

@ -59,6 +59,7 @@ std::shared_ptr<UINode> create_debug_panel(
Player& player,
bool allowDebugCheats
) {
auto network = engine.getNetwork();
auto& gui = engine.getGUI();
auto panel = std::make_shared<Panel>(
gui, glm::vec2(300, 200), glm::vec4(5.0f), 2.0f
@ -87,17 +88,18 @@ std::shared_ptr<UINode> create_debug_panel(
fpsMax = fps;
});
panel->listenInterval(1.0f, [&engine]() {
const auto& network = engine.getNetwork();
size_t totalDownload = network.getTotalDownload();
size_t totalUpload = network.getTotalUpload();
netSpeedString =
L"download: " + std::to_wstring(totalDownload - lastTotalDownload) +
L" B/s upload: " + std::to_wstring(totalUpload - lastTotalUpload) +
L" B/s";
lastTotalDownload = totalDownload;
lastTotalUpload = totalUpload;
});
if (network) {
panel->listenInterval(1.0f, [network]() {
size_t totalDownload = network->getTotalDownload();
size_t totalUpload = network->getTotalUpload();
netSpeedString =
L"download: " + std::to_wstring(totalDownload - lastTotalDownload) +
L" B/s upload: " + std::to_wstring(totalUpload - lastTotalUpload) +
L" B/s";
lastTotalDownload = totalDownload;
lastTotalUpload = totalUpload;
});
}
panel->add(create_label(gui, []() { return L"fps: "+fpsString;}));
@ -116,7 +118,9 @@ std::shared_ptr<UINode> create_debug_panel(
panel->add(create_label(gui, []() {
return L"lua-stack: " + std::to_wstring(scripting::get_values_on_stack());
}));
panel->add(create_label(gui, []() { return netSpeedString; }));
if (network) {
panel->add(create_label(gui, []() { return netSpeedString; }));
}
panel->add(create_label(gui, [&engine]() {
auto& settings = engine.getSettings();
bool culling = settings.graphics.frustumCulling.get();

View file

@ -15,7 +15,11 @@ using namespace scripting;
static int l_start_debug_instance(lua::State* L) {
int port = lua::tointeger(L, 1);
if (port == 0) {
port = engine->getNetwork().findFreePort();
auto network = engine->getNetwork();
if (network == nullptr) {
throw std::runtime_error("project has no network permission");
}
port = network->findFreePort();
if (port == -1) {
throw std::runtime_error("could not find free port");
}

View file

@ -39,18 +39,7 @@ static int l_read(lua::State* L) {
);
}
static std::set<std::string> writeable_entry_points {
"world", "export", "config"
};
static bool is_writeable(const std::string& entryPoint) {
if (entryPoint.length() < 2) {
return false;
}
if (entryPoint.substr(0, 2) == "W.") {
return true;
}
// todo: do better
auto device = io::get_device(entryPoint);
if (device == nullptr) {
return false;
@ -58,7 +47,7 @@ static bool is_writeable(const std::string& entryPoint) {
if (dynamic_cast<io::MemoryDevice*>(device.get())) {
return true;
}
if (writeable_entry_points.find(entryPoint) != writeable_entry_points.end()) {
if (engine->getPaths().isWriteable(entryPoint)) {
return true;
}
return false;

View file

@ -133,7 +133,7 @@ static int l_post(lua::State* L, network::Network& network) {
auto headers = read_headers(L, 3);
int currentRequestId = request_id++;
engine->getNetwork().post(
network.post(
url,
string,
[currentRequestId](std::vector<char> bytes) {
@ -240,7 +240,7 @@ static int l_recv(lua::State* L, network::Network& network) {
u64id_t id = lua::tointeger(L, 1);
int length = lua::tointeger(L, 2);
auto connection = engine->getNetwork().getConnection(id, false);
auto connection = network.getConnection(id, false);
if (connection == nullptr || connection->getTransportType() != network::TransportType::TCP) {
return 0;
@ -519,11 +519,21 @@ static int l_pull_events(lua::State* L, network::Network& network) {
return 1;
}
static int l_is_available(lua::State* L) {
return lua::pushboolean(L, engine->getNetwork() != nullptr);
}
template <int(*func)(lua::State*, network::Network&)>
int wrap(lua_State* L) {
int result = 0;
try {
result = func(L, engine->getNetwork());
auto network = engine->getNetwork();
if (network == nullptr) {
throw std::runtime_error(
"network subsystem is not available in the project"
);
}
result = func(L, *network);
}
// transform exception with description into lua_error
catch (std::exception& e) {
@ -543,6 +553,7 @@ const luaL_Reg networklib[] = {
{"get_total_upload", wrap<l_get_total_upload>},
{"get_total_download", wrap<l_get_total_download>},
{"find_free_port", wrap<l_find_free_port>},
{"is_available", lua::wrap<l_is_available>},
{"__pull_events", wrap<l_pull_events>},
{"__open_tcp", wrap<l_open_tcp>},
{"__open_udp", wrap<l_open_udp>},

View file

@ -194,5 +194,6 @@ void Network::update() {
}
std::unique_ptr<Network> Network::create(const NetworkSettings& settings) {
logger.info() << "initializing network";
return std::make_unique<Network>(network::create_curl_requests());
}

View file

@ -102,7 +102,7 @@ std::string platform::detect_locale() {
if (programLocaleName && preferredLocaleName) {
setlocale(LC_ALL, programLocaleName);
return std::string(preferredLocaleName, 5);
return std::string(preferredLocaleName);
}
return langs::FALLBACK_DEFAULT;
}