diff --git a/Src/Server/RemoteClient.cpp b/Src/Server/RemoteClient.cpp index 525804b..4957260 100644 --- a/Src/Server/RemoteClient.cpp +++ b/Src/Server/RemoteClient.cpp @@ -68,13 +68,9 @@ void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) { LOG.info() << "Игрок '" << Username << "' отключился " << info; } -bool RemoteClient::maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels, - const std::vector& uniq_sorted_defines) +void RemoteClient::murky_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels, + const std::vector& uniq_sorted_defines) { - bool lock = ResUses.RefChunkLock.exchange(1); - if(lock) - return false; - Pos::bvec4u localChunk = chunkPos & 0x3; Pos::GlobalRegion regionPos = chunkPos >> 2; @@ -84,12 +80,12 @@ bool RemoteClient::maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::Globa Отправить всё клиенту */ - std::vector + std::vector newTypes, /* Новые типы вокселей */ lostTypes /* Потерянные типы вокселей */; // Отметим использование этих вокселей - for(const DefVoxelId_t& id : uniq_sorted_defines) { + for(const DefVoxelId& id : uniq_sorted_defines) { auto iter = ResUses.DefVoxel.find(id); if(iter == ResUses.DefVoxel.end()) { // Новый тип @@ -109,7 +105,7 @@ bool RemoteClient::maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::Globa auto iterRegion = iterWorld->second.find(regionPos); if(iterRegion != iterWorld->second.end()) { // Уменьшим счётчик зависимостей - for(const DefVoxelId_t& id : iterRegion->second[localChunk.pack()].Voxel) { + for(const DefVoxelId& id : iterRegion->second[localChunk.pack()].Voxel) { auto iter = ResUses.DefVoxel.find(id); assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях if(--iter->second == 0) { @@ -129,12 +125,12 @@ bool RemoteClient::maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::Globa if(!newTypes.empty()) { // Добавляем новые типы в запрос NextRequest.Voxel.insert(NextRequest.Voxel.end(), newTypes.begin(), newTypes.end()); - for(DefVoxelId_t voxel : newTypes) + for(DefVoxelId voxel : newTypes) ResUses.RefDefVoxel[voxel] = {}; } if(!lostTypes.empty()) { - for(const DefVoxelId_t& id : lostTypes) { + for(const DefVoxelId& id : lostTypes) { auto iter = ResUses.RefDefVoxel.find(id); assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя decrementBinary(std::move(iter->second)); @@ -147,32 +143,25 @@ bool RemoteClient::maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::Globa } } - checkPacketBorder(4+4+8+2+4+compressed_voxels.size()); - NextPacket << (uint8_t) ToClient::L1::Content + murkyCheckPacketBorder(4+4+8+2+4+compressed_voxels.size()); + MurkyNextPacket << (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()); - - ResUses.RefChunkLock.exchange(0); - return true; + MurkyNextPacket.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size()); } -bool RemoteClient::maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes, - const std::vector& uniq_sorted_defines) +void RemoteClient::maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes, + const std::vector& uniq_sorted_defines) { - bool lock = ResUses.RefChunkLock.exchange(1); - if(lock) - return false; - Pos::bvec4u localChunk = chunkPos & 0x3; Pos::GlobalRegion regionPos = chunkPos >> 2; - std::vector + std::vector newTypes, /* Новые типы нод */ lostTypes /* Потерянные типы нод */; // Отметим использование этих нод - for(const DefNodeId_t& id : uniq_sorted_defines) { + for(const DefNodeId& id : uniq_sorted_defines) { auto iter = ResUses.DefNode.find(id); if(iter == ResUses.DefNode.end()) { // Новый тип @@ -192,7 +181,7 @@ bool RemoteClient::maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::Global auto iterRegion = iterWorld->second.find(regionPos); if(iterRegion != iterWorld->second.end()) { // Уменьшим счётчик зависимостей - for(const DefNodeId_t& id : iterRegion->second[localChunk.pack()].Node) { + for(const DefNodeId& id : iterRegion->second[localChunk.pack()].Node) { auto iter = ResUses.DefNode.find(id); assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях if(--iter->second == 0) { @@ -212,38 +201,35 @@ bool RemoteClient::maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::Global if(!newTypes.empty()) { // Добавляем новые типы в запрос NextRequest.Node.insert(NextRequest.Node.end(), newTypes.begin(), newTypes.end()); - for(DefNodeId_t node : newTypes) + for(DefNodeId node : newTypes) ResUses.RefDefNode[node] = {}; } if(!lostTypes.empty()) { - for(const DefNodeId_t& id : lostTypes) { + for(const DefNodeId& id : lostTypes) { auto iter = ResUses.RefDefNode.find(id); assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды decrementBinary(std::move(iter->second)); ResUses.RefDefNode.erase(iter); checkPacketBorder(16); - NextPacket << (uint8_t) ToClient::L1::Definition + MurkyNextPacket << (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 + MurkyNextPacket << (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()); - - ResUses.RefChunkLock.exchange(0); - return true; + MurkyNextPacket.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size()); } void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos) { - std::vector + std::vector lostTypesV /* Потерянные типы вокселей */; - std::vector + std::vector lostTypesN /* Потерянные типы нод */; // Уменьшаем зависимости вокселей и нод @@ -257,7 +243,7 @@ void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regi return; for(const auto &iterChunk : iterRegion->second) { - for(const DefVoxelId_t& id : iterChunk.Voxel) { + for(const DefVoxelId& id : iterChunk.Voxel) { auto iter = ResUses.DefVoxel.find(id); assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях if(--iter->second == 0) { @@ -267,7 +253,7 @@ void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regi } } - for(const DefNodeId_t& id : iterChunk.Node) { + for(const DefNodeId& id : iterChunk.Node) { auto iter = ResUses.DefNode.find(id); assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях if(--iter->second == 0) { @@ -282,7 +268,7 @@ void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regi } if(!lostTypesV.empty()) { - for(const DefVoxelId_t& id : lostTypesV) { + for(const DefVoxelId& id : lostTypesV) { auto iter = ResUses.RefDefVoxel.find(id); assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя decrementBinary(std::move(iter->second)); @@ -296,7 +282,7 @@ void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regi } if(!lostTypesN.empty()) { - for(const DefNodeId_t& id : lostTypesN) { + for(const DefNodeId& id : lostTypesN) { auto iter = ResUses.RefDefNode.find(id); assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды decrementBinary(std::move(iter->second)); @@ -564,10 +550,10 @@ void RemoteClient::informateIdToHash(const std::unordered_map &voxels) +void RemoteClient::informateDefVoxel(const std::unordered_map &voxels) { for(auto pair : voxels) { - DefVoxelId_t id = pair.first; + DefVoxelId id = pair.first; if(!ResUses.DefVoxel.contains(id)) continue; @@ -577,7 +563,7 @@ void RemoteClient::informateDefVoxel(const std::unordered_map &nodes) +void RemoteClient::informateDefNode(const std::unordered_map &nodes) { for(auto& [id, def] : nodes) { if(!ResUses.DefNode.contains(id)) diff --git a/Src/Server/RemoteClient.hpp b/Src/Server/RemoteClient.hpp index 8b65ca0..a8f8c52 100644 --- a/Src/Server/RemoteClient.hpp +++ b/Src/Server/RemoteClient.hpp @@ -5,7 +5,9 @@ #include #include "Abstract.hpp" #include "Common/Packets.hpp" +#include "Server/AssetsManager.hpp" #include "Server/ContentEventController.hpp" +#include "assets.hpp" #include #include #include @@ -137,13 +139,17 @@ public: состоянии для клиента и шаблоны, которые клиенту уже не нужны. Соответствующие менеджеры ресурсов будут следить за изменениями этих ресурсов и переотправлять их клиенту + + Информация о двоичных ресурсах будет получена сразу же при их запросе. + Действительная отправка ресурсов будет только по запросу клиента. + */ struct ResourceRequest { std::vector Hashes; - std::vector BinToHash[5 /*EnumBinResource*/]; + std::vector BinToHash[5 /*EnumBinResource*/]; - std::vector Voxel; - std::vector Node; + std::vector Voxel; + std::vector Node; std::vector World; std::vector Portal; std::vector Entity; @@ -163,7 +169,7 @@ struct ResourceRequest { } void uniq() { - for(std::vector *vec : {&Voxel, &Node, &World, + for(std::vector *vec : {&Voxel, &Node, &World, &Portal, &Entity, &Item }) { @@ -172,7 +178,7 @@ struct ResourceRequest { vec->erase(last, vec->end()); } - for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) + for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) { std::sort(BinToHash[type].begin(), BinToHash[type].end()); auto last = std::unique(BinToHash[type].begin(), BinToHash[type].end()); @@ -213,71 +219,85 @@ class RemoteClient { Если базовые ресурсы не известны, то они также запрашиваются. */ - struct ResUsesObj { - // Счётчики использования двоичных кэшируемых ресурсов + хэш привязанный к идентификатору - std::map> BinUse[5]; - - // Счётчики использование профилей контента - std::map DefVoxel; // Один чанк, одно использование - std::map DefNode; - std::map DefWorld; - std::map DefPortal; - std::map DefEntity; - std::map DefItem; // При передаче инвентарей? - - // Зависимость профилей контента от профилей ресурсов - // Нужно чтобы пересчитать зависимости к профилям ресурсов - struct RefDefBin_t { - std::vector Resources[5]; - }; + struct NetworkAndResource_t { + struct ResUsesObj { + // Счётчики использования двоичных кэшируемых ресурсов + хэш привязанный к идентификатору + std::map> AssetsUse[(int) EnumAssets::MAX_ENUM]; - std::map RefDefVoxel; - std::map RefDefNode; - std::map RefDefWorld; - std::map RefDefPortal; - std::map RefDefEntity; - std::map RefDefItem; + // Зависимость профилей контента от профилей ресурсов + // Нужно чтобы пересчитать зависимости к профилям ресурсов + struct RefAssets_t { + std::vector Resources[(int) EnumAssets::MAX_ENUM]; + }; - // Модификационные зависимости экземпляров профилей контента - struct ChunkRef { - // Отсортированные списки уникальных вокселей - std::vector Voxel; - std::vector Node; - }; - std::atomic_bool RefChunkLock = 0; - std::map>> RefChunk; - struct RefWorld_t { - DefWorldId_t Profile; - }; - std::map RefWorld; - struct RefPortal_t { - DefPortalId_t Profile; - }; - std::map RefPortal; - struct RefEntity_t { - DefEntityId_t Profile; - }; - std::map RefEntity; + std::map RefDefVoxel; + std::map RefDefNode; + std::map RefDefWorld; + std::map RefDefPortal; + std::map RefDefEntity; + std::map RefDefItem; - } ResUses; + // Счётчики использование профилей контента + 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; + + Net::Packet NextPacket; + std::vector SimplePackets; + ResourceRequest NextRequest; + }; - // Смена идентификаторов сервера на клиентские struct { - SCSKeyRemapper Entityes; - SCSKeyRemapper FuncEntityes; - } ResRemap; + // Ресурсы, отправленные на клиент в этой сессии + std::vector OnClient; + std::vector> ToSend; + } AssetsInWork; - Net::Packet NextPacket; - std::vector SimplePackets; - ResourceRequest NextRequest; + TOS::SpinlockObject NetworkAndResource; public: const std::string Username; Pos::Object CameraPos = {0, 0, 0}; ToServer::PacketQuat CameraQuat = {0}; TOS::SpinlockObject> Actions; - ResourceId_t RecievedAssets[(int) EnumAssets::MAX_ENUM] = {0}; + ResourceId RecievedAssets[(int) EnumAssets::MAX_ENUM] = {0}; // Регионы, наблюдаемые клиентом ContentViewInfo ContentViewState; @@ -303,6 +323,9 @@ public: Socket.pushPackets(simplePackets, smartPackets); } + // Возвращает список точек наблюдений клиентом с радиусом в регионах + std::vector> getViewPoints(); + /* Сервер собирает изменения миров, сжимает их и раздаёт на отправку игрокам */ @@ -311,6 +334,14 @@ public: // Отслеживаемое игроком использование контента TOS::Spinlock MurkyLock; + Net::Packet MurkyNextPacket; + std::vector MurkySimplePackets; + + void murkyCheckPacketBorder(uint16_t size) { + if(64000-MurkyNextPacket.size() < size || (MurkyNextPacket.size() != 0 && size == 0)) { + MurkySimplePackets.push_back(std::move(MurkyNextPacket)); + } + } // marky используются в BackingChunkPressure_t в GameServer во время заморозки мира от записи. // В это время просматриваются изменённые объекты и рассылаются изменения клиентам @@ -332,26 +363,38 @@ public: Синхронизация этапа с группой */ + /* + Использует пакеты + Используемые ресурсы + Запросы на ресурсы + + Объекты можно удалять когда это будет определено. + Потом при попытке отправить чанк будет проверка наблюдения + объекта клиентом + */ + // В зоне видимости добавился чанк или изменились его воксели - void murky_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels, - const std::vector& uniq_sorted_defines); + bool murky_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels, + const std::vector& uniq_sorted_defines); // В зоне видимости добавился чанк или изменились его ноды - void murky_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes, - const std::vector& uniq_sorted_defines); + bool murky_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes, + const std::vector& uniq_sorted_defines); // void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights); - // Регион удалён из зоны видимости - void murky_prepareRegionRemove(WorldId_t worldId, std::vector regionPoses); - // Мир появился в зоне видимости - void murky_prepareWorldUpdate(WorldId_t worldId, World* world); + // Мир удалён из зоны видимости - void murky_prepareWorldRemove(WorldId_t worldId); + void prepareWorldRemove(WorldId_t worldId); + // Регион удалён из зоны видимости + void prepareRegionRemove(WorldId_t worldId, std::vector regionPoses); + // Клиент перестал наблюдать за сущностью + void prepareEntityRemove(ServerEntityId_t entityId); // В зоне видимости добавилась новая сущность или она изменилась void prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity); + void prepareEntityUpdate_Dynamic(ServerEntityId_t entityId, const Entity *entity); + // Мир появился в зоне видимости или изменился + void prepareWorldUpdate(WorldId_t worldId, World* world); // Наблюдаемая сущность пересекла границы региона, у неё изменился серверный идентификатор void prepareEntitySwap(ServerEntityId_t prevEntityId, ServerEntityId_t nextEntityId); - // Клиент перестал наблюдать за сущностью - void prepareEntityRemove(ServerEntityId_t entityId); // В зоне видимости добавился порта или он изменился // void preparePortalUpdate(PortalId_t portalId, void* portal); @@ -373,11 +416,11 @@ public: // Привязывает локальный идентификатор с хешем. Если его нет у клиента, // то делается запрос на получение ресурсы для последующей отправки клиенту - void informateIdToHash(const std::unordered_map* resourcesLink); + void informateIdToHash(const std::unordered_map* resourcesLink); // Игровые определения - void informateDefVoxel(const std::unordered_map &voxels); - void informateDefNode(const std::unordered_map &nodes); + void informateDefVoxel(const std::unordered_map &voxels); + void informateDefNode(const std::unordered_map &nodes); void informateDefWorld(const std::unordered_map &worlds); void informateDefPortal(const std::unordered_map &portals); void informateDefEntity(const std::unordered_map &entityes);