Рефакторинг обработки наблюдаемых областей
This commit is contained in:
@@ -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")
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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] = ®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<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> ®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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user