From a29e772f358be4e8173bab191b986ef1596e9845 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Wed, 7 Jan 2026 13:56:10 +0600 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D0=B5=D0=B5=20=D0=BD=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=80=D0=B5=D1=81=D1=83=D1=80=D1=81=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/Client/Abstract.hpp | 38 +++++++-- Src/Client/AssetsManager.hpp | 87 ++++++++++---------- Src/Client/ServerSession.cpp | 87 +------------------- Src/Client/Vulkan/VulkanRenderSession.cpp | 99 ++--------------------- Src/Client/Vulkan/VulkanRenderSession.hpp | 81 +++++-------------- 5 files changed, 102 insertions(+), 290 deletions(-) diff --git a/Src/Client/Abstract.hpp b/Src/Client/Abstract.hpp index ca81474..d206aa5 100644 --- a/Src/Client/Abstract.hpp +++ b/Src/Client/Abstract.hpp @@ -60,15 +60,40 @@ public: // states }; +struct AssetsModelUpdate { + ResourceId Id = 0; + HeadlessModel Model; + HeadlessModel::Header Header; +}; + +struct AssetsNodestateUpdate { + ResourceId Id = 0; + HeadlessNodeState Nodestate; + HeadlessNodeState::Header Header; +}; + +struct AssetsTextureUpdate { + ResourceId Id = 0; + uint16_t Width = 0; + uint16_t Height = 0; + std::vector Pixels; + ResourceHeader Header; +}; + +struct AssetsBinaryUpdate { + ResourceId Id = 0; + std::u8string Data; +}; + /* Интерфейс рендера текущего подключения к серверу */ class IRenderSession { public: // Объект уведомления об изменениях struct TickSyncData { - // Новые или изменённые используемые теперь двоичные ресурсы - std::unordered_map> Assets_ChangeOrAdd; - // Более не используемые ресурсы - std::unordered_map> Assets_Lost; + // Изменения в ассетах. + std::vector AssetsModels; + std::vector AssetsNodestates; + std::vector AssetsTextures; // Новые или изменённые профили контента std::unordered_map> Profiles_ChangeOrAdd; @@ -87,7 +112,7 @@ public: // Началась стадия изменения данных IServerSession, все должны приостановить работу virtual void pushStageTickSync() = 0; // После изменения внутренних данных IServerSession, IRenderSession уведомляется об изменениях - virtual void tickSync(const TickSyncData& data) = 0; + virtual void tickSync(TickSyncData& data) = 0; // Установить позицию для камеры virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) = 0; @@ -190,9 +215,6 @@ public: // Включить логирование входящих сетевых пакетов на клиенте. bool DebugLogPackets = false; - // Используемые двоичные ресурсы - std::unordered_map> Assets; - // Используемые профили контента struct { std::unordered_map DefVoxel; diff --git a/Src/Client/AssetsManager.hpp b/Src/Client/AssetsManager.hpp index f653c91..223eeaf 100644 --- a/Src/Client/AssetsManager.hpp +++ b/Src/Client/AssetsManager.hpp @@ -25,6 +25,7 @@ #include "boost/asio/io_context.hpp" #include "png++/image.hpp" #include +#include "Abstract.hpp" namespace LV::Client { @@ -33,41 +34,14 @@ namespace fs = std::filesystem; class AssetsManager : public IdProvider { public: struct ResourceUpdates { - struct ModelUpdate { - ResourceId Id = 0; - HeadlessModel Model; - HeadlessModel::Header Header; - }; - struct NodestateUpdate { - ResourceId Id = 0; - HeadlessNodeState Nodestate; - HeadlessNodeState::Header Header; - }; - - struct TextureUpdate { - ResourceId Id = 0; - uint16_t Width = 0; - uint16_t Height = 0; - std::vector Pixels; - std::string Domain; - std::string Key; - ResourceHeader Header; - }; - - struct BinaryUpdate { - ResourceId Id = 0; - std::u8string Data; - }; - - std::vector Models; - std::vector Nodestates; - /// TODO: Добавить анимацию из меты - std::vector Textures; - std::vector Particles; - std::vector Animations; - std::vector Sounds; - std::vector Fonts; + std::vector Models; + std::vector Nodestates; + std::vector Textures; + std::vector Particles; + std::vector Animations; + std::vector Sounds; + std::vector Fonts; }; public: @@ -115,6 +89,23 @@ public: Out_applyResourcesUpdate applyResourcesUpdate(const Out_checkAndPrepareResourcesUpdate& orr) { Out_applyResourcesUpdate result; + for(size_t type = 0; type < static_cast(EnumAssets::MAX_ENUM); ++type) { + for(ResourceId id : orr.RP.LostLinks[type]) { + std::optional res = ResourcePacks.getResource((EnumAssets) type, id); + assert(res); + + auto hashIter = HashToPath.find(res->Hash); + assert(hashIter != HashToPath.end()); + auto& entry = hashIter->second; + auto iter = std::find(entry.begin(), entry.end(), res->Path); + assert(iter != entry.end()); + entry.erase(iter); + + if(entry.empty()) + HashToPath.erase(hashIter); + } + } + ResourcePacks.applyResourcesUpdate(orr.RP); ExtraSource.applyResourcesUpdate(orr.ES); @@ -124,6 +115,7 @@ public: for(const auto& res : orr.RP.ResourceUpdates[type]) { // Помечаем ресурс для обновления PendingUpdateFromAsync[type].push_back(std::get(res)); + HashToPath[std::get(res)].push_back(std::get(res)); } for(ResourceId id : orr.RP.LostLinks[type]) { @@ -476,17 +468,20 @@ private: const auto& map = ServerToClientMap[static_cast(EnumAssets::Model)]; if(id >= map.size()) return 0; + return map[id]; }; auto mapTextureId = [&](ResourceId id) -> ResourceId { const auto& map = ServerToClientMap[static_cast(EnumAssets::Texture)]; if(id >= map.size()) return 0; + return map[id]; }; auto rebindHeader = [&](EnumAssets type, const ResourceHeader& header) -> ResourceHeader { if(header.empty()) return {}; + std::vector bytes; bytes.resize(header.size()); std::memcpy(bytes.data(), header.data(), header.size()); @@ -497,6 +492,7 @@ private: mapTextureId, [](const std::string&) {} ); + return ResourceHeader(reinterpret_cast(rebound.data()), rebound.size()); }; @@ -570,31 +566,30 @@ private: RU.Models.push_back({id, std::move(hm), std::move(headerParsed)}); updated++; } else if(type == EnumAssets::Texture) { - ResourceUpdates::TextureUpdate update; - update.Id = id; - update.Domain = std::move(domain); - update.Key = std::move(key); - update.Header = std::move(finalHeader); + AssetsTextureUpdate entry; + entry.Id = id; + entry.Header = std::move(finalHeader); if(!data.empty()) { iResource sres(reinterpret_cast(data.data()), data.size()); iBinaryStream stream = sres.makeStream(); png::image img(stream.Stream); - update.Width = static_cast(img.get_width()); - update.Height = static_cast(img.get_height()); - update.Pixels.resize(static_cast(update.Width) * update.Height); - for(uint32_t y = 0; y < update.Height; ++y) { + entry.Width = static_cast(img.get_width()); + entry.Height = static_cast(img.get_height()); + entry.Pixels.resize(static_cast(entry.Width) * entry.Height); + for(uint32_t y = 0; y < entry.Height; ++y) { const auto& row = img.get_pixbuf().operator[](y); - for(uint32_t x = 0; x < update.Width; ++x) { + for(uint32_t x = 0; x < entry.Width; ++x) { const auto& px = row[x]; uint32_t rgba = (uint32_t(px.alpha) << 24) | (uint32_t(px.red) << 16) | (uint32_t(px.green) << 8) | uint32_t(px.blue); - update.Pixels[x + y * update.Width] = rgba; + entry.Pixels[x + y * entry.Width] = rgba; } } } - RU.Textures.push_back(std::move(update)); + + RU.Textures.push_back(std::move(entry)); updated++; } else if(type == EnumAssets::Particle) { RU.Particles.push_back({id, std::move(data)}); diff --git a/Src/Client/ServerSession.cpp b/Src/Client/ServerSession.cpp index 4c48772..f0ffe56 100644 --- a/Src/Client/ServerSession.cpp +++ b/Src/Client/ServerSession.cpp @@ -512,90 +512,9 @@ void ServerSession::update(GlobalTime gTime, float dTime) { { AssetsManager::ResourceUpdates updates = AM.pullResourceUpdates(); - - if(!updates.Models.empty()) { - auto& map = Assets[EnumAssets::Model]; - for(auto& update : updates.Models) { - AssetEntry entry; - entry.Id = update.Id; - entry.Model = std::move(update.Model); - entry.ModelHeader = std::move(update.Header); - map[entry.Id] = std::move(entry); - result.Assets_ChangeOrAdd[EnumAssets::Model].push_back(update.Id); - } - } - - if(!updates.Nodestates.empty()) { - auto& map = Assets[EnumAssets::Nodestate]; - for(auto& update : updates.Nodestates) { - AssetEntry entry; - entry.Id = update.Id; - entry.Nodestate = std::move(update.Nodestate); - entry.NodestateHeader = std::move(update.Header); - map[entry.Id] = std::move(entry); - result.Assets_ChangeOrAdd[EnumAssets::Nodestate].push_back(update.Id); - } - } - - if(!updates.Textures.empty()) { - auto& map = Assets[EnumAssets::Texture]; - for(auto& update : updates.Textures) { - AssetEntry entry; - entry.Id = update.Id; - entry.Domain = std::move(update.Domain); - entry.Key = std::move(update.Key); - entry.Width = update.Width; - entry.Height = update.Height; - entry.Pixels = std::move(update.Pixels); - entry.Header = std::move(update.Header); - map[entry.Id] = std::move(entry); - result.Assets_ChangeOrAdd[EnumAssets::Texture].push_back(update.Id); - } - } - - if(!updates.Particles.empty()) { - auto& map = Assets[EnumAssets::Particle]; - for(auto& update : updates.Particles) { - AssetEntry entry; - entry.Id = update.Id; - entry.Data = std::move(update.Data); - map[entry.Id] = std::move(entry); - result.Assets_ChangeOrAdd[EnumAssets::Particle].push_back(update.Id); - } - } - - if(!updates.Animations.empty()) { - auto& map = Assets[EnumAssets::Animation]; - for(auto& update : updates.Animations) { - AssetEntry entry; - entry.Id = update.Id; - entry.Data = std::move(update.Data); - map[entry.Id] = std::move(entry); - result.Assets_ChangeOrAdd[EnumAssets::Animation].push_back(update.Id); - } - } - - if(!updates.Sounds.empty()) { - auto& map = Assets[EnumAssets::Sound]; - for(auto& update : updates.Sounds) { - AssetEntry entry; - entry.Id = update.Id; - entry.Data = std::move(update.Data); - map[entry.Id] = std::move(entry); - result.Assets_ChangeOrAdd[EnumAssets::Sound].push_back(update.Id); - } - } - - if(!updates.Fonts.empty()) { - auto& map = Assets[EnumAssets::Font]; - for(auto& update : updates.Fonts) { - AssetEntry entry; - entry.Id = update.Id; - entry.Data = std::move(update.Data); - map[entry.Id] = std::move(entry); - result.Assets_ChangeOrAdd[EnumAssets::Font].push_back(update.Id); - } - } + result.AssetsNodestates = std::move(updates.Nodestates); + result.AssetsModels = std::move(updates.Models); + result.AssetsTextures = std::move(updates.Textures); } for(auto& [id, _] : profile_Voxel_AddOrChange) diff --git a/Src/Client/Vulkan/VulkanRenderSession.cpp b/Src/Client/Vulkan/VulkanRenderSession.cpp index dfbfe7a..8ac30de 100644 --- a/Src/Client/Vulkan/VulkanRenderSession.cpp +++ b/Src/Client/Vulkan/VulkanRenderSession.cpp @@ -1662,7 +1662,7 @@ void VulkanRenderSession::pushStageTickSync() { CP.pushStageTickSync(); } -void VulkanRenderSession::tickSync(const TickSyncData& data) { +void VulkanRenderSession::tickSync(TickSyncData& data) { // Изменение ассетов // Профили // Чанки @@ -1680,77 +1680,16 @@ void VulkanRenderSession::tickSync(const TickSyncData& data) { if(auto iter = data.Profiles_Lost.find(EnumDefContent::Voxel); iter != data.Profiles_Lost.end()) mcpData.ChangedVoxels.insert(mcpData.ChangedVoxels.end(), iter->second.begin(), iter->second.end()); - std::vector modelResources; - std::vector modelLost; - if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Model); iter != data.Assets_ChangeOrAdd.end()) { - const auto& list = ServerSession->Assets[EnumAssets::Model]; - for(ResourceId id : iter->second) { - auto entryIter = list.find(id); - if(entryIter == list.end()) - continue; - - modelResources.push_back(&entryIter->second); - } - } - if(auto iter = data.Assets_Lost.find(EnumAssets::Model); iter != data.Assets_Lost.end()) - modelLost.insert(modelLost.end(), iter->second.begin(), iter->second.end()); - std::vector changedModels; - if(!modelResources.empty() || !modelLost.empty()) { - const auto& modelAssets = ServerSession->Assets[EnumAssets::Model]; - changedModels = MP.onModelChanges(std::move(modelResources), std::move(modelLost), &modelAssets); - } + if(!data.AssetsModels.empty()) + changedModels = MP.onModelChanges(std::move(data.AssetsModels)); - if(TP) { - std::vector textureResources; - std::vector textureLost; - - if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Texture); iter != data.Assets_ChangeOrAdd.end()) { - const auto& list = ServerSession->Assets[EnumAssets::Texture]; - for(ResourceId id : iter->second) { - auto entryIter = list.find(id); - if(entryIter == list.end()) - continue; - - textureResources.push_back({ - .Id = id, - .Width = entryIter->second.Width, - .Height = entryIter->second.Height, - .Pixels = entryIter->second.Pixels, - .Domain = entryIter->second.Domain, - .Key = entryIter->second.Key - }); - } - } - - if(auto iter = data.Assets_Lost.find(EnumAssets::Texture); iter != data.Assets_Lost.end()) - textureLost.insert(textureLost.end(), iter->second.begin(), iter->second.end()); - - if(!textureResources.empty() || !textureLost.empty()) - TP->onTexturesChanges(std::move(textureResources), std::move(textureLost)); - } + if(TP && !data.AssetsTextures.empty()) + TP->onTexturesChanges(std::move(data.AssetsTextures)); std::vector changedNodestates; - if(NSP) { - std::vector nodestateResources; - std::vector nodestateLost; - - if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Nodestate); iter != data.Assets_ChangeOrAdd.end()) { - const auto& list = ServerSession->Assets[EnumAssets::Nodestate]; - for(ResourceId id : iter->second) { - auto entryIter = list.find(id); - if(entryIter == list.end()) - continue; - - nodestateResources.push_back(&entryIter->second); - } - } - - if(auto iter = data.Assets_Lost.find(EnumAssets::Nodestate); iter != data.Assets_Lost.end()) - nodestateLost.insert(nodestateLost.end(), iter->second.begin(), iter->second.end()); - - if(!nodestateResources.empty() || !nodestateLost.empty() || !changedModels.empty()) - changedNodestates = NSP->onNodestateChanges(std::move(nodestateResources), std::move(nodestateLost), changedModels); + if(NSP && (!data.AssetsNodestates.empty() || !changedModels.empty())) { + changedNodestates = NSP->onNodestateChanges(std::move(data.AssetsNodestates), std::move(changedModels)); } if(!changedNodestates.empty()) { @@ -2050,29 +1989,7 @@ void VulkanRenderSession::ensureEntityTexture() { if(EntityTextureReady || !TP || !NSP) return; - auto iter = ServerSession->Assets.find(EnumAssets::Texture); - if(iter == ServerSession->Assets.end() || iter->second.empty()) - return; - - const AssetEntry* picked = nullptr; - for(const auto& [id, entry] : iter->second) { - if(entry.Key == "default.png") { - picked = &entry; - break; - } - } - if(!picked) { - for(const auto& [id, entry] : iter->second) { - if(entry.Key == "grass.png") { - picked = &entry; - break; - } - } - } - if(!picked) - picked = &iter->second.begin()->second; - - updateTestQuadTexture(NSP->getTextureId(picked->Id)); + return; } void VulkanRenderSession::ensureAtlasLayerPreview() { diff --git a/Src/Client/Vulkan/VulkanRenderSession.hpp b/Src/Client/Vulkan/VulkanRenderSession.hpp index b65a868..380bf3e 100644 --- a/Src/Client/Vulkan/VulkanRenderSession.hpp +++ b/Src/Client/Vulkan/VulkanRenderSession.hpp @@ -89,11 +89,8 @@ public: } // Применяет изменения, возвращая все затронутые модели - std::vector onModelChanges(std::vector newOrChanged, - std::vector lost, - const std::unordered_map* modelAssets) { + std::vector onModelChanges(std::vector entries) { std::vector result; - (void)modelAssets; std::move_only_function makeUnready; makeUnready = [&](ResourceId id) { @@ -124,29 +121,15 @@ public: iterModel->second.Ready = false; }; - - for(ResourceId lostId : lost) { - makeUnready(lostId); - } - - for(ResourceId lostId : lost) { - auto iterModel = Models.find(lostId); - if(iterModel == Models.end()) - continue; - - Models.erase(iterModel); - } - for(const AssetEntry* entry : newOrChanged) { - if(!entry) - continue; - const AssetsModel key = entry->Id; + for(const AssetsModelUpdate& entry : entries) { + const AssetsModel key = entry.Id; result.push_back(key); makeUnready(key); ModelObject model; - const HeadlessModel& hm = entry->Model; - const HeadlessModel::Header& header = entry->ModelHeader; + const HeadlessModel& hm = entry.Model; + const HeadlessModel::Header& header = entry.Header; try { model.TextureMap.clear(); @@ -587,30 +570,25 @@ public: } // Применяет изменения, возвращая все затронутые модели - std::vector onTexturesChanges(std::vector newOrChanged, std::vector lost) { + std::vector onTexturesChanges(std::vector entries) { std::lock_guard lock(Mutex); std::vector result; - for(auto& update : newOrChanged) { - const AssetsTexture key = update.Id; + for(auto& entry : entries) { + const AssetsTexture key = entry.Id; result.push_back(key); - if(update.Width == 0 || update.Height == 0 || update.Pixels.empty()) + if(entry.Width == 0 || entry.Height == 0 || entry.Pixels.empty()) continue; Atlas->updateTexture(key, StoredTexture( - update.Width, - update.Height, - std::move(update.Pixels) + entry.Width, + entry.Height, + std::move(entry.Pixels) )); bool animated = false; - if(auto anim = getDefaultAnimation(update.Key, update.Width, update.Height)) { - AnimatedSources[key] = *anim; - animated = true; - } else { - AnimatedSources.erase(key); - } + AnimatedSources.erase(key); NeedsUpload = true; @@ -618,19 +596,11 @@ public: uint32_t idx = debugTextureLogCount.fetch_add(1); if(idx < 128) { LOG.debug() << "Texture loaded id=" << key - << " key=" << update.Domain << ':' << update.Key - << " size=" << update.Width << 'x' << update.Height + << " size=" << entry.Width << 'x' << entry.Height << " animated=" << (animated ? 1 : 0); } } - for(AssetsTexture key : lost) { - result.push_back(key); - Atlas->freeTexture(key); - AnimatedSources.erase(key); - NeedsUpload = true; - } - std::sort(result.begin(), result.end()); auto eraseIter = std::unique(result.begin(), result.end()); result.erase(eraseIter, result.end()); @@ -844,27 +814,16 @@ public: {} // Применяет изменения, возвращает изменённые описания состояний - std::vector onNodestateChanges(std::vector newOrChanged, std::vector lost, std::vector changedModels) { + std::vector onNodestateChanges(std::vector newOrChanged, std::vector changedModels) { std::vector result; - - for(ResourceId lostId : lost) { - auto iterNodestate = Nodestates.find(lostId); - if(iterNodestate == Nodestates.end()) - continue; - - result.push_back(lostId); - Nodestates.erase(iterNodestate); - } - for(const AssetEntry* entry : newOrChanged) { - if(!entry) - continue; - const AssetsNodestate key = entry->Id; + for(const AssetsNodestateUpdate& entry : newOrChanged) { + const AssetsNodestate key = entry.Id; result.push_back(key); PreparedNodeState nodestate; - static_cast(nodestate) = entry->Nodestate; - nodestate.LocalToModel.assign(entry->NodestateHeader.Models.begin(), entry->NodestateHeader.Models.end()); + static_cast(nodestate) = entry.Nodestate; + nodestate.LocalToModel.assign(entry.Header.Models.begin(), entry.Header.Models.end()); Nodestates.insert_or_assign(key, std::move(nodestate)); if(key < 64) { @@ -1321,7 +1280,7 @@ public: virtual void prepareTickSync() override; virtual void pushStageTickSync() override; - virtual void tickSync(const TickSyncData& data) override; + virtual void tickSync(TickSyncData& data) override; virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) override;