From c1df4a21f3bd6881c5524674910b654aff9e0d41 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Mon, 4 Aug 2025 17:42:12 +0600 Subject: [PATCH] * --- Src/Client/Vulkan/Vulkan.cpp | 2 +- Src/Server/GameServer.cpp | 421 ++++++++++++++++--------- Src/Server/GameServer.hpp | 31 +- Src/Server/SaveBackends/Filesystem.cpp | 10 +- 4 files changed, 309 insertions(+), 155 deletions(-) diff --git a/Src/Client/Vulkan/Vulkan.cpp b/Src/Client/Vulkan/Vulkan.cpp index 012e19e..d91b16f 100644 --- a/Src/Client/Vulkan/Vulkan.cpp +++ b/Src/Client/Vulkan/Vulkan.cpp @@ -53,7 +53,7 @@ struct ServerObj { Net::SocketServer LS; ServerObj(asio::io_context &ioc) - : GS(ioc, ""), LS(ioc, [&](tcp::socket sock) -> coro<> { co_await GS.pushSocketConnect(std::move(sock)); }, 7890) + : GS(ioc, "worlds/test/"), LS(ioc, [&](tcp::socket sock) -> coro<> { co_await GS.pushSocketConnect(std::move(sock)); }, 7890) { } }; diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index 8224763..73a77f6 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include "Server/SaveBackend.hpp" #include "Server/World.hpp" #include "TOSLib.hpp" +#include "boost/json.hpp" #include "boost/json/array.hpp" #include "boost/json/object.hpp" #include "boost/json/parse_into.hpp" @@ -35,78 +37,49 @@ namespace js = boost::json; -int luaPanic(lua_State* L) -{ - size_t length; - const char *str = luaL_checklstring(L, -1, &length); - MAKE_ERROR("LUA PANIC: unprotected error in call to Lua API (" << std::string_view(str, length) << ")"); - - return 0; -} - -int luaAtException(lua_State* L, sol::optional exc, std::string_view view) { - MAKE_ERROR("LUA EXCEPTION: unprotected error in call to Lua API (" << view << ")"); - - return 0; -} - namespace LV::Server { -struct ModDepend { - std::string Id; - uint32_t VersionMin[4], VersionMax[4]; -}; +std::string ModInfo::dump() const { + js::object obj; -struct ModInfo { - std::string Id, Name, Description, Author; - uint32_t Version[4]; - std::vector Dependencies, Optional; - float LoadPriority; - fs::path Path; - bool HasLiveReload; - - std::string dump() const { - js::object obj; - - obj["id"] = Id; - obj["name"] = Name; - obj["description"] = Description; - obj["author"] = Author; - obj["version"] = {Version[0], Version[1], Version[2], Version[3]}; - obj["hasLiveReload"] = HasLiveReload; - - { - js::array arr; - for(const auto& depend : Dependencies) { - js::object obj; - obj["id"] = depend.Id; - obj["version_min"] = {depend.VersionMin[0], depend.VersionMin[1], depend.VersionMin[2], depend.VersionMin[3]}; - obj["version_max"] = {depend.VersionMax[0], depend.VersionMax[1], depend.VersionMax[2], depend.VersionMax[3]}; - arr.push_back(obj); - } - - obj["depend"] = arr; + obj["id"] = Id; + obj["name"] = Name; + obj["description"] = Description; + obj["author"] = Author; + obj["version"] = {Version[0], Version[1], Version[2], Version[3]}; + obj["hasLiveReload"] = HasLiveReload; + + { + js::array arr; + for(const auto& depend : Dependencies) { + js::object obj; + obj["id"] = depend.Id; + obj["version_min"] = {depend.MinVersion[0], depend.MinVersion[1], depend.MinVersion[2], depend.MinVersion[3]}; + obj["version_max"] = {depend.MaxVersion[0], depend.MaxVersion[1], depend.MaxVersion[2], depend.MaxVersion[3]}; + arr.push_back(obj); } - { - js::array arr; - for(const auto& depend : Optional) { - js::object obj; - obj["id"] = depend.Id; - obj["version_min"] = {depend.VersionMin[0], depend.VersionMin[1], depend.VersionMin[2], depend.VersionMin[3]}; - obj["version_max"] = {depend.VersionMax[0], depend.VersionMax[1], depend.VersionMax[2], depend.VersionMax[3]}; - arr.push_back(obj); - } - - obj["optional_depend"] = arr; - } - - obj["load_priority"] = LoadPriority; - obj["path"] = Path.string(); - - return js::serialize(obj); + obj["depend"] = arr; } -}; + + { + js::array arr; + for(const auto& depend : Optional) { + js::object obj; + obj["id"] = depend.Id; + obj["version_min"] = {depend.MinVersion[0], depend.MinVersion[1], depend.MinVersion[2], depend.MinVersion[3]}; + obj["version_max"] = {depend.MaxVersion[0], depend.MaxVersion[1], depend.MaxVersion[2], depend.MaxVersion[3]}; + arr.push_back(obj); + } + + obj["optional_depend"] = arr; + } + + obj["load_priority"] = LoadPriority; + obj["path"] = Path.string(); + + return js::serialize(obj); +} struct ModPreloadInfo { std::vector Mods; @@ -171,12 +144,12 @@ ModPreloadInfo preLoadMods(const std::vector& dirs) { if(obj.contains("depends")) { js::array arr = obj["depends"].as_array(); for(auto& iter : arr) { - ModDepend depend; + ModRequest depend; if(iter.is_string()) { depend.Id = iter.as_string(); - std::fill(depend.VersionMin, depend.VersionMin+4, 0); - std::fill(depend.VersionMax, depend.VersionMax+4, uint32_t(-1)); + std::fill(depend.MinVersion.begin(), depend.MinVersion.end(), 0); + std::fill(depend.MaxVersion.begin(), depend.MaxVersion.end(), uint32_t(-1)); } else if(iter.is_object()) { js::object d = iter.as_object(); depend.Id = d.at("id").as_string(); @@ -184,18 +157,18 @@ ModPreloadInfo preLoadMods(const std::vector& dirs) { if(d.contains("version_min")) { js::array v = d.at("version_min").as_array(); for(int iter = 0; iter < 4; iter++) { - depend.VersionMin[iter] = v.at(iter).as_int64(); + depend.MinVersion[iter] = v.at(iter).as_int64(); } } else - std::fill(depend.VersionMin, depend.VersionMin+4, 0); + std::fill(depend.MinVersion.begin(), depend.MinVersion.end(), 0); if(d.contains("version_max")) { js::array v = d.at("version_max").as_array(); for(int iter = 0; iter < 4; iter++) { - depend.VersionMax[iter] = v.at(iter).as_int64(); + depend.MaxVersion[iter] = v.at(iter).as_int64(); } } else - std::fill(depend.VersionMax, depend.VersionMax+4, uint32_t(-1)); + std::fill(depend.MaxVersion.begin(), depend.MaxVersion.end(), uint32_t(-1)); } info.Dependencies.push_back(depend); @@ -206,12 +179,12 @@ ModPreloadInfo preLoadMods(const std::vector& dirs) { if(obj.contains("optional_depends")) { js::array arr = obj["optional_depends"].as_array(); for(auto& iter : arr) { - ModDepend depend; + ModRequest depend; if(iter.is_string()) { depend.Id = iter.as_string(); - std::fill(depend.VersionMin, depend.VersionMin+4, 0); - std::fill(depend.VersionMax, depend.VersionMax+4, uint32_t(-1)); + std::fill(depend.MinVersion.begin(), depend.MinVersion.end(), 0); + std::fill(depend.MaxVersion.begin(), depend.MaxVersion.end(), uint32_t(-1)); } else if(iter.is_object()) { js::object d = iter.as_object(); depend.Id = d.at("id").as_string(); @@ -219,18 +192,18 @@ ModPreloadInfo preLoadMods(const std::vector& dirs) { if(d.contains("version_min")) { js::array v = d.at("version_min").as_array(); for(int iter = 0; iter < 4; iter++) { - depend.VersionMin[iter] = v.at(iter).as_int64(); + depend.MinVersion[iter] = v.at(iter).as_int64(); } } else - std::fill(depend.VersionMin, depend.VersionMin+4, 0); + std::fill(depend.MinVersion.begin(), depend.MinVersion.end(), 0); if(d.contains("version_max")) { js::array v = d.at("version_max").as_array(); for(int iter = 0; iter < 4; iter++) { - depend.VersionMax[iter] = v.at(iter).as_int64(); + depend.MaxVersion[iter] = v.at(iter).as_int64(); } } else - std::fill(depend.VersionMax, depend.VersionMax+4, uint32_t(-1)); + std::fill(depend.MaxVersion.begin(), depend.MaxVersion.end(), uint32_t(-1)); } info.Optional.push_back(depend); @@ -272,10 +245,10 @@ std::vector> rangDepends(const std::vector& mods) for(size_t index = 0; index < state.size(); index++) { ModInfo &mod = state[index]; - std::vector depends = mod.Dependencies; + std::vector depends = mod.Dependencies; depends.insert(depends.end(), mod.Optional.begin(), mod.Optional.end()); - for(ModDepend &depend : depends) { + for(ModRequest &depend : depends) { for(size_t index2 = 0; index2 < state.size(); index2++) { ModInfo &mod2 = state[index]; if(depend.Id == mod2.Id) { @@ -332,7 +305,7 @@ std::variant> buildLoadChain(const std::ve endState.insert(endState.end(), toLoad.begin(), toLoad.end()); for(const ModInfo& mmod : endState) { - for(const ModDepend& depend : mmod.Dependencies) { + for(const ModRequest& depend : mmod.Dependencies) { std::vector lerrors; bool contains = false; @@ -340,19 +313,15 @@ std::variant> buildLoadChain(const std::ve if(depend.Id != mmod2.Id) continue; - if(depend.VersionMin[0] > mmod2.Version[0] - || depend.VersionMin[1] > mmod2.Version[1] - || depend.VersionMin[2] > mmod2.Version[2] - || depend.VersionMin[3] > mmod2.Version[3] - ) { + if(depend.MinVersion > mmod2.Version) { goto versionMismatch; } - if(depend.VersionMax[0] != uint32_t(-1)) { - if(depend.VersionMax[0] < mmod2.Version[0] - || depend.VersionMax[1] < mmod2.Version[1] - || depend.VersionMax[2] < mmod2.Version[2] - || depend.VersionMax[3] < mmod2.Version[3] + if(depend.MaxVersion[0] != uint32_t(-1)) { + if(depend.MaxVersion < mmod2.Version + || depend.MaxVersion[1] < mmod2.Version[1] + || depend.MaxVersion[2] < mmod2.Version[2] + || depend.MaxVersion[3] < mmod2.Version[3] ) { goto versionMismatch; } @@ -363,18 +332,18 @@ std::variant> buildLoadChain(const std::ve versionMismatch: std::stringstream ss; - ss << depend.VersionMin[0] << '.'; - ss << depend.VersionMin[1] << '.'; - ss << depend.VersionMin[2] << '.'; - ss << depend.VersionMin[3]; + ss << depend.MinVersion[0] << '.'; + ss << depend.MinVersion[1] << '.'; + ss << depend.MinVersion[2] << '.'; + ss << depend.MinVersion[3]; std::string verMin = ss.str(); ss.str(""); - if(depend.VersionMax[0] != uint32_t(-1)) { - ss << depend.VersionMax[0] << '.'; - ss << (depend.VersionMax[1] == uint32_t(-1) ? "*" : std::to_string(depend.VersionMax[1])) << '.'; - ss << (depend.VersionMax[2] == uint32_t(-1) ? "*" : std::to_string(depend.VersionMax[2])) << '.'; - ss << (depend.VersionMax[3] == uint32_t(-1) ? "*" : std::to_string(depend.VersionMax[3])) << '.'; + if(depend.MaxVersion[0] != uint32_t(-1)) { + ss << depend.MaxVersion[0] << '.'; + ss << (depend.MaxVersion[1] == uint32_t(-1) ? "*" : std::to_string(depend.MaxVersion[1])) << '.'; + ss << (depend.MaxVersion[2] == uint32_t(-1) ? "*" : std::to_string(depend.MaxVersion[2])) << '.'; + ss << (depend.MaxVersion[3] == uint32_t(-1) ? "*" : std::to_string(depend.MaxVersion[3])) << '.'; verMin += " -> " + ss.str(); ss.str(""); } @@ -419,49 +388,95 @@ std::variant> buildLoadChain(const std::ve return ModLoadTree{unloadChain, loadChain}; } +/* + Находит необходимые моды в доступных загруженных и зависимости к ним +*/ + +std::variant, std::vector> resolveDepends(const std::vector& requests, const std::vector& mods) { + std::vector toLoad; + std::vector errors; + + std::vector next; + + // Найти те, что имеют чёткую версию загрузки + // Собрать с не чёткой версией загрузки + + for(const ModRequest& request : requests) { + if(request.MinVersion != request.MaxVersion) { + next.push_back(request); + continue; + } + + bool find = false, versionMismatch = false; + + for(const ModInfo& mod : mods) { + if(request.Id != mod.Id) + continue; + + if(request.MaxVersion != mod.Version) { + versionMismatch = true; + continue; + } + + find = true; + toLoad.push_back(mod); + next.insert(next.end(), mod.Dependencies.begin(), mod.Dependencies.end()); + break; + } + + if(!find) { + if(versionMismatch) + errors.push_back("Не найден мод " + request.Id + " соответствующей версии"); + else + errors.push_back("Не найден мод " + request.Id); + } + } + + // assert(next.empty()); + // :( + + for(const ModRequest& request : next) { + bool find = false, versionMismatch = false; + + for(const ModInfo& mod : mods) { + if(request.Id != mod.Id) + continue; + + if(request.MaxVersion < mod.Version) { + versionMismatch = true; + continue; + } + + if(request.MinVersion > mod.Version) { + versionMismatch = true; + continue; + } + + find = true; + toLoad.push_back(mod); + break; + } + + if(!find) { + if(versionMismatch) + errors.push_back("Не найден мод " + request.Id + " соответствующей версии"); + else + errors.push_back("Не найден мод " + request.Id); + } + } + + + if(!errors.empty()) + return errors; + + return toLoad; +} + GameServer::GameServer(asio::io_context &ioc, fs::path worldPath) : AsyncObject(ioc), Content(ioc) { - BackingChunkPressure.Threads.resize(4); - BackingNoiseGenerator.Threads.resize(4); - BackingAsyncLua.Threads.resize(4); - init(worldPath); - - BackingChunkPressure.Worlds = &Expanse.Worlds; - for(size_t iter = 0; iter < BackingChunkPressure.Threads.size(); iter++) { - BackingChunkPressure.Threads[iter] = std::thread(&BackingChunkPressure_t::run, &BackingChunkPressure, iter); - } - - for(size_t iter = 0; iter < BackingNoiseGenerator.Threads.size(); iter++) { - BackingNoiseGenerator.Threads[iter] = std::thread(&BackingNoiseGenerator_t::run, &BackingNoiseGenerator, iter); - } - - for(size_t iter = 0; iter < BackingAsyncLua.Threads.size(); iter++) { - BackingAsyncLua.Threads[iter] = std::thread(&BackingAsyncLua_t::run, &BackingAsyncLua, iter); - } - - // Тест луа - - // sol::state lua; - // lua.set_exception_handler(luaAtException); - - // sol::load_result res = lua.load_file("/home/mr_s/Workspace/Alpha/LuaVox/Work/mods/init.lua"); - // sol::function func = res.call<>(); - // int type = func(); - - // LOG.debug() << type; - - fs::path mods = "mods"; - auto [info, errors] = preLoadMods({mods}); - for(const auto& mod : info) { - LOG.debug() << mod.dump(); - } - - for(const std::string& error : errors) { - LOG.warn() << error; - } } GameServer::~GameServer() { @@ -1247,7 +1262,6 @@ std::string GameServer::deBuildTexturePipeline(const TexturePipeline& pipeline) return ""; } - void GameServer::init(fs::path worldPath) { // world.json @@ -1257,6 +1271,7 @@ void GameServer::init(fs::path worldPath) { LOG.info() << "Обработка файла " << worldJson.string(); js::object sbWorld, sbPlayer, sbAuth, sbModStorage; + std::vector modsToLoad; if(!fs::exists(worldJson)) { MAKE_ERROR("Файл отсутствует"); @@ -1292,6 +1307,46 @@ void GameServer::init(fs::path worldPath) { sbModStorage = sb.at("mod_storage").as_object(); } + { + js::array arr = obj.at("mods").as_array(); + for(const js::value& v : arr) { + ModRequest mi; + + if(v.is_string()) { + mi.Id = v.as_string(); + std::fill(mi.MinVersion.begin(), mi.MinVersion.end(), 0); + std::fill(mi.MaxVersion.begin(), mi.MaxVersion.end(), uint32_t(-1)); + } else { + js::object value = v.as_object(); + mi.Id = value.at("id").as_string(); + if(value.contains("version")) { + js::array version = value.at("version").as_array(); + for(int iter = 0; iter < 4; iter++) + mi.MaxVersion[iter] = version.at(iter).as_int64(); + mi.MinVersion = mi.MaxVersion; + } else { + if(value.contains("max_version")) { + js::array version = value.at("max_version").as_array(); + for(int iter = 0; iter < 4; iter++) + mi.MaxVersion[iter] = version.at(iter).as_int64(); + } else { + std::fill(mi.MaxVersion.begin(), mi.MaxVersion.end(), uint32_t(-1)); + } + + if(value.contains("min_version")) { + js::array version = value.at("min_version").as_array(); + for(int iter = 0; iter < 4; iter++) + mi.MinVersion[iter] = version.at(iter).as_int64(); + } else { + std::fill(mi.MinVersion.begin(), mi.MinVersion.end(), uint32_t(0)); + } + } + } + + modsToLoad.push_back(mi); + } + } + } catch(const std::exception& exc) { MAKE_ERROR("Ошибка структуры параметров: " << exc.what()); } @@ -1311,7 +1366,91 @@ void GameServer::init(fs::path worldPath) { LOG.info() << "Инициализация модов"; + ModPreloadInfo mpi = preLoadMods({"mods"}); + for(const std::string& error : mpi.Errors) { + LOG.warn() << error; + } + + LOG.info() << "Выборка модов"; + + std::variant, std::vector> resolveDependsResult = resolveDepends(modsToLoad, mpi.Mods); + if(resolveDependsResult.index() == 1) { + for(const std::string& error : std::get<1>(resolveDependsResult)) + LOG.warn() << error; + + MAKE_ERROR("Не удалось удовлетворить зависимости модов"); + } + + LOG.info() << "Построение этапов загрузки модов"; + + std::variant> buildLoadChainResult = buildLoadChain({}, {}, std::get<0>(resolveDependsResult)); + assert(buildLoadChainResult.index() == 0); + + ModLoadTree mlt = std::get<0>(buildLoadChainResult); + assert(mlt.UnloadChain.empty()); + + LOG.info() << "Загрузка инстансов модов"; + + + // Тест луа + + + // sol::function func = res.call<>(); + // int type = func(); + + // LOG.debug() << type; + + LoadedMods = mlt.LoadChain; + + LuaMainState.open_libraries(); + + for(const ModInfo& info : mlt.LoadChain) { + LOG.info() << info.Id; + sol::load_result res = LuaMainState.load_file(info.Path / "init.lua"); + ModInstances.emplace_back(info.Id, res.call()); + } + + LOG.info() << "Пре Инициализация"; + + for(auto& [id, core] : ModInstances) { + sol::function func = core.get_or("preInit", {}); + func(); + } + + LOG.info() << "Инициализация"; + + for(auto& [id, core] : ModInstances) { + sol::function func = core.get_or("init", {}); + func(); + } + + LOG.info() << "Пост Инициализация"; + + for(auto& [id, core] : ModInstances) { + sol::function func = core.get_or("postInit", {}); + func(); + } + + // Загрузить миры с существующими профилями + Expanse.Worlds[0] = std::make_unique(0); + + BackingChunkPressure.Threads.resize(4); + BackingChunkPressure.Worlds = &Expanse.Worlds; + for(size_t iter = 0; iter < BackingChunkPressure.Threads.size(); iter++) { + BackingChunkPressure.Threads[iter] = std::thread(&BackingChunkPressure_t::run, &BackingChunkPressure, iter); + } + + BackingNoiseGenerator.Threads.resize(4); + for(size_t iter = 0; iter < BackingNoiseGenerator.Threads.size(); iter++) { + BackingNoiseGenerator.Threads[iter] = std::thread(&BackingNoiseGenerator_t::run, &BackingNoiseGenerator, iter); + } + + BackingAsyncLua.Threads.resize(4); + for(size_t iter = 0; iter < BackingAsyncLua.Threads.size(); iter++) { + BackingAsyncLua.Threads[iter] = std::thread(&BackingAsyncLua_t::run, &BackingAsyncLua, iter); + } + RunThread = std::thread(&GameServer::prerun, this); } diff --git a/Src/Server/GameServer.hpp b/Src/Server/GameServer.hpp index bae30e0..c830872 100644 --- a/Src/Server/GameServer.hpp +++ b/Src/Server/GameServer.hpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include "ContentEventController.hpp" @@ -27,6 +29,22 @@ namespace LV::Server { +struct ModRequest { + std::string Id; + std::array MinVersion, MaxVersion; +}; + +struct ModInfo { + std::string Id, Name, Description, Author; + std::array Version; + std::vector Dependencies, Optional; + float LoadPriority; + fs::path Path; + bool HasLiveReload; + + std::string dump() const; +}; + namespace fs = std::filesystem; class GameServer : public AsyncObject { @@ -263,6 +281,10 @@ class GameServer : public AsyncObject { void run(int id); } BackingAsyncLua; + sol::state LuaMainState; + std::vector LoadedMods; + std::vector> ModInstances; + public: GameServer(asio::io_context &ioc, fs::path worldPath); virtual ~GameServer(); @@ -296,15 +318,6 @@ private: void prerun(); void run(); - struct ModInfo { - std::string Id, Title, Description; - fs::path Path; - - std::vector Dependencies, OptionalDependencies; - }; - - std::vector readModDataPath(const fs::path& modsDir); - /* Подключение/отключение игроков */ diff --git a/Src/Server/SaveBackends/Filesystem.cpp b/Src/Server/SaveBackends/Filesystem.cpp index 749e795..6076428 100644 --- a/Src/Server/SaveBackends/Filesystem.cpp +++ b/Src/Server/SaveBackends/Filesystem.cpp @@ -22,7 +22,7 @@ class WSB_Filesystem : public IWorldSaveBackend { public: WSB_Filesystem(const boost::json::object &data) { - Dir = (std::string) data.at("Path").as_string(); + Dir = (std::string) data.at("path").as_string(); } virtual ~WSB_Filesystem() { @@ -111,7 +111,7 @@ class PSB_Filesystem : public IPlayerSaveBackend { public: PSB_Filesystem(const boost::json::object &data) { - Dir = (std::string) data.at("Path").as_string(); + Dir = (std::string) data.at("path").as_string(); } virtual ~PSB_Filesystem() { @@ -146,7 +146,7 @@ class ASB_Filesystem : public IAuthSaveBackend { public: ASB_Filesystem(const boost::json::object &data) { - Dir = (std::string) data.at("Path").as_string(); + Dir = (std::string) data.at("path").as_string(); } virtual ~ASB_Filesystem() { @@ -174,6 +174,7 @@ public: data.Id = jobj.at("Id").as_uint64(); data.PasswordHash = jobj.at("PasswordHash").as_string(); + co_return true; } virtual coro<> save(std::string playerId, const SB_Auth& data) override { @@ -185,6 +186,7 @@ public: fs::create_directories(getPath(playerId).parent_path()); std::ofstream fd(getPath(playerId)); fd << js::serialize(jobj); + co_return; } virtual coro<> remove(std::string username) override { @@ -198,7 +200,7 @@ class MSSB_Filesystem : public IModStorageSaveBackend { public: MSSB_Filesystem(const boost::json::object &data) { - Dir = (std::string) data.at("Path").as_string(); + Dir = (std::string) data.at("path").as_string(); } virtual ~MSSB_Filesystem() {