From 5b02fec75e2d0a1e5f7f09dc8a46d87c3fa86034 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Fri, 29 Aug 2025 17:21:03 +0600 Subject: [PATCH] * --- Src/Client/Abstract.hpp | 77 ++++++----- Src/Client/ServerSession.cpp | 246 +++++++++++++++++++++++------------ Src/Client/ServerSession.hpp | 24 +++- Src/Client/Vulkan/Vulkan.cpp | 2 +- 4 files changed, 228 insertions(+), 121 deletions(-) diff --git a/Src/Client/Abstract.hpp b/Src/Client/Abstract.hpp index 287ba73..44cd918 100644 --- a/Src/Client/Abstract.hpp +++ b/Src/Client/Abstract.hpp @@ -60,26 +60,35 @@ public: // states }; -struct AssetEntry { - EnumAssets Type; - ResourceId Id; - std::string Domain, Key; - Resource Res; -}; - /* Интерфейс рендера текущего подключения к серверу */ class IRenderSession { public: - // Изменённые ресурсы (кроме звуков) - virtual void onAssetsChanges(std::unordered_map> resources) = 0; - // Потерянные ресурсы - virtual void onAssetsLost(std::unordered_map> resources) = 0; + // Объект уведомления об изменениях + struct TickSyncData { + // Новые или изменённые используемые теперь двоичные ресурсы + std::unordered_map> Assets_ChangeOrAdd; + // Более не используемые ресурсы + std::unordered_map> Assets_Lost; - virtual void onContentDefinesAdd(std::unordered_map>) = 0; - virtual void onContentDefinesLost(std::unordered_map>) = 0; + // Новые или изменённые профили контента + std::unordered_map> Profiles_ChangeOrAdd; + // Более не используемые профили + std::unordered_map> Profiles_Lost; + + // Новые или изменённые чанки + std::unordered_map> Chunks_ChangeOrAdd; + // Более не отслеживаемые регионы + std::unordered_map> Chunks_Lost; + }; + +public: + // Серверная сессия собирается обработать данные такток сервера (изменение профилей, ресурсов, прочих игровых данных) + virtual void prepareTickSync() = 0; + // Началась стадия изменения данных IServerSession, все должны приостановить работу + virtual void pushStageTickSync() = 0; + // После изменения внутренних данных IServerSession, IRenderSession уведомляется об изменениях + virtual void tickSync(TickSyncData& data) = 0; - // Сообщаем об изменившихся чанках - virtual void onChunksChange(WorldId_t worldId, const std::unordered_set &changeOrAddList, const std::unordered_set &remove) = 0; // Установить позицию для камеры virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) = 0; @@ -143,19 +152,27 @@ struct DefItemInfo { struct DefVoxel_t {}; struct DefNode_t {}; -/* Интерфейс обработчика сессии с сервером */ -class IServerSession { - // struct ArrayHasher { - // std::size_t operator()(const Hash_t& a) const { - // std::size_t h = 0; - // for (auto e : a) - // h ^= std::hash{}(e) + 0x9e3779b9 + (h << 6) + (h >> 2); - - // return h; - // } - // }; +struct AssetEntry { + EnumAssets Type; + ResourceId Id; + std::string Domain, Key; + Resource Res; +}; +/* + Интерфейс обработчика сессии с сервером. + + Данный здесь меняются только меж вызовами + IRenderSession::pushStageTickSync + и + IRenderSession::tickSync +*/ +class IServerSession { public: + // Используемые двоичные ресурсы + std::unordered_map> Assets; + + // Используемые профили контента struct { std::unordered_map DefVoxel; std::unordered_map DefNode; @@ -163,17 +180,19 @@ public: std::unordered_map DefPortal; std::unordered_map DefEntity; std::unordered_map DefItem; - } Registry; + } Profiles; + // Видимый контент struct { std::unordered_map Worlds; // std::unordered_map Portals; std::unordered_map Entityes; - } Data; + } Content; virtual ~IServerSession(); - virtual void atFreeDrawTime(GlobalTime gTime, float dTime) = 0; + // Обновление сессии с сервером, может начатся стадия IRenderSession::tickSync + virtual void update(GlobalTime gTime, float dTime) = 0; }; diff --git a/Src/Client/ServerSession.cpp b/Src/Client/ServerSession.cpp index bf15180..9405e61 100644 --- a/Src/Client/ServerSession.cpp +++ b/Src/Client/ServerSession.cpp @@ -6,6 +6,7 @@ #include "TOSLib.hpp" #include "glm/ext/quaternion_geometric.hpp" #include +#include #include #include #include @@ -320,37 +321,170 @@ void ServerSession::onJoystick() { } -void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) { +void ServerSession::update(GlobalTime gTime, float dTime) { + // Если были получены ресурсы, отправим их на запись в кеш + if(!AsyncContext.LoadedAssets.get_read().empty()) { + std::vector assets = std::move(*AsyncContext.LoadedAssets.lock()); + std::vector resources; + resources.reserve(assets.size()); + + for(AssetEntry& entry : assets) { + resources.push_back(entry.Res); + AsyncContext.ReceivedResources[entry.Type].push_back(entry); + + // // Проверяем используется ли сейчас ресурс + // auto iter = Assets.ExistBinds[(int) entry.Type].find(entry.Id); + // if(iter == Assets.ExistBinds[(int) entry.Type].end()) { + // // Не используется + // Assets.NotInUse[(int) entry.Type][entry.Domain + ':' + entry.Key] = {entry, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)}; + // } else { + // // Используется + // Assets.InUse[(int) entry.Type][entry.Id] = entry; + // changedResources[entry.Type].insert({entry.Id, entry}); + // } + } + + AM->pushResources(std::move(resources)); + } + + + + // Разбираемся с полученными меж тактами привязками ресурсов + if(!AsyncContext.AssetsBinds.get_read().empty()) { + AssetsBindsChange abc; + + // Нужно объеденить изменения в один AssetsBindsChange (abc) + { + std::vector list = std::move(*AsyncContext.AssetsBinds.lock()); + + for(AssetsBindsChange entry : list) { + for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) + std::sort(entry.Lost[type].begin(), entry.Lost[type].end()); + + // Если до этого была объявлена привязка, а теперь она потеряна, то просто сокращаем значения. + // Иначе дописываем в lost + for(ssize_t iter = abc.Binds.size()-1; iter >= 0; iter--) { + const AssetBindEntry& abe = abc.Binds[iter]; + auto& lost = entry.Lost[(int) abe.Type]; + auto iterator = std::lower_bound(lost.begin(), lost.end(), abe.Id); + if(iterator != lost.end()) { + // Привязка будет удалена + lost.erase(iterator); + abc.Binds.erase(abc.Binds.begin()+iter); + } + } + + for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) { + abc.Lost[type].append_range(entry.Lost[type]); + entry.Lost[type].clear(); + std::sort(abc.Lost[type].begin(), abc.Lost[type].end()); + } + + for(AssetBindEntry& abe : entry.Binds) { + auto iterator = std::lower_bound(entry.Lost[(int) abe.Type].begin(), entry.Lost[(int) abe.Type].end(), abe.Id); + if(iterator != entry.Lost[(int) abe.Type].end()) { + // Получили новую привязку, которая была удалена в предыдущем такте + entry.Lost[(int) abe.Type].erase(iterator); + } else { + // Данная привязка не удалялась, может она была изменена? + bool hasChanged = false; + for(AssetBindEntry& abe2 : abc.Binds) { + if(abe2.Type == abe.Type && abe2.Id == abe.Id) { + // Привязка была изменена + abe2 = std::move(abe); + hasChanged = true; + break; + } + } + + if(!hasChanged) + // Изменения не было, это просто новая привязка + abc.Binds.emplace_back(std::move(abe)); + } + } + + entry.Binds.clear(); + } + } + + // Запрос к дисковому кешу новых ресурсов + std::vector needToLoad; + for(const AssetBindEntry& bind : abc.Binds) { + bool needQuery = false; + // Проверить in memory кеш по домену+ключу + { + std::string dk = bind.Domain + ':' + bind.Key; + auto &niubdk = Assets.NotInUse[(int) bind.Type]; + auto iter = niubdk.find(dk); + if(iter != niubdk.end()) { + // Есть ресурс + needQuery = true; + } + } + + // Проверить если такой запрос уже был отправлен в AssetsManager и ожидает ответа + if(!needQuery) { + auto& list = AsyncContext.ResourceWait[(int) bind.Type]; + auto iterDomain = list.find(bind.Domain); + if(iterDomain != list.end()) { + for(const auto& [key, hash] : iterDomain->second) { + if(key == bind.Key && hash == bind.Hash) { + needQuery = true; + break; + } + } + } + } + + // Assets.ExistBinds[(int) bind.Type].insert(bind.Id); + + // Под рукой нет ресурса, отправим на проверку в AssetsManager + if(needQuery) { + AsyncContext.ResourceWait[(int) bind.Type][bind.Domain].emplace_back(bind.Key, bind.Hash); + needToLoad.emplace_back(bind.Hash, bind.Type, bind.Domain, bind.Key, bind.Id); + } + } + + // Отправляем запрос на получение ресурсов + if(!needToLoad.empty()) + AM->pushReads(std::move(needToLoad)); + } + + if(!AsyncContext.TickSequence.get_read().empty()) { + // Есть такты с сервера + // Оповещаем о подготовке к обработке тактов + if(RS) + RS->prepareTickSync(); + + IRenderSession::TickSyncData result; + // Перевариваем данные по тактам + + + + + if(RS) + RS->pushStageTickSync(); + + // Применяем изменения по ресурсам, профилям и контенту + + if(RS) + RS->tickSync(result); + } + + // Здесь нужно обработать управляющие пакеты + + + + + + + // Оповещение модуля рендера об изменениях ресурсов std::unordered_map> changedResources; std::unordered_map> lostResources; // Обработка полученных ресурсов - if(!AsyncContext.LoadedAssets.get_read().empty()) { - std::vector assets = std::move(*AsyncContext.LoadedAssets.lock()); - // Для сохранения ресурсов в кеше - std::vector resources; - resources.reserve(assets.size()); - - - for(AssetEntry& entry : assets) { - resources.push_back(entry.Res); - - // Проверяем используется ли сейчас ресурс - auto iter = Assets.ExistBinds[(int) entry.Type].find(entry.Id); - if(iter == Assets.ExistBinds[(int) entry.Type].end()) { - // Не используется - Assets.NotInUse[(int) entry.Type][entry.Domain + ':' + entry.Key] = {entry, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)}; - } else { - // Используется - Assets.InUse[(int) entry.Type][entry.Id] = entry; - changedResources[entry.Type].insert({entry.Id, entry}); - } - } - - // Сохраняем в кеш - AM->pushResources(std::move(resources)); - } + // Обработка полученных тактов while(!AsyncContext.TickSequence.get_read().empty()) { @@ -372,69 +506,9 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) { lostResources[(EnumAssets) type].insert_range(tick.AssetsLost[type]); } - // Запрос к дисковому кешу - std::vector needToLoad; - // Новые привязки ресурсов - for(const AssetBindEntry& bind : tick.AssetsBinds) { - Assets.ExistBinds[(int) bind.Type].insert(bind.Id); - - // Проверить in memory кеш по домену+ключу - { - std::string dk = bind.Domain + ':' + bind.Key; - auto &niubdk = Assets.NotInUse[(int) bind.Type]; - auto iter = niubdk.find(dk); - if(iter != niubdk.end()) { - // Есть ресурс - Assets.InUse[(int) bind.Type][bind.Id] = std::get<0>(iter->second); - changedResources[bind.Type].insert({bind.Id, std::get<0>(iter->second)}); - lostResources[bind.Type].erase(bind.Id); - continue; - } - } - - // Под рукой нет ресурса, отправим на проверку в AssetsManager - needToLoad.emplace_back(bind.Hash, bind.Type, bind.Domain, bind.Key, bind.Id); - } - - if(!needToLoad.empty()) - AM->pushReads(std::move(needToLoad)); } // Получаем ресурсы, загруженные с дискового кеша - { - std::vector request; - std::vector>> resources = AM->pullReads(); - for(auto& [key, res] : resources) { - if(!res) { - // Нужно запросить ресурс с сервера - request.push_back(key.Hash); - } else { - auto& a = Assets.ExistBinds[(int) key.Type]; - AssetEntry entry = {key.Type, key.Id, key.Domain, key.Key, *res}; - - if(a.contains(key.Id)) { - // Ресурс ещё нужен - Assets.InUse[(int) key.Type][key.Id] = entry; - changedResources[key.Type].insert({key.Id, entry}); - } else { - // Ресурс уже не нужен - Assets.NotInUse[(int) key.Type][key.Domain + ':' + key.Key] = {entry, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)}; - } - } - } - - if(!request.empty()) { - assert(request.size() < (1 << 16)); - Net::Packet p; - p << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::ResourceRequest - << uint16_t(request.size()); - - for(Hash_t& hash : request) - p.write((const std::byte*) hash.data(), 32); - - Socket->pushPacket(std::move(p)); - } - } if(RS) { // Уведомляем рендер опотерянных и изменённых ресурсах diff --git a/Src/Client/ServerSession.hpp b/Src/Client/ServerSession.hpp index 616228d..4d3a9fa 100644 --- a/Src/Client/ServerSession.hpp +++ b/Src/Client/ServerSession.hpp @@ -64,7 +64,7 @@ public: // IServerSession - virtual void atFreeDrawTime(GlobalTime gTime, float dTime) override; + virtual void update(GlobalTime gTime, float dTime) override; void setRenderSession(IRenderSession* session); private: @@ -105,24 +105,38 @@ private: std::vector LostWorld; // std::vector> + }; + + struct AssetsBindsChange { // Новые привязки ресурсов - std::vector AssetsBinds; + std::vector Binds; // Потерянные из видимости ресурсы - std::vector AssetsLost[(int) EnumAssets::MAX_ENUM]; + std::vector Lost[(int) EnumAssets::MAX_ENUM]; }; struct { // Сюда обращается ветка, обрабатывающая сокет; run() // Получение ресурсов с сервера std::unordered_map AssetsLoading; - // Получение привязок - // Накопление данных за такт сервера TickData ThisTickEntry; + // Сбда обращается ветка обновления IServerSession, накапливая данные до SyncTick + // Ресурсы, ожидающие ответа от менеджера кеша + std::unordered_map>> ResourceWait[(int) EnumAssets::MAX_ENUM]; + // Полученные ресурсы в ожидании стадии синхронизации такта + std::unordered_map> ReceivedResources; + // Полученные изменения связок в ожидании стадии синхронизации такта + AssetsBindsChange Binds; + // Список ресурсов на которые уже был отправлен запрос на загрузку ресурса + std::vector AlreadyLoading; + + // Обменный пункт // Полученные ресурсы с сервера TOS::SpinlockObject> LoadedAssets; + // Изменения в наблюдаемых ресурсах + TOS::SpinlockObject> AssetsBinds; // Пакеты обновлений игрового мира TOS::SpinlockObject> TickSequence; } AsyncContext; diff --git a/Src/Client/Vulkan/Vulkan.cpp b/Src/Client/Vulkan/Vulkan.cpp index cb16978..6a372a5 100644 --- a/Src/Client/Vulkan/Vulkan.cpp +++ b/Src/Client/Vulkan/Vulkan.cpp @@ -611,7 +611,7 @@ void Vulkan::run() if(Game.RSession) Game.RSession->pushStage(EnumRenderStage::WorldUpdate); - Game.Session->atFreeDrawTime(gTime, dTime); + Game.Session->update(gTime, dTime); } Screen.State = DrawState::End; }