From 83c4628995dcdc241e2e1d0ebf0910c71f45d793 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Sun, 4 Jan 2026 13:36:52 +0600 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84?= =?UTF-8?q?=D0=B5=D0=B9=D1=81=D0=B0=20=D0=BF=D1=80=D0=B5=D0=B4=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/Client/ServerSession.cpp | 98 ++++ Src/Client/ServerSession.hpp | 3 + Src/Common/Abstract.cpp | 36 +- Src/Common/Abstract.hpp | 16 +- Src/Common/AssetsPreloader.cpp | 8 +- Src/Common/AssetsPreloader.hpp | 41 +- Src/Common/Packets.hpp | 110 +---- Src/Server/GameServer.cpp | 256 +++------- Src/Server/GameServer.hpp | 4 +- Src/Server/RemoteClient.cpp | 847 ++++++++------------------------- Src/Server/RemoteClient.hpp | 259 +++++----- 11 files changed, 545 insertions(+), 1133 deletions(-) diff --git a/Src/Client/ServerSession.cpp b/Src/Client/ServerSession.cpp index e2dcaa7..6c7e805 100644 --- a/Src/Client/ServerSession.cpp +++ b/Src/Client/ServerSession.cpp @@ -114,6 +114,10 @@ ServerSession::ServerSession(asio::io_context &ioc, std::unique_ptr ServerSession::rP_Resource(Net::AsyncSocket &sock) { AsyncContext.AssetsBinds.lock()->push_back(AssetsBindsChange(binds, {})); co_return; } + case ToClient::L2Resource::BindDK: + { + uint32_t count = co_await sock.read(); + for(size_t iter = 0; iter < count; iter++) { + uint8_t type = co_await sock.read(); + if(type >= (int) EnumAssets::MAX_ENUM) + protocolError(); + + std::string domain = co_await sock.read(); + std::string key = co_await sock.read(); + + ResourceId serverId = NextServerId[type]++; + auto& table = ServerIdToDK[type]; + if(table.size() <= serverId) + table.resize(serverId+1); + table[serverId] = {std::move(domain), std::move(key)}; + } + co_return; + } + case ToClient::L2Resource::BindHash: + { + uint32_t count = co_await sock.read(); + std::vector binds; + binds.reserve(count); + + for(size_t iter = 0; iter < count; iter++) { + uint8_t type = co_await sock.read(); + if(type >= (int) EnumAssets::MAX_ENUM) + protocolError(); + + uint32_t id = co_await sock.read(); + Hash_t hash; + co_await sock.read((std::byte*) hash.data(), hash.size()); + uint32_t headerSize = co_await sock.read(); + std::vector header; + if(headerSize > 0) { + header.resize(headerSize); + co_await sock.read((std::byte*) header.data(), header.size()); + } + + auto& table = ServerIdToDK[type]; + if(id >= table.size()) { + LOG.warn() << "BindHash without domain/key for id=" << id; + continue; + } + + const auto& [domain, key] = table[id]; + if(domain.empty() && key.empty()) { + LOG.warn() << "BindHash missing domain/key for id=" << id; + continue; + } + + AssetsManager::BindResult bindResult = AM->bindServerResource( + (EnumAssets) type, (ResourceId) id, domain, key, hash, header); + + if(!bindResult.Changed) + continue; + + binds.emplace_back(AssetBindEntry{ + .Type = (EnumAssets) type, + .Id = bindResult.LocalId, + .Domain = domain, + .Key = key, + .Hash = hash, + .Header = std::move(header) + }); + + if(binds.back().Domain == "test" + && (binds.back().Type == EnumAssets::Nodestate + || binds.back().Type == EnumAssets::Model + || binds.back().Type == EnumAssets::Texture)) + { + uint32_t idx = debugResourceLogCount.fetch_add(1); + if(idx < 128) { + LOG.debug() << "Bind asset type=" << assetTypeName(binds.back().Type) + << " id=" << binds.back().Id + << " key=" << binds.back().Domain << ':' << binds.back().Key + << " hash=" << int(binds.back().Hash[0]) << '.' + << int(binds.back().Hash[1]) << '.' + << int(binds.back().Hash[2]) << '.' + << int(binds.back().Hash[3]); + } + } + } + + if(!binds.empty()) + AsyncContext.AssetsBinds.lock()->push_back(AssetsBindsChange(binds, {})); + co_return; + } case ToClient::L2Resource::Lost: { uint32_t count = co_await sock.read(); diff --git a/Src/Client/ServerSession.hpp b/Src/Client/ServerSession.hpp index b0a30d0..3600583 100644 --- a/Src/Client/ServerSession.hpp +++ b/Src/Client/ServerSession.hpp @@ -95,6 +95,9 @@ private: std::vector Header; }; + std::array>, (int) EnumAssets::MAX_ENUM> ServerIdToDK; + std::array NextServerId = {}; + struct TickData { std::vector> Profile_Voxel_AddOrChange; std::vector Profile_Voxel_Lost; diff --git a/Src/Common/Abstract.cpp b/Src/Common/Abstract.cpp index ac7c32d..29c4683 100644 --- a/Src/Common/Abstract.cpp +++ b/Src/Common/Abstract.cpp @@ -71,7 +71,7 @@ PrecompiledTexturePipeline compileTexturePipeline(const std::string &cmd, std::s } -CompressedVoxels compressVoxels_byte(const std::vector& voxels) { +std::u8string compressVoxels_byte(const std::vector& voxels) { std::u8string compressed; std::vector defines; DefVoxelId maxValue = 0; @@ -164,10 +164,10 @@ CompressedVoxels compressVoxels_byte(const std::vector& voxels) { } } - return {compressLinear(compressed), defines}; + return compressLinear(compressed); } -CompressedVoxels compressVoxels_bit(const std::vector& voxels) { +std::u8string compressVoxels_bit(const std::vector& voxels) { std::vector profile; std::vector one_byte[7]; @@ -310,10 +310,10 @@ CompressedVoxels compressVoxels_bit(const std::vector& voxels) { for(size_t iter = 0; iter < buff.size(); iter++) compressed[iter / 8] |= (buff[iter] << (iter % 8)); - return {compressLinear(compressed), profile}; + return compressLinear(compressed); } -CompressedVoxels compressVoxels(const std::vector& voxels, bool fast) { +std::u8string compressVoxels(const std::vector& voxels, bool fast) { if(fast) return compressVoxels_byte(voxels); else @@ -697,24 +697,8 @@ CompressedNodes compressNodes_bit(const Node* nodes) { return {compressLinear(compressed), profiles}; } -CompressedNodes compressNodes(const Node* nodes, bool fast) { - std::u8string data(16*16*16*sizeof(Node), '\0'); - const char8_t *ptr = (const char8_t*) nodes; - std::copy(ptr, ptr+16*16*16*4, data.data()); - - std::vector node(16*16*16); - for(int iter = 0; iter < 16*16*16; iter++) { - node[iter] = nodes[iter].NodeId; - } - - { - std::sort(node.begin(), node.end()); - auto last = std::unique(node.begin(), node.end()); - node.erase(last, node.end()); - node.shrink_to_fit(); - } - - return {compressLinear(data), std::move(node)}; +std::u8string compressNodes(const Node* nodes, bool fast) { + return compressLinear(std::u8string_view((const char8_t*) nodes, 16*16*16*sizeof(Node))); // if(fast) // return compressNodes_byte(nodes); @@ -854,7 +838,7 @@ void unCompressNodes(const std::u8string& compressed, Node* ptr) { // return unCompressNodes_bit(next, ptr); } -std::u8string compressLinear(const std::u8string& data) { +std::u8string compressLinear(std::u8string_view data) { std::stringstream in; in.write((const char*) data.data(), data.size()); @@ -869,7 +853,7 @@ std::u8string compressLinear(const std::u8string& data) { return *(std::u8string*) &outString; } -std::u8string unCompressLinear(const std::u8string& data) { +std::u8string unCompressLinear(std::u8string_view data) { std::stringstream in; in.write((const char*) data.data(), data.size()); @@ -1488,7 +1472,7 @@ uint16_t PreparedNodeState::parseCondition(const std::string_view expression) { // }; } -std::pair> PreparedNodeState::parseModel(const std::string_view modid, const js::object& obj) { +std::pair> HeadlessNodeState::parseModel(const std::string_view modid, const js::object& obj) { // ModelToLocalId bool uvlock; diff --git a/Src/Common/Abstract.hpp b/Src/Common/Abstract.hpp index 51c93d1..4d287a6 100644 --- a/Src/Common/Abstract.hpp +++ b/Src/Common/Abstract.hpp @@ -469,13 +469,7 @@ struct VoxelCube { } }; -struct CompressedVoxels { - std::u8string Compressed; - // Уникальный сортированный список идентификаторов вокселей - std::vector Defines; -}; - -CompressedVoxels compressVoxels(const std::vector& voxels, bool fast = true); +std::u8string compressVoxels(const std::vector& voxels, bool fast = true); std::vector unCompressVoxels(const std::u8string& compressed); struct Node { @@ -493,11 +487,11 @@ struct CompressedNodes { std::vector Defines; }; -CompressedNodes compressNodes(const Node* nodes, bool fast = true); -void unCompressNodes(const std::u8string& compressed, Node* ptr); +std::u8string compressNodes(const Node* nodes, bool fast = true); +void unCompressNodes(std::u8string_view compressed, Node* ptr); -std::u8string compressLinear(const std::u8string& data); -std::u8string unCompressLinear(const std::u8string& data); +std::u8string compressLinear(std::u8string_view data); +std::u8string unCompressLinear(std::u8string_view data); inline std::pair parseDomainKey(const std::string_view value, const std::string_view defaultDomain = "core") { size_t pos = value.find(':'); diff --git a/Src/Common/AssetsPreloader.cpp b/Src/Common/AssetsPreloader.cpp index de0af42..d9db274 100644 --- a/Src/Common/AssetsPreloader.cpp +++ b/Src/Common/AssetsPreloader.cpp @@ -262,7 +262,7 @@ AssetsPreloader::Out_reloadResources AssetsPreloader::_reloadResources(const Ass // Домен неизвестен движку, все ресурсы в нём новые for(const auto& [key, info] : table) { PendingResource resource = buildResource(static_cast(type), domain, key, info); - result.NewOrChange[(int) type][domain].push_back(std::move(resource)); + result.NewOrChange[type][domain].push_back(std::move(resource)); } } else { for(const auto& [key, info] : table) { @@ -354,14 +354,14 @@ AssetsPreloader::Out_applyResourceChange AssetsPreloader::applyResourceChange(co // Связали с хешем HashToId[resource.Hash] = {static_cast(type), pending.Id}; // Осведомили о новом/изменённом ресурсе - result.NewOrChange[type].push_back({pending.Id, std::move(resource)}); + result.NewOrChange[type].emplace_back(pending.Id, resource.Hash, std::move(resource.Header)); } } // Не должно быть ресурсов, которые были помечены как потерянные #ifndef NDEBUG std::unordered_set changed; - for(const auto& [id, _] : result.NewOrChange[type]) + for(const auto& [id, _, _] : result.NewOrChange[type]) changed.insert(id); auto& lost = result.Lost[type]; @@ -421,4 +421,4 @@ AssetsPreloader::Out_bakeId AssetsPreloader::bakeIdTables() { return result; } -} \ No newline at end of file +} diff --git a/Src/Common/AssetsPreloader.hpp b/Src/Common/AssetsPreloader.hpp index faa79f9..47059b2 100644 --- a/Src/Common/AssetsPreloader.hpp +++ b/Src/Common/AssetsPreloader.hpp @@ -145,19 +145,40 @@ public: std::u8string Header; }; + struct BindDomainKeyInfo { + std::string Domain; + std::string Key; + }; + + struct BindHashHeaderInfo { + ResourceId Id; + Hash_t Hash; + std::u8string Header; + }; + struct Out_reloadResources { - std::unordered_map> Lost[(int) AssetType::MAX_ENUM]; std::unordered_map> NewOrChange[(int) AssetType::MAX_ENUM]; + std::unordered_map> Lost[(int) AssetType::MAX_ENUM]; }; struct Out_applyResourceChange { - std::vector Lost[(int) AssetType::MAX_ENUM]; - std::vector> NewOrChange[(int) AssetType::MAX_ENUM]; + std::array< + std::vector, + static_cast(AssetType::MAX_ENUM) + > NewOrChange; + + std::array< + std::vector, + static_cast(AssetType::MAX_ENUM) + > Lost; }; struct Out_bakeId { // Новые привязки - std::array>, static_cast(AssetType::MAX_ENUM)> IdToDK; + std::array< + std::vector, + static_cast(AssetType::MAX_ENUM) + > IdToDK; }; struct ReloadStatus { @@ -297,7 +318,7 @@ private: Многопоточная таблица обратного резолва. Идентификатор -> домен+ключ */ - std::array>, static_cast(AssetType::MAX_ENUM)> IdToDK; + std::array, static_cast(AssetType::MAX_ENUM)> IdToDK; /* Таблица в которой выделяются новые идентификаторы, которых не нашлось в DKToId. @@ -313,7 +334,7 @@ private: Списки в которых пишутся новые привязки. Начала спиской исходят из LastSendId. Id + LastSendId -> домен+ключ */ - std::array>>, static_cast(AssetType::MAX_ENUM)> NewIdToDK; + std::array>, static_cast(AssetType::MAX_ENUM)> NewIdToDK; // Загруженные ресурсы std::array, static_cast(AssetType::MAX_ENUM)> MediaResources; @@ -370,7 +391,7 @@ inline ResourceId AssetsPreloader::_getIdNew(AssetType type, std::string_view do auto lock2 = NewIdToDK[static_cast(type)].lock(); lock.unlock(); - lock2->emplace_back(domain, key); + lock2->emplace_back((std::string) domain, (std::string) key); return id; } @@ -389,17 +410,17 @@ inline std::optionalsecond; const MediaResource* res = getResource(type, id); - if (!res) { + if(!res) { HashToId.erase(iter); return std::nullopt; } - if (res->Hash != hash) { + if(res->Hash != hash) { HashToId.erase(iter); return std::nullopt; } diff --git a/Src/Common/Packets.hpp b/Src/Common/Packets.hpp index 4ad0ec2..27df237 100644 --- a/Src/Common/Packets.hpp +++ b/Src/Common/Packets.hpp @@ -83,106 +83,26 @@ enum struct L2System : uint8_t { } -namespace ToClient { +enum struct ToClient : uint8_t { + Init, // Первый пакет от сервера + Disconnect, // Отключаем клиента -/* - uint8_t+uint8_t - 0 - Системное - 0 - Инициализация WorldId_c+ObjectPos - 1 - Отключение от сервера String(Причина) - 2 - Привязка камеры к сущности EntityId_c - 3 - Отвязка камеры - 1 - Оповещение о доступном ресурсе - 0 - Текстура TextureId_c+Hash - 1 - Освобождение текстуры TextureId_c - 2 - Звук SoundId_c+Hash - 3 - Освобождение звука SoundId_c - 4 - Модель ModelId_c+Hash - 5 - Освобождение модели ModelId_c - 253 - Инициирование передачи ресурса StreamId+ResType+ResId+Size+Hash - 254 - Передача чанка данных StreamId+Size+Data - 255 - Передача отменена StreamId - 2 - Новые определения - 0 - Мир DefWorldId_c+определение - 1 - Освобождение мира DefWorldId_c - 2 - Воксель DefVoxelId_c+определение - 3 - Освобождение вокселя DefVoxelId_c - 4 - Нода DefNodeId_c+определение - 5 - Освобождение ноды DefNodeId_c - 6 - Портал DefPortalId_c+определение - 7 - Освобождение портала DefPortalId_c - 8 - Сущность DefEntityId_c+определение - 9 - Освобождение сущности DefEntityId_c - 3 - Новый контент - 0 - Мир, новый/изменён WorldId_c+... - 1 - Мир/Удалён WorldId_c - 2 - Портал, новый/изменён PortalId_c+... - 3 - Портал/Удалён PortalId_c - 4 - Сущность, новый/изменён EntityId_c+... - 5 - Сущность/Удалёна EntityId_c - 6 - Чанк/Воксели WorldId_c+GlobalChunk+... - 7 - Чанк/Ноды WorldId_c+GlobalChunk+... - 8 - Чанк/Призмы освещения WorldId_c+GlobalChunk+... - 9 - Чанк/Удалён WorldId_c+GlobalChunk + AssetsBindDK, // Привязка AssetsId к домен+ключ + AssetsBindHH, // Привязка AssetsId к hash+header + AssetsInitSend, // Начало отправки запрошенного клиентом ресурса + AssetsNextSend, // Продолжение отправки ресурса + DefinitionsUpdate, // Обновление и потеря профилей контента (воксели, ноды, сущности, миры, ...) + ChunkVoxels, // Обновление вокселей чанка + ChunkNodes, // Обновление нод чанка + ChunkLightPrism, // + RemoveRegion, // Удаление региона из зоны видимости -*/ + Tick, // Новые или потерянные игровые объекты (миры, сущности), динамичные данные такта (положение сущностей) -// Первый уровень -enum struct L1 : uint8_t { - System, - Resource, - Definition, - Content -}; - -// Второй уровень -enum struct L2System : uint8_t { - Init, - Disconnect, - LinkCameraToEntity, - UnlinkCamera, - SyncTick -}; - -enum struct L2Resource : uint8_t { - Bind, // Привязка идентификаторов ресурсов к хешам - Lost, - InitResSend = 253, - ChunkSend -}; - -enum struct L2Definition : uint8_t { - World, - FreeWorld, - Voxel, - FreeVoxel, - Node, - FreeNode, - Portal, - FreePortal, - Entity, - FreeEntity, - FuncEntity, - FreeFuncEntity, - Item, - FreeItem -}; - -enum struct L2Content : uint8_t { - World, - RemoveWorld, - Portal, - RemovePortal, - Entity, - RemoveEntity, - ChunkVoxels, - ChunkNodes, - ChunkLightPrism, - RemoveRegion + TestLinkCameraToEntity, // Привязываем камеру к сущности + TestUnlinkCamera, // Отвязываем от сущности }; } - -} diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index e7f9843..09330e2 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -619,35 +619,13 @@ void GameServer::BackingChunkPressure_t::run(int id) { } // Сжатие и отправка игрокам - struct PostponedV { - WorldId_t WorldId; - Pos::GlobalChunk Chunk; - CompressedVoxels Data; - }; - - struct PostponedN { - WorldId_t WorldId; - Pos::GlobalChunk Chunk; - CompressedNodes Data; - }; - - std::list>> postponedVoxels; - std::list>> postponedNodes; - - std::vector cecs; - for(auto& [worldId, world] : dump) { for(auto& [regionPos, region] : world) { for(auto& [chunkPos, chunk] : region.Voxels) { - CompressedVoxels cmp = compressVoxels(chunk); - Pos::GlobalChunk chunkPosR = (Pos::GlobalChunk(regionPos) << 2) + chunkPos; + std::u8string cmp = compressVoxels(chunk); for(auto& ptr : region.NewCECs) { - bool accepted = ptr->maybe_prepareChunkUpdate_Voxels(worldId, - chunkPosR, cmp.Compressed, cmp.Defines); - if(!accepted) { - cecs.push_back(ptr.get()); - } + ptr->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, cmp); } if((region.IsChunkChanged_Voxels >> chunkPos.pack()) & 0x1) { @@ -663,30 +641,16 @@ void GameServer::BackingChunkPressure_t::run(int id) { if(skip) continue; - bool accepted = ptr->maybe_prepareChunkUpdate_Voxels(worldId, - chunkPosR, cmp.Compressed, cmp.Defines); - if(!accepted) { - cecs.push_back(ptr.get()); - } + ptr->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, cmp); } } - - if(!cecs.empty()) { - postponedVoxels.push_back({{worldId, chunkPosR, std::move(cmp)}, cecs}); - cecs.clear(); - } } for(auto& [chunkPos, chunk] : region.Nodes) { - CompressedNodes cmp = compressNodes(chunk.data()); - Pos::GlobalChunk chunkPosR = (Pos::GlobalChunk(regionPos) << 2) + chunkPos; + std::u8string cmp = compressNodes(chunk.data()); for(auto& ptr : region.NewCECs) { - bool accepted = ptr->maybe_prepareChunkUpdate_Nodes(worldId, - chunkPosR, cmp.Compressed, cmp.Defines); - if(!accepted) { - cecs.push_back(ptr.get()); - } + ptr->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, cmp); } if((region.IsChunkChanged_Nodes >> chunkPos.pack()) & 0x1) { @@ -702,60 +666,9 @@ void GameServer::BackingChunkPressure_t::run(int id) { if(skip) continue; - bool accepted = ptr->maybe_prepareChunkUpdate_Nodes(worldId, - chunkPosR, cmp.Compressed, cmp.Defines); - if(!accepted) { - cecs.push_back(ptr.get()); - } + ptr->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, cmp); } } - - if(!cecs.empty()) { - postponedNodes.push_back({{worldId, chunkPosR, std::move(cmp)}, cecs}); - cecs.clear(); - } - } - } - } - - while(!postponedVoxels.empty() || !postponedNodes.empty()) { - { - auto begin = postponedVoxels.begin(), end = postponedVoxels.end(); - while(begin != end) { - auto& [worldId, chunkPos, cmp] = begin->first; - for(RemoteClient* cec : begin->second) { - bool accepted = cec->maybe_prepareChunkUpdate_Voxels(worldId, chunkPos, cmp.Compressed, cmp.Defines); - if(!accepted) - cecs.push_back(cec); - } - - if(cecs.empty()) { - begin = postponedVoxels.erase(begin); - } else { - begin->second = cecs; - cecs.clear(); - begin++; - } - } - } - - { - auto begin = postponedNodes.begin(), end = postponedNodes.end(); - while(begin != end) { - auto& [worldId, chunkPos, cmp] = begin->first; - for(RemoteClient* cec : begin->second) { - bool accepted = cec->maybe_prepareChunkUpdate_Nodes(worldId, chunkPos, cmp.Compressed, cmp.Defines); - if(!accepted) - cecs.push_back(cec); - } - - if(cecs.empty()) { - begin = postponedNodes.erase(begin); - } else { - begin->second = cecs; - cecs.clear(); - begin++; - } } } } @@ -1880,33 +1793,43 @@ void GameServer::stepModInitializations() { } void GameServer::reloadMods() { - LOG.info() << "Перезагрузка модов: ассеты и зависимости"; + std::vector packetsToSend; - AssetsPreloader::Out_reloadResources changes = Content.AM.reloadResources(AssetsInit); - AssetsPreloader::Out_applyResourceChange applied = Content.AM.applyResourceChange(changes); + LOG.info() << "Перезагрузка модов"; + { + // TODO: перезагрузка модов - size_t changedCount = 0; - size_t lostCount = 0; - for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) { - for(const auto& entry : applied.NewOrChange[type]) { - Content.OnContentChanges.AssetsInfo[type].push_back(entry.first); - changedCount++; + Content.CM.buildEndProfiles(); + } + + LOG.info() << "Перезагрузка ассетов"; + { + { + AssetsPreloader::Out_applyResourceChange applied + = Content.AM.applyResourceChange(Content.AM.reloadResources(AssetsInit)); + + if(!applied.NewOrChange.empty() || !applied.Lost.empty()) + packetsToSend.push_back( + RemoteClient::makePacket_informateAssets_HH( + applied.NewOrChange, + applied.Lost + ) + ); } + - lostCount += applied.Lost[type].size(); + { + AssetsPreloader::Out_bakeId baked = Content.AM.bakeIdTables(); + if(!baked.IdToDK.empty()) + packetsToSend.push_back(RemoteClient::makePacket_informateAssets_DK(baked.IdToDK)); + } } - Content.CM.markAllProfilesDirty(EnumDefContent::Node); - Content.CM.buildEndProfiles(); - - std::vector nodeIds = Content.CM.collectProfileIds(EnumDefContent::Node); - if(!nodeIds.empty()) { - Content.OnContentChanges.Node.append_range(nodeIds); + // Отправка пакетов + for(std::shared_ptr& cec : Game.RemoteClients) { + auto copy = packetsToSend; + cec->pushPackets(©); } - - LOG.info() << "Перезагрузка завершена: обновлено ассетов=" << changedCount - << " удалено=" << lostCount - << " нод=" << nodeIds.size(); } IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() { @@ -2678,107 +2601,48 @@ void GameServer::stepSyncContent() { } - // Сбор запросов на ресурсы и профили + отправка пакетов игрокам + // Сбор запросов на ресурсы + отправка пакетов игрокам ResourceRequest full = std::move(Content.OnContentChanges); for(std::shared_ptr& cec : Game.RemoteClients) { - full.insert(cec->pushPreparedPackets()); + full.merge(cec->pushPreparedPackets()); } full.uniq(); - // Информируем о запрошенных ассетах - std::vector> resources; - for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) { - for(ResourceId resId : full.AssetsInfo[type]) { - const AssetsPreloader::MediaResource* media = Content.AM.getResource((EnumAssets) type, resId); - if(!media) - continue; - - Resource resource(media->Resource->data(), media->Resource->size()); - resources.emplace_back((EnumAssets) type, resId, media->Domain, media->Key, std::move(resource), media->Dependencies); + std::vector packetsToAll; + { + AssetsPreloader::Out_bakeId baked = Content.AM.bakeIdTables(); + if(!baked.IdToDK.empty()) { + packetsToAll.push_back(RemoteClient::makePacket_informateAssets_DK(baked.IdToDK)); } } + // Оповещаем о двоичных ресурсах по запросу + std::vector binaryResources; for(const Hash_t& hash : full.Hashes) { - std::optional> result = Content.AM.getResource(hash); + std::optional< + std::tuple + > result = Content.AM.getResource(hash); + if(!result) continue; auto& [type, id, media] = *result; Resource resource(*media->Resource); - resources.emplace_back(type, id, media->Domain, media->Key, std::move(resource), media->Header); - } - - // Информируем о запрошенных профилях - std::vector> voxels; - for(DefVoxelId id : full.Voxel) { - auto value = Content.CM.getProfile_Voxel(id); - if(!value) - continue; - - voxels.emplace_back(id, *value); - } - - std::vector> nodes; - for(DefNodeId id : full.Node) { - auto value = Content.CM.getProfile_Node(id); - if(!value) - continue; - - nodes.emplace_back(id, *value); - } - - std::vector> worlds; - for(DefWorldId id : full.World) { - auto value = Content.CM.getProfile_World(id); - if(!value) - continue; - - worlds.emplace_back(id, *value); - } - - std::vector> portals; - for(DefPortalId id : full.Portal) { - auto value = Content.CM.getProfile_Portal(id); - if(!value) - continue; - - portals.emplace_back(id, *value); - } - - std::vector> entities; - for(DefEntityId id : full.Entity) { - auto value = Content.CM.getProfile_Entity(id); - if(!value) - continue; - - entities.emplace_back(id, *value); - } - - std::vector> items; - for(DefItemId id : full.Item) { - auto value = Content.CM.getProfile_Item(id); - if(!value) - continue; - - items.emplace_back(id, *value); + binaryResources.push_back(AssetBinaryInfo{ + .Data = std::move(resource), + .Hash = media->Hash + }); } for(std::shared_ptr& remoteClient : Game.RemoteClients) { - if(!resources.empty()) - remoteClient->informateAssets(resources); - if(!voxels.empty()) - remoteClient->informateDefVoxel(voxels); - if(!nodes.empty()) - remoteClient->informateDefNode(nodes); - if(!worlds.empty()) - remoteClient->informateDefWorld(worlds); - if(!portals.empty()) - remoteClient->informateDefPortal(portals); - if(!entities.empty()) - remoteClient->informateDefEntity(entities); - if(!items.empty()) - remoteClient->informateDefItem(items); + if(!binaryResources.empty()) + remoteClient->informateBinaryAssets(binaryResources); + + if(!packetsToAll.empty()) { + auto copy = packetsToAll; + remoteClient->pushPackets(©); + } } diff --git a/Src/Server/GameServer.hpp b/Src/Server/GameServer.hpp index ca09eee..1a28f15 100644 --- a/Src/Server/GameServer.hpp +++ b/Src/Server/GameServer.hpp @@ -80,8 +80,8 @@ class GameServer : public AsyncObject { ResourceRequest OnContentChanges; - ContentObj(asio::io_context& ioc) - : AM(ioc), CM(AM) + ContentObj(asio::io_context&) + : AM(), CM(AM) {} } Content; diff --git a/Src/Server/RemoteClient.cpp b/Src/Server/RemoteClient.cpp index 326dbb8..6af8c05 100644 --- a/Src/Server/RemoteClient.cpp +++ b/Src/Server/RemoteClient.cpp @@ -11,11 +11,153 @@ #include #include #include -#include "sha2.hpp" namespace LV::Server { +Net::Packet RemoteClient::makePacket_informateAssets_DK( + const std::array< + std::vector, + static_cast(EnumAssets::MAX_ENUM) + >& dkVector +) { + Net::Packet pack; + + // Сжатие по дедубликации доменов + std::unordered_map domainsToId; + + { + std::unordered_set domains; + + for(size_t type = 0; type < static_cast(EnumAssets::MAX_ENUM); type++) { + for(const auto& bind : dkVector[type]) { + domains.insert(bind.Domain); + } + } + + pack << uint16_t(domains.size()); + + int counter = 0; + for(const std::string& domain : domains) { + pack << domain; + domainsToId[domain] = counter++; + } + } + + // Запись связок домен+ключ + for(size_t type = 0; type < static_cast(EnumAssets::MAX_ENUM); type++) { + const std::vector& binds = dkVector[type]; + pack << uint32_t(binds.size()); + + for(const auto& bind : binds) { + auto iter = domainsToId.find(bind.Domain); + assert(iter != domainsToId.end()); + + pack << iter->second << bind.Key; + } + } + + // Сжатие + std::u8string compressed = compressLinear(pack.complite()); + pack << uint8_t(ToClient::AssetsBindDK) << (const std::string&) compressed; + + return pack; +} + +Net::Packet RemoteClient::makePacket_informateAssets_HH( + const std::array< + std::vector, + static_cast(EnumAssets::MAX_ENUM) + >& hhVector, + const std::array< + std::vector, + static_cast(EnumAssets::MAX_ENUM) + >& lost +) { + Net::Packet pack; + pack << uint8_t(ToClient::AssetsBindHH); + + // Запись связок hash+header + for(size_t type = 0; type < static_cast(EnumAssets::MAX_ENUM); type++) { + const std::vector& binds = hhVector[type]; + pack << uint32_t(binds.size()); + + for(const auto& bind : binds) { + pack << bind.Id; + pack.write((const std::byte*) bind.Hash.data(), bind.Hash.size()); + pack << (const std::string&) bind.Header; + } + } + + return pack; +} + +std::vector RemoteClient::makePackets_sendDefContentUpdate( + std::array< + std::vector< + std::pair< + ResourceId, // Идентификатор профиля + std::u8string // Двоичный формат профиля + > + >, + static_cast(EnumDefContent::MAX_ENUM) + > newOrUpdate, // Новые или изменённые + std::array< + std::vector, + static_cast(EnumDefContent::MAX_ENUM) + > lost, // Потерянные профили + std::array< + std::vector>, + static_cast(EnumDefContent::MAX_ENUM) + > idToDK // Новые привязки +) { + std::vector packets; + Net::Packet pack; + + auto check = [&](size_t needSize) { + if(pack.size()+needSize > 65500) { + packets.emplace_back(std::move(pack)); + pack.clear(); + } + }; + + pack << (uint8_t) ToClient::DefinitionsUpdate; + pack << uint32_t(newOrUpdate.size()); + for(size_t type = 0; type < static_cast(EnumDefContent::MAX_ENUM); type++) { + pack << uint32_t(newOrUpdate[type].size()); + + for(const auto& [id, data] : newOrUpdate[type]) { + check(data.size()); + pack << id << (const std::string&) data; + } + } + + pack << uint32_t(lost.size()); + for(size_t type = 0; type < static_cast(EnumDefContent::MAX_ENUM); type++) { + pack << uint32_t(lost[type].size()); + + for(ResourceId id : lost[type]) { + check(4); + pack << id; + } + } + + pack << uint32_t(idToDK.size()); + for(size_t type = 0; type < static_cast(EnumDefContent::MAX_ENUM); type++) { + pack << uint32_t(idToDK[type].size()); + + for(const auto& [domain, key] : idToDK[type]) { + check(domain.size() + key.size() + 8); + pack << key << domain; + } + } + + if(pack.size()) + packets.emplace_back(std::move(pack)); + + return packets; +} + namespace { const char* assetTypeName(EnumAssets type) { @@ -71,8 +213,7 @@ void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) { IsGoingShutdown = true; Net::Packet packet; - packet << (uint8_t) ToClient::L1::System - << (uint8_t) ToClient::L2System::Disconnect + packet << (uint8_t) ToClient::Disconnect << (uint8_t) type << reason; std::string info; @@ -88,309 +229,64 @@ void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) { LOG.info() << "Игрок '" << Username << "' отключился " << info; } -void RemoteClient::NetworkAndResource_t::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels, - const std::vector& uniq_sorted_defines) -{ - Pos::bvec4u localChunk = chunkPos & 0x3; - Pos::GlobalRegion regionPos = chunkPos >> 2; - /* - Обновить зависимости - Запросить недостающие - Отправить всё клиенту - */ +// void RemoteClient::prepareChunkUpdate_Voxels( +// WorldId_t worldId, +// Pos::GlobalChunk chunkPos, +// const std::u8string& compressed_voxels +// ) { +// Pos::bvec4u localChunk = chunkPos & 0x3; +// Pos::GlobalRegion regionPos = chunkPos >> 2; - std::vector - newTypes, /* Новые типы вокселей */ - lostTypes /* Потерянные типы вокселей */; +// packet << (uint8_t) ToClient::ChunkVoxels +// << worldId << chunkPos.pack() << uint32_t(compressed_voxels.size()); +// packet.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size()); +// } - // Отметим использование этих вокселей - for(const DefVoxelId& id : uniq_sorted_defines) { - auto iter = ResUses.DefVoxel.find(id); - if(iter == ResUses.DefVoxel.end()) { - // Новый тип - newTypes.push_back(id); - ResUses.DefVoxel[id] = 1; - } else { - // Увеличиваем счётчик - iter->second++; - } - } +// void RemoteClient::prepareChunkUpdate_Nodes( +// WorldId_t worldId, +// Pos::GlobalChunk chunkPos, +// const std::u8string& compressed_nodes +// ) { +// Pos::bvec4u localChunk = chunkPos & 0x3; +// Pos::GlobalRegion regionPos = chunkPos >> 2; - auto iterWorld = ResUses.RefChunk.find(worldId); - - if(iterWorld != ResUses.RefChunk.end()) - // Исключим зависимости предыдущей версии чанка - { - auto iterRegion = iterWorld->second.find(regionPos); - if(iterRegion != iterWorld->second.end()) { - // Уменьшим счётчик зависимостей - for(const DefVoxelId& id : iterRegion->second[localChunk.pack()].Voxel) { - auto iter = ResUses.DefVoxel.find(id); - assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях - if(--iter->second == 0) { - // Вокселя больше нет в зависимостях - lostTypes.push_back(id); - ResUses.DefVoxel.erase(iter); - } - } - } - } else { - ResUses.RefChunk[worldId] = {}; - iterWorld = ResUses.RefChunk.find(worldId); - } - - iterWorld->second[regionPos][localChunk.pack()].Voxel = uniq_sorted_defines; - - if(!newTypes.empty()) { - // Добавляем новые типы в запрос - NextRequest.Voxel.insert(NextRequest.Voxel.end(), newTypes.begin(), newTypes.end()); - for(DefVoxelId voxel : newTypes) - ResUses.RefDefVoxel[voxel] = {}; - } - - if(!lostTypes.empty()) { - for(const DefVoxelId& id : lostTypes) { - auto iter = ResUses.RefDefVoxel.find(id); - assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя - decrementAssets(std::move(iter->second)); - ResUses.RefDefVoxel.erase(iter); - - checkPacketBorder(16); - NextPacket << (uint8_t) ToClient::L1::Definition - << (uint8_t) ToClient::L2Definition::FreeVoxel - << id; - } - } - - checkPacketBorder(4+4+8+2+4+compressed_voxels.size()); - NextPacket << (uint8_t) ToClient::L1::Content - << (uint8_t) ToClient::L2Content::ChunkVoxels - << worldId << chunkPos.pack() << uint32_t(compressed_voxels.size()); - NextPacket.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size()); -} - -void RemoteClient::NetworkAndResource_t::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes, - const std::vector& uniq_sorted_defines) -{ - Pos::bvec4u localChunk = chunkPos & 0x3; - Pos::GlobalRegion regionPos = chunkPos >> 2; - - std::vector - newTypes, /* Новые типы нод */ - lostTypes /* Потерянные типы нод */; - - // Отметим использование этих нод - for(const DefNodeId& id : uniq_sorted_defines) { - auto iter = ResUses.DefNode.find(id); - if(iter == ResUses.DefNode.end()) { - // Новый тип - newTypes.push_back(id); - ResUses.DefNode[id] = 1; - } else { - // Увеличиваем счётчик - iter->second++; - } - } - - auto iterWorld = ResUses.RefChunk.find(worldId); - - if(iterWorld != ResUses.RefChunk.end()) - // Исключим зависимости предыдущей версии чанка - { - auto iterRegion = iterWorld->second.find(regionPos); - if(iterRegion != iterWorld->second.end()) { - // Уменьшим счётчик зависимостей - for(const DefNodeId& id : iterRegion->second[localChunk.pack()].Node) { - auto iter = ResUses.DefNode.find(id); - assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях - if(--iter->second == 0) { - // Ноды больше нет в зависимостях - lostTypes.push_back(id); - ResUses.DefNode.erase(iter); - } - } - } - } else { - ResUses.RefChunk[worldId] = {}; - iterWorld = ResUses.RefChunk.find(worldId); - } - - iterWorld->second[regionPos][localChunk.pack()].Node = uniq_sorted_defines; - - if(!newTypes.empty()) { - // Добавляем новые типы в запрос - NextRequest.Node.insert(NextRequest.Node.end(), newTypes.begin(), newTypes.end()); - for(DefNodeId node : newTypes) - ResUses.RefDefNode[node] = {}; - } - - if(!lostTypes.empty()) { - for(const DefNodeId& id : lostTypes) { - auto iter = ResUses.RefDefNode.find(id); - assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды - decrementAssets(std::move(iter->second)); - ResUses.RefDefNode.erase(iter); - - checkPacketBorder(16); - NextPacket << (uint8_t) ToClient::L1::Definition - << (uint8_t) ToClient::L2Definition::FreeNode - << id; - } - } - - checkPacketBorder(4+4+8+4+compressed_nodes.size()); - NextPacket << (uint8_t) ToClient::L1::Content - << (uint8_t) ToClient::L2Content::ChunkNodes - << worldId << chunkPos.pack() << uint32_t(compressed_nodes.size()); - NextPacket.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size()); -} +// packet << (uint8_t) ToClient::ChunkNodes +// << worldId << chunkPos.pack() << uint32_t(compressed_nodes.size()); +// packet.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size()); +// } void RemoteClient::NetworkAndResource_t::prepareRegionsRemove(WorldId_t worldId, std::vector regionPoses) { - std::vector - lostTypesV /* Потерянные типы вокселей */; - std::vector - lostTypesN /* Потерянные типы нод */; - - for(Pos::GlobalRegion regionPos : regionPoses) - // Уменьшаем зависимости вокселей и нод - { - auto iterWorld = ResUses.RefChunk.find(worldId); - if(iterWorld == ResUses.RefChunk.end()) - return; - - auto iterRegion = iterWorld->second.find(regionPos); - if(iterRegion == iterWorld->second.end()) - return; - - for(const auto &iterChunk : iterRegion->second) { - for(const DefVoxelId& id : iterChunk.Voxel) { - auto iter = ResUses.DefVoxel.find(id); - assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях - if(--iter->second == 0) { - // Вокселя больше нет в зависимостях - lostTypesV.push_back(id); - ResUses.DefVoxel.erase(iter); - } - } - - for(const DefNodeId& id : iterChunk.Node) { - auto iter = ResUses.DefNode.find(id); - assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях - if(--iter->second == 0) { - // Ноды больше нет в зависимостях - lostTypesN.push_back(id); - ResUses.DefNode.erase(iter); - } - } - } - - iterWorld->second.erase(iterRegion); - } - - if(!lostTypesV.empty()) { - for(const DefVoxelId& id : lostTypesV) { - auto iter = ResUses.RefDefVoxel.find(id); - assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя - decrementAssets(std::move(iter->second)); - ResUses.RefDefVoxel.erase(iter); - - checkPacketBorder(16); - NextPacket << (uint8_t) ToClient::L1::Definition - << (uint8_t) ToClient::L2Definition::FreeVoxel - << id; - } - } - - if(!lostTypesN.empty()) { - for(const DefNodeId& id : lostTypesN) { - auto iter = ResUses.RefDefNode.find(id); - assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды - decrementAssets(std::move(iter->second)); - ResUses.RefDefNode.erase(iter); - - checkPacketBorder(16); - NextPacket << (uint8_t) ToClient::L1::Definition - << (uint8_t) ToClient::L2Definition::FreeNode - << id; - } - } - - for(Pos::GlobalRegion regionPos : regionPoses) { checkPacketBorder(16); - NextPacket << (uint8_t) ToClient::L1::Content - << (uint8_t) ToClient::L2Content::RemoveRegion + NextPacket << (uint8_t) ToClient::RemoveRegion << worldId << regionPos.pack(); } } void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate(const std::vector>& entities) { - for(auto& [entityId, entity] : entities) { - // Сопоставим с идентификатором клиента - ClientEntityId_t ceId = ReMapEntities.toClient(entityId); + // for(auto& [entityId, entity] : entities) { + // // Сопоставим с идентификатором клиента + // ClientEntityId_t ceId = ReMapEntities.toClient(entityId); - // Профиль новый - { - DefEntityId profile = entity->getDefId(); - auto iter = ResUses.DefEntity.find(profile); - if(iter == ResUses.DefEntity.end()) { - // Клиенту неизвестен профиль - NextRequest.Entity.push_back(profile); - ResUses.DefEntity[profile] = 1; - } else - iter->second++; - } + // checkPacketBorder(32); + // NextPacket << (uint8_t) ToClient::Entity + // << ceId + // << (uint32_t) entity->getDefId() + // << (uint32_t) entity->WorldId + // << entity->Pos.x + // << entity->Pos.y + // << entity->Pos.z; - // Добавление модификационных зависимостей - // incrementBinary({}, {}, {}, {}, {}); - - // Старые данные - { - auto iterEntity = ResUses.RefEntity.find(entityId); - if(iterEntity != ResUses.RefEntity.end()) { - // Убавляем зависимость к старому профилю - auto iterProfile = ResUses.DefEntity.find(iterEntity->second.Profile); - assert(iterProfile != ResUses.DefEntity.end()); // Старый профиль должен быть - if(--iterProfile->second == 0) { - // Старый профиль больше не нужен - auto iterProfileRef = ResUses.RefDefEntity.find(iterEntity->second.Profile); - decrementAssets(std::move(iterProfileRef->second)); - ResUses.DefEntity.erase(iterProfile); - } - - // Убавляем зависимость к модификационным данным - // iterEntity->second. - // decrementBinary({}, {}, {}, {}, {}); - } - } - - ResUses_t::RefEntity_t refEntity; - refEntity.Profile = entity->getDefId(); - if(!ResUses.RefDefEntity.contains(refEntity.Profile)) - ResUses.RefDefEntity[refEntity.Profile] = {}; - - checkPacketBorder(32); - NextPacket << (uint8_t) ToClient::L1::Content - << (uint8_t) ToClient::L2Content::Entity - << ceId - << (uint32_t) refEntity.Profile - << (uint32_t) entity->WorldId - << entity->Pos.x - << entity->Pos.y - << entity->Pos.z; - - { - ToServer::PacketQuat q; - q.fromQuat(entity->Quat); - for(int iter = 0; iter < 5; iter++) - NextPacket << q.Data[iter]; - } - - ResUses.RefEntity[entityId] = std::move(refEntity); - } + // { + // ToServer::PacketQuat q; + // q.fromQuat(entity->Quat); + // for(int iter = 0; iter < 5; iter++) + // NextPacket << q.Data[iter]; + // } + // } } void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate_Dynamic(const std::vector>& entities) @@ -405,114 +301,23 @@ void RemoteClient::NetworkAndResource_t::prepareEntitySwap(ServerEntityId_t prev void RemoteClient::NetworkAndResource_t::prepareEntitiesRemove(const std::vector& entityIds) { - for(ServerEntityId_t entityId : entityIds) { - ClientEntityId_t cId = ReMapEntities.erase(entityId); + // for(ServerEntityId_t entityId : entityIds) { + // ClientEntityId_t cId = ReMapEntities.erase(entityId); - // Убавляем старые данные - { - auto iterEntity = ResUses.RefEntity.find(entityId); - assert(iterEntity != ResUses.RefEntity.end()); // Зависимости должны быть - - // Убавляем модификационные заависимости - //decrementBinary(std::vector &&textures, std::vector &&animation, std::vector &&sounds, std::vector &&models, std::vector &&fonts) - - // Убавляем зависимость к профилю - auto iterProfile = ResUses.DefEntity.find(iterEntity->second.Profile); - assert(iterProfile != ResUses.DefEntity.end()); // Профиль должен быть - if(--iterProfile->second == 0) { - // Профиль больше не используется - auto iterProfileRef = ResUses.RefDefEntity.find(iterEntity->second.Profile); - - decrementAssets(std::move(iterProfileRef->second)); - - ResUses.RefDefEntity.erase(iterProfileRef); - ResUses.DefEntity.erase(iterProfile); - } - - ResUses.RefEntity.erase(iterEntity); - } - - checkPacketBorder(16); - NextPacket << (uint8_t) ToClient::L1::Content - << (uint8_t) ToClient::L2Content::RemoveEntity - << cId; - } + // checkPacketBorder(16); + // NextPacket << (uint8_t) ToClient::L1::Content + // << (uint8_t) ToClient::L2Content::RemoveEntity + // << cId; + // } } void RemoteClient::NetworkAndResource_t::prepareWorldUpdate(WorldId_t worldId, World* world) { - // Добавление зависимостей - ResUses.RefChunk[worldId]; - - // Профиль - { - DefWorldId defWorld = world->getDefId(); - auto iterWorldProf = ResUses.DefWorld.find(defWorld); - if(iterWorldProf == ResUses.DefWorld.end()) { - // Профиль мира неизвестен клиенту - ResUses.DefWorld[defWorld] = 1; - NextRequest.World.push_back(defWorld); - } else { - iterWorldProf->second++; - } - } - - // Если есть предыдущая версия мира - { - auto iterWorld = ResUses.RefWorld.find(worldId); - if(iterWorld != ResUses.RefWorld.end()) { - // Мир известен клиенту - - // Убавляем модицикационные зависимости предыдущей версии мира - // iterWorld->second. - - // Убавляем зависимости старого профиля - auto iterWorldProf = ResUses.DefWorld.find(iterWorld->second.Profile); - assert(iterWorldProf != ResUses.DefWorld.end()); // Старый профиль должен быть известен - if(--iterWorldProf->second == 0) { - // Старый профиль более ни кем не используется - ResUses.DefWorld.erase(iterWorldProf); - auto iterWorldProfRef = ResUses.RefDefWorld.find(iterWorld->second.Profile); - assert(iterWorldProfRef != ResUses.RefDefWorld.end()); // Зависимости предыдущего профиля также должны быть - decrementAssets(std::move(iterWorldProfRef->second)); - ResUses.RefDefWorld.erase(iterWorldProfRef); - } - } - } - - // Указываем модификационные зависимости текущей версии мира - ResUses.RefWorld[worldId] = {world->getDefId()}; - // TODO: отправить мир } void RemoteClient::NetworkAndResource_t::prepareWorldRemove(WorldId_t worldId) { - // Чанки уже удалены prepareChunkRemove - // Обновление зависимостей - auto iterWorld = ResUses.RefWorld.find(worldId); - assert(iterWorld != ResUses.RefWorld.end()); - - // Убавляем модификационные зависимости - // decrementBinary(std::move(iterWorld->second.Texture), std::move(iterWorld->second.Model), {}, {}); - - auto iterWorldProf = ResUses.DefWorld.find(iterWorld->second.Profile); - assert(iterWorldProf != ResUses.DefWorld.end()); // Профиль мира должен быть - if(--iterWorldProf->second == 0) { - // Профиль мира более не используется - ResUses.DefWorld.erase(iterWorldProf); - // Убавляем зависимости профиля - auto iterWorldProfDef = ResUses.RefDefWorld.find(iterWorld->second.Profile); - assert(iterWorldProfDef != ResUses.RefDefWorld.end()); // Зависимости профиля должны быть - decrementAssets(std::move(iterWorldProfDef->second)); - ResUses.RefDefWorld.erase(iterWorldProfDef); - } - - ResUses.RefWorld.erase(iterWorld); - - auto iter = ResUses.RefChunk.find(worldId); - assert(iter->second.empty()); - ResUses.RefChunk.erase(iter); } // void RemoteClient::NetworkAndResource_t::preparePortalUpdate(PortalId portalId, void* portal) {} @@ -522,8 +327,7 @@ void RemoteClient::prepareCameraSetEntity(ServerEntityId_t entityId) { auto lock = NetworkAndResource.lock(); ClientEntityId_t cId = lock->ReMapEntities.toClient(entityId); lock->checkPacketBorder(8); - lock->NextPacket << (uint8_t) ToClient::L1::System - << (uint8_t) ToClient::L2System::LinkCameraToEntity + lock->NextPacket << (uint8_t) ToClient::TestLinkCameraToEntity << cId; } @@ -551,7 +355,7 @@ ResourceRequest RemoteClient::pushPreparedPackets() { { Net::Packet p; - p << (uint8_t) ToClient::L1::System << (uint8_t) ToClient::L2System::SyncTick; + p << (uint8_t) ToClient::Tick; toSend.push_back(std::move(p)); } @@ -563,192 +367,27 @@ ResourceRequest RemoteClient::pushPreparedPackets() { return std::move(nextRequest); } -void RemoteClient::informateAssets(const std::vector>>& resources) +void RemoteClient::informateBinaryAssets(const std::vector& resources) { - std::vector>> newForClient; - static std::atomic debugSendLogCount = 0; - - for(auto& [type, resId, domain, key, resource, header] : resources) { - auto hash = resource.hash(); - Hash_t headerHash = sha2::sha256(header.data(), header.size()); + for(const AssetBinaryInfo& resource : resources) { auto lock = NetworkAndResource.lock(); - - // Проверка запрашиваемых клиентом ресурсов - { - auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), hash); - if(iter != lock->ClientRequested.end()) - { - lock->ClientRequested.erase(iter); - lock.unlock(); - - auto it = std::lower_bound(AssetsInWork.OnClient.begin(), AssetsInWork.OnClient.end(), hash); - - if(it == AssetsInWork.OnClient.end() || *it != hash) { - AssetsInWork.OnClient.insert(it, hash); - AssetsInWork.ToSend.emplace_back(type, domain, key, resId, resource, 0); - if(domain == "test" - && (type == EnumAssets::Nodestate - || type == EnumAssets::Model - || type == EnumAssets::Texture)) - { - if(debugSendLogCount.fetch_add(1) < 64) { - LOG.debug() << "Queue resource send type=" << assetTypeName(type) - << " id=" << resId - << " key=" << domain << ':' << key - << " size=" << resource.size() - << " hash=" << int(hash[0]) << '.' - << int(hash[1]) << '.' - << int(hash[2]) << '.' - << int(hash[3]); - } - } - } else { - LOG.warn() << "Клиент повторно запросил имеющийся у него ресурс"; - } - - lock = NetworkAndResource.lock(); - } - } - - // Информирование клиента о привязках ресурсов к идентификатору - { - // Посмотрим что известно клиенту - if(auto iter = lock->ResUses.AssetsUse[(int) type].find(resId); - iter != lock->ResUses.AssetsUse[(int) type].end() - && (iter->second.second.Hash != hash || iter->second.second.HeaderHash != headerHash) - ) { - lock.unlock(); - // Требуется перепривязать идентификатор к новому хешу - newForClient.push_back({(EnumAssets) type, resId, domain, key, hash, header}); - iter->second.second.Hash = hash; - iter->second.second.HeaderHash = headerHash; - } - } - } - - // Отправляем новые привязки ресурсов - if(!newForClient.empty()) { - assert(newForClient.size() < 65535*4); - auto lock = NetworkAndResource.lock(); - - lock->checkPacketBorder(2+1+4); - lock->NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение - << ((uint8_t) ToClient::L2Resource::Bind) << uint32_t(newForClient.size()); - - for(auto& [type, resId, domain, key, hash, header] : newForClient) { - // TODO: может внести ограничение на длину домена и ключа? - const size_t entrySize = 1 + 4 + 2 + domain.size() + 2 + key.size() + 32 + 4 + header.size(); - lock->checkPacketBorder(entrySize); - lock->NextPacket << uint8_t(type) << uint32_t(resId) << domain << key; - lock->NextPacket.write((const std::byte*) hash.data(), hash.size()); - lock->NextPacket << uint32_t(header.size()); - if(!header.empty()) - lock->NextPacket.write((const std::byte*) header.data(), header.size()); - } - } -} - -void RemoteClient::NetworkAndResource_t::informateDefVoxel(const std::vector>& voxels) -{ - for(auto pair : voxels) { - DefVoxelId id = pair.first; - if(!ResUses.DefVoxel.contains(id)) + auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), resource.Hash); + if(iter == lock->ClientRequested.end()) continue; - NextPacket << (uint8_t) ToClient::L1::Definition - << (uint8_t) ToClient::L2Definition::Voxel - << id; - } -} + lock->ClientRequested.erase(iter); + lock.unlock(); -void RemoteClient::NetworkAndResource_t::informateDefNode(const std::vector>& nodes) -{ - for(auto& [id, def] : nodes) { - if(!ResUses.DefNode.contains(id)) - continue; - - checkPacketBorder(1+1+4+4); - NextPacket << (uint8_t) ToClient::L1::Definition - << (uint8_t) ToClient::L2Definition::Node - << id << (uint32_t) def->NodestateId; - - ResUses_t::RefAssets_t refs; - { - refs.Resources[(uint8_t) EnumAssets::Nodestate].push_back(def->NodestateId); - refs.Resources[(uint8_t) EnumAssets::Texture] = def->TextureDeps; - refs.Resources[(uint8_t) EnumAssets::Model] = def->ModelDeps; - - incrementAssets(refs); - } - - { - auto iterDefRef = ResUses.RefDefNode.find(id); - if(iterDefRef != ResUses.RefDefNode.end()) { - decrementAssets(std::move(iterDefRef->second)); - iterDefRef->second = std::move(refs); - } else { - ResUses.RefDefNode[id] = std::move(refs); - } + auto it = std::lower_bound(AssetsInWork.OnClient.begin(), AssetsInWork.OnClient.end(), resource.Hash); + if(it == AssetsInWork.OnClient.end() || *it != resource.Hash) { + AssetsInWork.OnClient.insert(it, resource.Hash); + AssetsInWork.ToSend.emplace_back(resource.Data, 0); + } else { + LOG.warn() << "Клиент повторно запросил имеющийся у него ресурс"; } } } -void RemoteClient::NetworkAndResource_t::informateDefWorld(const std::vector>& worlds) -{ - // for(auto pair : worlds) { - // DefWorldId_t id = pair.first; - // if(!ResUses.DefWorld.contains(id)) - // continue; - - // NextPacket << (uint8_t) ToClient::L1::Definition - // << (uint8_t) ToClient::L2Definition::World - // << id; - // } -} - -void RemoteClient::NetworkAndResource_t::informateDefPortal(const std::vector>& portals) -{ - // for(auto pair : portals) { - // DefPortalId_t id = pair.first; - // if(!ResUses.DefPortal.contains(id)) - // continue; - - // NextPacket << (uint8_t) ToClient::L1::Definition - // << (uint8_t) ToClient::L2Definition::Portal - // << id; - // } -} - -void RemoteClient::NetworkAndResource_t::informateDefEntity(const std::vector>& entityes) -{ - for(auto pair : entityes) { - DefEntityId id = pair.first; - if(!ResUses.DefEntity.contains(id)) - continue; - - checkPacketBorder(8); - NextPacket << (uint8_t) ToClient::L1::Definition - << (uint8_t) ToClient::L2Definition::Entity - << id; - - if(!ResUses.RefDefEntity.contains(id)) - ResUses.RefDefEntity[id] = {}; - } -} - -void RemoteClient::NetworkAndResource_t::informateDefItem(const std::vector>& items) -{ - // for(auto pair : items) { - // DefItemId_t id = pair.first; - // if(!ResUses.DefNode.contains(id)) - // continue; - - // NextPacket << (uint8_t) ToClient::L1::Definition - // << (uint8_t) ToClient::L2Definition::FuncEntity - // << id; - // } -} - void RemoteClient::protocolError() { shutdown(EnumDisconnect::ProtocolError, "Ошибка протокола"); } @@ -850,48 +489,6 @@ coro<> RemoteClient::rP_System(Net::AsyncSocket &sock) { } } -void RemoteClient::NetworkAndResource_t::incrementAssets(const ResUses_t::RefAssets_t& bin) { - for(int iter = 0; iter < 5; iter++) { - auto &use = ResUses.AssetsUse[iter]; - - for(ResourceId id : bin.Resources[iter]) { - if(++std::get<0>(use[id]) == 1) { - NextRequest.AssetsInfo[iter].push_back(id); - // LOG.debug() << "Новое определение (тип " << iter << ") -> " << id; - } - } - } -} - -void RemoteClient::NetworkAndResource_t::decrementAssets(ResUses_t::RefAssets_t&& bin) { - std::vector> lost; - - for(int iter = 0; iter < (int) EnumAssets::MAX_ENUM; iter++) { - auto &use = ResUses.AssetsUse[iter]; - - for(ResourceId id : bin.Resources[iter]) { - if(--std::get<0>(use[id]) == 0) { - use.erase(use.find(id)); - - lost.push_back({(EnumAssets) iter, id}); - // LOG.debug() << "Потеряно определение (тип " << iter << ") -> " << id; - } - } - } - - if(!lost.empty()) { - assert(lost.size() < 65535*4); - - checkPacketBorder(1+1+4+lost.size()*(1+4)); - NextPacket << (uint8_t) ToClient::L1::Resource - << (uint8_t) ToClient::L2Resource::Lost - << uint32_t(lost.size()); - - for(auto& [type, id] : lost) - NextPacket << uint8_t(type) << uint32_t(id); - } -} - void RemoteClient::onUpdate() { Pos::Object cameraPos = CameraPos; @@ -944,36 +541,15 @@ void RemoteClient::onUpdate() { bool hasFullSended = false; - for(auto& [type, domain, key, id, res, sended] : toSend) { + for(auto& [res, sended] : toSend) { if(sended == 0) { // Оповещаем о начале отправки ресурса - const size_t initSize = 1 + 1 + 4 + 32 + 4 + 1 - + 2 + domain.size() - + 2 + key.size(); + const size_t initSize = 1 + 1 + 4 + 32 + 4 + 1; if(p.size() + initSize > kMaxAssetPacketSize) flushAssetsPacket(); - p << (uint8_t) ToClient::L1::Resource - << (uint8_t) ToClient::L2Resource::InitResSend + p << (uint8_t) ToClient::AssetsInitSend << uint32_t(res.size()); p.write((const std::byte*) res.hash().data(), 32); - p << uint32_t(id) << uint8_t(type) << domain << key; - if(domain == "test" - && (type == EnumAssets::Nodestate - || type == EnumAssets::Model - || type == EnumAssets::Texture)) - { - if(debugInitSendLogCount.fetch_add(1) < 64) { - const auto hash = res.hash(); - LOG.debug() << "Send InitResSend type=" << assetTypeName(type) - << " id=" << id - << " key=" << domain << ':' << key - << " size=" << res.size() - << " hash=" << int(hash[0]) << '.' - << int(hash[1]) << '.' - << int(hash[2]) << '.' - << int(hash[3]); - } - } } // Отправляем чанк @@ -981,8 +557,7 @@ void RemoteClient::onUpdate() { const size_t chunkMsgSize = 1 + 1 + 32 + 4 + willSend; if(p.size() + chunkMsgSize > kMaxAssetPacketSize) flushAssetsPacket(); - p << (uint8_t) ToClient::L1::Resource - << (uint8_t) ToClient::L2Resource::ChunkSend; + p << (uint8_t) ToClient::AssetsNextSend; p.write((const std::byte*) res.hash().data(), 32); p << uint32_t(willSend); p.write(res.data() + sended, willSend); @@ -995,7 +570,7 @@ void RemoteClient::onUpdate() { if(hasFullSended) { for(ssize_t iter = toSend.size()-1; iter >= 0; iter--) { - if(std::get<4>(toSend[iter]).size() == std::get<5>(toSend[iter])) { + if(std::get<0>(toSend[iter]).size() == std::get<1>(toSend[iter])) { toSend.erase(toSend.begin()+iter); } } diff --git a/Src/Server/RemoteClient.hpp b/Src/Server/RemoteClient.hpp index cfe7829..7fc7a7c 100644 --- a/Src/Server/RemoteClient.hpp +++ b/Src/Server/RemoteClient.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -141,55 +142,26 @@ public: Информация о двоичных ресурсах будет получена сразу же при их запросе. Действительная отправка ресурсов будет только по запросу клиента. - */ struct ResourceRequest { - std::vector Hashes; - std::vector AssetsInfo[(int) EnumAssets::MAX_ENUM]; + std::vector Hashes; - std::vector Voxel; - std::vector Node; - std::vector World; - std::vector Portal; - std::vector Entity; - std::vector Item; - - void insert(const ResourceRequest &obj) { + void merge(const ResourceRequest &obj) { Hashes.insert(Hashes.end(), obj.Hashes.begin(), obj.Hashes.end()); - for(int iter = 0; iter < (int) EnumAssets::MAX_ENUM; iter++) - AssetsInfo[iter].insert(AssetsInfo[iter].end(), obj.AssetsInfo[iter].begin(), obj.AssetsInfo[iter].end()); - - Voxel.insert(Voxel.end(), obj.Voxel.begin(), obj.Voxel.end()); - Node.insert(Node.end(), obj.Node.begin(), obj.Node.end()); - World.insert(World.end(), obj.World.begin(), obj.World.end()); - Portal.insert(Portal.end(), obj.Portal.begin(), obj.Portal.end()); - Entity.insert(Entity.end(), obj.Entity.begin(), obj.Entity.end()); - Item.insert(Item.end(), obj.Item.begin(), obj.Item.end()); } void uniq() { - for(std::vector *vec : {&Voxel, &Node, &World, - &Portal, &Entity, &Item - }) - { - std::sort(vec->begin(), vec->end()); - auto last = std::unique(vec->begin(), vec->end()); - vec->erase(last, vec->end()); - } - - for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) - { - std::sort(AssetsInfo[type].begin(), AssetsInfo[type].end()); - auto last = std::unique(AssetsInfo[type].begin(), AssetsInfo[type].end()); - AssetsInfo[type].erase(last, AssetsInfo[type].end()); - } - std::sort(Hashes.begin(), Hashes.end()); auto last = std::unique(Hashes.begin(), Hashes.end()); Hashes.erase(last, Hashes.end()); } }; +struct AssetBinaryInfo { + Resource Data; + Hash_t Hash; +}; + // using EntityKey = std::tuple; @@ -209,67 +181,25 @@ class RemoteClient { bool IsConnected = true, IsGoingShutdown = false; struct NetworkAndResource_t { - struct ResUses_t { - // Счётчики использования двоичных кэшируемых ресурсов + хэш привязанный к идентификатору - // Хэш используется для того, чтобы исключить повторные объявления неизменившихся ресурсов - struct AssetBindState { - Hash_t Hash; - Hash_t HeaderHash; - }; - std::map> AssetsUse[(int) EnumAssets::MAX_ENUM]; - - // Зависимость профилей контента от профилей ресурсов - // Нужно чтобы пересчитать зависимости к профилям ресурсов - struct RefAssets_t { - std::vector Resources[(int) EnumAssets::MAX_ENUM]; - }; - - std::map RefDefVoxel; - std::map RefDefNode; - std::map RefDefWorld; - std::map RefDefPortal; - std::map RefDefEntity; - std::map RefDefItem; - - // Счётчики использование профилей контента - std::map DefVoxel; // Один чанк, одно использование - std::map DefNode; - std::map DefWorld; - std::map DefPortal; - std::map DefEntity; - std::map DefItem; // При передаче инвентарей? - - - // Зависимость наблюдаемых чанков от профилей нод и вокселей - struct ChunkRef { - // Отсортированные списки уникальных вокселей - std::vector Voxel; - std::vector Node; - }; - - std::map>> RefChunk; - - // Модификационные зависимости экземпляров профилей контента - // У сущностей в мире могут дополнительно изменятся свойства, переписывая их профиль - struct RefWorld_t { - DefWorldId Profile; - RefAssets_t Assets; - }; - std::map RefWorld; - struct RefPortal_t { - DefPortalId Profile; - RefAssets_t Assets; - }; - // std::map RefPortal; - struct RefEntity_t { - DefEntityId Profile; - RefAssets_t Assets; - }; - std::map RefEntity; - } ResUses; - // Смена идентификаторов сервера на клиентские SCSKeyRemapper ReMapEntities; + // Накопленные чанки для отправки + std::unordered_map< + WorldId_t, // Миры + std::unordered_map< + Pos::GlobalRegion, // Регионы + std::pair< + std::unordered_map< // Воксели + Pos::bvec4u, // Чанки + std::u8string + >, + std::unordered_map< // Ноды + Pos::bvec4u, // Чанки + std::u8string + > + > + > + > ChunksToSend; // Запрос информации об ассетах и профилях контента ResourceRequest NextRequest; @@ -277,9 +207,6 @@ class RemoteClient { /// TODO: здесь может быть засор std::vector ClientRequested; - void incrementAssets(const ResUses_t::RefAssets_t& bin); - void decrementAssets(ResUses_t::RefAssets_t&& bin); - Net::Packet NextPacket; std::vector SimplePackets; void checkPacketBorder(uint16_t size) { @@ -288,10 +215,24 @@ class RemoteClient { } } - void prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels, - const std::vector& uniq_sorted_defines); - void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes, - const std::vector& uniq_sorted_defines); + void prepareChunkUpdate_Voxels( + WorldId_t worldId, + Pos::GlobalRegion regionPos, + Pos::bvec4u chunkPos, + const std::u8string& compressed_voxels + ) { + ChunksToSend[worldId][regionPos].first[chunkPos] = compressed_voxels; + } + + void prepareChunkUpdate_Nodes( + WorldId_t worldId, + Pos::GlobalRegion regionPos, + Pos::bvec4u chunkPos, + const std::u8string& compressed_nodes + ) { + ChunksToSend[worldId][regionPos].second[chunkPos] = compressed_nodes; + } + void prepareEntitiesRemove(const std::vector& entityId); void prepareRegionsRemove(WorldId_t worldId, std::vector regionPoses); void prepareWorldRemove(WorldId_t worldId); @@ -299,12 +240,6 @@ class RemoteClient { void prepareEntitiesUpdate_Dynamic(const std::vector>& entities); void prepareEntitySwap(ServerEntityId_t prevEntityId, ServerEntityId_t nextEntityId); void prepareWorldUpdate(WorldId_t worldId, World* world); - void informateDefVoxel(const std::vector>& voxels); - void informateDefNode(const std::vector>& nodes); - void informateDefWorld(const std::vector>& worlds); - void informateDefPortal(const std::vector>& portals); - void informateDefEntity(const std::vector>& entityes); - void informateDefItem(const std::vector>& items); }; struct { @@ -318,8 +253,8 @@ class RemoteClient { // Ресурсы, отправленные на клиент в этой сессии std::vector OnClient; // Отправляемые на клиент ресурсы - // Тип, домен, ключ, идентификатор, ресурс, количество отправленных байт - std::vector> ToSend; + // Ресурс, количество отправленных байт + std::vector> ToSend; // Пакет с ресурсами std::vector AssetsPackets; Net::Packet AssetsPacket; @@ -375,32 +310,27 @@ public: // если возвращает false, то блокировка сейчас находится у другого потока // и запрос не был обработан. - // В зоне видимости добавился чанк или изменились его воксели - bool maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels, - const std::vector& uniq_sorted_defines) - { - auto lock = NetworkAndResource.tryLock(); - if(!lock) - return false; - - lock->prepareChunkUpdate_Voxels(worldId, chunkPos, compressed_voxels, uniq_sorted_defines); - return true; + // Создаёт пакет отправки вокселей чанка + void prepareChunkUpdate_Voxels( + WorldId_t worldId, + Pos::GlobalRegion regionPos, + Pos::bvec4u chunkPos, + const std::u8string& compressed_voxels + ) { + NetworkAndResource.lock()->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, compressed_voxels); } - // В зоне видимости добавился чанк или изменились его ноды - bool maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes, - const std::vector& uniq_sorted_defines) - { - auto lock = NetworkAndResource.tryLock(); - if(!lock) - return false; - - lock->prepareChunkUpdate_Nodes(worldId, chunkPos, compressed_nodes, uniq_sorted_defines); - return true; + // Создаёт пакет отправки нод чанка + void prepareChunkUpdate_Nodes( + WorldId_t worldId, + Pos::GlobalRegion regionPos, + Pos::bvec4u chunkPos, + const std::u8string& compressed_nodes + ) { + NetworkAndResource.lock()->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, compressed_nodes); } - // void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights); - - // Клиент перестал наблюдать за сущностью + + // Клиент перестал наблюдать за сущностями void prepareEntitiesRemove(const std::vector& entityId) { NetworkAndResource.lock()->prepareEntitiesRemove(entityId); } // Регион удалён из зоны видимости void prepareRegionsRemove(WorldId_t worldId, std::vector regionPoses) { NetworkAndResource.lock()->prepareRegionsRemove(worldId, regionPoses); } @@ -426,29 +356,52 @@ public: // Отправка подготовленных пакетов ResourceRequest pushPreparedPackets(); - // Сообщить о ресурсах - // Сюда приходят все обновления ресурсов движка - // Глобально их можно запросить в выдаче pushPreparedPackets() + // Создаёт пакет для всех игроков с оповещением о новых идентификаторах (id -> domain+key) + static Net::Packet makePacket_informateAssets_DK( + const std::array< + std::vector, + static_cast(EnumAssets::MAX_ENUM) + >& dkVector + ); - // Нужно передавать клиенту информацию о новых привязках - // id -> домен+ключ - // id -> hash+header + // Создаёт пакет для всех игроков с оповещением об изменении файлов ресурсов (id -> hash+header) + static Net::Packet makePacket_informateAssets_HH( + const std::array< + std::vector, + static_cast(EnumAssets::MAX_ENUM) + >& hhVector, + const std::array< + std::vector, + static_cast(EnumAssets::MAX_ENUM) + >& lost + ); - // По запросу клиента отправлять нужные ресурсы по hash - - /// TODO: новый void informateAssets(); - - // Оповещение о запрошенных (и не только) ассетах - void informateAssets(const std::vector>>& resources); - - // Игровые определения - void informateDefVoxel(const std::vector>& voxels) { NetworkAndResource.lock()->informateDefVoxel(voxels); } - void informateDefNode(const std::vector>& nodes) { NetworkAndResource.lock()->informateDefNode(nodes); } - void informateDefWorld(const std::vector>& worlds) { NetworkAndResource.lock()->informateDefWorld(worlds); } - void informateDefPortal(const std::vector>& portals) { NetworkAndResource.lock()->informateDefPortal(portals); } - void informateDefEntity(const std::vector>& entityes) { NetworkAndResource.lock()->informateDefEntity(entityes); } - void informateDefItem(const std::vector>& items) { NetworkAndResource.lock()->informateDefItem(items); } + // Оповещение о двоичных ресурсах (стриминг по запросу) + void informateBinaryAssets( + const std::vector& resources + ); + // Создаёт пакет об обновлении игровых профилей + static std::vector makePackets_sendDefContentUpdate( + std::array< + std::vector< + std::pair< + ResourceId, // Идентификатор профиля + std::u8string // Двоичный формат профиля + > + >, + static_cast(EnumDefContent::MAX_ENUM) + > newOrUpdate, // Новые или изменённые + std::array< + std::vector, + static_cast(EnumDefContent::MAX_ENUM) + > lost, // Потерянные профили + std::array< + std::vector>, + static_cast(EnumDefContent::MAX_ENUM) + > idToDK // Новые привязки + ); + void onUpdate(); private: