From d47a5cc09091c19d3c0adc07dc360f41b60d3fc0 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Wed, 31 Dec 2025 16:49:01 +0600 Subject: [PATCH] =?UTF-8?q?codex5.2:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D0=BB=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B5=20=D1=81=20=D1=80?= =?UTF-8?q?=D0=B5=D1=81=D1=83=D1=80=D1=81=D0=B0=D0=BC=D0=B8=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=81=D1=82=D0=BE=D1=80=D0=BE=D0=BD=D0=B5=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B8=D0=B5=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/Client/AssetsManager.cpp | 17 ++++++----- Src/Client/ServerSession.cpp | 6 +++- docs/resources.md | 57 ++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 docs/resources.md diff --git a/Src/Client/AssetsManager.cpp b/Src/Client/AssetsManager.cpp index 22db1f0..fbb0765 100644 --- a/Src/Client/AssetsManager.cpp +++ b/Src/Client/AssetsManager.cpp @@ -310,14 +310,14 @@ void AssetsManager::readWriteThread(AsyncUseControl::Lock lock) { sqlite3_reset(STMT_DISK_CONTAINS); if(finded) { - sqlite3_bind_blob(STMT_DISK_CONTAINS, 1, (const void*) rk.Hash.data(), 32, SQLITE_STATIC); - sqlite3_bind_int(STMT_DISK_CONTAINS, 2, time(nullptr)); - if(sqlite3_step(STMT_DISK_CONTAINS) != SQLITE_DONE) { - sqlite3_reset(STMT_DISK_CONTAINS); - MAKE_ERROR("Не удалось выполнить подготовленный запрос STMT_DISK_CONTAINS: " << sqlite3_errmsg(DB)); + sqlite3_bind_int(STMT_DISK_UPDATE_TIME, 1, time(nullptr)); + sqlite3_bind_blob(STMT_DISK_UPDATE_TIME, 2, (const void*) rk.Hash.data(), 32, SQLITE_STATIC); + if(sqlite3_step(STMT_DISK_UPDATE_TIME) != SQLITE_DONE) { + sqlite3_reset(STMT_DISK_UPDATE_TIME); + MAKE_ERROR("Не удалось выполнить подготовленный запрос STMT_DISK_UPDATE_TIME: " << sqlite3_errmsg(DB)); } - - sqlite3_reset(STMT_DISK_CONTAINS); + + sqlite3_reset(STMT_DISK_UPDATE_TIME); } } @@ -372,6 +372,7 @@ void AssetsManager::readWriteThread(AsyncUseControl::Lock lock) { } fs::path end = PathFiles / hashKey.substr(0, 2) / hashKey.substr(2); + fs::create_directories(end.parent_path()); std::ofstream fd(end, std::ios::binary); fd.write((const char*) res.data(), res.size()); @@ -412,4 +413,4 @@ std::string AssetsManager::hashToString(const Hash_t& hash) { return ss.str(); } -} \ No newline at end of file +} diff --git a/Src/Client/ServerSession.cpp b/Src/Client/ServerSession.cpp index 6e46e25..f77654d 100644 --- a/Src/Client/ServerSession.cpp +++ b/Src/Client/ServerSession.cpp @@ -1100,6 +1100,10 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) { ); AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash)); + + auto iter = std::lower_bound(AsyncContext.AlreadyLoading.begin(), AsyncContext.AlreadyLoading.end(), hash); + if(iter != AsyncContext.AlreadyLoading.end() && *iter == hash) + AsyncContext.AlreadyLoading.erase(iter); } } catch(const std::exception& exc) { std::string err = exc.what(); @@ -1246,4 +1250,4 @@ coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) { } } -} \ No newline at end of file +} diff --git a/docs/resources.md b/docs/resources.md new file mode 100644 index 0000000..81414d5 --- /dev/null +++ b/docs/resources.md @@ -0,0 +1,57 @@ +# Resources and asset transfer + +This document describes how binary resources are discovered, cached, and transferred from server to client. + +## Resource types +- Binary resources are grouped by EnumAssets (nodestate, particle, animation, model, texture, sound, font). +- Each resource is addressed by a domain + key pair and also identified by a SHA-256 hash (Hash_t). +- The hash is the canonical payload identity: if the hash matches, the content is identical. + +## High-level flow +1) Server tracks resource usage per client. +2) Server announces bindings (type + id + domain:key + hash) to the client. +3) Client checks local packs and cache; if missing, client requests by hash. +4) Server streams requested resources in chunks. +5) Client assembles the payload, stores it in cache, and marks the resource as loaded. + +## Server side +- Resource usage counters are maintained in `RemoteClient::NetworkAndResource_t::ResUses`. +- When content references change, `incrementAssets` and `decrementAssets` update counters. +- `ResourceRequest` is built from per-client usage and sent to `GameServer::stepSyncContent`. +- `GameServer::stepSyncContent` resolves resource data via `AssetsManager` and calls `RemoteClient::informateAssets`. +- `RemoteClient::informateAssets`: + - Sends bind notifications when a hash for an id changes or is new to the client. + - Queues full resources only if the client explicitly requested the hash. +- Streaming happens in `RemoteClient::onUpdate`: + - `InitResSend` announces the total size + hash + type/id + domain/key. + - `ChunkSend` transmits raw payload slices until complete. + +## Client side +- `ServerSession::rP_Resource` handles bind/lost/init/chunk packets. +- `Bind` and `Lost` update the in-memory bindings queue. +- `InitResSend` creates an `AssetLoading` entry keyed by hash. +- `ChunkSend` appends bytes to the loading entry; when finished, the asset is enqueued for cache write. +- The update loop (`ServerSession::update`) pulls assets from `AssetsManager`: + - If cache miss: sends `ResourceRequest` with the hash. + - If cache hit: directly marks the resource as loaded. + +## Client cache (`AssetsManager`) +- Reads check, in order: + 1) Resource packs on disk (assets directories) + 2) Inline sqlite cache (small resources) + 3) File-based cache (large resources) +- Writes store small resources in sqlite and larger ones as files under `Cache/blobs/`. +- The cache also tracks last-used timestamps for eviction policies. + +## Packet types (Resources) +- `Bind`: server -> client mapping of (type, id, domain, key, hash). +- `Lost`: server -> client removing mapping of (type, id). +- `InitResSend`: server -> client resource stream header. +- `ChunkSend`: server -> client resource stream payload. +- `ResourceRequest`: client -> server, list of hashes that are missing. + +## Common failure modes +- Missing bind update: client will never request the hash. +- Missing cache entry or stale `AlreadyLoading`: client stops requesting resources it still needs. +- Interrupted streaming: asset stays in `AssetsLoading` without being finalized. +