codex-5.2: перестройка Client/AssetsManager

This commit is contained in:
2026-01-05 01:50:17 +06:00
parent 8ce820569a
commit 6c7a6df8f6
6 changed files with 1015 additions and 596 deletions

View File

@@ -12,6 +12,7 @@
#include <utility>
#include <vector>
#include "Client/AssetsCacheManager.hpp"
#include "Client/AssetsHeaderCodec.hpp"
#include "Common/Abstract.hpp"
#include "TOSLib.hpp"
@@ -25,88 +26,131 @@ public:
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<uint8_t> Header;
};
// Результат биндинга ресурса сервера.
struct BindResult {
// Итоговый локальный идентификатор.
AssetId LocalId = 0;
// Признак изменения бинда (хеш/заголовок).
bool Changed = false;
// Признак новой привязки.
bool NewBinding = false;
// Идентификатор, от которого произошёл ребинд (если был).
std::optional<AssetId> ReboundFrom;
};
// Регистрация набора ресурспаков.
struct PackRegister {
// Пути до паков (директории/архивы).
std::vector<fs::path> 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<std::vector<AssetId>, static_cast<size_t>(AssetType::MAX_ENUM)> ChangeOrAdd;
// Потерянные ресурсы по типам.
std::array<std::vector<AssetId>, static_cast<size_t>(AssetType::MAX_ENUM)> Lost;
};
struct ParsedHeader {
AssetType Type{};
std::vector<AssetId> ModelDeps;
std::vector<AssetId> TextureDeps;
std::vector<std::vector<uint8_t>> TexturePipelines;
};
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<uint8_t> header);
// Отвязать серверный id и вернуть актуальный локальный id (если был).
std::optional<AssetId> unbindServerResource(AssetType type, AssetId serverId);
// Сбросить все серверные бинды.
void clearServerBindings();
// Получить данные бинда по локальному id.
const BindInfo* getBind(AssetType type, AssetId localId) const;
// Перебиндить хедер, заменив id зависимостей.
std::vector<uint8_t> rebindHeader(AssetType type, const std::vector<uint8_t>& header, bool serverIds = true);
// Распарсить хедер ресурса.
static std::optional<ParsedHeader> parseHeader(AssetType type, const std::vector<uint8_t>& header);
void pushResources(std::vector<Resource> resources) {
Cache->pushResources(std::move(resources));
}
// Протолкнуть новые ресурсы в память и кэш.
void pushResources(std::vector<Resource> resources);
// Поставить запросы чтения ресурсов.
void pushReads(std::vector<ResourceKey> reads);
// Получить готовые результаты чтения.
std::vector<std::pair<ResourceKey, std::optional<Resource>>> pullReads();
// Продвинуть асинхронные источники (кэш).
void tickSources();
// Получить или создать локальный id по домену/ключу.
AssetId getOrCreateLocalId(AssetType type, std::string_view domain, std::string_view key);
// Получить локальный id по серверному id (если есть).
std::optional<AssetId> getLocalIdFromServer(AssetType type, AssetId serverId) const;
private:
// Связка домен/ключ для локального id.
struct DomainKey {
// Домен ресурса.
std::string Domain;
// Ключ ресурса.
std::string Key;
// Признак валидности записи.
bool Known = false;
};
@@ -122,28 +166,127 @@ private:
detail::TSVHash,
detail::TSVEq>;
struct PerType {
// Таблица домен/ключ -> локальный id.
IdTable DKToLocal;
// Таблица локальный id -> домен/ключ.
std::vector<DomainKey> LocalToDK;
// Union-Find родительские ссылки для ребиндов.
std::vector<AssetId> LocalParent;
// Таблица серверный id -> локальный id.
std::vector<AssetId> ServerToLocal;
// Бинды с сервером по локальному id.
std::vector<std::optional<BindInfo>> BindInfos;
// Ресурсы, собранные из паков.
PackTable PackResources;
// Следующий локальный id.
AssetId NextLocalId = 1;
};
enum class SourceStatus {
Hit,
Miss,
Pending
};
struct SourceResult {
// Статус ответа источника.
SourceStatus Status = SourceStatus::Miss;
// Значение ресурса, если найден.
std::optional<Resource> Value;
// Индекс источника.
size_t SourceIndex = 0;
};
struct SourceReady {
// Хеш готового ресурса.
Hash_t Hash{};
// Значение ресурса, если найден.
std::optional<Resource> Value;
// Индекс источника.
size_t SourceIndex = 0;
};
class IResourceSource {
public:
virtual ~IResourceSource() = default;
// Попытка получить ресурс синхронно.
virtual SourceResult tryGet(const ResourceKey& key) = 0;
// Забрать готовые результаты асинхронных запросов.
virtual void collectReady(std::vector<SourceReady>& out) = 0;
// Признак асинхронности источника.
virtual bool isAsync() const = 0;
// Запустить асинхронные запросы по хешам.
virtual void startPending(std::vector<Hash_t> hashes) = 0;
};
struct SourceEntry {
// Экземпляр источника.
std::unique_ptr<IResourceSource> 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<AssetId>* reboundFrom);
// Найти ресурс в паке по домену/ключу.
std::optional<PackResource> findPackResource(AssetType type, std::string_view domain, std::string_view key) const;
// Логгер подсистемы.
Logger LOG = "Client>AssetsManager";
// Менеджер файлового кэша.
AssetsCacheManager::Ptr Cache;
std::array<IdTable, static_cast<size_t>(AssetType::MAX_ENUM)> DKToLocal;
std::array<std::vector<DomainKey>, static_cast<size_t>(AssetType::MAX_ENUM)> LocalToDK;
std::array<std::vector<AssetId>, static_cast<size_t>(AssetType::MAX_ENUM)> LocalParent;
std::array<std::vector<AssetId>, static_cast<size_t>(AssetType::MAX_ENUM)> ServerToLocal;
std::array<std::vector<std::optional<BindInfo>>, static_cast<size_t>(AssetType::MAX_ENUM)> BindInfos;
std::array<PackTable, static_cast<size_t>(AssetType::MAX_ENUM)> PackResources;
std::array<AssetId, static_cast<size_t>(AssetType::MAX_ENUM)> NextLocalId{};
// Таблицы данных по каждому типу ресурсов.
std::array<PerType, static_cast<size_t>(AssetType::MAX_ENUM)> Types;
// Список источников ресурсов.
std::vector<SourceEntry> Sources;
// Кэш попаданий по хешу.
std::unordered_map<Hash_t, SourceCacheEntry> SourceCacheByHash;
// Индекс источника паков.
size_t PackSourceIndex = 0;
// Индекс памяти (RAM) как источника.
size_t MemorySourceIndex = 0;
// Индекс файлового кэша.
size_t CacheSourceIndex = 0;
// Ресурсы в памяти по хешу.
std::unordered_map<Hash_t, Resource> MemoryResourcesByHash;
// Ожидающие запросы, сгруппированные по хешу.
std::unordered_map<Hash_t, std::vector<ResourceKey>> PendingReadsByHash;
// Готовые ответы на чтение.
std::vector<std::pair<ResourceKey, std::optional<Resource>>> ReadyReads;
};