#pragma once #include "Common/Abstract.hpp" #include "TOSLib.hpp" #include "Common/Net.hpp" #include "assets.hpp" #include "sha2.hpp" #include #include #include #include #include #include #include namespace LV::Server { namespace fs = std::filesystem; struct Resource { private: struct InlineMMap { boost::interprocess::file_mapping MMap; boost::interprocess::mapped_region Region; Hash_t Hash; InlineMMap(fs::path path) : MMap(path.c_str(), boost::interprocess::read_only), Region(MMap, boost::interprocess::read_only) { Hash = sha2::sha256((const uint8_t*) Region.get_address(), Region.get_size()); } const std::byte* data() const { return (const std::byte*) Region.get_address(); } size_t size() const { return Region.get_size(); } }; struct InlinePtr { std::vector Data; Hash_t Hash; InlinePtr(const uint8_t* data, size_t size) { Data.resize(size); std::copy(data, data+size, Data.data()); Hash = sha2::sha256(data, size); } const std::byte* data() const { return (const std::byte*) Data.data(); } size_t size() const { return Data.size(); } }; std::shared_ptr> In; public: Resource(fs::path path) : In(std::make_shared>(InlineMMap(path))) {} Resource(const uint8_t* data, size_t size) : In(std::make_shared>(InlinePtr(data, size))) {} Resource(const Resource&) = default; Resource(Resource&&) = default; Resource& operator=(const Resource&) = default; Resource& operator=(Resource&&) = default; bool operator<=>(const Resource&) const = default; const std::byte* data() const { return std::visit([](auto& obj){ return obj.data(); }, *In); } size_t size() const { return std::visit([](auto& obj){ return obj.size(); }, *In); } Hash_t hash() const { return std::visit([](auto& obj){ return obj.Hash; }, *In); } }; /* Используется для расчёта коллизии, если это необходимо. glTF конвертируется в кубы */ struct PreparedModelCollision { struct Cuboid { glm::vec3 From, To; uint8_t Faces; std::vector Transformations; }; std::vector Cuboids; std::vector SubModels; PreparedModelCollision(const PreparedModel& model); PreparedModelCollision(const std::string& domain, const js::object& glTF); PreparedModelCollision(const std::string& domain, Resource glb); PreparedModelCollision() = default; PreparedModelCollision(const PreparedModelCollision&) = default; PreparedModelCollision(PreparedModelCollision&&) = default; PreparedModelCollision& operator=(const PreparedModelCollision&) = default; PreparedModelCollision& operator=(PreparedModelCollision&&) = default; }; /* Работает с ресурсами из папок assets. Использует папку server_cache/assets для хранения преобразованных ресурсов */ class AssetsManager { public: struct ResourceChangeObj { // Потерянные ресурсы std::unordered_map> Lost[(int) EnumAssets::MAX_ENUM]; // Домен и ключ ресурса std::unordered_map>> NewOrChange[(int) EnumAssets::MAX_ENUM]; std::unordered_map>> Nodestates; std::unordered_map>> Models; }; private: // Данные об отслеживаемых файлах struct DataEntry { // Время последнего изменения файла fs::file_time_type FileChangeTime; Resource Res; std::string Domain, Key; }; template struct TableEntry { static constexpr size_t ChunkSize = 4096; bool IsFull = false; std::bitset Empty; std::array, ChunkSize> Entries; TableEntry() { Empty.set(); } }; struct Local { // Связь ресурсов по идентификаторам std::vector>> Table[(int) EnumAssets::MAX_ENUM]; // Распаршенные ресурсы, для использования сервером std::vector>> Table_NodeState; std::vector>> Table_Model; // Связь домены -> {ключ -> идентификатор} std::unordered_map> KeyToId[(int) EnumAssets::MAX_ENUM]; std::tuple&> nextId(EnumAssets type); }; TOS::SpinlockObject LocalObj; /* Загрузка ресурса с файла. При необходимости приводится к внутреннему формату и сохраняется в кеше */ void loadResourceFromFile (EnumAssets type, ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const; void loadResourceFromLua (EnumAssets type, ResourceChangeObj& out, const std::string& domain, const std::string& key, const sol::table& profile) const; void loadResourceFromFile_Nodestate (ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const; void loadResourceFromFile_Particle (ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const; void loadResourceFromFile_Animation (ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const; void loadResourceFromFile_Model (ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const; void loadResourceFromFile_Texture (ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const; void loadResourceFromFile_Sound (ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const; void loadResourceFromFile_Font (ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const; void loadResourceFromLua_Nodestate (ResourceChangeObj& out, const std::string& domain, const std::string& key, const sol::table& profile) const; void loadResourceFromLua_Particle (ResourceChangeObj& out, const std::string& domain, const std::string& key, const sol::table& profile) const; void loadResourceFromLua_Animation (ResourceChangeObj& out, const std::string& domain, const std::string& key, const sol::table& profile) const; void loadResourceFromLua_Model (ResourceChangeObj& out, const std::string& domain, const std::string& key, const sol::table& profile) const; void loadResourceFromLua_Texture (ResourceChangeObj& out, const std::string& domain, const std::string& key, const sol::table& profile) const; void loadResourceFromLua_Sound (ResourceChangeObj& out, const std::string& domain, const std::string& key, const sol::table& profile) const; void loadResourceFromLua_Font (ResourceChangeObj& out, const std::string& domain, const std::string& key, const sol::table& profile) const; public: AssetsManager(asio::io_context& ioc); ~AssetsManager(); /* Перепроверка изменений ресурсов по дате изменения, пересчёт хешей. Обнаруженные изменения должны быть отправлены всем клиентам. Ресурсы будут обработаны в подходящий формат и сохранены в кеше. Одновременно может выполнятся только одна такая функция Используется в GameServer */ struct AssetsRegister { /* Пути до активных папок assets, соответствую порядку загруженным модам. От последнего мода к первому. Тот файл, что был загружен раньше и будет использоваться */ std::vector Assets; /* У этих ресурсов приоритет выше, если их удастся получить, то использоваться будут именно они Domain -> {key + data} */ std::unordered_map> Custom[(int) EnumAssets::MAX_ENUM]; }; ResourceChangeObj recheckResources(const AssetsRegister&); /* Применяет расчитанные изменения. Раздаёт идентификаторы ресурсам и записывает их в таблицу */ struct Out_applyResourceChange { std::vector Lost[(int) EnumAssets::MAX_ENUM]; std::vector> NewOrChange[(int) EnumAssets::MAX_ENUM]; }; Out_applyResourceChange applyResourceChange(const ResourceChangeObj& orr); /* Выдаёт идентификатор ресурса, даже если он не существует или был удалён. resource должен содержать домен и путь */ ResourceId getId(EnumAssets type, const std::string& domain, const std::string& key) { auto lock = LocalObj.lock(); auto& keyToId = lock->KeyToId[(int) type]; if(auto iterKTI = keyToId.find(domain); iterKTI != keyToId.end()) { if(auto iterKey = iterKTI->second.find(key); iterKey != iterKTI->second.end()) { return iterKey->second; } } auto [id, entry] = lock->nextId(type); keyToId[domain][key] = id; return id; } std::optional> getResource(EnumAssets type, ResourceId id) { auto lock = LocalObj.lock(); assert(id < lock->Table[(int) type].size()*TableEntry::ChunkSize); auto& value = lock->Table[(int) type][id / TableEntry::ChunkSize]->Entries[id % TableEntry::ChunkSize]; if(value) return {{value->Res, value->Domain, value->Key}}; else return std::nullopt; } }; }