This commit is contained in:
2025-07-07 18:42:52 +06:00
parent 9bc18b5396
commit 8fe8057d9c
10 changed files with 227 additions and 156 deletions

View File

@@ -23,22 +23,42 @@
namespace LV::Server {
GameServer::GameServer(asio::io_context &ioc, fs::path worldPath)
: AsyncObject(ioc),
Content(ioc, nullptr, nullptr, nullptr, nullptr, nullptr)
{
init(worldPath);
BackingChunkPressure.Threads.resize(1);
BackingChunkPressure.Worlds = &Expanse.Worlds;
for(size_t iter = 0; iter < BackingChunkPressure.Threads.size(); iter++) {
BackingChunkPressure.Threads[iter] = std::thread(&BackingChunkPressure_t::run, &BackingChunkPressure, iter);
}
BackingNoiseGenerator.Threads.resize(1);
for(size_t iter = 0; iter < BackingNoiseGenerator.Threads.size(); iter++) {
BackingNoiseGenerator.Threads[iter] = std::thread(&BackingNoiseGenerator_t::run, &BackingNoiseGenerator, iter);
}
}
GameServer::~GameServer() {
shutdown("on ~GameServer");
Backing.NeedShutdown = true;
Backing.Symaphore.notify_all();
BackingChunkPressure.NeedShutdown = true;
BackingChunkPressure.Symaphore.notify_all();
BackingNoiseGenerator.NeedShutdown = true;
RunThread.join();
WorkDeadline.cancel();
UseLock.wait_no_use();
Backing.stop();
BackingChunkPressure.stop();
BackingNoiseGenerator.stop();
LOG.info() << "Сервер уничтожен";
}
void GameServer::BackingChunkPressure_t::run(int id) {
LOG.debug() << "Старт фонового потока " << id;
LOG.debug() << "Старт потока " << id;
try {
while(true) {
@@ -46,7 +66,7 @@ void GameServer::BackingChunkPressure_t::run(int id) {
std::unique_lock<std::mutex> lock(Mutex);
Symaphore.wait(lock, [&](){ return RunCollect != 0 || NeedShutdown; });
if(NeedShutdown) {
LOG.debug() << "Завершение выполнения фонового потока " << id;
LOG.debug() << "Завершение выполнения потока " << id;
break;
}
}
@@ -83,6 +103,7 @@ void GameServer::BackingChunkPressure_t::run(int id) {
regionObj.IsChunkChanged_Nodes = 0;
if(!regionObj.NewCECs.empty()) {
dumpRegion.CECs = regionObj.CECs;
dumpRegion.NewCECs = std::move(regionObj.NewCECs);
dumpRegion.Voxels = regionObj.Voxels;
@@ -296,12 +317,58 @@ void GameServer::BackingChunkPressure_t::run(int id) {
} catch(const std::exception& exc) {
std::unique_lock<std::mutex> lock(Mutex);
NeedShutdown = true;
LOG.error() << "Ошибка выполнения фонового потока " << id << ":\n" << exc.what();
LOG.error() << "Ошибка выполнения потока " << id << ":\n" << exc.what();
}
Symaphore.notify_all();
}
void GameServer::BackingNoiseGenerator_t::run(int id) {
LOG.debug() << "Старт потока " << id;
try {
while(true) {
if(NeedShutdown) {
LOG.debug() << "Завершение выполнения потока " << id;
break;
}
if(Input.get_read().empty())
TOS::Time::sleep3(50);
NoiseKey key;
{
auto lock = Input.lock();
if(lock->empty())
continue;
key = lock->front();
lock->pop();
}
Pos::GlobalNode posNode = key.RegionPos;
posNode <<= 6;
std::array<float, 64*64*64> data;
float *ptr = &data[0];
for(int z = 0; z < 64; z++)
for(int y = 0; y < 64; y++)
for(int x = 0; x < 64; x++, ptr++) {
*ptr = TOS::genRand(); //glm::perlin(glm::vec3(posNode.x+x, posNode.y+y, posNode.z+z));
}
Output.lock()->push_back({key, std::move(data)});
}
} catch(const std::exception& exc) {
NeedShutdown = true;
LOG.error() << "Ошибка выполнения потока " << id << ":\n" << exc.what();
}
}
static thread_local std::vector<ContentViewCircle> TL_Circles;
std::vector<ContentViewCircle> GameServer::Expanse_t::accumulateContentViewCircles(ContentViewCircle circle, int depth)
@@ -596,6 +663,16 @@ void GameServer::run() {
stepGlobalStep();
stepSyncContent();
// Прочие моменты
if(!IsGoingShutdown) {
if(BackingChunkPressure.NeedShutdown
|| BackingNoiseGenerator.NeedShutdown)
{
LOG.error() << "Ошибка работы одного из модулей";
IsGoingShutdown = true;
}
}
// Сон или подгонка длительности такта при высоких нагрузках
std::chrono::steady_clock::time_point atTickEnd = std::chrono::steady_clock::now();
float currentWastedTime = double((atTickEnd-atTickStart).count() * std::chrono::steady_clock::duration::period::num) / std::chrono::steady_clock::duration::period::den;
@@ -629,7 +706,7 @@ void GameServer::stepConnections() {
lock->clear();
}
Backing.endCollectChanges();
BackingChunkPressure.endCollectChanges();
// Отключение игроков
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
@@ -664,6 +741,7 @@ IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
IWorldSaveBackend::TickSyncInfo_In toDB;
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
break;
assert(cec);
// Пересчитать зоны наблюдения
if(cec->CrossedBorder) {
@@ -754,55 +832,28 @@ void GameServer::stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db
// Синхронизация с генератором шума
std::unordered_map<WorldId_t, std::vector<std::pair<Pos::GlobalRegion, std::array<float, 64*64*64>>>> calculatedNoise;
for(auto& [worldId, regions] : db.NotExisten) {
auto &r = calculatedNoise[worldId];
for(Pos::GlobalRegion pos : regions) {
if(IsGoingShutdown)
break;
r.emplace_back();
std::get<0>(r.back()) = pos;
auto &region = std::get<1>(r.back());
Pos::GlobalNode posNode = pos;
posNode <<= 6;
float *ptr = &region[0];
for(int z = 0; z < 64; z++)
for(int y = 0; y < 64; y++)
for(int x = 0; x < 64; x++, ptr++) {
*ptr = TOS::genRand(); //glm::perlin(glm::vec3(posNode.x+x, posNode.y+y, posNode.z+z));
}
}
}
if(IsGoingShutdown)
return;
std::vector<std::pair<BackingNoiseGenerator_t::NoiseKey, std::array<float, 64*64*64>>> calculatedNoise = BackingNoiseGenerator.tickSync(std::move(db.NotExisten));
std::unordered_map<WorldId_t, std::vector<std::pair<Pos::GlobalRegion, World::RegionIn>>> toLoadRegions;
// Синхронизация с контроллером асинхронных обработчиков луа
// 2.2 и 3.1
// Обработка шума
for(auto& [WorldId_t, regions] : calculatedNoise) {
auto &list = toLoadRegions[WorldId_t];
for(auto& [pos, noise] : regions) {
auto &obj = list.emplace_back(pos, World::RegionIn()).second;
float *ptr = &noise[0];
for(auto& [key, region] : calculatedNoise) {
LOG.debug() << "Сгенерирован " << key.WId << ' ' << key.RegionPos.x << ' '
<< key.RegionPos.y << ' ' << key.RegionPos.z;
for(int z = 0; z < 64; z++)
for(int y = 0; y < 64; y++)
for(int x = 0; x < 64; x++, ptr++) {
DefVoxelId_t id = *ptr > 0.5 ? 1 : 0;
Pos::bvec64u nodePos(x, y, z);
auto &node = obj.Nodes[Pos::bvec4u(nodePos >> 4).pack()][Pos::bvec16u(nodePos & 0xf).pack()];
node.NodeId = id;
node.Meta = 0;
}
auto &obj = toLoadRegions[key.WId].emplace_back(key.RegionPos, World::RegionIn()).second;
float *ptr = &region[0];
for(int z = 0; z < 64; z++)
for(int y = 0; y < 64; y++)
for(int x = 0; x < 64; x++, ptr++) {
DefVoxelId_t id = *ptr > 0.5 ? 1 : 0;
Pos::bvec64u nodePos(x, y, z);
auto &node = obj.Nodes[Pos::bvec4u(nodePos >> 4).pack()][Pos::bvec16u(nodePos & 0xf).pack()];
node.NodeId = id;
node.Meta = 0;
}
}
@@ -1349,7 +1400,7 @@ void GameServer::stepSyncContent() {
full.insert(cec->Remote->pushPreparedPackets());
}
Backing.startCollectChanges();
BackingChunkPressure.startCollectChanges();
full.uniq();

View File

@@ -6,10 +6,12 @@
#include <boost/asio/io_context.hpp>
#include <condition_variable>
#include <filesystem>
#include "Common/Abstract.hpp"
#include "RemoteClient.hpp"
#include "Server/Abstract.hpp"
#include <TOSLib.hpp>
#include <memory>
#include <queue>
#include <set>
#include <thread>
#include <unordered_map>
@@ -119,12 +121,6 @@ class GameServer : public AsyncObject {
/*
Обязательно между тактами
После окончания такта пул копирует изменённые чанки
- синхронизация сбора в stepDatabaseSync -
сжимает их и отправляет клиентам
- синхронизация в начале stepPlayerProceed -
^ к этому моменту все данные должны быть отправлены
Далее при подписании на новые регионы они будут добавлены в пул на обработку
Генерация шума
OpenCL или пул
@@ -139,6 +135,16 @@ class GameServer : public AsyncObject {
Локальный поток должен собирать ключи профилей для базы
Остальное внутри базы
*/
/*
Отправка изменений чанков клиентам
После окончания такта пул копирует изменённые чанки
- синхронизация сбора в stepDatabaseSync -
сжимает их и отправляет клиентам
- синхронизация в начале stepPlayerProceed -
^ к этому моменту все данные должны быть отправлены в RemoteClient
*/
struct BackingChunkPressure_t {
TOS::Logger LOG = "BackingChunkPressure";
bool NeedShutdown = false;
@@ -176,25 +182,52 @@ class GameServer : public AsyncObject {
}
void run(int id);
} Backing;
} BackingChunkPressure;
struct BackingNoiseGenerator_t {
struct NoiseKey {
WorldId_t WId;
Pos::GlobalRegion RegionPos;
};
TOS::Logger LOG = "BackingNoiseGenerator";
bool NeedShutdown = false;
std::vector<std::thread> Threads;
TOS::SpinlockObject<std::queue<NoiseKey>> Input;
TOS::SpinlockObject<std::vector<std::pair<NoiseKey, std::array<float, 64*64*64>>>> Output;
void stop() {
NeedShutdown = true;
for(std::thread& thread : Threads)
thread.join();
}
void run(int id);
std::vector<std::pair<NoiseKey, std::array<float, 64*64*64>>>
tickSync(std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> &&input) {
{
auto lock = Input.lock();
for(auto& [worldId, region] : input) {
for(auto& regionPos : region)
lock->push({worldId, regionPos});
}
}
auto lock = Output.lock();
std::vector<std::pair<NoiseKey, std::array<float, 64*64*64>>> out = std::move(*lock);
lock->reserve(8000);
return std::move(out);
}
} BackingNoiseGenerator;
public:
GameServer(asio::io_context &ioc, fs::path worldPath)
: AsyncObject(ioc),
Content(ioc, nullptr, nullptr, nullptr, nullptr, nullptr)
{
init(worldPath);
Backing.Threads.resize(4);
Backing.Worlds = &Expanse.Worlds;
for(size_t iter = 0; iter < Backing.Threads.size(); iter++) {
Backing.Threads[iter] = std::thread(&BackingChunkPressure_t::run, &Backing, iter);
}
}
GameServer(asio::io_context &ioc, fs::path worldPath);
virtual ~GameServer();
void shutdown(const std::string reason) {
if(ShutdownReason.empty())
ShutdownReason = reason;

View File

@@ -137,7 +137,7 @@ bool RemoteClient::maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::Globa
}
}
checkPacketBorder(1+4+8+2+4+compressed_voxels.size());
checkPacketBorder(4+4+8+2+4+compressed_voxels.size());
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::ChunkVoxels
<< worldId << chunkPos.pack() << uint32_t(compressed_voxels.size());
@@ -211,7 +211,7 @@ bool RemoteClient::maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::Global
}
}
checkPacketBorder(1+4+8+4+compressed_nodes.size());
checkPacketBorder(4+4+8+4+compressed_nodes.size());
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::ChunkNodes
<< worldId << chunkPos.pack() << uint32_t(compressed_nodes.size());

View File

@@ -19,8 +19,6 @@ World::~World() {
std::vector<Pos::GlobalRegion> World::onCEC_RegionsEnter(std::shared_ptr<ContentEventController> cec, const std::vector<Pos::GlobalRegion>& enter) {
std::vector<Pos::GlobalRegion> out;
TOS::Logger("Test").debug() << "Start";
for(const Pos::GlobalRegion &pos : enter) {
auto iterRegion = Regions.find(pos);
if(iterRegion == Regions.end()) {
@@ -48,8 +46,6 @@ std::vector<Pos::GlobalRegion> World::onCEC_RegionsEnter(std::shared_ptr<Content
}
TOS::Logger("Test").debug() << "End";
return out;
}