Рефакторинг обработки наблюдаемых областей

This commit is contained in:
2025-03-02 21:44:16 +06:00
parent a007aa85d0
commit 00d0eec88f
15 changed files with 512 additions and 381 deletions

View File

@@ -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")

View File

@@ -2,6 +2,7 @@
#include "Client/Abstract.hpp"
#include "Common/Abstract.hpp"
#include "Common/Net.hpp"
#include "TOSLib.hpp"
#include <GLFW/glfw3.h>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/this_coro.hpp>
@@ -440,18 +441,26 @@ coro<> ServerSession::rP_Definition(Net::AsyncSocket &sock) {
uint8_t second = co_await sock.read<uint8_t>();
switch((ToClient::L2Definition) second) {
case ToClient::L2Definition::World:
case ToClient::L2Definition::World: {
DefWorldId_c cdId = co_await sock.read<DefWorldId_c>();
co_return;
case ToClient::L2Definition::FreeWorld:
}
case ToClient::L2Definition::FreeWorld: {
DefWorldId_c cdId = co_await sock.read<DefWorldId_c>();
co_return;
case ToClient::L2Definition::Voxel:
}
case ToClient::L2Definition::Voxel: {
DefVoxelId_c cdId = co_await sock.read<DefVoxelId_c>();
co_return;
case ToClient::L2Definition::FreeVoxel:
}
case ToClient::L2Definition::FreeVoxel: {
DefVoxelId_c cdId = co_await sock.read<DefVoxelId_c>();
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<uint8_t>();
WorldId_c wcId = co_await sock.read<WorldId_c>();
Pos::GlobalChunk::Key posKey = co_await sock.read<Pos::GlobalChunk::Key>();
Pos::GlobalChunk pos = *(Pos::GlobalChunk*) &posKey;
std::vector<VoxelCube> cubes(co_await sock.read<uint16_t>());
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];

View File

@@ -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); }
};
}

View File

@@ -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<Packet> *simplePackets, std::vector<SmartPacket> *smartPackets) {
@@ -49,6 +50,7 @@ void AsyncSocket::pushPackets(std::vector<Packet> *simplePackets, std::vector<Sm
|| SendPackets.SmartBuffer.size() + (smartPackets ? smartPackets->size() : 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<AsyncContext> 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<size_t>(packetSize, NetPool::PageSize);

View File

@@ -2,6 +2,7 @@
#include "MemoryPool.hpp"
#include "Async.hpp"
#include "TOSLib.hpp"
#include <atomic>
#include <boost/asio.hpp>
@@ -97,6 +98,7 @@ protected:
uint16_t needWrite = std::min<uint16_t>(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;

View File

@@ -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<VoxelCube_Region>& 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);
}
}
}

View File

@@ -12,10 +12,14 @@ ContentEventController::ContentEventController(std::unique_ptr<RemoteClient> &&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<Pos::GlobalRegion> &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<Pos::Local16_u> &enter, const std::unordered_set<Pos::Local16_u> &lost) {
if(!Subscribed.Chunks.contains(worldId)) {
Remote->prepareWorldNew(worldId, worldObj);
}
std::unordered_set<Pos::Local16_u> &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<Pos::Local16_u, const std::vector<VoxelCube>*> &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<Pos::Local16_u, const std::unordered_map<Pos::Local16_u, Node>*> &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<Pos::Local16_u, const LightPrism*> &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);
}
}

View File

@@ -2,7 +2,10 @@
#include <Common/Abstract.hpp>
#include "Abstract.hpp"
#include <bitset>
#include <map>
#include <memory>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -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<Pos::GlobalRegion, std::bitset<4096>>; // 1 - чанк виден, 0 - не виден
struct ContentViewGlobal_DiffInfo;
struct ContentViewGlobal : public std::map<WorldId_t, ContentViewWorld> {
// Вычисляет половинную разницу между текущей и предыдущей области видимости
// Возвращает области, которые появились по отношению к old, чтобы получить области потерянные из виду поменять местами *this и old
ContentViewGlobal_DiffInfo calcDiffWith(const ContentViewGlobal &old) const;
};
struct ContentViewGlobal_DiffInfo {
// Новые увиденные чанки
ContentViewGlobal View;
// Регионы
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> Regions;
// Миры
std::vector<WorldId_t> 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<Pos::GlobalRegion, std::bitset<4096>> &newRegions = newWorldView;
const std::map<Pos::GlobalRegion, std::bitset<4096>> &oldRegions = oldWorldIter->second;
std::map<Pos::GlobalRegion, std::bitset<4096>> *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<PortalId_t> Portals;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<Pos::Local16_u>>> Chunks;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<LocalEntityId_t>>> Entities;
} Subscribed;
@@ -94,23 +164,31 @@ public:
// Управляется сервером
std::unique_ptr<RemoteClient> Remote;
// Регионы сюда заглядывают
std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> ContentViewCircles;
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> SubscribedRegions;
// Каждый такт значения изменений обновляются GameServer'ом
// Объявленная в чанках территория точно отслеживается (активная зона)
ContentViewGlobal ContentViewState;
ContentViewGlobal_DiffInfo ContentView_NewView, ContentView_LostView;
// size_t CVCHash = 0; // Хэш для std::vector<ContentViewCircle>
// std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> SubscribedRegions;
public:
ContentEventController(std::unique_ptr<RemoteClient> &&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<Pos::GlobalRegion> &lost);
void onChunksEnterLost(WorldId_t worldId, World *worldObj, Pos::GlobalRegion regionId, const std::unordered_set<Pos::Local16_u> &enter, const std::unordered_set<Pos::Local16_u> &lost);
// Фильтровать не отслеживаемые миры
void onWorldUpdate(WorldId_t worldId, World *worldObj);
// Нужно фильтровать неотслеживаемые чанки
void onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::Local16_u, const std::vector<VoxelCube>*> &chunks);
void onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::Local16_u, const std::unordered_map<Pos::Local16_u, Node>*> &chunks);
@@ -140,4 +218,20 @@ struct hash<LV::Server::ServerObjectPos> {
};
template <>
struct hash<LV::Server::ContentViewCircle> {
size_t operator()(const LV::Server::ContentViewCircle& obj) const noexcept {
// Используем стандартную функцию хеширования для uint32_t, glm::i16vec3 и int32_t
auto worldIdHash = std::hash<uint32_t>{}(obj.WorldId) << 32;
auto posHash =
std::hash<int16_t>{}(obj.Pos.x) ^
(std::hash<int16_t>{}(obj.Pos.y) << 16) ^
(std::hash<int16_t>{}(obj.Pos.z) << 32);
auto rangeHash = std::hash<int32_t>{}(obj.Range);
return worldIdHash ^
posHash ^
(~rangeHash << 32);
}
};
}

View File

@@ -14,6 +14,7 @@
#include <unordered_set>
#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<ContentViewCircle> TL_Circles;
std::vector<ContentViewCircle> GameServer::WorldObj::calcCVCs(ContentViewCircle circle, int depth)
std::vector<ContentViewCircle> 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<WorldId_t, std::vector<ContentViewCircle>> GameServer::WorldObj::remapCVCsByWorld(const std::vector<ContentViewCircle> &list) {
std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> out;
// std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> GameServer::WorldObj::remapCVCsByWorld(const std::vector<ContentViewCircle> &list) {
// std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> 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<ContentViewCircle> &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<ContentEventController> &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<ContentEventController> &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<Pos::GlobalRegion> 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<Pos::Local16_u> 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<Pos::Local16_u> 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<Pos::Local16_u>(lostChunks.begin(), lostChunks.end()));
// Нужно отправить полную информацию о новых наблюдаемых чанках наблюдателю
if(!newChunksSet.empty()) {
if(new_chunkBitset) {
std::unordered_map<Pos::Local16_u, const LightPrism*> newLightPrism;
std::unordered_map<Pos::Local16_u, const std::vector<VoxelCube>*> newVoxels;
std::unordered_map<Pos::Local16_u, const std::unordered_map<Pos::Local16_u, Node>*> newNodes;
for(Pos::Local16_u cPos : newChunksSet) {
newLightPrism[cPos] = &region.Lights[0][0][cPos.X][cPos.Y][cPos.Z];
newVoxels[cPos] = &region.Voxels[cPos.X][cPos.Y][cPos.Z];
newNodes[cPos] = &region.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, &region.Lights[0][0][chunkPos.X][chunkPos.Y][chunkPos.Z]});
newVoxels.insert({chunkPos, &region.Voxels[chunkPos.X][chunkPos.Y][chunkPos.Z]});
newNodes.insert({chunkPos, &region.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<LocalEntityId_t> 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<LocalEntityId_t> 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<LocalEntityId_t> 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<LocalEntityId_t> 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<LocalEntityId_t>(lostEntityes.begin(), lostEntityes.end()));
// Отправить полную информацию о новых наблюдаемых сущностях наблюдателю
}
// doesNotObserveEntityes:
// cec->onEntityEnterLost(pWorld.first, pRegion.first, newEntityesSet, std::unordered_set<LocalEntityId_t>(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<ContentEventController*, ContentViewGlobal_DiffInfo> 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<ContentViewCircle> newCVCs = Expanse.accumulateContentViewCircles(cvc);
//size_t hash = (std::hash<std::vector<ContentViewCircle>>{})(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<Pos::GlobalRegion> lost;
// Проверяем отслеживаемые регионы
std::vector<Pos::GlobalRegion> regionsResult;
for(auto &pair : cec.ContentViewCircles) {
auto world = Expanse.Worlds.find(pair.first);
if(world == Expanse.Worlds.end())
continue;
std::vector<Pos::GlobalRegion> 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<Pos::GlobalRegion> &regionsRight = 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 &region : 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();
}
}

View File

@@ -75,15 +75,22 @@ class GameServer : public AsyncObject {
} Game;
struct WorldObj {
struct Expanse_t {
std::unordered_map<ContentBridgeId_t, ContentBridge> ContentBridges;
std::vector<ContentViewCircle> calcCVCs(ContentViewCircle circle, int depth = 2);
std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> remapCVCsByWorld(const std::vector<ContentViewCircle> &list);
std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> calcAndRemapCVC(ContentViewCircle circle, int depth = 2) {
return remapCVCsByWorld(calcCVCs(circle, depth));
// Вычисляет окружности обозримой области
// depth ограничивает глубину входа в ContentBridges
std::vector<ContentViewCircle> accumulateContentViewCircles(ContentViewCircle circle, int depth = 2);
static ContentViewGlobal makeContentViewGlobal(const std::vector<ContentViewCircle> &views);
ContentViewGlobal makeContentViewGlobal(ContentViewCircle circle, int depth = 2) {
return makeContentViewGlobal(accumulateContentViewCircles(circle, depth));
}
// std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> remapCVCsByWorld(const std::vector<ContentViewCircle> &list);
// std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> calcAndRemapCVC(ContentViewCircle circle, int depth = 2) {
// return remapCVCsByWorld(calcCVCs(circle, depth));
// }
std::unordered_map<WorldId_t, std::unique_ptr<World>> 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();

View File

@@ -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<BinTextureId_t> lostTextures, newTextures;
std::unordered_set<BinSoundId_t> lostSounds, newSounds;
std::unordered_set<BinModelId_t> 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<BinTextureId_t> lostTextures, newTextures;
std::unordered_set<BinSoundId_t> lostSounds, newSounds;
std::unordered_set<BinModelId_t> 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) {}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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
{

View File

@@ -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();