From 9bc18b53961f383538697faaa1c4b4f2bb12a9a0 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 7 Jul 2025 12:46:33 +0600 Subject: [PATCH] * --- CMakeLists.txt | 8 +- Src/Client/ServerSession.hpp | 2 + Src/Server/GameServer.cpp | 279 ++++++++++++++++++++++++++++++++--- Src/Server/GameServer.hpp | 27 ++-- Src/Server/World.cpp | 10 +- Src/Server/World.hpp | 6 +- 6 files changed, 285 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d370f02..19c87c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,12 +13,12 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -DGL set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") # -rdynamic # gprof -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") +# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") # sanitizer -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_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.hpp b/Src/Client/ServerSession.hpp index d9b4a5b..cbb5c37 100644 --- a/Src/Client/ServerSession.hpp +++ b/Src/Client/ServerSession.hpp @@ -8,6 +8,7 @@ #include "Common/Packets.hpp" #include #include +#include #include #include #include @@ -66,6 +67,7 @@ public: assert(Socket.get()); try { + fs::create_directories("Cache"); CHDB = CacheHandlerBasic::Create(ioc, "Cache"); // Отправка информации о загруженном кеше diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index e1f8b78..fd19aac 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -37,24 +37,259 @@ GameServer::~GameServer() { LOG.info() << "Сервер уничтожен"; } -void GameServer::Backing_t::run(int id) { +void GameServer::BackingChunkPressure_t::run(int id) { LOG.debug() << "Старт фонового потока " << id; try { while(true) { { std::unique_lock lock(Mutex); - Symaphore.wait(lock, [&](){ return Run != 0 || NeedShutdown; }); + Symaphore.wait(lock, [&](){ return RunCollect != 0 || NeedShutdown; }); if(NeedShutdown) { LOG.debug() << "Завершение выполнения фонового потока " << id; + break; } } - // Работа + // Сбор данных + size_t pullSize = Threads.size(); + size_t counter = 0; + struct Dump { + std::vector> CECs, NewCECs; + std::unordered_map> Voxels; + std::unordered_map> Nodes; + uint64_t IsChunkChanged_Nodes, IsChunkChanged_Voxels; + }; + + std::vector>>> dump; + + for(const auto& [worldId, world] : *Worlds) { + const auto &worldObj = *world; + std::vector> dumpWorld; + + for(const auto& [regionPos, region] : worldObj.Regions) { + auto& regionObj = *region; + if(counter++ % pullSize != 0) { + counter %= pullSize; + continue; + } + + Dump dumpRegion; + + dumpRegion.IsChunkChanged_Voxels = regionObj.IsChunkChanged_Voxels; + regionObj.IsChunkChanged_Voxels = 0; + dumpRegion.IsChunkChanged_Nodes = regionObj.IsChunkChanged_Nodes; + regionObj.IsChunkChanged_Nodes = 0; + + if(!regionObj.NewCECs.empty()) { + dumpRegion.NewCECs = std::move(regionObj.NewCECs); + dumpRegion.Voxels = regionObj.Voxels; + + for(int z = 0; z < 4; z++) + for(int y = 0; y < 4; y++) + for(int x = 0; x < 4; x++) + { + auto &toPtr = dumpRegion.Nodes[Pos::bvec4u(x, y, z).pack()]; + const Node *fromPtr = (const Node*) ®ionObj.Nodes[0][0][0][x][y][z]; + std::copy(fromPtr, fromPtr+16*16*16, toPtr.data()); + } + } else { + if(regionObj.IsChunkChanged_Voxels) { + for(int index = 0; index < 64; index++) { + if((regionObj.IsChunkChanged_Voxels >> index) & 0x1) + continue; + + Pos::bvec4u chunkPos; + chunkPos.unpack(index); + + auto voxelIter = regionObj.Voxels.find(chunkPos); + if(voxelIter != regionObj.Voxels.end()) { + dumpRegion.Voxels[chunkPos] = voxelIter->second; + } else { + dumpRegion.Voxels[chunkPos] = {}; + } + } + } + + if(regionObj.IsChunkChanged_Nodes) { + for(int index = 0; index < 64; index++) { + if((regionObj.IsChunkChanged_Nodes >> index) & 0x1) + continue; + + Pos::bvec4u chunkPos; + chunkPos.unpack(index); + + auto &toPtr = dumpRegion.Nodes[chunkPos]; + const Node *fromPtr = (const Node*) ®ionObj.Nodes[0][0][0][chunkPos.x][chunkPos.y][chunkPos.z]; + std::copy(fromPtr, fromPtr+16*16*16, toPtr.data()); + } + } + } + + if(!dumpRegion.CECs.empty()) { + dumpWorld.push_back({regionPos, std::move(dumpRegion)}); + } + } + + if(!dumpWorld.empty()) { + dump.push_back({worldId, std::move(dumpWorld)}); + } + } + + // Синхронизация { std::unique_lock lock(Mutex); - Run--; + RunCollect--; + Symaphore.notify_all(); + } + + // Сжатие и отправка игрокам + struct PostponedV { + WorldId_t WorldId; + Pos::GlobalChunk Chunk; + CompressedVoxels Data; + }; + + struct PostponedN { + WorldId_t WorldId; + Pos::GlobalChunk Chunk; + CompressedNodes Data; + }; + + std::list>> postponedVoxels; + std::list>> postponedNodes; + + std::vector cecs; + + for(auto& [worldId, world] : dump) { + for(auto& [regionPos, region] : world) { + for(auto& [chunkPos, chunk] : region.Voxels) { + CompressedVoxels cmp = compressVoxels(chunk); + Pos::GlobalChunk chunkPosR = (Pos::GlobalChunk(regionPos) << 2) + chunkPos; + + for(auto& ptr : region.NewCECs) { + bool accepted = ptr->Remote->maybe_prepareChunkUpdate_Voxels(worldId, + chunkPosR, cmp.Compressed, cmp.Defines); + if(!accepted) { + cecs.push_back(ptr.get()); + } + } + + if((region.IsChunkChanged_Voxels >> chunkPos.pack()) & 0x1) { + for(auto& ptr : region.CECs) { + bool skip = false; + for(auto& ptr2 : region.CECs) { + if(ptr == ptr2) { + skip = true; + break; + } + } + + if(skip) + continue; + + bool accepted = ptr->Remote->maybe_prepareChunkUpdate_Voxels(worldId, + chunkPosR, cmp.Compressed, cmp.Defines); + if(!accepted) { + cecs.push_back(ptr.get()); + } + } + } + + if(!cecs.empty()) { + postponedVoxels.push_back({{worldId, chunkPosR, std::move(cmp)}, cecs}); + cecs.clear(); + } + } + + for(auto& [chunkPos, chunk] : region.Nodes) { + CompressedNodes cmp = compressNodes(chunk.data()); + Pos::GlobalChunk chunkPosR = (Pos::GlobalChunk(regionPos) << 2) + chunkPos; + + for(auto& ptr : region.NewCECs) { + bool accepted = ptr->Remote->maybe_prepareChunkUpdate_Nodes(worldId, + chunkPosR, cmp.Compressed, cmp.Defines); + if(!accepted) { + cecs.push_back(ptr.get()); + } + } + + if((region.IsChunkChanged_Nodes >> chunkPos.pack()) & 0x1) { + for(auto& ptr : region.CECs) { + bool skip = false; + for(auto& ptr2 : region.CECs) { + if(ptr == ptr2) { + skip = true; + break; + } + } + + if(skip) + continue; + + bool accepted = ptr->Remote->maybe_prepareChunkUpdate_Nodes(worldId, + chunkPosR, cmp.Compressed, cmp.Defines); + if(!accepted) { + cecs.push_back(ptr.get()); + } + } + } + + if(!cecs.empty()) { + postponedNodes.push_back({{worldId, chunkPosR, std::move(cmp)}, cecs}); + cecs.clear(); + } + } + } + } + + while(!postponedVoxels.empty() || !postponedNodes.empty()) { + { + auto begin = postponedVoxels.begin(), end = postponedVoxels.end(); + while(begin != end) { + auto& [worldId, chunkPos, cmp] = begin->first; + for(ContentEventController* cec : begin->second) { + bool accepted = cec->Remote->maybe_prepareChunkUpdate_Voxels(worldId, chunkPos, cmp.Compressed, cmp.Defines); + if(!accepted) + cecs.push_back(cec); + } + + if(cecs.empty()) { + begin = postponedVoxels.erase(begin); + } else { + begin->second = cecs; + cecs.clear(); + begin++; + } + } + } + + { + auto begin = postponedNodes.begin(), end = postponedNodes.end(); + while(begin != end) { + auto& [worldId, chunkPos, cmp] = begin->first; + for(ContentEventController* cec : begin->second) { + bool accepted = cec->Remote->maybe_prepareChunkUpdate_Nodes(worldId, chunkPos, cmp.Compressed, cmp.Defines); + if(!accepted) + cecs.push_back(cec); + } + + if(cecs.empty()) { + begin = postponedNodes.erase(begin); + } else { + begin->second = cecs; + cecs.clear(); + begin++; + } + } + } + } + + // Синхронизация + { + std::unique_lock lock(Mutex); + RunCompress--; Symaphore.notify_all(); } } @@ -328,7 +563,7 @@ void GameServer::run() { if(IsGoingShutdown) { // Отключить игроков - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr &cec : Game.CECs) { cec->Remote->shutdown(EnumDisconnect::ByInterface, ShutdownReason); } @@ -394,10 +629,10 @@ void GameServer::stepConnections() { lock->clear(); } - Backing.end(); + Backing.endCollectChanges(); // Отключение игроков - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { // Убрать отключившихся if(!cec->Remote->isConnected()) { // Отписываем наблюдателя от миров @@ -405,7 +640,7 @@ void GameServer::stepConnections() { auto wIter = Expanse.Worlds.find(wPair.first); assert(wIter != Expanse.Worlds.end()); - wIter->second->onCEC_RegionsLost(cec.get(), wPair.second); + wIter->second->onCEC_RegionsLost(cec, wPair.second); } std::string username = cec->Remote->Username; @@ -417,7 +652,7 @@ void GameServer::stepConnections() { // Вычистить невалидные ссылки на игроков Game.CECs.erase(std::remove_if(Game.CECs.begin(), Game.CECs.end(), - [](const std::unique_ptr& ptr) { return !ptr; }), + [](const std::shared_ptr& ptr) { return !ptr; }), Game.CECs.end()); } @@ -428,7 +663,7 @@ void GameServer::stepModInitializations() { IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() { IWorldSaveBackend::TickSyncInfo_In toDB; - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { assert(cec); // Пересчитать зоны наблюдения if(cec->CrossedBorder) { @@ -465,7 +700,7 @@ IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() { auto iterWorld = Expanse.Worlds.find(worldId); assert(iterWorld != Expanse.Worlds.end()); - std::vector notLoaded = iterWorld->second->onCEC_RegionsEnter(cec.get(), regions, worldId); + std::vector notLoaded = iterWorld->second->onCEC_RegionsEnter(cec, regions); if(!notLoaded.empty()) { // Добавляем к списку на загрузку std::vector &tl = toDB.Load[worldId]; @@ -478,7 +713,7 @@ IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() { auto iterWorld = Expanse.Worlds.find(worldId); assert(iterWorld != Expanse.Worlds.end()); - iterWorld->second->onCEC_RegionsLost(cec.get(), regions); + iterWorld->second->onCEC_RegionsLost(cec, regions); } } } @@ -596,7 +831,7 @@ void GameServer::stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db newRegions.push_back(pos); std::sort(newRegions.begin(), newRegions.end()); - std::unordered_map> toSubscribe; + std::unordered_map, std::vector> toSubscribe; for(auto& cec : Game.CECs) { auto iterViewWorld = cec->ContentViewState.Regions.find(worldId); @@ -605,13 +840,13 @@ void GameServer::stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db for(auto& pos : iterViewWorld->second) { if(std::binary_search(newRegions.begin(), newRegions.end(), pos)) - toSubscribe[cec.get()].push_back(pos); + toSubscribe[cec].push_back(pos); } } iterWorld->second->pushRegions(std::move(regions)); for(auto& [cec, poses] : toSubscribe) { - iterWorld->second->onCEC_RegionsEnter(cec, poses, worldId); + iterWorld->second->onCEC_RegionsEnter(cec, poses); } } } @@ -1071,7 +1306,7 @@ void GameServer::stepSyncContent() { Content.Texture.update(CurrentTickDuration); if(Content.Texture.hasPreparedInformation()) { auto table = Content.Texture.takePreparedInformation(); - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { cec->Remote->informateBinTexture(table); } } @@ -1079,7 +1314,7 @@ void GameServer::stepSyncContent() { Content.Animation.update(CurrentTickDuration); if(Content.Animation.hasPreparedInformation()) { auto table = Content.Animation.takePreparedInformation(); - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { cec->Remote->informateBinAnimation(table); } } @@ -1087,7 +1322,7 @@ void GameServer::stepSyncContent() { Content.Model.update(CurrentTickDuration); if(Content.Model.hasPreparedInformation()) { auto table = Content.Model.takePreparedInformation(); - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { cec->Remote->informateBinModel(table); } } @@ -1095,7 +1330,7 @@ void GameServer::stepSyncContent() { Content.Sound.update(CurrentTickDuration); if(Content.Sound.hasPreparedInformation()) { auto table = Content.Sound.takePreparedInformation(); - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { cec->Remote->informateBinSound(table); } } @@ -1103,18 +1338,18 @@ void GameServer::stepSyncContent() { Content.Font.update(CurrentTickDuration); if(Content.Font.hasPreparedInformation()) { auto table = Content.Font.takePreparedInformation(); - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { cec->Remote->informateBinFont(table); } } // Сбор запросов на ресурсы и профили + отправка пакетов игрокам ResourceRequest full; - for(std::unique_ptr &cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { full.insert(cec->Remote->pushPreparedPackets()); } - Backing.start(); + Backing.startCollectChanges(); full.uniq(); diff --git a/Src/Server/GameServer.hpp b/Src/Server/GameServer.hpp index ec1ea66..e82382c 100644 --- a/Src/Server/GameServer.hpp +++ b/Src/Server/GameServer.hpp @@ -72,7 +72,7 @@ class GameServer : public AsyncObject { } Content; struct { - std::vector> CECs; + std::vector> CECs; ServerTime AfterStartTime = {0, 0}; } Game; @@ -117,10 +117,6 @@ class GameServer : public AsyncObject { std::unique_ptr ModStorage; } SaveBackend; - enum class EnumBackingScenario { - ChunksChanges // Сжатие изменённых чанков и отправка клиентам - }; - /* Обязательно между тактами После окончания такта пул копирует изменённые чанки @@ -143,24 +139,29 @@ class GameServer : public AsyncObject { Локальный поток должен собирать ключи профилей для базы Остальное внутри базы */ - struct Backing_t { - TOS::Logger LOG = "Backing"; + struct BackingChunkPressure_t { + TOS::Logger LOG = "BackingChunkPressure"; bool NeedShutdown = false; std::vector Threads; std::mutex Mutex; - std::atomic_int Run = 0; + int RunCollect = 0, RunCompress = 0; std::condition_variable Symaphore; std::unordered_map> *Worlds; - void start() { + void startCollectChanges() { std::lock_guard lock(Mutex); - Run = Threads.size(); + RunCompress = RunCollect = Threads.size(); Symaphore.notify_all(); } - void end() { + void endCollectChanges() { std::unique_lock lock(Mutex); - Symaphore.wait(lock, [&](){ return Run == 0 || NeedShutdown; }); + Symaphore.wait(lock, [&](){ return RunCollect == 0 || NeedShutdown; }); + } + + void endWithResults() { + std::unique_lock lock(Mutex); + Symaphore.wait(lock, [&](){ return RunCompress == 0 || NeedShutdown; }); } void stop() { @@ -187,7 +188,7 @@ public: Backing.Threads.resize(4); Backing.Worlds = &Expanse.Worlds; for(size_t iter = 0; iter < Backing.Threads.size(); iter++) { - Backing.Threads[iter] = std::thread(Backing.Run, &Backing, iter); + Backing.Threads[iter] = std::thread(&BackingChunkPressure_t::run, &Backing, iter); } } diff --git a/Src/Server/World.cpp b/Src/Server/World.cpp index 7a736f5..2e42e0d 100644 --- a/Src/Server/World.cpp +++ b/Src/Server/World.cpp @@ -16,7 +16,7 @@ World::~World() { } -std::vector World::onCEC_RegionsEnter(ContentEventController *cec, const std::vector& enter, WorldId_t wId) { +std::vector World::onCEC_RegionsEnter(std::shared_ptr cec, const std::vector& enter) { std::vector out; TOS::Logger("Test").debug() << "Start"; @@ -30,6 +30,7 @@ std::vector World::onCEC_RegionsEnter(ContentEventController auto ®ion = *iterRegion->second; region.CECs.push_back(cec); + region.NewCECs.push_back(cec); // Отправить клиенту информацию о чанках и сущностях std::unordered_map*> voxels; std::unordered_map nodes; @@ -44,8 +45,7 @@ std::vector World::onCEC_RegionsEnter(ContentEventController nodes[Pos::bvec4u(x, y, z)] = (const Node*) ®ion.Nodes[0][0][0][x][y][z]; } - cec->onChunksUpdate_Voxels(wId, pos, voxels); - cec->onChunksUpdate_Nodes(wId, pos, nodes); + } TOS::Logger("Test").debug() << "End"; @@ -53,13 +53,13 @@ std::vector World::onCEC_RegionsEnter(ContentEventController return out; } -void World::onCEC_RegionsLost(ContentEventController *cec, const std::vector &lost) { +void World::onCEC_RegionsLost(std::shared_ptr cec, const std::vector &lost) { for(const Pos::GlobalRegion &pos : lost) { auto region = Regions.find(pos); if(region == Regions.end()) continue; - std::vector &CECs = region->second->CECs; + std::vector> &CECs = region->second->CECs; for(size_t iter = 0; iter < CECs.size(); iter++) { if(CECs[iter] == cec) { CECs.erase(CECs.begin()+iter); diff --git a/Src/Server/World.hpp b/Src/Server/World.hpp index 764a4fb..b950e8f 100644 --- a/Src/Server/World.hpp +++ b/Src/Server/World.hpp @@ -25,7 +25,7 @@ public: Node Nodes[16][16][16][4][4][4]; std::vector Entityes; - std::vector CECs; + std::vector> CECs, NewCECs; // Используется для прорежения количества проверок на наблюдаемые чанки и сущности // В одно обновление региона - проверка одного наблюдателя uint16_t CEC_NextChunkAndEntityesViewCheck = 0; @@ -149,8 +149,8 @@ public: Возвращает список не загруженных регионов, на которые соответственно игрока не получилось подписать При подписи происходит отправка всех чанков и сущностей региона */ - std::vector onCEC_RegionsEnter(ContentEventController *cec, const std::vector &enter, WorldId_t wId); - void onCEC_RegionsLost(ContentEventController *cec, const std::vector& lost); + std::vector onCEC_RegionsEnter(std::shared_ptr cec, const std::vector &enter); + void onCEC_RegionsLost(std::shared_ptr cec, const std::vector& lost); struct SaveUnloadInfo { std::vector ToUnload; std::vector> ToSave;