diff --git a/res/project.toml b/res/project.toml index c30e72ddf..c7e2fb69d 100644 --- a/res/project.toml +++ b/res/project.toml @@ -1,3 +1,6 @@ # default project name = "default" base_packs = ["base"] +permissions = [ + "network" +] diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index f55a503b3..664710a30 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -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 diff --git a/src/devtools/DebuggingServer.cpp b/src/devtools/DebuggingServer.cpp index b1aba6384..ad56e46ec 100644 --- a/src/devtools/DebuggingServer.cpp +++ b/src/devtools/DebuggingServer.cpp @@ -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.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(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(engine.getNetwork(), client); + std::make_unique(*network, client); connectionEstablished = false; } diff --git a/src/devtools/Project.cpp b/src/devtools/Project.cpp index 61e99c6a0..86c73640e 100644 --- a/src/devtools/Project.cpp +++ b/src/devtools/Project.cpp @@ -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 perms; + dv::get(src, "permissions", perms); + permissions.permissions = + std::set(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(); +} diff --git a/src/devtools/Project.hpp b/src/devtools/Project.hpp index f0851b304..ba2d0d407 100644 --- a/src/devtools/Project.hpp +++ b/src/devtools/Project.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -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 permissions; + + bool has(const std::string& name) const; +}; + struct Project : Serializable { std::string name; std::string title; std::vector basePacks; std::unique_ptr clientScript; std::unique_ptr setupCoroutine; + Permissions permissions; ~Project(); diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index f79fae75e..9dcfbaa98 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -134,10 +134,14 @@ void Engine::initialize(CoreParameters coreParameters) { } paths = std::make_unique(params); loadProject(); + paths->setupProject(*project); editor = std::make_unique(*this); cmd = std::make_unique(); - 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(); diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index 6b09b2cab..06246f8c6 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -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 @@ -161,8 +159,8 @@ public: return *window; } - network::Network& getNetwork() { - return *network; + network::Network* getNetwork() { + return network.get(); } cmd::CommandsInterpreter& getCmd() { diff --git a/src/engine/EnginePaths.cpp b/src/engine/EnginePaths.cpp index 6c05614ed..7cb71079c 100644 --- a/src/engine/EnginePaths.cpp +++ b/src/engine/EnginePaths.cpp @@ -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(*scriptFolder)); @@ -239,6 +243,22 @@ void EnginePaths::setEntryPoints(std::vector 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 EnginePaths::parsePath(std::string_view path) { size_t separator = path.find(':'); if (separator == std::string::npos) { diff --git a/src/engine/EnginePaths.hpp b/src/engine/EnginePaths.hpp index 876394b46..a216ac453 100644 --- a/src/engine/EnginePaths.hpp +++ b/src/engine/EnginePaths.hpp @@ -9,6 +9,7 @@ #include #include #include +#include struct PathsRoot { std::string name; @@ -42,6 +43,8 @@ private: std::vector roots; }; +struct Project; + class EnginePaths { public: ResPaths resPaths; @@ -65,6 +68,10 @@ public: void setEntryPoints(std::vector entryPoints); + void setupProject(const Project& project); + + bool isWriteable(const std::string& entryPoint) const; + std::vector scanForWorlds() const; static std::tuple parsePath(std::string_view view); @@ -81,6 +88,7 @@ private: std::vector entryPoints; std::unordered_map writeables; std::vector mounted; + std::set initiallyWriteables; void cleanup(); }; diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 32439b659..5dc70af0d 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -59,6 +59,7 @@ std::shared_ptr create_debug_panel( Player& player, bool allowDebugCheats ) { + auto network = engine.getNetwork(); auto& gui = engine.getGUI(); auto panel = std::make_shared( gui, glm::vec2(300, 200), glm::vec4(5.0f), 2.0f @@ -87,17 +88,18 @@ std::shared_ptr 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 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(); diff --git a/src/logic/scripting/lua/libs/libapp.cpp b/src/logic/scripting/lua/libs/libapp.cpp index 724bac654..7c406d1aa 100644 --- a/src/logic/scripting/lua/libs/libapp.cpp +++ b/src/logic/scripting/lua/libs/libapp.cpp @@ -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"); } diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp index 197cd7dba..687008152 100644 --- a/src/logic/scripting/lua/libs/libfile.cpp +++ b/src/logic/scripting/lua/libs/libfile.cpp @@ -39,18 +39,7 @@ static int l_read(lua::State* L) { ); } -static std::set 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(device.get())) { return true; } - if (writeable_entry_points.find(entryPoint) != writeable_entry_points.end()) { + if (engine->getPaths().isWriteable(entryPoint)) { return true; } return false; diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index b1b3a41f2..390845207 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -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 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 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}, {"get_total_download", wrap}, {"find_free_port", wrap}, + {"is_available", lua::wrap}, {"__pull_events", wrap}, {"__open_tcp", wrap}, {"__open_udp", wrap}, diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 868273bf7..a0e1eb606 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -194,5 +194,6 @@ void Network::update() { } std::unique_ptr Network::create(const NetworkSettings& settings) { + logger.info() << "initializing network"; return std::make_unique(network::create_curl_requests()); } diff --git a/src/util/platform.cpp b/src/util/platform.cpp index 81ddaa8b7..16adbf89f 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -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; }