From 24c133b2f7a644f97a90b7c46318cfad751354e3 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Sat, 2 Aug 2025 18:28:09 +0600 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B0=D1=80=D1=81=D0=B5=D1=80=20=D1=84?= =?UTF-8?q?=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D0=BC=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/Server/GameServer.cpp | 319 +++++++++++++++++++++++++++++--------- 1 file changed, 250 insertions(+), 69 deletions(-) diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index bc6cab6..991cce7 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,8 +23,10 @@ #include "Server/SaveBackend.hpp" #include "Server/World.hpp" #include "TOSLib.hpp" +#include "boost/json/array.hpp" #include "boost/json/object.hpp" #include "boost/json/parse_into.hpp" +#include "boost/json/serialize.hpp" #include "glm/gtc/noise.hpp" #include @@ -49,17 +52,58 @@ int luaAtException(lua_State* L, sol::optional exc, std:: namespace LV::Server { -struct ModeDepend { +struct ModDepend { std::string Id; uint32_t VersionMin[4], VersionMax[4]; }; struct ModInfo { - std::string Id, Name, Description; + std::string Id, Name, Description, Author; uint32_t Version[4]; - std::vector Dependency, Optional; + std::vector Dependencies, Optional; float LoadPriority; fs::path Path; + + 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]}; + + { + 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; + } + + { + 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); + } }; struct ModPreloadInfo { @@ -71,7 +115,6 @@ ModPreloadInfo preLoadMods(const std::vector& dirs) { std::vector mods; std::vector errors; - for(const fs::path& p : dirs) { try { if(!fs::is_directory(p)) @@ -109,7 +152,96 @@ ModPreloadInfo preLoadMods(const std::vector& dirs) { try { js::object obj = js::parse(data).as_object(); + + ModInfo info; + info.Id = obj.at("id").as_string(); + info.Name = obj.contains("title") ? obj["title"].as_string() : ""; + info.Description = obj.contains("description") ? obj["description"].as_string() : ""; + info.Author = obj.contains("author") ? obj["author"].as_string() : ""; + + { + js::array version = obj.at("version").as_array(); + for(int iter = 0; iter < 4; iter++) + info.Version[iter] = version.at(iter).as_int64(); + } + + if(obj.contains("depends")) { + js::array arr = obj["depends"].as_array(); + for(auto& iter : arr) { + ModDepend 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)); + } else if(iter.is_object()) { + js::object d = iter.as_object(); + depend.Id = d.at("id").as_string(); + + 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(); + } + } else + std::fill(depend.VersionMin, depend.VersionMin+4, 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(); + } + } else + std::fill(depend.VersionMax, depend.VersionMax+4, uint32_t(-1)); + } + + info.Dependencies.push_back(depend); + } + } + + if(obj.contains("optional_depends")) { + js::array arr = obj["optional_depends"].as_array(); + for(auto& iter : arr) { + ModDepend 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)); + } else if(iter.is_object()) { + js::object d = iter.as_object(); + depend.Id = d.at("id").as_string(); + + 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(); + } + } else + std::fill(depend.VersionMin, depend.VersionMin+4, 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(); + } + } else + std::fill(depend.VersionMax, depend.VersionMax+4, uint32_t(-1)); + } + + info.Optional.push_back(depend); + } + } + + if(obj.contains("load_priority")) { + info.LoadPriority = obj.at("load_priority").as_double(); + } else { + info.LoadPriority = 0.5; + } + + info.Path = modPath; + mods.push_back(info); } catch (const std::exception& exc) { errors.push_back("Не удалось распарсить mod.json '" + modPath.string() + "': " + exc.what()); @@ -124,6 +256,107 @@ ModPreloadInfo preLoadMods(const std::vector& dirs) { errors.push_back("Неопределённая ошибка при работе с директорией: " + p.string()); } } + + return {mods, errors}; +} + +struct ModLoadTree { + std::vector UnloadChain, LoadChain; +}; + +std::variant> buildLoadChain(const std::vector& loaded, const std::vector& toUnload, const std::vector& toLoad) { + // Проверить обязательные зависимости в конечном состоянии + { + std::vector errors; + std::vector endState; + for(const ModInfo& lmod : loaded) { + bool contains = false; + + for(const ModInfo& umod : toUnload) { + if(lmod.Id == umod.Id) { + contains = true; + break; + } + } + + if(!contains) + endState.push_back(lmod); + } + + endState.insert(endState.end(), toLoad.begin(), toLoad.end()); + + for(const ModInfo& mmod : endState) { + for(const ModDepend& depend : mmod.Dependencies) { + std::vector lerrors; + bool contains = false; + + for(const ModInfo& mmod2 : endState) { + 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] + ) { + 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] + ) { + goto versionMismatch; + } + } + + contains = true; + continue; + + versionMismatch: + std::stringstream ss; + ss << depend.VersionMin[0] << '.'; + ss << depend.VersionMin[1] << '.'; + ss << depend.VersionMin[2] << '.'; + ss << depend.VersionMin[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])) << '.'; + verMin += " -> " + ss.str(); + ss.str(""); + } + + ss << mmod2.Version[0] << '.'; + ss << mmod2.Version[1] << '.'; + ss << mmod2.Version[2] << '.'; + ss << mmod2.Version[3]; + std::string ver = ss.str(); + ss.str(""); + + lerrors.push_back("Мод " + mmod.Name+"("+mmod.Id+") требует "+mmod2.Name+"("+mmod2.Id+", "+verMin+"), найдена версия " + ver); + } + + if(!contains) + errors.insert(errors.end(), lerrors.begin(), lerrors.end()); + } + } + + if(!errors.empty()) + return errors; + } + + std::vector unloadChain, loadChain; + + + + return ModLoadTree{unloadChain, loadChain}; } GameServer::GameServer(asio::io_context &ioc, fs::path worldPath) @@ -151,22 +384,24 @@ GameServer::GameServer(asio::io_context &ioc, fs::path worldPath) // Тест луа - sol::state lua; - // lua.open_libraries(); - // lua.set_panic(luaPanic); - lua.set_exception_handler(luaAtException); + // 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(); - // lua.script(R"( + // 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; + // 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() { @@ -1049,60 +1284,6 @@ void GameServer::run() { LOG.info() << "Сервер завершил работу"; } -std::vector GameServer::readModDataPath(const fs::path& modsDir) { - if(!fs::exists(modsDir)) - return {}; - - std::vector infos; - - try { - fs::directory_iterator begin(modsDir), end; - for(; begin != end; begin++) { - if(!begin->is_directory()) - continue; - - fs::path mod_conf = begin->path() / "mod.json"; - if(!fs::exists(mod_conf)) { - LOG.debug() << "Директория в папке с модами не содержит файл mod.json: " << begin->path().filename(); - continue; - } - - try { - std::ifstream fd(mod_conf); - js::object obj = js::parse(fd).as_object(); - - GameServer::ModInfo info; - - info.Id = obj.at("Id").as_string(); - info.Title = obj.contains("Title") ? obj["Title"].as_string() : ""; - info.Description = obj.contains("Description") ? obj["Description"].as_string() : ""; - - if(obj.contains("Dependencies")) { - js::array arr = obj["Dependencies"].as_array(); - for(auto& iter : arr) { - info.Dependencies.push_back((std::string) iter.as_string()); - } - } - - if(obj.contains("OptionalDependencies")) { - js::array arr = obj["OptionalDependencies"].as_array(); - for(auto& iter : arr) { - info.OptionalDependencies.push_back((std::string) iter.as_string()); - } - } - - } catch(const std::exception &exc) { - LOG.warn() << "Не удалось прочитать " << mod_conf.string(); - } - } - } catch(const std::exception &exc) { - LOG.warn() << "Не удалось прочитать моды из директории " << modsDir.string() << "\n" << exc.what(); - } - - - return infos; -} - void GameServer::stepConnections() { // Подключить новых игроков if(!External.NewConnectedPlayers.no_lock_readable().empty()) {