From 00d0eec88fb3ebd35f38e0be1a6ce5108d672cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Sun, 2 Mar 2025 21:44:16 +0600 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20=D0=BD=D0=B0=D0=B1=D0=BB=D1=8E=D0=B4=D0=B0?= =?UTF-8?q?=D0=B5=D0=BC=D1=8B=D1=85=20=D0=BE=D0=B1=D0=BB=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- Src/Client/ServerSession.cpp | 23 +- Src/Common/Abstract.hpp | 35 +++ Src/Common/Net.cpp | 20 +- Src/Common/Net.hpp | 4 +- Src/Server/Abstract.hpp | 4 +- Src/Server/ContentEventController.cpp | 114 +++---- Src/Server/ContentEventController.hpp | 116 ++++++- Src/Server/GameServer.cpp | 415 +++++++++++++------------- Src/Server/GameServer.hpp | 33 +- Src/Server/RemoteClient.cpp | 114 +++---- Src/Server/RemoteClient.hpp | 2 - Src/TOSLib.cpp | 8 +- Src/TOSLib.hpp | 2 +- Src/main.cpp | 1 + 15 files changed, 512 insertions(+), 381 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a72f234..75748c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") # -rdy # sanitizer # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") -#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") +# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize=null -fno-sanitize=alignment") diff --git a/Src/Client/ServerSession.cpp b/Src/Client/ServerSession.cpp index 94ac37d..8d86896 100644 --- a/Src/Client/ServerSession.cpp +++ b/Src/Client/ServerSession.cpp @@ -2,6 +2,7 @@ #include "Client/Abstract.hpp" #include "Common/Abstract.hpp" #include "Common/Net.hpp" +#include "TOSLib.hpp" #include #include #include @@ -440,18 +441,26 @@ coro<> ServerSession::rP_Definition(Net::AsyncSocket &sock) { uint8_t second = co_await sock.read(); switch((ToClient::L2Definition) second) { - case ToClient::L2Definition::World: + case ToClient::L2Definition::World: { + DefWorldId_c cdId = co_await sock.read(); co_return; - case ToClient::L2Definition::FreeWorld: + } + case ToClient::L2Definition::FreeWorld: { + DefWorldId_c cdId = co_await sock.read(); co_return; - case ToClient::L2Definition::Voxel: + } + case ToClient::L2Definition::Voxel: { + DefVoxelId_c cdId = co_await sock.read(); co_return; - case ToClient::L2Definition::FreeVoxel: + } + case ToClient::L2Definition::FreeVoxel: { + DefVoxelId_c cdId = co_await sock.read(); co_return; + } case ToClient::L2Definition::Node: co_return; @@ -499,12 +508,16 @@ coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) { co_return; case ToClient::L2Content::ChunkVoxels: { - WorldId_c wcId = co_await sock.read(); + WorldId_c wcId = co_await sock.read(); Pos::GlobalChunk::Key posKey = co_await sock.read(); Pos::GlobalChunk pos = *(Pos::GlobalChunk*) &posKey; std::vector cubes(co_await sock.read()); uint16_t debugCubesCount = cubes.size(); + if(debugCubesCount > 1) { + int g = 0; + g++; + } for(size_t iter = 0; iter < cubes.size(); iter++) { VoxelCube &cube = cubes[iter]; diff --git a/Src/Common/Abstract.hpp b/Src/Common/Abstract.hpp index db0fa00..e8cff56 100644 --- a/Src/Common/Abstract.hpp +++ b/Src/Common/Abstract.hpp @@ -23,6 +23,13 @@ struct Local16_u { return Key(X) | (Key(Y) << 4) | (Key(Z) << 8); }; + Local16_u& operator=(const Key &key) { + X = key & 0xf; + Y = (key >> 4) & 0xf; + Z = (key >> 8) & 0xf; + return *this; + } + Local4_u left() const { return Local4_u{uint8_t(uint16_t(X) >> 2), uint8_t(uint16_t(Y) >> 2), uint8_t(uint16_t(Z) >> 2)}; } Local4_u right() const { return Local4_u{uint8_t(uint16_t(X) & 0b11), uint8_t(uint16_t(Y) & 0b11), uint8_t(uint16_t(Z) & 0b11)}; } }; @@ -88,7 +95,15 @@ struct GlobalChunk { return Key(uint16_t(X)) | (Key(uint16_t(Y)) << 16) | (Key(uint16_t(Z)) << 32); }; + operator glm::i16vec3() const { + return {X, Y, Z}; + } + auto operator<=>(const GlobalChunk&) const = default; + + Local16_u toLocal() const { + return Local16_u(X & 0xf, Y & 0xf, Z & 0xf); + } }; struct GlobalRegion { @@ -100,6 +115,24 @@ struct GlobalRegion { }; auto operator<=>(const GlobalRegion&) const = default; + + void fromChunk(const GlobalChunk &posChunk) { + X = posChunk.X >> 4; + Y = posChunk.Y >> 4; + Z = posChunk.Z >> 4; + } + + GlobalChunk toChunk() const { + return GlobalChunk(uint16_t(X) << 4, uint16_t(Y) << 4, uint16_t(Z) << 4); + } + + GlobalChunk toChunk(const Pos::Local16_u &posLocal) const { + return GlobalChunk( + (uint16_t(X) << 4) | posLocal.X, + (uint16_t(Y) << 4) | posLocal.Y, + (uint16_t(Z) << 4) | posLocal.Z + ); + } }; using Object = glm::i32vec3; @@ -110,6 +143,8 @@ struct Object_t { static glm::vec3 asFloatVec(Object &obj) { return glm::vec3(float(obj.x)/float(BS), float(obj.y)/float(BS), float(obj.z)/float(BS)); } static GlobalNode asNodePos(Object &obj) { return GlobalNode(obj.x >> BS_Bit, obj.y >> BS_Bit, obj.z >> BS_Bit); } + static GlobalChunk asChunkPos(Object &obj) { return GlobalChunk(obj.x >> BS_Bit >> 4, obj.y >> BS_Bit >> 4, obj.z >> BS_Bit >> 4); } + static GlobalChunk asRegionsPos(Object &obj) { return GlobalChunk(obj.x >> BS_Bit >> 8, obj.y >> BS_Bit >> 8, obj.z >> BS_Bit >> 8); } }; } diff --git a/Src/Common/Net.cpp b/Src/Common/Net.cpp index 932fd15..772d3be 100644 --- a/Src/Common/Net.cpp +++ b/Src/Common/Net.cpp @@ -31,14 +31,15 @@ AsyncSocket::~AsyncSocket() { if(SendPackets.Context) SendPackets.Context->NeedShutdown = true; - boost::lock_guard lock(SendPackets.Mtx); + { + boost::lock_guard lock(SendPackets.Mtx); + + SendPackets.SenderGuard.cancel(); + WorkDeadline.cancel(); + } if(Socket.is_open()) try { Socket.close(); } catch(...) {} - - - SendPackets.SenderGuard.cancel(); - WorkDeadline.cancel(); } void AsyncSocket::pushPackets(std::vector *simplePackets, std::vector *smartPackets) { @@ -49,6 +50,7 @@ void AsyncSocket::pushPackets(std::vector *simplePackets, std::vectorsize() : 0) >= MAX_SMART_PACKETS || SendPackets.SizeInQueue >= MAX_PACKETS_SIZE_IN_WAIT)) { + lock.unlock(); try { Socket.close(); } catch(...) {} // TODO: std::cout << "Передоз пакетами, сокет закрыт" << std::endl; } @@ -175,14 +177,14 @@ coro<> AsyncSocket::runSender(std::shared_ptr context) { while(!SendPackets.SmartBuffer.empty()) { SmartPacket &packet = SendPackets.SmartBuffer.front(); - if(SendSize+packet.size() >= SendBuffer.size()) - break; - - if(packet.IsStillRelevant && !packet.IsStillRelevant()) { + if(packet.IsStillRelevant && !packet.IsStillRelevant (/* */)) { SendPackets.SmartBuffer.pop_front(); continue; } + if(SendSize+packet.size() >= SendBuffer.size()) + break; + size_t packetSize = packet.size(); for(const auto &page : packet.getPages()) { size_t needCopy = std::min(packetSize, NetPool::PageSize); diff --git a/Src/Common/Net.hpp b/Src/Common/Net.hpp index e373e29..27342ab 100644 --- a/Src/Common/Net.hpp +++ b/Src/Common/Net.hpp @@ -2,6 +2,7 @@ #include "MemoryPool.hpp" #include "Async.hpp" +#include "TOSLib.hpp" #include #include @@ -97,6 +98,7 @@ protected: uint16_t needWrite = std::min(Pages.size()*NetPool::PageSize-Size, size); std::byte *ptr = &Pages.back().front() + (Size % NetPool::PageSize); std::copy(data, data+needWrite, ptr); + data += needWrite; Size += needWrite; size -= needWrite; } @@ -177,7 +179,7 @@ protected: }; class AsyncSocket : public AsyncObject { - NetPool::Array<32> RecvBuffer, SendBuffer; + NetPool::Array<16> RecvBuffer, SendBuffer; size_t RecvPos = 0, RecvSize = 0, SendSize = 0; bool ReadShutdowned = false; tcp::socket Socket; diff --git a/Src/Server/Abstract.hpp b/Src/Server/Abstract.hpp index 44f50a4..3b60734 100644 --- a/Src/Server/Abstract.hpp +++ b/Src/Server/Abstract.hpp @@ -65,8 +65,8 @@ struct ServerTime { }; struct VoxelCube { - Pos::Local256_u Left, Right; DefVoxelId_t VoxelId; + Pos::Local256_u Left, Right; auto operator<=>(const VoxelCube&) const = default; }; @@ -333,7 +333,7 @@ inline void convertRegionVoxelsToChunks(const std::vector& reg }; int chunkIndex = z * 16 * 16 + y * 16 + x; - chunks[chunkIndex].emplace_back(left, right, region.VoxelId); + chunks[chunkIndex].emplace_back(region.VoxelId, left, right); } } } diff --git a/Src/Server/ContentEventController.cpp b/Src/Server/ContentEventController.cpp index fd8ad44..35d29af 100644 --- a/Src/Server/ContentEventController.cpp +++ b/Src/Server/ContentEventController.cpp @@ -12,10 +12,14 @@ ContentEventController::ContentEventController(std::unique_ptr &&r { } -uint8_t ContentEventController::getViewRange() const { +uint16_t ContentEventController::getViewRangeActive() const { return 16; } +uint16_t ContentEventController::getViewRangeBackground() const { + return 0; +} + ServerObjectPos ContentEventController::getLastPos() const { return {0, Remote->CameraPos}; } @@ -24,82 +28,54 @@ ServerObjectPos ContentEventController::getPos() const { return {0, Remote->CameraPos}; } -void ContentEventController::onRegionsLost(WorldId_t worldId, const std::vector &lost) { - auto pWorld = Subscribed.Chunks.find(worldId); - if(pWorld == Subscribed.Chunks.end()) - return; - - for(Pos::GlobalRegion rPos : lost) { - auto pRegion = pWorld->second.find(rPos); - if(pRegion != pWorld->second.end()) { - for(Pos::Local16_u lChunkPos : pRegion->second) { - Pos::GlobalChunk gChunkPos( - (pRegion->first.X << 4) | lChunkPos.X, - (pRegion->first.Y << 4) | lChunkPos.Y, - (pRegion->first.Z << 4) | lChunkPos.Z - ); - - Remote->prepareChunkRemove(worldId, gChunkPos); +void ContentEventController::checkContentViewChanges() { + // Очистка уже не наблюдаемых чанков + for(const auto &[worldId, regions] : ContentView_LostView.View) { + for(const auto &[regionPos, chunks] : regions) { + size_t bitPos = chunks._Find_first(); + while(bitPos != chunks.size()) { + Pos::Local16_u chunkPosLocal; + chunkPosLocal = bitPos; + Pos::GlobalChunk chunkPos = regionPos.toChunk(chunkPosLocal); + Remote->prepareChunkRemove(worldId, chunkPos); + bitPos = chunks._Find_next(bitPos); } - - pWorld->second.erase(pRegion); } } - if(pWorld->second.empty()) { - Subscribed.Chunks.erase(pWorld); + // Очистка миров + for(WorldId_t worldId : ContentView_LostView.Worlds) { Remote->prepareWorldRemove(worldId); } } -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()); +void ContentEventController::onWorldUpdate(WorldId_t worldId, World *worldObj) +{ + auto pWorld = ContentViewState.find(worldId); + if(pWorld == ContentViewState.end()) + return; - for(Pos::Local16_u cPos : lost) { - chunks.erase(cPos); - - Pos::GlobalChunk chunkPos( - (regionPos.X << 4) | cPos.X, - (regionPos.Y << 4) | cPos.Y, - (regionPos.Z << 4) | cPos.Z - ); - - Remote->prepareChunkRemove(worldId, chunkPos); - } - - if(Subscribed.Chunks[worldId].empty()) { - Subscribed.Chunks.erase(Subscribed.Chunks.find(worldId)); - Remote->prepareWorldRemove(worldId); - } + Remote->prepareWorldUpdate(worldId, worldObj); } void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map*> &chunks) { - auto pWorld = Subscribed.Chunks.find(worldId); - if(pWorld == Subscribed.Chunks.end()) + auto pWorld = ContentViewState.find(worldId); + if(pWorld == ContentViewState.end()) return; auto pRegion = pWorld->second.find(regionPos); if(pRegion == pWorld->second.end()) return; + const std::bitset<4096> &chunkBitset = pRegion->second; + for(auto pChunk : chunks) { - if(!pRegion->second.contains(pChunk.first)) + if(!chunkBitset.test(pChunk.first)) continue; - Pos::GlobalChunk chunkPos( - (regionPos.X << 4) | pChunk.first.X, - (regionPos.Y << 4) | pChunk.first.Y, - (regionPos.Z << 4) | pChunk.first.Z - ); - + Pos::GlobalChunk chunkPos = regionPos.toChunk(pChunk.first); Remote->prepareChunkUpdate_Voxels(worldId, chunkPos, *pChunk.second); } } @@ -107,24 +83,21 @@ void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::Globa void ContentEventController::onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map*> &chunks) { - auto pWorld = Subscribed.Chunks.find(worldId); - if(pWorld == Subscribed.Chunks.end()) + auto pWorld = ContentViewState.find(worldId); + if(pWorld == ContentViewState.end()) return; auto pRegion = pWorld->second.find(regionPos); if(pRegion == pWorld->second.end()) return; + const std::bitset<4096> &chunkBitset = pRegion->second; + for(auto pChunk : chunks) { - if(!pRegion->second.contains(pChunk.first)) + if(!chunkBitset.test(pChunk.first)) continue; - Pos::GlobalChunk chunkPos( - (regionPos.X << 4) | pChunk.first.X, - (regionPos.Y << 4) | pChunk.first.Y, - (regionPos.Z << 4) | pChunk.first.Z - ); - + Pos::GlobalChunk chunkPos = regionPos.toChunk(pChunk.first); Remote->prepareChunkUpdate_Nodes(worldId, chunkPos, *pChunk.second); } } @@ -132,24 +105,21 @@ void ContentEventController::onChunksUpdate_Nodes(WorldId_t worldId, Pos::Global void ContentEventController::onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map &chunks) { - auto pWorld = Subscribed.Chunks.find(worldId); - if(pWorld == Subscribed.Chunks.end()) + auto pWorld = ContentViewState.find(worldId); + if(pWorld == ContentViewState.end()) return; auto pRegion = pWorld->second.find(regionPos); if(pRegion == pWorld->second.end()) return; + const std::bitset<4096> &chunkBitset = pRegion->second; + for(auto pChunk : chunks) { - if(!pRegion->second.contains(pChunk.first)) + if(!chunkBitset.test(pChunk.first)) continue; - Pos::GlobalChunk chunkPos( - (regionPos.X << 4) | pChunk.first.X, - (regionPos.Y << 4) | pChunk.first.Y, - (regionPos.Z << 4) | pChunk.first.Z - ); - + Pos::GlobalChunk chunkPos = regionPos.toChunk(pChunk.first); Remote->prepareChunkUpdate_LightPrism(worldId, chunkPos, pChunk.second); } } diff --git a/Src/Server/ContentEventController.hpp b/Src/Server/ContentEventController.hpp index 7943e08..01dadbb 100644 --- a/Src/Server/ContentEventController.hpp +++ b/Src/Server/ContentEventController.hpp @@ -2,7 +2,10 @@ #include #include "Abstract.hpp" +#include +#include #include +#include #include #include #include @@ -28,7 +31,7 @@ struct ContentViewCircle { WorldId_t WorldId; // Позиция в чанках glm::i16vec3 Pos; - // (Единица равна размеру чанку) в квадрате + // (Единица равна размеру чанка) в квадрате int32_t Range; inline int32_t sqrDistance(Pos::GlobalRegion regionPos) const { @@ -59,6 +62,74 @@ struct ContentViewCircle { } }; +// Регион -> чанки попавшие под обозрение Pos::Local16_u +using ContentViewWorld = std::map>; // 1 - чанк виден, 0 - не виден + +struct ContentViewGlobal_DiffInfo; + +struct ContentViewGlobal : public std::map { + // Вычисляет половинную разницу между текущей и предыдущей области видимости + // Возвращает области, которые появились по отношению к old, чтобы получить области потерянные из виду поменять местами *this и old + ContentViewGlobal_DiffInfo calcDiffWith(const ContentViewGlobal &old) const; +}; + +struct ContentViewGlobal_DiffInfo { + // Новые увиденные чанки + ContentViewGlobal View; + // Регионы + std::unordered_map> Regions; + // Миры + std::vector Worlds; + + bool empty() const { + return View.empty() && Regions.empty() && Worlds.empty(); + } +}; + +inline ContentViewGlobal_DiffInfo ContentViewGlobal::calcDiffWith(const ContentViewGlobal &old) const { + ContentViewGlobal_DiffInfo newView; + + // Рассматриваем разницу меж мирами + for(const auto &[newWorldId, newWorldView] : *this) { + auto oldWorldIter = old.find(newWorldId); + if(oldWorldIter == old.end()) { // В старом состоянии нет мира + newView.View[newWorldId] = newWorldView; + newView.Worlds.push_back(newWorldId); + auto &newRegions = newView.Regions[newWorldId]; + for(const auto &[regionPos, _] : newWorldView) + newRegions.push_back(regionPos); + } else { + const std::map> &newRegions = newWorldView; + const std::map> &oldRegions = oldWorldIter->second; + std::map> *diffRegions = nullptr; + + // Рассматриваем разницу меж регионами + for(const auto &[newRegionPos, newRegionBitField] : newRegions) { + auto oldRegionIter = oldRegions.find(newRegionPos); + if(oldRegionIter == oldRegions.end()) { // В старой описи мира нет региона + if(!diffRegions) + diffRegions = &newView.View[newWorldId]; + + (*diffRegions)[newRegionPos] = newRegionBitField; + newView.Regions[newWorldId].push_back(newRegionPos); + } else { + const std::bitset<4096> &oldChunks = oldRegionIter->second; + std::bitset<4096> chunks = (~oldChunks) & newRegionBitField; // Останется поле с новыми чанками + if(chunks._Find_first() != chunks.size()) { + // Есть новые чанки + if(!diffRegions) + diffRegions = &newView.View[newWorldId]; + + (*diffRegions)[newRegionPos] = chunks; + } + } + } + } + } + + return newView; +} + /* Мост контента, для отслеживания событий из удалённх точек @@ -86,7 +157,6 @@ private: struct SubscribedObj { // Используется регионами std::vector Portals; - std::unordered_map>> Chunks; std::unordered_map>> Entities; } Subscribed; @@ -94,23 +164,31 @@ public: // Управляется сервером std::unique_ptr Remote; // Регионы сюда заглядывают - std::unordered_map> ContentViewCircles; - std::unordered_map> SubscribedRegions; + // Каждый такт значения изменений обновляются GameServer'ом + // Объявленная в чанках территория точно отслеживается (активная зона) + ContentViewGlobal ContentViewState; + ContentViewGlobal_DiffInfo ContentView_NewView, ContentView_LostView; + + // size_t CVCHash = 0; // Хэш для std::vector + // std::unordered_map> SubscribedRegions; public: ContentEventController(std::unique_ptr &&remote); - // Измеряется в чанках в длину - uint8_t getViewRange() const; + // Измеряется в чанках в радиусе (активная зона) + uint16_t getViewRangeActive() const; + // Измеряется в чанках в радиусе (Декоративная зона) + getViewRangeActive() + uint16_t getViewRangeBackground() const; ServerObjectPos getLastPos() const; ServerObjectPos getPos() const; - // Навешивается слушателем событий на регионы + // Проверка на необходимость подгрузки новых определений миров + // и очистка клиента от не наблюдаемых данных + void checkContentViewChanges(); // Здесь приходят частично фильтрованные события - - // Регионы следят за чанками, которые видят игроки - void onRegionsLost(WorldId_t worldId, const std::vector &lost); - void onChunksEnterLost(WorldId_t worldId, World *worldObj, Pos::GlobalRegion regionId, const std::unordered_set &enter, const std::unordered_set &lost); + // Фильтровать не отслеживаемые миры + void onWorldUpdate(WorldId_t worldId, World *worldObj); + // Нужно фильтровать неотслеживаемые чанки 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); @@ -140,4 +218,20 @@ struct hash { }; +template <> +struct hash { + size_t operator()(const LV::Server::ContentViewCircle& obj) const noexcept { + // Используем стандартную функцию хеширования для uint32_t, glm::i16vec3 и int32_t + auto worldIdHash = std::hash{}(obj.WorldId) << 32; + auto posHash = + std::hash{}(obj.Pos.x) ^ + (std::hash{}(obj.Pos.y) << 16) ^ + (std::hash{}(obj.Pos.z) << 32); + auto rangeHash = std::hash{}(obj.Range); + + return worldIdHash ^ + posHash ^ + (~rangeHash << 32); + } +}; } diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index 2c5616b..ea11699 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -14,6 +14,7 @@ #include #include "SaveBackends/Filesystem.hpp" #include "Server/SaveBackend.hpp" +#include "Server/World.hpp" namespace LV::Server { @@ -27,16 +28,16 @@ GameServer::~GameServer() { static thread_local std::vector TL_Circles; -std::vector GameServer::WorldObj::calcCVCs(ContentViewCircle circle, int depth) +std::vector GameServer::Expanse_t::accumulateContentViewCircles(ContentViewCircle circle, int depth) { TL_Circles.clear(); TL_Circles.reserve(256); TL_Circles.push_back(circle); - _calcContentViewCircles(circle, depth); + _accumulateContentViewCircles(circle, depth); return TL_Circles; } -void GameServer::WorldObj::_calcContentViewCircles(ContentViewCircle circle, int depth) { +void GameServer::Expanse_t::_accumulateContentViewCircles(ContentViewCircle circle, int depth) { for(const auto &pair : ContentBridges) { auto &br = pair.second; if(br.LeftWorld == circle.WorldId) { @@ -62,7 +63,7 @@ void GameServer::WorldObj::_calcContentViewCircles(ContentViewCircle circle, int TL_Circles.push_back(circleNew); if(depth > 1) - _calcContentViewCircles(circleNew, depth-1); + _accumulateContentViewCircles(circleNew, depth-1); } } @@ -89,20 +90,51 @@ void GameServer::WorldObj::_calcContentViewCircles(ContentViewCircle circle, int TL_Circles.push_back(circleNew); if(depth > 1) - _calcContentViewCircles(circleNew, depth-1); + _accumulateContentViewCircles(circleNew, depth-1); } } } } -std::unordered_map> GameServer::WorldObj::remapCVCsByWorld(const std::vector &list) { - std::unordered_map> out; +// std::unordered_map> GameServer::WorldObj::remapCVCsByWorld(const std::vector &list) { +// std::unordered_map> out; - for(const ContentViewCircle &circle : list) { - out[circle.WorldId].push_back(circle); +// for(const ContentViewCircle &circle : list) { +// out[circle.WorldId].push_back(circle); +// } + +// return out; +// } + + +ContentViewGlobal GameServer::Expanse_t::makeContentViewGlobal(const std::vector &views) { + ContentViewGlobal cvg; + Pos::GlobalRegion posRegion, lastPosRegion; + std::bitset<4096> *cache = nullptr; + + for(const ContentViewCircle &circle : views) { + ContentViewWorld &cvw = cvg[circle.WorldId]; + uint16_t chunkRange = std::sqrt(circle.Range); + for(int32_t z = -chunkRange; z <= chunkRange; z++) + for(int32_t y = -chunkRange; y <= chunkRange; y++) + for(int32_t x = -chunkRange; x <= chunkRange; x++) + { + if(z*z+y*y+x*x > circle.Range) + continue; + + Pos::GlobalChunk posChunk(x+circle.Pos.x, y+circle.Pos.y, z+circle.Pos.z); + posRegion.fromChunk(posChunk); + + if(!cache || lastPosRegion != posRegion) { + lastPosRegion = posRegion; + cache = &cvw[posRegion]; + } + + cache->_Unchecked_set(posChunk.toLocal()); + } } - return out; + return cvg; } coro<> GameServer::pushSocketConnect(tcp::socket socket) { @@ -238,11 +270,12 @@ Region* GameServer::forceGetRegion(WorldId_t worldId, Pos::GlobalRegion pos) { region->IsLoaded = true; region->load(&data); } else { + region->IsLoaded = true; if(pos.Y == 0) { for(int z = 0; z < 16; z++) for(int x = 0; x < 16; x++) { - region->Voxels[x][0][z].push_back({{0, 0, 0}, {255, 255, 255}, 0}); + region->Voxels[x][0][z].push_back({0, {0, 0, 0}, {255, 255, 255},}); } } } @@ -317,6 +350,8 @@ void GameServer::run() { // stepContent(); + stepSyncWithAsync(); + // Принять события от игроков stepPlayers(); @@ -384,6 +419,29 @@ void GameServer::stepContent() { } } +void GameServer::stepSyncWithAsync() { + for(std::unique_ptr &cec : Game.CECs) { + assert(cec); + + for(const auto &[worldId, regions] : cec->ContentViewState) { + for(const auto &[regionPos, chunkBitfield] : regions) { + forceGetRegion(worldId, regionPos); + } + } + + // Подпись на регионы + for(const auto &[worldId, newRegions] : cec->ContentView_NewView.Regions) { + auto worldIter = Expanse.Worlds.find(worldId); + assert(worldIter != Expanse.Worlds.end() && "TODO: Логика не определена"); + assert(worldIter->second); + World &world = *worldIter->second; + + // Подписать наблюдателей на регионы миров + world.onCEC_RegionsEnter(cec.get(), newRegions); + } + } +} + void GameServer::stepPlayers() { // Подключить новых игроков if(!External.NewConnectedPlayers.no_lock_readable().empty()) { @@ -401,12 +459,19 @@ void GameServer::stepPlayers() { for(std::unique_ptr &cec : Game.CECs) { // Убрать отключившихся if(!cec->Remote->isConnected()) { - for(auto wPair : cec->SubscribedRegions) { + // Отписываем наблюдателя от миров + for(auto wPair : cec->ContentViewState) { auto wIter = Expanse.Worlds.find(wPair.first); if(wIter == Expanse.Worlds.end()) continue; - wIter->second->onCEC_RegionsLost(cec.get(), wPair.second); + std::vector regions; + regions.reserve(wPair.second.size()); + for(const auto &[rPos, _] : wPair.second) { + regions.push_back(rPos); + } + + wIter->second->onCEC_RegionsLost(cec.get(), regions); } std::string username = cec->Remote->Username; @@ -675,12 +740,22 @@ void GameServer::stepWorlds() { for(ContentEventController *cec : region.CECs) { cecIndex++; - auto cvc = cec->ContentViewCircles.find(pWorld.first); - if(cvc == cec->ContentViewCircles.end()) - // Ничего не должно отслеживаться + auto cvwIter = cec->ContentViewState.find(pWorld.first); + if(cvwIter == cec->ContentViewState.end()) + // Мир не отслеживается continue; - // Пересылка изменений в мире + + const ContentViewWorld &cvw = cvwIter->second; + auto chunkBitsetIter = cvw.find(pRegion.first); + if(chunkBitsetIter == cvw.end()) + // Регион не отслеживается + continue; + + // Наблюдаемые чанки + const std::bitset<4096> &chunkBitset = chunkBitsetIter->second; + + // Пересылка изменений в регионе if(!ChangedLightPrism.empty()) cec->onChunksUpdate_LightPrism(pWorld.first, pRegion.first, ChangedLightPrism); @@ -690,75 +765,30 @@ void GameServer::stepWorlds() { if(!ChangedNodes.empty()) cec->onChunksUpdate_Nodes(pWorld.first, pRegion.first, ChangedNodes); - // То, что уже отслеживает наблюдатель - const auto &subs = cec->getSubscribed(); + // Отправка полной информации о новых наблюдаемых чанках + { + const std::bitset<4096> *new_chunkBitset = nullptr; + try { new_chunkBitset = &cec->ContentView_NewView.View.at(pWorld.first).at(pRegion.first); } catch(...) {} - // Проверка отслеживания чанков - if(cecIndex-1 == region.CEC_NextChunkAndEntityesViewCheck) { - // Чанки, которые игрок уже не видит и которые только что увидел - - std::vector lostChunks, newChunks; - - // Проверим чанки которые наблюдатель может наблюдать - // TODO: Есть что оптимальнее? LV::Server::ContentViewCircle::isIn() x 77754347 - for(int z = 0; z < 16; z++) - for(int y = 0; y < 16; y++) - for(int x = 0; x < 16; x++) { - Pos::GlobalChunk gcPos((pRegion.first.X << 4) | x, - (pRegion.first.Y << 4) | y, - (pRegion.first.Z << 4) | z); - - for(const ContentViewCircle &circle : cvc->second) { - if(circle.isIn(gcPos)) - newChunks.push_back(Pos::Local16_u(x, y, z)); - } - } - - std::unordered_set newChunksSet(newChunks.begin(), newChunks.end()); - - { - auto iterR_W = subs.Chunks.find(pWorld.first); - if(iterR_W == subs.Chunks.end()) - // Если мир не отслеживается наблюдателем - goto doesNotObserve; - - auto iterR_W_R = iterR_W->second.find(pRegion.first); - if(iterR_W_R == iterR_W->second.end()) - // Если регион не отслеживается наблюдателем - goto doesNotObserve; - - // Подходят ли уже наблюдаемые чанки под наблюдательные области - for(Pos::Local16_u cPos : iterR_W_R->second) { - Pos::GlobalChunk gcPos((pRegion.first.X << 4) | cPos.X, - (pRegion.first.Y << 4) | cPos.Y, - (pRegion.first.Z << 4) | cPos.Z); - - for(const ContentViewCircle &circle : cvc->second) { - if(!circle.isIn(gcPos)) - lostChunks.push_back(cPos); - } - } - - // Удалим чанки которые наблюдатель уже видит - for(Pos::Local16_u cPos : iterR_W_R->second) - newChunksSet.erase(cPos); - } - - doesNotObserve: - - if(!newChunksSet.empty() || !lostChunks.empty()) - cec->onChunksEnterLost(pWorld.first, pWorld.second.get(), pRegion.first, newChunksSet, std::unordered_set(lostChunks.begin(), lostChunks.end())); - - // Нужно отправить полную информацию о новых наблюдаемых чанках наблюдателю - if(!newChunksSet.empty()) { + if(new_chunkBitset) { std::unordered_map newLightPrism; std::unordered_map*> newVoxels; std::unordered_map*> newNodes; - for(Pos::Local16_u cPos : newChunksSet) { - newLightPrism[cPos] = ®ion.Lights[0][0][cPos.X][cPos.Y][cPos.Z]; - newVoxels[cPos] = ®ion.Voxels[cPos.X][cPos.Y][cPos.Z]; - newNodes[cPos] = ®ion.Nodes[cPos.X][cPos.Y][cPos.Z]; + newLightPrism.reserve(new_chunkBitset->count()); + newVoxels.reserve(new_chunkBitset->count()); + newNodes.reserve(new_chunkBitset->count()); + + size_t bitPos = new_chunkBitset->_Find_first(); + while(bitPos != new_chunkBitset->size()) { + Pos::Local16_u chunkPos; + chunkPos = bitPos; + + newLightPrism.insert({chunkPos, ®ion.Lights[0][0][chunkPos.X][chunkPos.Y][chunkPos.Z]}); + newVoxels.insert({chunkPos, ®ion.Voxels[chunkPos.X][chunkPos.Y][chunkPos.Z]}); + newNodes.insert({chunkPos, ®ion.Nodes[chunkPos.X][chunkPos.Y][chunkPos.Z]}); + + bitPos = new_chunkBitset->_Find_next(bitPos); } cec->onChunksUpdate_LightPrism(pWorld.first, pRegion.first, newLightPrism); @@ -767,73 +797,76 @@ void GameServer::stepWorlds() { } } - // Проверка отслеживания сущностей - if(cecIndex-1 == region.CEC_NextChunkAndEntityesViewCheck) { - std::vector newEntityes, lostEntityes; - for(size_t iter = 0; iter < region.Entityes.size(); iter++) { - Entity &entity = region.Entityes[iter]; + // То, что уже отслеживает наблюдатель + const auto &subs = cec->getSubscribed(); - if(entity.IsRemoved) - continue; + // // Проверка отслеживания сущностей + // if(cecIndex-1 == region.CEC_NextChunkAndEntityesViewCheck) { + // std::vector newEntityes, lostEntityes; + // for(size_t iter = 0; iter < region.Entityes.size(); iter++) { + // Entity &entity = region.Entityes[iter]; - for(const ContentViewCircle &circle : cvc->second) { - int x = entity.ABBOX.x >> 17; - int y = entity.ABBOX.y >> 17; - int z = entity.ABBOX.z >> 17; + // if(entity.IsRemoved) + // continue; - uint32_t size = 0; - if(circle.isIn(entity.Pos, x*x+y*y+z*z)) - newEntityes.push_back(iter); - } - } + // for(const ContentViewCircle &circle : cvc->second) { + // int x = entity.ABBOX.x >> 17; + // int y = entity.ABBOX.y >> 17; + // int z = entity.ABBOX.z >> 17; - std::unordered_set newEntityesSet(newEntityes.begin(), newEntityes.end()); + // uint32_t size = 0; + // if(circle.isIn(entity.Pos, x*x+y*y+z*z)) + // newEntityes.push_back(iter); + // } + // } - { - auto iterR_W = subs.Entities.find(pWorld.first); - if(iterR_W == subs.Entities.end()) - // Если мир не отслеживается наблюдателем - goto doesNotObserveEntityes; + // std::unordered_set newEntityesSet(newEntityes.begin(), newEntityes.end()); - auto iterR_W_R = iterR_W->second.find(pRegion.first); - if(iterR_W_R == iterR_W->second.end()) - // Если регион не отслеживается наблюдателем - goto doesNotObserveEntityes; + // { + // auto iterR_W = subs.Entities.find(pWorld.first); + // if(iterR_W == subs.Entities.end()) + // // Если мир не отслеживается наблюдателем + // goto doesNotObserveEntityes; - // Подходят ли уже наблюдаемые сущности под наблюдательные области - for(LocalEntityId_t eId : iterR_W_R->second) { - if(eId >= region.Entityes.size()) { - lostEntityes.push_back(eId); - break; - } + // auto iterR_W_R = iterR_W->second.find(pRegion.first); + // if(iterR_W_R == iterR_W->second.end()) + // // Если регион не отслеживается наблюдателем + // goto doesNotObserveEntityes; - Entity &entity = region.Entityes[eId]; + // // Подходят ли уже наблюдаемые сущности под наблюдательные области + // for(LocalEntityId_t eId : iterR_W_R->second) { + // if(eId >= region.Entityes.size()) { + // lostEntityes.push_back(eId); + // break; + // } - if(entity.IsRemoved) { - lostEntityes.push_back(eId); - break; - } + // Entity &entity = region.Entityes[eId]; - int x = entity.ABBOX.x >> 17; - int y = entity.ABBOX.y >> 17; - int z = entity.ABBOX.z >> 17; + // if(entity.IsRemoved) { + // lostEntityes.push_back(eId); + // break; + // } - for(const ContentViewCircle &circle : cvc->second) { - if(!circle.isIn(entity.Pos, x*x+y*y+z*z)) - lostEntityes.push_back(eId); - } - } + // int x = entity.ABBOX.x >> 17; + // int y = entity.ABBOX.y >> 17; + // int z = entity.ABBOX.z >> 17; - // Удалим чанки которые наблюдатель уже видит - for(LocalEntityId_t eId : iterR_W_R->second) - newEntityesSet.erase(eId); - } + // for(const ContentViewCircle &circle : cvc->second) { + // if(!circle.isIn(entity.Pos, x*x+y*y+z*z)) + // lostEntityes.push_back(eId); + // } + // } - doesNotObserveEntityes: + // // Удалим чанки которые наблюдатель уже видит + // for(LocalEntityId_t eId : iterR_W_R->second) + // newEntityesSet.erase(eId); + // } - cec->onEntityEnterLost(pWorld.first, pRegion.first, newEntityesSet, std::unordered_set(lostEntityes.begin(), lostEntityes.end())); - // Отправить полную информацию о новых наблюдаемых сущностях наблюдателю - } + // doesNotObserveEntityes: + + // cec->onEntityEnterLost(pWorld.first, pRegion.first, newEntityesSet, std::unordered_set(lostEntityes.begin(), lostEntityes.end())); + // // Отправить полную информацию о новых наблюдаемых сущностях наблюдателю + // } if(!region.Entityes.empty()) cec->onEntityUpdates(pWorld.first, pRegion.first, region.Entityes); @@ -904,6 +937,17 @@ void GameServer::stepViewContent() { if(Game.CECs.empty()) return; + // Затереть изменения предыдущего такта + for(auto &cecPtr : Game.CECs) { + assert(cecPtr); + cecPtr->ContentView_NewView = {}; + cecPtr->ContentView_LostView = {}; + } + + // Если наблюдаемая территория изменяется + // -> Новая увиденная + Старая потерянная + std::unordered_map lost_CVG; + // Обновления поля зрения for(int iter = 0; iter < 1; iter++) { if(++Game.CEC_NextRebuildViewCircles >= Game.CECs.size()) @@ -914,91 +958,38 @@ void GameServer::stepViewContent() { ContentViewCircle cvc; cvc.WorldId = oPos.WorldId; - cvc.Pos = {oPos.ObjectPos.x >> (Pos::Object_t::BS_Bit+4), oPos.ObjectPos.y >> (Pos::Object_t::BS_Bit+4), oPos.ObjectPos.z >> (Pos::Object_t::BS_Bit+4)}; - cvc.Range = cec.getViewRange(); + cvc.Pos = Pos::Object_t::asChunkPos(oPos.ObjectPos); + cvc.Range = cec.getViewRangeActive(); cvc.Range *= cvc.Range; - cec.ContentViewCircles = Expanse.calcAndRemapCVC(cvc); + std::vector newCVCs = Expanse.accumulateContentViewCircles(cvc); + //size_t hash = (std::hash>{})(newCVCs); + if(/*hash != cec.CVCHash*/ true) { + //cec.CVCHash = hash; + ContentViewGlobal newCbg = Expanse_t::makeContentViewGlobal(newCVCs); + ContentViewGlobal_DiffInfo newView = newCbg.calcDiffWith(cec.ContentViewState); + ContentViewGlobal_DiffInfo lostView = cec.ContentViewState.calcDiffWith(newCbg); + if(!newView.empty() || !lostView.empty()) { + lost_CVG.insert({&cec, {newView}}); + cec.ContentViewState = std::move(newCbg); + cec.ContentView_NewView = std::move(newView); + cec.ContentView_LostView = std::move(lostView); + } + } } - // Прогрузить то, что видят игроки - for(int iter = 0; iter < 1; iter++) { - if(++Game.CEC_NextCheckRegions >= Game.CECs.size()) - Game.CEC_NextCheckRegions = 0; + for(const auto &[cec, lostView] : lost_CVG) { + // Отписать наблюдателей от регионов миров + for(const auto &[worldId, lostList] : lostView.Regions) { + auto worldIter = Expanse.Worlds.find(worldId); + assert(worldIter != Expanse.Worlds.end() && "TODO: Логика не определена"); + assert(worldIter->second); - ContentEventController &cec = *Game.CECs[Game.CEC_NextRebuildViewCircles]; - ServerObjectPos oLPos = cec.getLastPos(), oPos = cec.getPos(); - - std::vector lost; - - // Проверяем отслеживаемые регионы - std::vector regionsResult; - for(auto &pair : cec.ContentViewCircles) { - auto world = Expanse.Worlds.find(pair.first); - if(world == Expanse.Worlds.end()) - continue; - - std::vector regionsLeft; - - for(ContentViewCircle &circle : pair.second) { - int16_t offset = (circle.Range >> __builtin_popcount(circle.Range))+1; - glm::i16vec3 left = (circle.Pos >> int16_t(4))-int16_t(offset); - glm::i16vec3 right = (circle.Pos >> int16_t(4))+int16_t(offset); - for(int x = left.x; x <= right.x; x++) - for(int y = left.y; y <= right.y; y++) - for(int z = left.z; z <= right.z; z++) { - if(circle.isIn(Pos::GlobalRegion(x, y, z))) - regionsLeft.emplace_back(x, y, z); - } - } - - std::sort(regionsLeft.begin(), regionsLeft.end()); - auto last = std::unique(regionsLeft.begin(), regionsLeft.end()); - regionsLeft.erase(last, regionsLeft.end()); - - std::vector ®ionsRight = cec.SubscribedRegions[pair.first]; - std::sort(regionsRight.begin(), regionsRight.end()); - - std::set_difference(regionsLeft.begin(), regionsLeft.end(), - regionsRight.begin(), regionsRight.end(), - std::back_inserter(regionsResult)); - - if(!regionsResult.empty()) { - regionsRight.insert(regionsRight.end(), regionsResult.begin(), regionsResult.end()); - world->second->onCEC_RegionsEnter(&cec, regionsResult); - regionsResult.clear(); - } + World &world = *worldIter->second; + world.onCEC_RegionsLost(cec, lostList); } - // Снимаем подписки с регионов - for(auto &pairSR : cec.SubscribedRegions) { - auto CVCs = cec.ContentViewCircles.find(pairSR.first); - - if(CVCs == cec.ContentViewCircles.end()) { - lost = pairSR.second; - } else { - for(Pos::GlobalRegion ®ion : pairSR.second) { - bool inView = false; - for(ContentViewCircle &circle : CVCs->second) { - if(circle.isIn(region)) { - inView = true; - break; - } - } - - if(!inView) - lost.push_back(region); - } - } - - cec.onRegionsLost(pairSR.first, lost); - - auto world = Expanse.Worlds.find(pairSR.first); - if(world != Expanse.Worlds.end()) - world->second->onCEC_RegionsLost(&cec, lost); - - lost.clear(); - } + cec->checkContentViewChanges(); } } diff --git a/Src/Server/GameServer.hpp b/Src/Server/GameServer.hpp index f26ffa7..4bd57d0 100644 --- a/Src/Server/GameServer.hpp +++ b/Src/Server/GameServer.hpp @@ -75,15 +75,22 @@ class GameServer : public AsyncObject { } Game; - struct WorldObj { + struct Expanse_t { std::unordered_map ContentBridges; - std::vector calcCVCs(ContentViewCircle circle, int depth = 2); - std::unordered_map> remapCVCsByWorld(const std::vector &list); - std::unordered_map> calcAndRemapCVC(ContentViewCircle circle, int depth = 2) { - return remapCVCsByWorld(calcCVCs(circle, depth)); + // Вычисляет окружности обозримой области + // depth ограничивает глубину входа в ContentBridges + std::vector accumulateContentViewCircles(ContentViewCircle circle, int depth = 2); + static ContentViewGlobal makeContentViewGlobal(const std::vector &views); + ContentViewGlobal makeContentViewGlobal(ContentViewCircle circle, int depth = 2) { + return makeContentViewGlobal(accumulateContentViewCircles(circle, depth)); } + // std::unordered_map> remapCVCsByWorld(const std::vector &list); + // std::unordered_map> calcAndRemapCVC(ContentViewCircle circle, int depth = 2) { + // return remapCVCsByWorld(calcCVCs(circle, depth)); + // } + std::unordered_map> Worlds; /* @@ -92,7 +99,7 @@ class GameServer : public AsyncObject { */ private: - void _calcContentViewCircles(ContentViewCircle circle, int depth); + void _accumulateContentViewCircles(ContentViewCircle circle, int depth); } Expanse; struct { @@ -143,8 +150,22 @@ private: void run(); void stepContent(); + /* + Дождаться и получить необходимые данные с бд или диска + Получить несрочные данные + */ + void stepSyncWithAsync(); void stepPlayers(); void stepWorlds(); + /* + Пересмотр наблюдаемых зон (чанки, регионы, миры) + Добавить требуемые регионы в список на предзагрузку с приоритетом + TODO: нужен механизм асинхронной загрузки регионов с бд + + В начале следующего такта обязательное дожидание прогрузки активной зоны + и + оповещение миров об изменениях в наблюдаемых регионах + */ void stepViewContent(); void stepSendPlayersPackets(); void stepLoadRegions(); diff --git a/Src/Server/RemoteClient.cpp b/Src/Server/RemoteClient.cpp index 59f82c5..0d61d74 100644 --- a/Src/Server/RemoteClient.cpp +++ b/Src/Server/RemoteClient.cpp @@ -127,6 +127,7 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::ChunkVoxels << wcId << Pos::GlobalChunk::Key(chunkPos); + NextPacket << uint16_t(voxels.size()); // TODO: for(const VoxelCube &cube : voxels) { @@ -134,6 +135,7 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk << 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, @@ -171,72 +173,73 @@ void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkP << cwId << Pos::GlobalChunk::Key(chunkPos); } -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 << " -> " << int(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); - - checkPacketBorder(16); - 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(); + auto iter = ResUses.Worlds.find(worldId); + if(iter == ResUses.Worlds.end()) { + // Новый мир + WorldId_c cwId = ResRemap.Worlds.toClient(worldId); - if(--ResUses.DefWorld[res.DefId] == 0) { - // Определение больше не используется - ResUses.DefWorld.erase(ResUses.DefWorld.find(res.DefId)); - DefWorldId_c cdId = ResRemap.DefWorlds.erase(res.DefId); + ResUsesObj::WorldResourceUse &res = iter->second; + res.DefId = world->getDefId(); - // TODO: отправить пакет потери идентификатора - LOG.debug() << "Определение мира потеряно: " << res.DefId << " -> " << cdId; - } - - if(++ResUses.DefWorld[newDef] == 1) { - // Новое определение мира - DefWorldId_c cdId = ResRemap.DefWorlds.toClient(newDef); - NextRequest.NewWorlds.push_back(newDef); + if(++ResUses.DefWorld[res.DefId] == 1) { + // Новое определение + NextRequest.NewWorlds.push_back(res.DefId); + DefWorldId_c cdId = ResRemap.DefWorlds.toClient(res.DefId); + + LOG.debug() << "Новое определение мира: " << res.DefId << " -> " << int(cdId); + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::World + << cdId; // incrementBinary(Textures, Sounds, Models); - // TODO: отправить пакет о новом определении мира - LOG.debug() << "Новое определение мира: " << newDef << " -> " << cdId; } - res.DefId = newDef; + incrementBinary(res.Textures, res.Sounds, res.Models); + + LOG.debug() << "Новый мир: " << worldId << " -> " << int(cwId); + + } else { + 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 = worldId ? ResRemap.Worlds.toClient(worldId) : worldId; + // TODO: отправить пакет об изменении мира + LOG.debug() << "Изменение мира: " << worldId << " -> " << cId; } - - // 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 = worldId ? ResRemap.Worlds.toClient(worldId) : worldId; - // TODO: отправить пакет об изменении мира - LOG.debug() << "Изменение мира: " << worldId << " -> " << cId; } void RemoteClient::prepareWorldRemove(WorldId_t worldId) @@ -332,7 +335,6 @@ void RemoteClient::prepareEntityRemove(GlobalEntityId_t entityId) << cId; } -void RemoteClient::preparePortalNew(PortalId_t portalId, void* portal) {} void RemoteClient::preparePortalUpdate(PortalId_t portalId, void* portal) {} void RemoteClient::preparePortalRemove(PortalId_t portalId) {} diff --git a/Src/Server/RemoteClient.hpp b/Src/Server/RemoteClient.hpp index c160021..9ff45a6 100644 --- a/Src/Server/RemoteClient.hpp +++ b/Src/Server/RemoteClient.hpp @@ -302,11 +302,9 @@ public: void prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity); void prepareEntityRemove(GlobalEntityId_t entityId); - 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); diff --git a/Src/TOSLib.cpp b/Src/TOSLib.cpp index 2c2ca8f..b8d2b26 100644 --- a/Src/TOSLib.cpp +++ b/Src/TOSLib.cpp @@ -418,13 +418,13 @@ namespace Enc { std::string toHex(const uint8_t *begin, size_t size) { - std::string out(size, ' '); + std::string out(size*2, ' '); char *data = out.data(); for(const uint8_t *end = begin + size; begin != end; begin++) { - *(data++) = "0123456789abcdf"[*begin & 0xf]; *(data++) = "0123456789abcdf"[(*begin >> 4) & 0xf]; + *(data++) = "0123456789abcdf"[*begin & 0xf]; } return out; @@ -586,8 +586,10 @@ std::string makeStacktrace(int stack_up) if(Str::contains(line.str(), "boost::asio::asio_handler_invoke")) break; - else + else { out += line.str(); + out += '\n'; + } } return out; diff --git a/Src/TOSLib.hpp b/Src/TOSLib.hpp index f2416aa..c30b6ee 100644 --- a/Src/TOSLib.hpp +++ b/Src/TOSLib.hpp @@ -470,7 +470,7 @@ inline double genRand(double min = 1, double max = 0) return res*(max-min)+min; } -std::string makeStackTrace(int stack_up = 1); +std::string makeStacktrace(int stack_up = 1); struct Timer { diff --git a/Src/main.cpp b/Src/main.cpp index 8dc7d82..5453df3 100644 --- a/Src/main.cpp +++ b/Src/main.cpp @@ -22,6 +22,7 @@ int main() { int main() { TOS::Logger::addLogOutput(".*", TOS::EnumLogType::All); + TOS::Logger::addLogFile(".*", TOS::EnumLogType::All, "log.raw"); std::cout << "Hello world!" << std::endl; return LV::main();