#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "Client/AssetsCacheManager.hpp" #include "Client/AssetsHeaderCodec.hpp" #include "Common/Abstract.hpp" #include "TOSLib.hpp" namespace LV::Client { namespace fs = std::filesystem; class AssetsManager { public: using Ptr = std::shared_ptr; using AssetType = EnumAssets; using AssetId = ResourceId; // Ключ запроса ресурса (идентификация + хеш для поиска источника). struct ResourceKey { // Хеш ресурса, используемый для поиска в источниках и кэше. Hash_t Hash{}; // Тип ресурса (модель, текстура и т.д.). AssetType Type{}; // Домен ресурса. std::string Domain; // Ключ ресурса внутри домена. std::string Key; // Идентификатор ресурса на стороне клиента/локальный. AssetId Id = 0; }; // Информация о биндинге серверного ресурса на локальный id. struct BindInfo { // Тип ресурса. AssetType Type{}; // Локальный идентификатор. AssetId LocalId = 0; // Домен ресурса. std::string Domain; // Ключ ресурса. std::string Key; // Хеш ресурса. Hash_t Hash{}; // Бинарный заголовок с зависимостями. std::vector Header; }; // Результат биндинга ресурса сервера. struct BindResult { // Итоговый локальный идентификатор. AssetId LocalId = 0; // Признак изменения бинда (хеш/заголовок). bool Changed = false; // Признак новой привязки. bool NewBinding = false; // Идентификатор, от которого произошёл ребинд (если был). std::optional ReboundFrom; }; // Регистрация набора ресурспаков. struct PackRegister { // Пути до паков (директории/архивы). std::vector Packs; }; // Ресурс, собранный из пака. struct PackResource { // Тип ресурса. AssetType Type{}; // Локальный идентификатор. AssetId LocalId = 0; // Домен ресурса. std::string Domain; // Ключ ресурса. std::string Key; // Тело ресурса. Resource Res; // Хеш ресурса. Hash_t Hash{}; // Заголовок ресурса (например, зависимости). std::u8string Header; }; // Результат пересканирования паков. struct PackReloadResult { // Добавленные/изменённые ресурсы по типам. std::array, static_cast(AssetType::MAX_ENUM)> ChangeOrAdd; // Потерянные ресурсы по типам. std::array, static_cast(AssetType::MAX_ENUM)> Lost; }; using ParsedHeader = AssetsHeaderCodec::ParsedHeader; // Фабрика с настройкой лимитов кэша. static Ptr Create(asio::io_context& ioc, const fs::path& cachePath, size_t maxCacheDirectorySize = 8 * 1024 * 1024 * 1024ULL, size_t maxLifeTime = 7 * 24 * 60 * 60) { return Ptr(new AssetsManager(ioc, cachePath, maxCacheDirectorySize, maxLifeTime)); } // Пересканировать ресурспаки и вернуть изменившиеся/утраченные ресурсы. PackReloadResult reloadPacks(const PackRegister& reg); // Связать серверный ресурс с локальным id и записать метаданные. BindResult bindServerResource(AssetType type, AssetId serverId, std::string domain, std::string key, const Hash_t& hash, std::vector header); // Отвязать серверный id и вернуть актуальный локальный id (если был). std::optional unbindServerResource(AssetType type, AssetId serverId); // Сбросить все серверные бинды. void clearServerBindings(); // Получить данные бинда по локальному id. const BindInfo* getBind(AssetType type, AssetId localId) const; // Перебиндить хедер, заменив id зависимостей. std::vector rebindHeader(AssetType type, const std::vector& header, bool serverIds = true); // Распарсить хедер ресурса. static std::optional parseHeader(AssetType type, const std::vector& header); // Протолкнуть новые ресурсы в память и кэш. void pushResources(std::vector resources); // Поставить запросы чтения ресурсов. void pushReads(std::vector reads); // Получить готовые результаты чтения. std::vector>> pullReads(); // Продвинуть асинхронные источники (кэш). void tickSources(); // Получить или создать локальный id по домену/ключу. AssetId getOrCreateLocalId(AssetType type, std::string_view domain, std::string_view key); // Получить локальный id по серверному id (если есть). std::optional getLocalIdFromServer(AssetType type, AssetId serverId) const; private: // Связка домен/ключ для локального id. struct DomainKey { // Домен ресурса. std::string Domain; // Ключ ресурса. std::string Key; // Признак валидности записи. bool Known = false; }; using IdTable = std::unordered_map< std::string, std::unordered_map, detail::TSVHash, detail::TSVEq>; using PackTable = std::unordered_map< std::string, std::unordered_map, detail::TSVHash, detail::TSVEq>; struct PerType { // Таблица домен/ключ -> локальный id. IdTable DKToLocal; // Таблица локальный id -> домен/ключ. std::vector LocalToDK; // Union-Find родительские ссылки для ребиндов. std::vector LocalParent; // Таблица серверный id -> локальный id. std::vector ServerToLocal; // Бинды с сервером по локальному id. std::vector> BindInfos; // Ресурсы, собранные из паков. PackTable PackResources; // Следующий локальный id. AssetId NextLocalId = 1; }; enum class SourceStatus { Hit, Miss, Pending }; struct SourceResult { // Статус ответа источника. SourceStatus Status = SourceStatus::Miss; // Значение ресурса, если найден. std::optional Value; // Индекс источника. size_t SourceIndex = 0; }; struct SourceReady { // Хеш готового ресурса. Hash_t Hash{}; // Значение ресурса, если найден. std::optional Value; // Индекс источника. size_t SourceIndex = 0; }; class IResourceSource { public: virtual ~IResourceSource() = default; // Попытка получить ресурс синхронно. virtual SourceResult tryGet(const ResourceKey& key) = 0; // Забрать готовые результаты асинхронных запросов. virtual void collectReady(std::vector& out) = 0; // Признак асинхронности источника. virtual bool isAsync() const = 0; // Запустить асинхронные запросы по хешам. virtual void startPending(std::vector hashes) = 0; }; struct SourceEntry { // Экземпляр источника. std::unique_ptr Source; // Поколение для инвалидирования кэша. size_t Generation = 0; }; struct SourceCacheEntry { // Индекс источника, где был найден хеш. size_t SourceIndex = 0; // Поколение источника на момент кэширования. size_t Generation = 0; }; // Конструктор с зависимостью от io_context и кэш-пути. AssetsManager(asio::io_context& ioc, const fs::path& cachePath, size_t maxCacheDirectorySize, size_t maxLifeTime); // Инициализация списка источников. void initSources(); // Забрать готовые результаты из источников. void collectReadyFromSources(); // Запросить ресурс в источниках, с учётом кэша. SourceResult querySources(const ResourceKey& key); // Запомнить успешный источник для хеша. void registerSourceHit(const Hash_t& hash, size_t sourceIndex); // Инвалидировать кэш по конкретному источнику. void invalidateSourceCache(size_t sourceIndex); // Инвалидировать весь кэш источников. void invalidateAllSourceCache(); // Выделить новый локальный id. AssetId allocateLocalId(AssetType type); // Получить корневой локальный id с компрессией пути. AssetId resolveLocalIdMutable(AssetType type, AssetId localId); // Получить корневой локальный id без мутаций. AssetId resolveLocalId(AssetType type, AssetId localId) const; // Объединить два локальных id в один. void unionLocalIds(AssetType type, AssetId fromId, AssetId toId, std::optional* reboundFrom); // Найти ресурс в паке по домену/ключу. std::optional findPackResource(AssetType type, std::string_view domain, std::string_view key) const; // Логгер подсистемы. Logger LOG = "Client>AssetsManager"; // Менеджер файлового кэша. AssetsCacheManager::Ptr Cache; // Таблицы данных по каждому типу ресурсов. std::array(AssetType::MAX_ENUM)> Types; // Список источников ресурсов. std::vector Sources; // Кэш попаданий по хешу. std::unordered_map SourceCacheByHash; // Индекс источника паков. size_t PackSourceIndex = 0; // Индекс памяти (RAM) как источника. size_t MemorySourceIndex = 0; // Индекс файлового кэша. size_t CacheSourceIndex = 0; // Ресурсы в памяти по хешу. std::unordered_map MemoryResourcesByHash; // Ожидающие запросы, сгруппированные по хешу. std::unordered_map> PendingReadsByHash; // Готовые ответы на чтение. std::vector>> ReadyReads; }; } // namespace LV::Client