diff --git a/Src/Common/Packets.hpp b/Src/Common/Packets.hpp new file mode 100644 index 0000000..45afe1b --- /dev/null +++ b/Src/Common/Packets.hpp @@ -0,0 +1,158 @@ +#pragma once + +#include +#include + + +namespace LV { + +namespace ToServer { + +struct PacketQuat { + uint8_t Data[5]; + + void fromQuat(const glm::quat &quat) { + uint16_t + x = (quat.x+1)/2*0x3ff, + y = (quat.y+1)/2*0x3ff, + z = (quat.z+1)/2*0x3ff, + w = (quat.w+1)/2*0x3ff; + + for(uint8_t &val : Data) + val = 0; + + *(uint16_t*) Data |= x; + *(uint16_t*) (Data+1) |= y << 2; + *(uint16_t*) (Data+2) |= z << 4; + *(uint16_t*) (Data+3) |= w << 6; + } + + glm::quat toQuat() const { + const uint64_t &data = (const uint64_t&) *Data; + uint16_t + x = data & 0x3ff, + y = (data >> 10) & 0x3ff, + z = (data >> 20) & 0x3ff, + w = (data >> 30) & 0x3ff; + + float fx = (float(x)/0x3ff)*2-1; + float fy = (float(y)/0x3ff)*2-1; + float fz = (float(z)/0x3ff)*2-1; + float fw = (float(w)/0x3ff)*2-1; + + return glm::quat(fx, fy, fz, fw); + } +}; + +/* + uint8_t+uint8_t + 0 - Системное + 0 - Новая позиция камеры WorldId_c+ObjectPos+PacketQuat + +*/ + +} + +namespace ToClient { + +/* + 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 + + + +*/ + +// Первый уровень +enum struct L1 : uint8_t { + System, + Resource, + Definition, + Content +}; + +// Второй уровень +enum struct L2System : uint8_t { + Init, + Disconnect, + LinkCameraToEntity, + UnlinkCamera +}; + +enum struct L2Resource : uint8_t { + Texture, + FreeTexture, + Sound, + FreeSound, + Model, + FreeModel, + InitResSend = 253, + ChunkSend, + SendCanceled +}; + +enum struct L2Definition : uint8_t { + World, + FreeWorld, + Voxel, + FreeVoxel, + Node, + FreeNode, + Portal, + FreePortal, + Entity, + FreeEntity +}; + +enum struct L2Content : uint8_t { + World, + RemoveWorld, + Portal, + RemovePortal, + Entity, + RemoveEntity, + ChunkVoxels, + ChunkNodes, + ChunkLightPrism, + RemoveChunk +}; + +} + +} \ No newline at end of file diff --git a/Src/Server/Abstract.hpp b/Src/Server/Abstract.hpp index 183e9c3..65cd43d 100644 --- a/Src/Server/Abstract.hpp +++ b/Src/Server/Abstract.hpp @@ -9,39 +9,45 @@ namespace LV::Server { // Идентификаторы на стороне клиента +using TextureId_c = uint16_t; +using SoundId_c = uint16_t; +using ModelId_c = uint16_t; + +using DefWorldId_c = uint8_t; +using WorldId_c = uint8_t; using VoxelId_c = uint16_t; using NodeId_c = uint16_t; -using WorldId_c = uint8_t; +using DefPortalId_c = uint8_t; using PortalId_c = uint8_t; +using DefEntityId_c = uint16_t; using EntityId_c = uint16_t; -using TextureId_c = uint16_t; -using ModelId_c = uint16_t; using ResourceId_t = uint32_t; // Двоичные данные using BinTextureId_t = ResourceId_t; -using BinModelId_t = ResourceId_t; using BinSoundId_t = ResourceId_t; +using BinModelId_t = ResourceId_t; // Игровые определения +using DefWorldId_t = ResourceId_t; using DefVoxelId_t = ResourceId_t; using DefNodeId_t = ResourceId_t; -using DefWorldId_t = ResourceId_t; using DefPortalId_t = ResourceId_t; using DefEntityId_t = ResourceId_t; -// Конент, основанный на игровых определениях + +// Контент, основанный на игровых определениях using WorldId_t = ResourceId_t; // В одном регионе может быть максимум 2^16 сущностей. Клиенту адресуются сущности в формате <мир>+<позиция региона>+ // И если сущность перешла из одного региона в другой, идентификатор сущности на стороне клиента сохраняется using LocalEntityId_t = uint16_t; using GlobalEntityId_t = std::tuple; - - - using PortalId_t = uint16_t; + + + using MediaStreamId_t = uint16_t; using ContentBridgeId_t = uint16_t; using PlayerId_t = uint32_t; @@ -130,7 +136,7 @@ struct CollisionAABB : public AABB { union { struct { - EntityId_t Index; + LocalEntityId_t Index; } Entity; struct { @@ -161,6 +167,8 @@ struct CollisionAABB : public AABB { class Entity { + DefEntityId_t DefId; + public: LocalAABB ABBOX; @@ -185,11 +193,13 @@ public: IsRemoved = false; public: - Entity(); + Entity(DefEntityId_t defId); AABB aabbAtPos() { return {Pos-Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2), Pos+Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2)}; } + + DefEntityId_t getDefId() const { return DefId; } }; diff --git a/Src/Server/ContentEventController.cpp b/Src/Server/ContentEventController.cpp index 12cac5a..5d34188 100644 --- a/Src/Server/ContentEventController.cpp +++ b/Src/Server/ContentEventController.cpp @@ -2,6 +2,7 @@ #include "Common/Abstract.hpp" #include "RemoteClient.hpp" #include "Server/Abstract.hpp" +#include "World.hpp" namespace LV::Server { @@ -51,7 +52,11 @@ void ContentEventController::onRegionsLost(WorldId_t worldId, const std::vector< } } -void ContentEventController::onChunksEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set &enter, const std::unordered_set &lost) { +void ContentEventController::onChunksEnterLost(WorldId_t worldId, World *worldObj, Pos::GlobalRegion regionPos, const std::unordered_set &enter, const std::unordered_set &lost) { + if(!Subscribed.Chunks.contains(worldId)) { + Remote->prepareWorldNew(worldId, worldObj); + } + std::unordered_set &chunks = Subscribed.Chunks[worldId][regionPos]; chunks.insert(enter.begin(), enter.end()); @@ -67,6 +72,11 @@ void ContentEventController::onChunksEnterLost(WorldId_t worldId, Pos::GlobalReg Remote->prepareChunkRemove(worldId, chunkPos); } + + if(Subscribed.Chunks[worldId].empty()) { + Subscribed.Chunks.erase(Subscribed.Chunks.find(worldId)); + Remote->prepareWorldRemove(worldId); + } } void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, @@ -145,7 +155,7 @@ void ContentEventController::onChunksUpdate_LightPrism(WorldId_t worldId, Pos::G } void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, - const std::unordered_set &enter, const std::unordered_set &lost) + const std::unordered_set &enter, const std::unordered_set &lost) { auto pWorld = Subscribed.Entities.find(worldId); if(pWorld == Subscribed.Entities.end()) { @@ -161,9 +171,9 @@ void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalReg pRegion = pWorld->second.find(regionPos); } - std::unordered_set &entityesId = pRegion->second; + std::unordered_set &entityesId = pRegion->second; - for(EntityId_t eId : lost) { + for(LocalEntityId_t eId : lost) { entityesId.erase(eId); } @@ -177,13 +187,13 @@ void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalReg } // Сообщить Remote - for(EntityId_t eId : lost) { - Remote->prepareEntityRemove(worldId, regionPos, eId); + for(LocalEntityId_t eId : lost) { + Remote->prepareEntityRemove({worldId, regionPos, eId}); } } void ContentEventController::onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, - EntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newId) + LocalEntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, LocalEntityId_t newId) { // Проверим отслеживается ли эта сущность нами auto lpWorld = Subscribed.Entities.find(lastWorldId); @@ -210,13 +220,13 @@ void ContentEventController::onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegi lpRegion->second.erase(lpceId); npRegion->second.insert(newId); - Remote->prepareEntitySwap(lastWorldId, lastRegionPos, lastId, newWorldId, newRegionPos, newId); + Remote->prepareEntitySwap({lastWorldId, lastRegionPos, lastId}, {newWorldId, newRegionPos, newId}); goto entitySwaped; } } - Remote->prepareEntityRemove(lastWorldId, lastRegionPos, lastId); + Remote->prepareEntityRemove({lastWorldId, lastRegionPos, lastId}); entitySwaped: return; @@ -239,7 +249,7 @@ void ContentEventController::onEntityUpdates(WorldId_t worldId, Pos::GlobalRegio if(!lpRegion->second.contains(eId)) continue; - Remote->prepareEntityUpdate(worldId, regionPos, eId, &entities[eId]); + Remote->prepareEntityUpdate({worldId, regionPos, eId}, &entities[eId]); } } diff --git a/Src/Server/ContentEventController.hpp b/Src/Server/ContentEventController.hpp index 56c2c98..7943e08 100644 --- a/Src/Server/ContentEventController.hpp +++ b/Src/Server/ContentEventController.hpp @@ -12,12 +12,7 @@ namespace LV::Server { class RemoteClient; class GameServer; - -struct GlobalEntityId { - WorldId_t WorldId; - Pos::GlobalChunk ChunkPos; - EntityId_t EntityId; -}; +class World; struct ServerObjectPos { @@ -92,7 +87,7 @@ private: // Используется регионами std::vector Portals; std::unordered_map>> Chunks; - std::unordered_map>> Entities; + std::unordered_map>> Entities; } Subscribed; public: @@ -115,14 +110,14 @@ public: // Регионы следят за чанками, которые видят игроки void onRegionsLost(WorldId_t worldId, const std::vector &lost); - void onChunksEnterLost(WorldId_t worldId, Pos::GlobalRegion regionId, const std::unordered_set &enter, const std::unordered_set &lost); + void onChunksEnterLost(WorldId_t worldId, World *worldObj, Pos::GlobalRegion regionId, const std::unordered_set &enter, const std::unordered_set &lost); // Нужно фильтровать неотслеживаемые чанки void onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map*> &chunks); void onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map*> &chunks); void onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map &chunks); - void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set &enter, const std::unordered_set &lost); - void onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, EntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newId); + void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set &enter, const std::unordered_set &lost); + void onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, LocalEntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, LocalEntityId_t newId); void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::vector &entities); void onPortalEnterLost(const std::vector &enter, const std::vector &lost); diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index 29def32..72dfaf5 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -19,6 +19,7 @@ GameServer::~GameServer() { RunThread.join(); WorkDeadline.cancel(); UseLock.wait_no_use(); + LOG.info() << "Сервер уничтожен"; } static thread_local std::vector TL_Circles; @@ -270,7 +271,7 @@ void GameServer::run() { } // Конец - return; + break; } // @@ -313,6 +314,8 @@ void GameServer::run() { CurrentTickDuration += PerTickAdjustment; } } + + LOG.info() << "Сервер завершил работу"; } void GameServer::stepContent() { @@ -547,10 +550,10 @@ void GameServer::stepWorlds() { if(rPos != pRegion.first || pWorld.first != entity.WorldId) { Region *toRegion = Expanse.Worlds[entity.WorldId]->forceLoadOrGetRegion(rPos); - EntityId_t newId = toRegion->pushEntity(entity); + LocalEntityId_t newId = toRegion->pushEntity(entity); // toRegion->Entityes[newId].WorldId = Если мир изменился - if(newId == EntityId_t(-1)) { + if(newId == LocalEntityId_t(-1)) { // В другом регионе нет места } else { entity.IsRemoved = true; @@ -675,7 +678,7 @@ void GameServer::stepWorlds() { doesNotObserve: if(!newChunksSet.empty() || !lostChunks.empty()) - cec->onChunksEnterLost(pWorld.first, pRegion.first, newChunksSet, std::unordered_set(lostChunks.begin(), lostChunks.end())); + cec->onChunksEnterLost(pWorld.first, pWorld.second.get(), pRegion.first, newChunksSet, std::unordered_set(lostChunks.begin(), lostChunks.end())); // Нужно отправить полную информацию о новых наблюдаемых чанках наблюдателю if(!newChunksSet.empty()) { @@ -706,7 +709,7 @@ void GameServer::stepWorlds() { // Проверка отслеживания сущностей { - std::vector newEntityes, lostEntityes; + std::vector newEntityes, lostEntityes; for(size_t iter = 0; iter < region.Entityes.size(); iter++) { Entity &entity = region.Entityes[iter]; @@ -724,7 +727,7 @@ void GameServer::stepWorlds() { } } - std::unordered_set newEntityesSet(newEntityes.begin(), newEntityes.end()); + std::unordered_set newEntityesSet(newEntityes.begin(), newEntityes.end()); { auto iterR_W = subs.Entities.find(pWorld.first); @@ -738,7 +741,7 @@ void GameServer::stepWorlds() { goto doesNotObserveEntityes; // Подходят ли уже наблюдаемые сущности под наблюдательные области - for(EntityId_t eId : iterR_W_R->second) { + for(LocalEntityId_t eId : iterR_W_R->second) { if(eId >= region.Entityes.size()) { lostEntityes.push_back(eId); break; @@ -762,13 +765,13 @@ void GameServer::stepWorlds() { } // Удалим чанки которые наблюдатель уже видит - for(EntityId_t eId : iterR_W_R->second) + for(LocalEntityId_t eId : iterR_W_R->second) newEntityesSet.erase(eId); } doesNotObserveEntityes: - cec->onEntityEnterLost(pWorld.first, pRegion.first, newEntityesSet, std::unordered_set(lostEntityes.begin(), lostEntityes.end())); + cec->onEntityEnterLost(pWorld.first, pRegion.first, newEntityesSet, std::unordered_set(lostEntityes.begin(), lostEntityes.end())); // Отправить полную информацию о новых наблюдаемых сущностях наблюдателю } diff --git a/Src/Server/RemoteClient.cpp b/Src/Server/RemoteClient.cpp index 1df1ee1..92ac15d 100644 --- a/Src/Server/RemoteClient.cpp +++ b/Src/Server/RemoteClient.cpp @@ -7,6 +7,8 @@ #include #include #include +#include "World.hpp" +#include namespace LV::Server { @@ -43,77 +45,81 @@ void RemoteClient::shutdown(const std::string reason) { return; IsGoingShutdown = true; - // Отправить пакет о завершении работы + + NextPacket << (uint8_t) ToClient::L1::System + << (uint8_t) ToClient::L2System::Disconnect + << reason; } -void RemoteClient::prepareDefWorld(WorldId_t worldId, void* world) { - -} - -void RemoteClient::prepareDefVoxel(VoxelId_t voxelId, void* voxel) { - -} - -void RemoteClient::prepareDefNode(NodeId_t worldId, void* node) { - -} - -void RemoteClient::prepareDefMediaStream(MediaStreamId_t modelId, void* mediaStream) { - -} // Может прийти событие на чанк, про который ещё ничего не знаем void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::vector &voxels) { - WorldId_c wcId = rentWorldRemapId(worldId); - if(wcId == WorldId_c(-1)) - return; + WorldId_c wcId = ResRemap.Worlds.toClient(worldId); + assert(wcId != WorldId_c(-1)); // Перебиндить идентификаторы вокселей - std::vector NeedVoxels; + std::vector NeedVoxels; NeedVoxels.reserve(voxels.size()); for(const VoxelCube &cube : voxels) { - NeedVoxels.push_back(cube.Material); + NeedVoxels.push_back(cube.VoxelId); } - std::unordered_set NeedVoxelsSet(NeedVoxels.begin(), NeedVoxels.end()); + std::unordered_set NeedVoxelsSet(NeedVoxels.begin(), NeedVoxels.end()); // Собираем информацию о конвертации идентификаторов - std::unordered_map LocalRemapper; - for(VoxelId_t vId : NeedVoxelsSet) { - auto cvId = Remap.STC_Voxels.find(vId); - if(cvId == Remap.STC_Voxels.end()) { - // Нужно забронировать идентификатор - VoxelId_c cvnId = Remap.UsedVoxelIdC._Find_first(); - if(cvnId == VoxelId_c(-1)) - // Нет свободных идентификаторов - LocalRemapper[vId] = 0; - else { - NextRequest.NewVoxels.push_back(vId); - Remap.UsedVoxelIdC.reset(cvnId); - Remap.STC_Voxels[vId] = cvnId; - LocalRemapper[vId] = cvnId; + std::unordered_map LocalRemapper; + for(DefVoxelId_t vId : NeedVoxelsSet) { + LocalRemapper[vId] = ResRemap.DefVoxels.toClient(vId); + } + + // Проверить новые и забытые определения вокселей + { + std::unordered_set &prevSet = ResUses.ChunkVoxels[{worldId, chunkPos}]; + std::unordered_set &nextSet = NeedVoxelsSet; + + std::vector newVoxels, lostVoxels; + for(DefVoxelId_t id : prevSet) { + if(!nextSet.contains(id)) { + if(--ResUses.DefVoxel[id] == 0) { + // Определение больше не используется + + ResUses.DefVoxel.erase(ResUses.DefVoxel.find(id)); + VoxelId_c cId = ResRemap.DefVoxels.erase(id); + // TODO: отправить пакет потери идентификатора + LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId; + } } - } else { - LocalRemapper[vId] = cvId->second; } + + for(DefVoxelId_t id : nextSet) { + if(!prevSet.contains(id)) { + if(++ResUses.DefVoxel[id] == 1) { + // Определение только появилось + NextRequest.NewVoxels.push_back(id); + VoxelId_c cId = ResRemap.DefVoxels.toClient(id); + LOG.debug() << "Новое определение вокселя: " << id << " -> " << cId; + } + } + } + + prevSet = std::move(nextSet); } - Net::Packet packet; + // TODO: отправить новую информацию о расположении вокселей + LOG.debug() << "Новый чанк: " << worldId << " / " << chunkPos.X << ":" << chunkPos.Y << ":" << chunkPos.Z; // Packet Id - packet << uint16_t(0); - packet << wcId << Pos::GlobalChunk::Key(chunkPos); - packet << uint16_t(voxels.size()); + // NextPacket << uint16_t(0); + // NextPacket << wcId << Pos::GlobalChunk::Key(chunkPos); + // NextPacket << uint16_t(voxels.size()); - for(const VoxelCube &cube : voxels) { - packet << LocalRemapper[cube.Material] - << cube.Left.X << cube.Left.Y << cube.Left.Z - << cube.Right.X << cube.Right.Y << cube.Right.Z; - } - - SimplePackets.push_back(std::move(packet)); + // for(const VoxelCube &cube : voxels) { + // NextPacket << LocalRemapper[cube.VoxelId] + // << cube.Left.X << cube.Left.Y << cube.Left.Z + // << cube.Right.X << cube.Right.Y << cube.Right.Z; + // } } void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, @@ -131,144 +137,456 @@ void RemoteClient::prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalC void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos) { + // Понизим зависимости ресурсов + std::unordered_set &prevSet = ResUses.ChunkVoxels[{worldId, chunkPos}]; + for(DefVoxelId_t id : prevSet) { + if(--ResUses.DefVoxel[id] == 0) { + // Определение больше не используется + ResUses.DefVoxel.erase(ResUses.DefVoxel.find(id)); + VoxelId_c cId = ResRemap.DefVoxels.erase(id); + // TODO: отправить пакет потери идентификатора + LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId; + } + } + + LOG.debug() << "Чанк потерян: " << worldId << " / " << chunkPos.X << ":" << chunkPos.Y << ":" << chunkPos.Z; + WorldId_c cwId = ResRemap.Worlds.toClient(worldId); + NextPacket << (uint8_t) ToClient::L1::Content + << (uint8_t) ToClient::L2Content::RemoveChunk + << cwId << chunkPos.X << chunkPos.Y << chunkPos.Z; +} + +void RemoteClient::prepareWorldNew(WorldId_t worldId, World* world) +{ + ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId]; + res.DefId = world->getDefId(); + if(++ResUses.DefWorld[res.DefId] == 1) { + // Новое определение мира + DefWorldId_c cdId = ResRemap.DefWorlds.toClient(res.DefId); + NextRequest.NewWorlds.push_back(res.DefId); + + LOG.debug() << "Новое определение мира: " << res.DefId << " -> " << cdId; + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::World + << cdId; + } + + incrementBinary(res.Textures, res.Sounds, res.Models); + + WorldId_c cId = ResRemap.Worlds.toClient(worldId); + LOG.debug() << "Новый мир: " << worldId << " -> " << int(cId); + NextPacket << (uint8_t) ToClient::L1::Content + << (uint8_t) ToClient::L2Content::World + << cId; +} + +void RemoteClient::prepareWorldUpdate(WorldId_t worldId, World* world) +{ + ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId]; + + if(res.DefId != world->getDefId()) { + DefWorldId_t newDef = world->getDefId(); + + if(--ResUses.DefWorld[res.DefId] == 0) { + // Определение больше не используется + ResUses.DefWorld.erase(ResUses.DefWorld.find(res.DefId)); + DefWorldId_c cdId = ResRemap.DefWorlds.erase(res.DefId); + + // TODO: отправить пакет потери идентификатора + LOG.debug() << "Определение мира потеряно: " << res.DefId << " -> " << cdId; + } + + if(++ResUses.DefWorld[newDef] == 1) { + // Новое определение мира + DefWorldId_c cdId = ResRemap.DefWorlds.toClient(newDef); + NextRequest.NewWorlds.push_back(newDef); + + // incrementBinary(Textures, Sounds, Models); + // TODO: отправить пакет о новом определении мира + LOG.debug() << "Новое определение мира: " << newDef << " -> " << cdId; + } + + res.DefId = newDef; + } + + // TODO: определить различия между переопределением поверх определений + std::unordered_set lostTextures, newTextures; + std::unordered_set lostSounds, newSounds; + std::unordered_set lostModels, newModels; + + decrementBinary(lostTextures, lostSounds, lostModels); + decrementBinary(newTextures, newSounds, newModels); + + WorldId_c cId = ResRemap.Worlds.toClient(worldId); + // TODO: отправить пакет об изменении мира + LOG.debug() << "Изменение мира: " << worldId << " -> " << cId; } void RemoteClient::prepareWorldRemove(WorldId_t worldId) { - + // Чанки уже удалены prepareChunkRemove + // Понизим зависимости ресурсов + ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId]; + + WorldId_c cWorld = ResUses.Worlds.erase(worldId); + LOG.debug() << "Мир потерян: " << worldId << " -> " << cWorld; + + NextPacket << (uint8_t) ToClient::L1::Content + << (uint8_t) ToClient::L2Content::RemoveWorld + << cWorld; + + if(--ResUses.DefWorld[res.DefId] == 0) { + // Определение мира потеряно + ResUses.DefWorld.erase(ResUses.DefWorld.find(res.DefId)); + DefWorldId_c cdWorld = ResRemap.DefWorlds.erase(res.DefId); + + // TODO: отправить пакет о потере определения мира + LOG.debug() << "Определение мира потеряно: " << res.DefId << " -> " << cdWorld; + } + + decrementBinary(res.Textures, res.Sounds, res.Models); } -void RemoteClient::prepareEntitySwap(WorldId_t prevWorldId, Pos::GlobalRegion prevRegionPos, EntityId_t prevEntityId, - WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newEntityId) +void RemoteClient::prepareEntitySwap(GlobalEntityId_t prev, GlobalEntityId_t next) { - + ResRemap.Entityes.rebindClientKey(prev, next); + LOG.debug() << "Ребинд сущности: " << std::get<0>(prev) << " / " << std::get<1>(prev).X << ":" + << std::get<1>(prev).Y << ":" << std::get<1>(prev).Z << " / " << std::get<2>(prev) + << " -> " << std::get<0>(next) << " / " << std::get<1>(next).X << ":" + << std::get<1>(next).Y << ":" << std::get<1>(next).Z << " / " << std::get<2>(next); } -void RemoteClient::prepareEntityUpdate(WorldId_t worldId, Pos::GlobalRegion regionPos, - EntityId_t entityId, const Entity *entity) +void RemoteClient::prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity) { // Может прийти событие на сущность, про которую ещё ничего не знаем // Сопоставим с идентификатором клиента - EntityId_c ceId = -1; + EntityId_c ceId = ResRemap.Entityes.toClient(entityId); - auto pWorld = Remap.STC_Entityes.find(worldId); - if(pWorld != Remap.STC_Entityes.end()) { - auto pRegion = pWorld->second.find(regionPos); - if(pRegion != pWorld->second.end()) { - auto pId = pRegion->second.find(entityId); - if(pId != pRegion->second.end()) { - ceId = pId->second; - } - } - } + auto iter = ResUses.Entity.find(entityId); + if(iter == ResUses.Entity.end()) { + // Новая сущность - if(ceId == EntityId_c(-1)) { - // Клиент ещё не знает о сущности - // Выделяем идентификатор на стороне клиента для сущностей - ceId = Remap.UsedEntityIdC._Find_first(); - if(ceId != EntityId_c(-1)) { - Remap.UsedEntityIdC.reset(ceId); - Remap.CTS_Entityes[ceId] = {worldId, regionPos, entityId}; - Remap.STC_Entityes[worldId][regionPos][entityId] = ceId; + // WorldId_c cwId = ResRemap.Worlds.toClient(std::get<0>(entityId)); + + ResUsesObj::EntityResourceUse &res = ResUses.Entity[entityId]; + res.DefId = entity->getDefId(); + + if(++ResUses.DefEntity[res.DefId] == 1) { + // Новое определение + NextRequest.NewEntityes.push_back(res.DefId); + DefEntityId_c cId = ResRemap.DefEntityes.toClient(res.DefId); + LOG.debug() << "Новое определение сущности: " << res.DefId << " -> " << cId; + // TODO: Отправить пакет о новом определении + + // incrementBinary(Textures, Sounds, Models); } + incrementBinary(res.Textures, res.Sounds, res.Models); + + LOG.debug() << "Новая сущность: " << std::get<0>(entityId) << " / " << std::get<1>(entityId).X << ":" + << std::get<1>(entityId).Y << ":" << std::get<1>(entityId).Z << " / " << std::get<2>(entityId) + << " -> " << ceId; + + } else { + ResUsesObj::EntityResourceUse &res = iter->second; + LOG.debug() << "Обновление сущности: " << ceId; } - - if(ceId == EntityId_c(-1)) - return; // У клиента закончились идентификаторы - - // Перебиндить ресурсы скомпилированных конвейеров - // Отправить информацию о сущности - // entity ceId } -void RemoteClient::prepareEntityRemove(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId) +void RemoteClient::prepareEntityRemove(GlobalEntityId_t entityId) { - // Освобождаем идентификатор на стороне клиента - auto pWorld = Remap.STC_Entityes.find(worldId); - if(pWorld == Remap.STC_Entityes.end()) - return; + EntityId_c cId = ResRemap.Entityes.erase(entityId); + ResUsesObj::EntityResourceUse &res = ResUses.Entity[entityId]; - auto pRegion = pWorld->second.find(regionPos); - if(pRegion == pWorld->second.end()) - return; + if(--ResUses.DefEntity[res.DefId] == 0) { + LOG.debug() << "Потеряли определение сущности: " << res.DefId << " -> " << cId; + ResUses.DefEntity.erase(ResUses.DefEntity.find(res.DefId)); + ResRemap.DefEntityes.erase(res.DefId); - auto pId = pRegion->second.find(entityId); - if(pId == pRegion->second.end()) - return; - - EntityId_c ceId = pId->second; - Remap.UsedEntityIdC.set(ceId); - - { - auto pceid = Remap.CTS_Entityes.find(ceId); - if(pceid != Remap.CTS_Entityes.end()) - Remap.CTS_Entityes.erase(pceid); + // decrementBinary(std::unordered_set &textures, std::unordered_set &sounds, std::unordered_set &models) } - pRegion->second.erase(pId); + LOG.debug() << "Сущность потеряна: " << cId; - if(pRegion->second.empty()) { - pWorld->second.erase(pRegion); - - if(pWorld->second.empty()) - Remap.STC_Entityes.erase(pWorld); - } - - // Пакет об удалении сущности - // ceId + NextPacket << (uint8_t) ToClient::L1::Content + << (uint8_t) ToClient::L2Content::RemoveEntity + << cId; } void RemoteClient::preparePortalNew(PortalId_t portalId, void* portal) {} void RemoteClient::preparePortalUpdate(PortalId_t portalId, void* portal) {} void RemoteClient::preparePortalRemove(PortalId_t portalId) {} -void RemoteClient::prepareCameraSetEntity(WorldId_t worldId, Pos::GlobalChunk chunkPos, EntityId_t entityId) {} +void RemoteClient::prepareCameraSetEntity(GlobalEntityId_t entityId) { + +} ResourceRequest RemoteClient::pushPreparedPackets() { + if(NextPacket.size()) + SimplePackets.push_back(std::move(NextPacket)); + Socket.pushPackets(&SimplePackets); SimplePackets.clear(); NextRequest.uniq(); - return std::move(NextRequest); + return std::move(NextRequest); } -void RemoteClient::informateDefTexture(const std::unordered_map> &textures) { - -} - -void RemoteClient::informateDefModel(const std::unordered_map> &models) { - -} - -void RemoteClient::informateDefSound(const std::unordered_map> &sounds) { - -} - -WorldId_c RemoteClient::rentWorldRemapId(WorldId_t wId) +void RemoteClient::informateDefTexture(const std::unordered_map> &textures) { - WorldId_c wcId; + for(auto pair : textures) { + BinTextureId_t sId = pair.first; + if(!ResUses.BinTexture.contains(sId)) + continue; - auto cwId = Remap.STC_Worlds.find(wId); - if(cwId == Remap.STC_Worlds.end()) { - // Нужно забронировать идентификатор - wcId = Remap.UsedWorldIdC._Find_first(); - if(wcId == WorldId_c(-1)) - // Нет свободных идентификаторов - return wcId; - else { - NextRequest.NewWorlds.push_back(wId); - Remap.UsedWorldIdC.reset(wcId); - Remap.STC_Worlds[wId] = wcId; + TextureId_c cId = ResRemap.BinTextures.toClient(sId); + + NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение + << (uint8_t) ToClient::L2Resource::Texture << cId; + for(auto part : pair.second->Hash) + NextPacket << part; + + NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка + << (uint8_t) ToClient::L2Resource::InitResSend + << uint8_t(0) << uint8_t(0) << cId + << uint32_t(pair.second->Data.size()); + for(auto part : pair.second->Hash) + NextPacket << part; + + NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size()); + + size_t pos = 0; + while(pos < pair.second->Data.size()) { + if(NextPacket.size() > 64000) { + SimplePackets.push_back(std::move(NextPacket)); + } + + size_t need = std::min(pair.second->Data.size()-pos, std::min(NextPacket.size(), 64000)); + NextPacket.write(pair.second->Data.data()+pos, need); + pos += need; } - } else { - wcId = cwId->second; } - return wcId; + if(NextPacket.size()) + SimplePackets.push_back(std::move(NextPacket)); +} + +void RemoteClient::informateDefSound(const std::unordered_map> &sounds) +{ + for(auto pair : sounds) { + BinSoundId_t sId = pair.first; + if(!ResUses.BinSound.contains(sId)) + continue; + + SoundId_c cId = ResRemap.BinSounds.toClient(sId); + + NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение + << (uint8_t) ToClient::L2Resource::Sound << cId; + for(auto part : pair.second->Hash) + NextPacket << part; + + NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка + << (uint8_t) ToClient::L2Resource::InitResSend + << uint8_t(0) << uint8_t(1) << cId + << uint32_t(pair.second->Data.size()); + for(auto part : pair.second->Hash) + NextPacket << part; + + NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size()); + + size_t pos = 0; + while(pos < pair.second->Data.size()) { + if(NextPacket.size() >= 64000) { + SimplePackets.push_back(std::move(NextPacket)); + } + + size_t need = std::min(pair.second->Data.size()-pos, std::min(NextPacket.size(), 64000)); + NextPacket.write(pair.second->Data.data()+pos, need); + pos += need; + } + } +} + +void RemoteClient::informateDefModel(const std::unordered_map> &models) +{ + for(auto pair : models) { + BinModelId_t sId = pair.first; + if(!ResUses.BinModel.contains(sId)) + continue; + + ModelId_c cId = ResRemap.BinModels.toClient(sId); + + NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение + << (uint8_t) ToClient::L2Resource::Model << cId; + for(auto part : pair.second->Hash) + NextPacket << part; + + NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка + << (uint8_t) ToClient::L2Resource::InitResSend + << uint8_t(0) << uint8_t(2) << cId + << uint32_t(pair.second->Data.size()); + for(auto part : pair.second->Hash) + NextPacket << part; + + NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size()); + + size_t pos = 0; + while(pos < pair.second->Data.size()) { + if(NextPacket.size() >= 64000) { + SimplePackets.push_back(std::move(NextPacket)); + } + + size_t need = std::min(pair.second->Data.size()-pos, std::min(NextPacket.size(), 64000)); + NextPacket.write(pair.second->Data.data()+pos, need); + pos += need; + } + } +} + +void RemoteClient::informateDefWorld(const std::unordered_map &worlds) +{ + for(auto pair : worlds) { + DefWorldId_t sId = pair.first; + if(!ResUses.DefWorld.contains(sId)) + continue; + + DefWorldId_c cId = ResRemap.DefWorlds.toClient(sId); + + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::World + << cId; + } +} + +void RemoteClient::informateDefVoxel(const std::unordered_map &voxels) +{ + for(auto pair : voxels) { + DefVoxelId_t sId = pair.first; + if(!ResUses.DefWorld.contains(sId)) + continue; + + VoxelId_c cId = ResRemap.DefVoxels.toClient(sId); + + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::Voxel + << cId; + } +} + +void RemoteClient::informateDefNode(const std::unordered_map &nodes) +{ + for(auto pair : nodes) { + DefNodeId_t sId = pair.first; + if(!ResUses.DefNode.contains(sId)) + continue; + + NodeId_c cId = ResRemap.DefNodes.toClient(sId); + + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::Node + << cId; + } +} + +void RemoteClient::informateDefEntityes(const std::unordered_map &entityes) +{ + for(auto pair : entityes) { + DefEntityId_t sId = pair.first; + if(!ResUses.DefNode.contains(sId)) + continue; + + DefEntityId_c cId = ResRemap.DefEntityes.toClient(sId); + + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::Entity + << cId; + } +} + +void RemoteClient::informateDefPortals(const std::unordered_map &portals) +{ + for(auto pair : portals) { + DefPortalId_t sId = pair.first; + if(!ResUses.DefNode.contains(sId)) + continue; + + DefPortalId_c cId = ResRemap.DefPortals.toClient(sId); + + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::Portal + << cId; + } +} + +void RemoteClient::incrementBinary(std::unordered_set &textures, std::unordered_set &sounds, + std::unordered_set &models) +{ + for(BinTextureId_t id : textures) { + if(++ResUses.BinTexture[id] == 1) { + TextureId_c cId = ResRemap.BinTextures.toClient(id); + NextRequest.NewTextures.push_back(id); + LOG.debug() << "Новое определение текстуры: " << id << " -> " << cId; + } + } + + for(BinSoundId_t id : sounds) { + if(++ResUses.BinSound[id] == 1) { + SoundId_c cId = ResRemap.BinSounds.toClient(id); + NextRequest.NewSounds.push_back(id); + LOG.debug() << "Новое определение звука: " << id << " -> " << cId; + } + } + + for(BinModelId_t id : sounds) { + if(++ResUses.BinModel[id] == 1) { + ModelId_c cId = ResRemap.BinModels.toClient(id); + NextRequest.NewModels.push_back(id); + LOG.debug() << "Новое определение модели: " << id << " -> " << cId; + } + } +} + +void RemoteClient::decrementBinary(std::unordered_set &textures, std::unordered_set &sounds, + std::unordered_set &models) +{ + for(BinTextureId_t id : textures) { + if(--ResUses.BinTexture[id] == 0) { + ResUses.BinTexture.erase(ResUses.BinTexture.find(id)); + TextureId_c cId = ResRemap.BinTextures.erase(id); + LOG.debug() << "Потеряно определение текстуры: " << id << " -> " << cId; + + NextPacket << (uint8_t) ToClient::L1::Resource + << (uint8_t) ToClient::L2Resource::FreeTexture + << cId; + } + } + + for(BinSoundId_t id : sounds) { + if(--ResUses.BinSound[id] == 0) { + ResUses.BinSound.erase(ResUses.BinSound.find(id)); + SoundId_c cId = ResRemap.BinSounds.erase(id); + LOG.debug() << "Потеряно определение звука: " << id << " -> " << cId; + + NextPacket << (uint8_t) ToClient::L1::Resource + << (uint8_t) ToClient::L2Resource::FreeSound + << cId; + } + } + + for(BinModelId_t id : sounds) { + if(--ResUses.BinModel[id] == 0) { + ResUses.BinModel.erase(ResUses.BinModel.find(id)); + ModelId_c cId = ResRemap.BinModels.erase(id); + LOG.debug() << "Потеряно определение модели: " << id << " -> " << cId; + + NextPacket << (uint8_t) ToClient::L1::Resource + << (uint8_t) ToClient::L2Resource::FreeModel + << cId; + } + } } diff --git a/Src/Server/RemoteClient.hpp b/Src/Server/RemoteClient.hpp index 8267c3d..333671a 100644 --- a/Src/Server/RemoteClient.hpp +++ b/Src/Server/RemoteClient.hpp @@ -4,17 +4,18 @@ #include #include #include "Abstract.hpp" +#include "Server/ContentEventController.hpp" #include #include #include +#include #include #include #include namespace LV::Server { - -template && std::is_integral_v && sizeof(ServerKey) >= sizeof(ClientKey), int> = 0> +template= sizeof(ClientKey), int> = 0> class CSChunkedMapper { std::unordered_map, std::array>> Chunks; @@ -42,8 +43,8 @@ public: if(iChunk == Chunks.end()) MAKE_ERROR("Идентификатор не привязан"); - std::bitset<64> &bits = std::get<0>(iChunk.second); - std::array &keys = std::get<1>(iChunk.second); + std::bitset<64> &bits = std::get<0>(iChunk->second); + std::array &keys = std::get<1>(iChunk->second); assert(bits.test(subIndex) && "Идентификатор уже занят"); @@ -65,7 +66,7 @@ public: } }; -template && std::is_integral_v && sizeof(ServerKey) >= sizeof(ClientKey), int> = 0> +template= sizeof(ClientKey), int> = 0> class SCSKeyRemapper { std::bitset FreeClientKeys; std::map Map; @@ -78,9 +79,6 @@ public: // Соотнести идентификатор на стороне сервера с идентификатором на стороне клиента ClientKey toClient(ServerKey skey) { - if(skey == ServerKey(0)) - return ClientKey(0); - auto iter = Map.find(skey); if(iter == Map.end()) { // Идентификатор отсутствует, нужно его занять @@ -91,11 +89,12 @@ public: ClientKey ckey = ClientKey(pos+1); Map[skey] = ckey; - CSmapper.link(ckey, ckey); + CSmapper.link(ckey, skey); + FreeClientKeys.reset(pos); return ClientKey(pos); } - return iter.second; + return iter->second; } // Соотнести идентификатор на стороне клиента с идентификатором на стороне сервера @@ -107,14 +106,27 @@ public: ClientKey erase(ServerKey skey) { auto iter = Map.find(skey); - assert(iter != Map.end() && "Идентификатор не существует"); + if(iter == Map.end()) + return 0; - ClientKey ckey = iter.second; + ClientKey ckey = iter->second; CSmapper.erase(ckey); Map.erase(iter); + FreeClientKeys.set(ckey-1); return ckey; } + + void rebindClientKey(ServerKey prev, ServerKey next) { + auto iter = Map.find(prev); + + assert(iter != Map.end() && "Идентификатор не найден"); + ClientKey ckey = iter->second; + CSmapper.erase(ckey); + CSmapper.link(ckey, next); + Map.erase(iter); + Map[next] = ckey; + } }; /* @@ -124,27 +136,30 @@ public: этих ресурсов и переотправлять их клиенту */ struct ResourceRequest { + std::vector NewTextures; + std::vector NewModels; + std::vector NewSounds; + std::vector NewWorlds; std::vector NewVoxels; std::vector NewNodes; + std::vector NewPortals; std::vector NewEntityes; - std::vector NewTextures; - std::vector NewModels; - std::vector NewSounds; - void insert(const ResourceRequest &obj) { - NewWorlds.insert(NewWorlds.end(), obj.NewWorlds.begin(), obj.NewWorlds.end()); - NewVoxels.insert(NewVoxels.end(), obj.NewVoxels.begin(), obj.NewVoxels.end()); - NewNodes.insert(NewNodes.end(), obj.NewNodes.begin(), obj.NewNodes.end()); - NewEntityes.insert(NewEntityes.end(), obj.NewEntityes.begin(), obj.NewEntityes.end()); NewTextures.insert(NewTextures.end(), obj.NewTextures.begin(), obj.NewTextures.end()); NewModels.insert(NewModels.end(), obj.NewModels.begin(), obj.NewModels.end()); NewSounds.insert(NewSounds.end(), obj.NewSounds.begin(), obj.NewSounds.end()); + + NewWorlds.insert(NewWorlds.end(), obj.NewWorlds.begin(), obj.NewWorlds.end()); + NewVoxels.insert(NewVoxels.end(), obj.NewVoxels.begin(), obj.NewVoxels.end()); + NewNodes.insert(NewNodes.end(), obj.NewNodes.begin(), obj.NewNodes.end()); + NewPortals.insert(NewPortals.end(), obj.NewPortals.begin(), obj.NewPortals.end()); + NewEntityes.insert(NewEntityes.end(), obj.NewEntityes.begin(), obj.NewEntityes.end()); } void uniq() { - for(std::vector *vec : {&NewWorlds, &NewVoxels, &NewNodes, &NewEntityes, &NewTextures, &NewModels, &NewSounds}) { + for(std::vector *vec : {&NewTextures, &NewModels, &NewSounds, &NewWorlds, &NewVoxels, &NewNodes, &NewPortals, &NewEntityes}) { std::sort(vec->begin(), vec->end()); auto last = std::unique(vec->begin(), vec->end()); vec->erase(last, vec->end()); @@ -154,42 +169,97 @@ struct ResourceRequest { using EntityKey = std::tuple; + + + + + /* Обработчик сокета клиента. Подписывает клиента на отслеживание необходимых ресурсов на основе передаваемых клиенту данных */ class RemoteClient { + TOS::Logger LOG; DestroyLock UseLock; Net::AsyncSocket Socket; bool IsConnected = true, IsGoingShutdown = false; - struct { + struct ResUsesObj { // Счётчики использования базовых ресурсов высшими объектами - std::map TextureUses; - std::map SoundUses; + std::map BinTexture; + std::map BinSound; // Может использовать текстуры - std::map ModelUses; + std::map BinModel; // Будут использовать в своих определениях текстуры, звуки, модели - std::map VoxelDefUses; - std::map NodeDefUses; - std::map EntityDefUses; - std::map WorldDefUses; + std::map DefWorld; + std::map DefVoxel; + std::map DefNode; + std::map DefPortal; + std::map DefEntity; - // Чанки используют воксели, ноды, миры - // Сущности используют текстуры, модели, звуки, миры + + // Переписываемый контент + + // Сущности используют текстуры, звуки, модели + struct EntityResourceUse { + DefEntityId_t DefId; + + std::unordered_set Textures; + std::unordered_set Sounds; + std::unordered_set Models; + }; + + std::map Entity; + + // Чанки используют воксели, ноды + std::map, std::unordered_set> ChunkVoxels; + std::map, std::unordered_set> ChunkNodes; + + // Миры + struct WorldResourceUse { + DefWorldId_t DefId; + + std::unordered_set Textures; + std::unordered_set Sounds; + std::unordered_set Models; + }; + + std::map Worlds; + + + // Порталы + struct PortalResourceUse { + DefPortalId_t DefId; + + std::unordered_set Textures; + std::unordered_set Sounds; + std::unordered_set Models; + }; + + std::map Portals; - } Remap; + } ResUses; struct { - } BinaryResourceUsedIds; + SCSKeyRemapper BinTextures; + SCSKeyRemapper BinSounds; + SCSKeyRemapper BinModels; - struct { + SCSKeyRemapper DefWorlds; + SCSKeyRemapper DefVoxels; + SCSKeyRemapper DefNodes; + SCSKeyRemapper DefPortals; + SCSKeyRemapper DefEntityes; - } WorldUsedIds; + SCSKeyRemapper Worlds; + SCSKeyRemapper Portals; + SCSKeyRemapper Entityes; + } ResRemap; + Net::Packet NextPacket; ResourceRequest NextRequest; std::vector SimplePackets; @@ -198,7 +268,7 @@ public: public: RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username) - : Socket(ioc, std::move(socket)), Username(username) + : LOG("RemoteClient " + username), Socket(ioc, std::move(socket)), Username(username) { } @@ -225,27 +295,20 @@ public: void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights); void prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos); - void prepareEntitySwap(WorldId_t prevWorldId, Pos::GlobalRegion prevRegionPos, EntityId_t prevEntityId, - WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newEntityId); - void prepareEntityUpdate(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId, const Entity *entity); - void prepareEntityRemove(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId); + void prepareEntitySwap(GlobalEntityId_t prevEntityId, GlobalEntityId_t nextEntityId); + void prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity); + void prepareEntityRemove(GlobalEntityId_t entityId); - void prepareWorldNew(WorldId_t worldId, void* world); - void prepareWorldUpdate(WorldId_t worldId, void* world); + void prepareWorldNew(WorldId_t worldId, World* world); + void prepareWorldUpdate(WorldId_t worldId, World* world); void prepareWorldRemove(WorldId_t worldId); void preparePortalNew(PortalId_t portalId, void* portal); void preparePortalUpdate(PortalId_t portalId, void* portal); void preparePortalRemove(PortalId_t portalId); - - // Необходимые определения шаблонов игрового контента - void prepareDefPortal(DefNodeId_t defWorldId, void* node); - void prepareDefMediaStream(MediaStreamId_t modelId, void* mediaStream); - - // Прочие моменты - void prepareCameraSetEntity(WorldId_t worldId, Pos::GlobalChunk chunkPos, EntityId_t entityId); + void prepareCameraSetEntity(GlobalEntityId_t entityId); // Отправка подготовленных пакетов ResourceRequest pushPreparedPackets(); @@ -255,19 +318,22 @@ public: // Глобально их можно запросить в выдаче pushPreparedPackets() // Двоичные файлы - void informateDefTexture(const std::unordered_map> &textures); - void informateDefModel(const std::unordered_map> &models); - void informateDefSound(const std::unordered_map> &sounds); + void informateDefTexture(const std::unordered_map> &textures); + void informateDefSound(const std::unordered_map> &sounds); + void informateDefModel(const std::unordered_map> &models); // Игровые определения - void informateDefWorld(const std::unordered_map &worlds); + void informateDefWorld(const std::unordered_map &worlds); void informateDefVoxel(const std::unordered_map &voxels); void informateDefNode(const std::unordered_map &nodes); void informateDefEntityes(const std::unordered_map &entityes); void informateDefPortals(const std::unordered_map &portals); private: - WorldId_c rentWorldRemapId(WorldId_t wId); + void incrementBinary(std::unordered_set &textures, std::unordered_set &sounds, + std::unordered_set &models); + void decrementBinary(std::unordered_set &textures, std::unordered_set &sounds, + std::unordered_set &models); }; diff --git a/Src/Server/SaveBackend.hpp b/Src/Server/SaveBackend.hpp index 89444de..cbe4e96 100644 --- a/Src/Server/SaveBackend.hpp +++ b/Src/Server/SaveBackend.hpp @@ -12,9 +12,9 @@ namespace LV::Server { struct SB_Region { std::vector Voxels; - std::unordered_map VoxelsMap; + std::unordered_map VoxelsMap; std::unordered_map Nodes; - std::unordered_map NodeMap; + std::unordered_map NodeMap; std::vector Entityes; }; diff --git a/Src/Server/SaveBackends/Filesystem.cpp b/Src/Server/SaveBackends/Filesystem.cpp index ac890f8..bc641aa 100644 --- a/Src/Server/SaveBackends/Filesystem.cpp +++ b/Src/Server/SaveBackends/Filesystem.cpp @@ -48,7 +48,7 @@ public: for(js::value &jvVoxel : jaVoxels) { js::object &joVoxel = jvVoxel.as_object(); VoxelCube_Region cube; - cube.Material = joVoxel.at("Material").as_uint64(); + cube.VoxelId = joVoxel.at("Material").as_uint64(); cube.Left.X = joVoxel.at("LeftX").as_uint64(); cube.Left.Y = joVoxel.at("LeftY").as_uint64(); cube.Left.Z = joVoxel.at("LeftZ").as_uint64(); @@ -74,7 +74,7 @@ public: js::array jaVoxels; for(const VoxelCube_Region &cube : data->Voxels) { js::object joVoxel; - joVoxel["Material"] = cube.Material; + joVoxel["Material"] = cube.VoxelId; joVoxel["LeftX"] = cube.Left.X; joVoxel["LeftY"] = cube.Left.Y; joVoxel["LeftZ"] = cube.Left.Z; diff --git a/Src/Server/World.cpp b/Src/Server/World.cpp index 9f2fcc7..7775415 100644 --- a/Src/Server/World.cpp +++ b/Src/Server/World.cpp @@ -4,7 +4,9 @@ namespace LV::Server { -World::World(WorldId_t id) { +World::World(DefWorldId_t defId) + : DefId(defId) +{ } diff --git a/Src/Server/World.hpp b/Src/Server/World.hpp index 5d8ad0a..2043021 100644 --- a/Src/Server/World.hpp +++ b/Src/Server/World.hpp @@ -99,7 +99,7 @@ public: .Voxel = { .Chunk = Pos::Local16_u(beg.x, beg.y, beg.z), .Index = static_cast(iter), - .Id = cube.Material + .Id = cube.VoxelId } }; @@ -113,7 +113,7 @@ public: } - EntityId_t pushEntity(Entity &entity) { + LocalEntityId_t pushEntity(Entity &entity) { for(size_t iter = 0; iter < Entityes.size(); iter++) { Entity &obj = Entityes[iter]; @@ -130,19 +130,19 @@ public: return Entityes.size()-1; } - return EntityId_t(-1); + return LocalEntityId_t(-1); } }; class World { - WorldId_t Id; + DefWorldId_t DefId; public: std::vector NeedToLoad; std::unordered_map> Regions; public: - World(WorldId_t id); + World(DefWorldId_t defId); ~World(); /* @@ -155,6 +155,8 @@ public: void onCEC_RegionsLost(ContentEventController *cec, const std::vector &lost); Region* forceLoadOrGetRegion(Pos::GlobalRegion pos); + + DefWorldId_t getDefId() const { return DefId; } }; diff --git a/Src/main.cpp b/Src/main.cpp index f9e7211..f4ae01e 100644 --- a/Src/main.cpp +++ b/Src/main.cpp @@ -1,7 +1,10 @@ #include +#include #include +#include #include #include +#include #include #include #include "Client/ServerSession.hpp" @@ -18,6 +21,9 @@ coro<> runClient(asio::io_context &ioc, uint16_t port) { tcp::socket sock = co_await Net::asyncConnectTo("localhost:"+std::to_string(port)); co_await Client::ServerSession::asyncAuthorizeWithServer(sock, "DrSocalkwe3n", "1password2", 1); std::unique_ptr asock = co_await Client::ServerSession::asyncInitGameProtocol(ioc, std::move(sock)); + asio::deadline_timer timer(ioc); + timer.expires_from_now(boost::posix_time::seconds(1)); + co_await timer.async_wait(); } catch(const std::exception &exc) { std::cout << exc.what() << std::endl; }