Переделка ресурсов и этапов обновения

This commit is contained in:
2025-06-30 08:19:51 +06:00
parent cfc80660dd
commit 6fd0b3e9d5
11 changed files with 359 additions and 612 deletions

View File

@@ -164,7 +164,6 @@ public:
std::unordered_map<DefWorldId_t, DefWorldInfo> DefWorld; std::unordered_map<DefWorldId_t, DefWorldInfo> DefWorld;
std::unordered_map<DefPortalId_t, DefPortalInfo> DefPortal; std::unordered_map<DefPortalId_t, DefPortalInfo> DefPortal;
std::unordered_map<DefEntityId_t, DefEntityInfo> DefEntity; std::unordered_map<DefEntityId_t, DefEntityInfo> DefEntity;
std::unordered_map<DefFuncEntityId_t, DefFuncEntityInfo> DefFuncEntity;
std::unordered_map<DefItemId_t, DefItemInfo> DefItem; std::unordered_map<DefItemId_t, DefItemInfo> DefItem;
} Registry; } Registry;
@@ -172,7 +171,6 @@ public:
std::unordered_map<WorldId_t, WorldInfo> Worlds; std::unordered_map<WorldId_t, WorldInfo> Worlds;
std::unordered_map<PortalId_t, PortalInfo> Portals; std::unordered_map<PortalId_t, PortalInfo> Portals;
std::unordered_map<EntityId_t, EntityInfo> Entityes; std::unordered_map<EntityId_t, EntityInfo> Entityes;
std::unordered_map<FuncEntityId_t, FuncEntityInfo> FuncEntityes;
} Data; } Data;
virtual ~IServerSession(); virtual ~IServerSession();

View File

@@ -410,7 +410,6 @@ using DefNodeId_t = ResourceId_t;
using DefWorldId_t = ResourceId_t; using DefWorldId_t = ResourceId_t;
using DefPortalId_t = ResourceId_t; using DefPortalId_t = ResourceId_t;
using DefEntityId_t = ResourceId_t; using DefEntityId_t = ResourceId_t;
using DefFuncEntityId_t = ResourceId_t;
using DefItemId_t = ResourceId_t; using DefItemId_t = ResourceId_t;
// Контент, основанный на игровых определениях // Контент, основанный на игровых определениях

View File

@@ -178,7 +178,7 @@ enum struct L2Content : uint8_t {
ChunkVoxels, ChunkVoxels,
ChunkNodes, ChunkNodes,
ChunkLightPrism, ChunkLightPrism,
RemoveChunk RemoveRegion
}; };
} }

View File

@@ -224,16 +224,6 @@ public:
DefEntityId_t getDefId() const { return DefId; } DefEntityId_t getDefId() const { return DefId; }
}; };
class FuncEntity {
DefFuncEntityId_t DefId;
public:
FuncEntity(DefFuncEntityId_t defId);
DefFuncEntityId_t getDefId() const { return DefId; }
};
template<typename Vec> template<typename Vec>
struct VoxelCuboidsFuncs { struct VoxelCuboidsFuncs {

View File

@@ -3,6 +3,7 @@
#include "RemoteClient.hpp" #include "RemoteClient.hpp"
#include "Server/Abstract.hpp" #include "Server/Abstract.hpp"
#include "World.hpp" #include "World.hpp"
#include <algorithm>
namespace LV::Server { namespace LV::Server {
@@ -12,10 +13,6 @@ ContentEventController::ContentEventController(std::unique_ptr<RemoteClient> &&r
{ {
} }
uint16_t ContentEventController::getViewRangeActive() const {
return 1;
}
uint16_t ContentEventController::getViewRangeBackground() const { uint16_t ContentEventController::getViewRangeBackground() const {
return 0; return 0;
} }
@@ -28,31 +25,20 @@ ServerObjectPos ContentEventController::getPos() const {
return {0, Remote->CameraPos}; return {0, Remote->CameraPos};
} }
void ContentEventController::checkContentViewChanges() { void ContentEventController::removeUnobservable(const ContentViewInfo_Diff& diff) {
// Очистка уже не наблюдаемых чанков for(const auto& [worldId, regions] : diff.RegionsLost) {
for(const auto &[worldId, regions] : ContentView_LostView.View) { for(const Pos::GlobalRegion region : regions)
for(const auto &[regionPos, chunks] : regions) { Remote->prepareRegionRemove(worldId, region);
size_t bitPos = chunks._Find_first();
while(bitPos != chunks.size()) {
Pos::bvec4u chunkPosLocal;
chunkPosLocal.unpack(bitPos);
Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + chunkPosLocal;
Remote->prepareChunkRemove(worldId, chunkPos);
bitPos = chunks._Find_next(bitPos);
}
}
} }
// Очистка миров for(const WorldId_t worldId : diff.WorldsLost)
for(WorldId_t worldId : ContentView_LostView.Worlds) {
Remote->prepareWorldRemove(worldId); Remote->prepareWorldRemove(worldId);
}
} }
void ContentEventController::onWorldUpdate(WorldId_t worldId, World *worldObj) void ContentEventController::onWorldUpdate(WorldId_t worldId, World *worldObj)
{ {
auto pWorld = ContentViewState.find(worldId); auto pWorld = ContentViewState.Regions.find(worldId);
if(pWorld == ContentViewState.end()) if(pWorld == ContentViewState.Regions.end())
return; return;
Remote->prepareWorldUpdate(worldId, worldObj); Remote->prepareWorldUpdate(worldId, worldObj);
@@ -61,20 +47,14 @@ void ContentEventController::onWorldUpdate(WorldId_t worldId, World *worldObj)
void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos,
const std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*>& chunks) const std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*>& chunks)
{ {
auto pWorld = ContentViewState.find(worldId); auto pWorld = ContentViewState.Regions.find(worldId);
if(pWorld == ContentViewState.end()) if(pWorld == ContentViewState.Regions.end())
return; return;
auto pRegion = pWorld->second.find(regionPos); if(!std::binary_search(pWorld->second.begin(), pWorld->second.end(), regionPos))
if(pRegion == pWorld->second.end())
return; return;
const std::bitset<64> &chunkBitset = pRegion->second;
for(auto pChunk : chunks) { for(auto pChunk : chunks) {
if(!chunkBitset.test(pChunk.first.pack()))
continue;
Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + pChunk.first; Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + pChunk.first;
Remote->prepareChunkUpdate_Voxels(worldId, chunkPos, pChunk.second); Remote->prepareChunkUpdate_Voxels(worldId, chunkPos, pChunk.second);
} }
@@ -83,20 +63,14 @@ void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::Globa
void ContentEventController::onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, void ContentEventController::onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos,
const std::unordered_map<Pos::bvec4u, const Node*> &chunks) const std::unordered_map<Pos::bvec4u, const Node*> &chunks)
{ {
auto pWorld = ContentViewState.find(worldId); auto pWorld = ContentViewState.Regions.find(worldId);
if(pWorld == ContentViewState.end()) if(pWorld == ContentViewState.Regions.end())
return; return;
auto pRegion = pWorld->second.find(regionPos); if(!std::binary_search(pWorld->second.begin(), pWorld->second.end(), regionPos))
if(pRegion == pWorld->second.end())
return; return;
const std::bitset<64> &chunkBitset = pRegion->second;
for(auto pChunk : chunks) { for(auto pChunk : chunks) {
if(!chunkBitset.test(pChunk.first.pack()))
continue;
Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + Pos::GlobalChunk(pChunk.first); Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + Pos::GlobalChunk(pChunk.first);
Remote->prepareChunkUpdate_Nodes(worldId, chunkPos, pChunk.second); Remote->prepareChunkUpdate_Nodes(worldId, chunkPos, pChunk.second);
} }
@@ -127,99 +101,43 @@ void ContentEventController::onChunksUpdate_Nodes(WorldId_t worldId, Pos::Global
void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos,
const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost) const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost)
{ {
auto pWorld = Subscribed.Entities.find(worldId);
if(pWorld == Subscribed.Entities.end()) {
// pWorld = std::get<0>(Subscribed.Entities.emplace(std::make_pair(worldId, decltype(Subscribed.Entities)::value_type())));
Subscribed.Entities[worldId];
pWorld = Subscribed.Entities.find(worldId);
}
auto pRegion = pWorld->second.find(regionPos);
if(pRegion == pWorld->second.end()) {
// pRegion = std::get<0>(pWorld->second.emplace(std::make_pair(worldId, decltype(pWorld->second)::value_type())));
pWorld->second[regionPos];
pRegion = pWorld->second.find(regionPos);
}
std::unordered_set<RegionEntityId_t> &entityesId = pRegion->second;
for(RegionEntityId_t eId : lost) {
entityesId.erase(eId);
}
entityesId.insert(enter.begin(), enter.end());
if(entityesId.empty()) {
pWorld->second.erase(pRegion);
if(pWorld->second.empty())
Subscribed.Entities.erase(pWorld);
}
// Сообщить Remote // Сообщить Remote
for(RegionEntityId_t eId : lost) { for(RegionEntityId_t eId : lost) {
Remote->prepareEntityRemove({worldId, regionPos, eId}); Remote->prepareEntityRemove({worldId, regionPos, eId});
} }
} }
void ContentEventController::onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, void ContentEventController::onEntitySwap(ServerEntityId_t prevId, ServerEntityId_t newId)
RegionEntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, RegionEntityId_t newId)
{ {
// Проверим отслеживается ли эта сущность нами {
auto lpWorld = Subscribed.Entities.find(lastWorldId); auto pWorld = ContentViewState.Regions.find(std::get<0>(prevId));
if(lpWorld == Subscribed.Entities.end()) assert(pWorld != ContentViewState.Regions.end());
// Исходный мир нами не отслеживается assert(std::binary_search(pWorld->second.begin(), pWorld->second.end(), std::get<1>(prevId)));
return;
auto lpRegion = lpWorld->second.find(lastRegionPos);
if(lpRegion == lpWorld->second.end())
// Исходный регион нами не отслеживается
return;
auto lpceId = lpRegion->second.find(lastId);
if(lpceId == lpRegion->second.end())
// Сущность нами не отслеживается
return;
// Проверим отслеживается ли регион, в который будет перемещена сущность
auto npWorld = Subscribed.Entities.find(newWorldId);
if(npWorld != Subscribed.Entities.end()) {
auto npRegion = npWorld->second.find(newRegionPos);
if(npRegion != npWorld->second.end()) {
// Следующий регион отслеживается, перекинем сущность
lpRegion->second.erase(lpceId);
npRegion->second.insert(newId);
Remote->prepareEntitySwap({lastWorldId, lastRegionPos, lastId}, {newWorldId, newRegionPos, newId});
goto entitySwaped;
}
} }
Remote->prepareEntityRemove({lastWorldId, lastRegionPos, lastId}); {
auto npWorld = ContentViewState.Regions.find(std::get<0>(newId));
assert(npWorld != ContentViewState.Regions.end());
assert(std::binary_search(npWorld->second.begin(), npWorld->second.end(), std::get<1>(prevId)));
}
entitySwaped: Remote->prepareEntitySwap(prevId, newId);
return;
} }
void ContentEventController::onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, void ContentEventController::onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos,
const std::vector<Entity> &entities) const std::unordered_map<RegionEntityId_t, Entity*> &entities)
{ {
auto lpWorld = Subscribed.Entities.find(worldId); auto pWorld = ContentViewState.Regions.find(worldId);
if(lpWorld == Subscribed.Entities.end()) if(pWorld == ContentViewState.Regions.end())
// Исходный мир нами не отслеживается // Исходный мир нами не отслеживается
return; return;
auto lpRegion = lpWorld->second.find(regionPos); if(!std::binary_search(pWorld->second.begin(), pWorld->second.end(), regionPos))
if(lpRegion == lpWorld->second.end())
// Исходный регион нами не отслеживается // Исходный регион нами не отслеживается
return; return;
for(size_t eId = 0; eId < entities.size(); eId++) { for(const auto& [id, entity] : entities) {
if(!lpRegion->second.contains(eId)) Remote->prepareEntityUpdate({worldId, regionPos, id}, entity);
continue;
Remote->prepareEntityUpdate({worldId, regionPos, eId}, &entities[eId]);
} }
} }

View File

@@ -2,6 +2,7 @@
#include <Common/Abstract.hpp> #include <Common/Abstract.hpp>
#include "Abstract.hpp" #include "Abstract.hpp"
#include <algorithm>
#include <bitset> #include <bitset>
#include <map> #include <map>
#include <memory> #include <memory>
@@ -22,171 +23,130 @@ struct ServerObjectPos {
Pos::Object ObjectPos; Pos::Object ObjectPos;
}; };
/* /*
Сфера в которой отслеживаются события игроком Разница между информацией о наблюдаемых регионах
*/ */
struct ContentViewCircle { struct ContentViewInfo_Diff {
WorldId_t WorldId; // Изменения на уровне миров (увиден или потерян)
// Позиция в чанках std::vector<WorldId_t> WorldsNew, WorldsLost;
glm::i16vec3 Pos; // Изменения на уровне регионов
// (Единица равна размеру чанка) в квадрате std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> RegionsNew, RegionsLost;
int32_t Range;
inline int32_t sqrDistance(Pos::GlobalRegion regionPos) const {
glm::i32vec3 vec = Pos-(glm::i16vec3) ((Pos::GlobalChunk(regionPos) << 2) | 0b10);
return vec.x*vec.x+vec.y*vec.y+vec.z*vec.z;
};
inline int32_t sqrDistance(Pos::GlobalChunk chunkPos) const {
glm::i32vec3 vec = Pos-(glm::i16vec3) chunkPos;
return vec.x*vec.x+vec.y*vec.y+vec.z*vec.z;
};
inline int64_t sqrDistance(Pos::Object objectPos) const {
glm::i32vec3 vec = Pos-(glm::i16vec3) (objectPos >> 12 >> 4);
return vec.x*vec.x+vec.y*vec.y+vec.z*vec.z;
};
bool isIn(Pos::GlobalRegion regionPos) const {
return sqrDistance(regionPos) < Range+12; // (2×sqrt(3))^2
}
bool isIn(Pos::GlobalChunk chunkPos) const {
return sqrDistance(chunkPos) < Range+3; // (1×sqrt(3))^2
}
bool isIn(Pos::Object objectPos, int32_t size = 0) const {
return sqrDistance(objectPos) < Range+3+size;
}
};
// Регион -> чанки попавшие под обозрение Pos::bvec4u
using ContentViewWorld = std::map<Pos::GlobalRegion, std::bitset<64>>; // 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 { bool empty() const {
return View.empty() && Regions.empty() && Worlds.empty(); return WorldsNew.empty() && WorldsLost.empty() && RegionsNew.empty() && RegionsLost.empty();
} }
}; };
inline ContentViewGlobal_DiffInfo ContentViewGlobal::calcDiffWith(const ContentViewGlobal &old) const { /*
ContentViewGlobal_DiffInfo newView; То, какие регионы наблюдает игрок
*/
struct ContentViewInfo {
// std::vector<Pos::GlobalRegion> - сортированный и с уникальными значениями
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> Regions;
// Рассматриваем разницу меж мирами // Что изменилось относительно obj
for(const auto &[newWorldId, newWorldView] : *this) { // Перерасчёт должен проводится при смещении игрока или ContentBridge за границу региона
auto oldWorldIter = old.find(newWorldId); ContentViewInfo_Diff diffWith(const ContentViewInfo& obj) const {
if(oldWorldIter == old.end()) { // В старом состоянии нет мира ContentViewInfo_Diff out;
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<64>> &newRegions = newWorldView;
const std::map<Pos::GlobalRegion, std::bitset<64>> &oldRegions = oldWorldIter->second;
std::map<Pos::GlobalRegion, std::bitset<64>> *diffRegions = nullptr;
// Рассматриваем разницу меж регионами // Проверяем новые миры и регионы
for(const auto &[newRegionPos, newRegionBitField] : newRegions) { for(const auto& [key, regions] : Regions) {
auto oldRegionIter = oldRegions.find(newRegionPos); auto iterWorld = obj.Regions.find(key);
if(oldRegionIter == oldRegions.end()) { // В старой описи мира нет региона
if(!diffRegions)
diffRegions = &newView.View[newWorldId];
(*diffRegions)[newRegionPos] = newRegionBitField; if(iterWorld == obj.Regions.end()) {
newView.Regions[newWorldId].push_back(newRegionPos); out.WorldsNew.push_back(key);
} else { out.RegionsNew[key] = regions;
const std::bitset<64> &oldChunks = oldRegionIter->second; } else {
std::bitset<64> chunks = (~oldChunks) & newRegionBitField; // Останется поле с новыми чанками auto &vec = out.RegionsNew[key];
if(chunks._Find_first() != chunks.size()) { vec.reserve(8*8);
// Есть новые чанки std::set_difference(
if(!diffRegions) regions.begin(), regions.end(),
diffRegions = &newView.View[newWorldId]; iterWorld->second.begin(), iterWorld->second.end(),
std::back_inserter(vec)
(*diffRegions)[newRegionPos] = chunks; );
}
}
} }
} }
// Проверяем потерянные миры и регионы
for(const auto& [key, regions] : obj.Regions) {
auto iterWorld = Regions.find(key);
if(iterWorld == Regions.end()) {
out.WorldsLost.push_back(key);
out.RegionsLost[key] = regions;
} else {
auto &vec = out.RegionsLost[key];
vec.reserve(8*8);
std::set_difference(
regions.begin(), regions.end(),
iterWorld->second.begin(), iterWorld->second.end(),
std::back_inserter(vec)
);
}
}
// shrink_to_feet
for(auto& [_, regions] : out.RegionsNew)
regions.shrink_to_fit();
for(auto& [_, regions] : out.RegionsLost)
regions.shrink_to_fit();
return out;
} }
};
return newView;
}
/* /*
Мост контента, для отслеживания событий из удалённх точек Мост контента, для отслеживания событий из удалённых точек
По типу портала, через который можно видеть контент на расстоянии По типу портала, через который можно видеть контент на расстоянии
*/ */
struct ContentBridge { struct ContentBridge {
/* /*
false -> Из точки From видно контент из точки To false -> Из точки Left видно контент в точки Right
true -> Контент виден в обе стороны true -> Контент виден в обе стороны
*/ */
bool IsTwoWay = false; bool IsTwoWay = false;
WorldId_t LeftWorld; WorldId_t LeftWorld;
// Позиция в чанках Pos::GlobalRegion LeftPos;
glm::i16vec3 LeftPos;
WorldId_t RightWorld; WorldId_t RightWorld;
// Позиция в чанках Pos::GlobalRegion RightPos;
glm::i16vec3 RightPos; };
struct ContentViewCircle {
WorldId_t WorldId;
Pos::GlobalRegion Pos;
// Радиус в регионах в квадрате
int16_t Range;
}; };
/* Игрок */ /* Игрок */
class ContentEventController { class ContentEventController {
private: private:
struct SubscribedObj { struct SubscribedObj {
// Используется регионами // Используется регионами
std::vector<PortalId_t> Portals; std::vector<PortalId_t> Portals;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<RegionEntityId_t>>> Entities;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<RegionEntityId_t>>> FuncEntities;
} Subscribed; } Subscribed;
public: public:
// Управляется сервером // Управляется сервером
std::unique_ptr<RemoteClient> Remote; std::unique_ptr<RemoteClient> Remote;
// Регионы сюда заглядывают // Что сейчас наблюдает игрок
// Каждый такт значения изменений обновляются GameServer'ом ContentViewInfo ContentViewState;
// Объявленная в чанках территория точно отслеживается (активная зона) // Если игрок пересекал границы чанка (для перерасчёта ContentViewState)
ContentViewGlobal ContentViewState; bool CrossedBorder = true;
ContentViewGlobal_DiffInfo ContentView_NewView, ContentView_LostView;
// Миры добавленные в наблюдение в текущем такте
std::vector<WorldId_t> NewWorlds;
// size_t CVCHash = 0; // Хэш для std::vector<ContentViewCircle>
// std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> SubscribedRegions;
public: public:
ContentEventController(std::unique_ptr<RemoteClient> &&remote); ContentEventController(std::unique_ptr<RemoteClient>&& remote);
// Измеряется в чанках в радиусе (активная зона) // Измеряется в чанках в регионах (активная зона)
uint16_t getViewRangeActive() const; static constexpr uint16_t getViewRangeActive() { return 2; }
// Измеряется в чанках в радиусе (Декоративная зона) + getViewRangeActive() // Измеряется в чанках в радиусе (Декоративная зона) + getViewRangeActive()
uint16_t getViewRangeBackground() const; uint16_t getViewRangeBackground() const;
ServerObjectPos getLastPos() const; ServerObjectPos getLastPos() const;
ServerObjectPos getPos() const; ServerObjectPos getPos() const;
// Проверка на необходимость подгрузки новых определений миров // Очищает более не наблюдаемые чанки и миры
// и очистка клиента от не наблюдаемых данных void removeUnobservable(const ContentViewInfo_Diff& diff);
void checkContentViewChanges();
// Здесь приходят частично фильтрованные события // Здесь приходят частично фильтрованные события
// Фильтровать не отслеживаемые миры // Фильтровать не отслеживаемые миры
void onWorldUpdate(WorldId_t worldId, World *worldObj); void onWorldUpdate(WorldId_t worldId, World *worldObj);
@@ -197,12 +157,8 @@ public:
//void onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::bvec4u, const LightPrism*> &chunks); //void onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::bvec4u, const LightPrism*> &chunks);
void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost); void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost);
void onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, RegionEntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, RegionEntityId_t newId); void onEntitySwap(ServerEntityId_t prevId, ServerEntityId_t newId);
void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::vector<Entity> &entities); void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<RegionEntityId_t, Entity*> &entities);
void onFuncEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost);
void onFuncEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, RegionEntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, RegionEntityId_t newId);
void onFuncEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::vector<Entity> &entities);
void onPortalEnterLost(const std::vector<void*> &enter, const std::vector<void*> &lost); void onPortalEnterLost(const std::vector<void*> &enter, const std::vector<void*> &lost);
void onPortalUpdates(const std::vector<void*> &portals); void onPortalUpdates(const std::vector<void*> &portals);
@@ -211,33 +167,15 @@ public:
}; };
} }
namespace std { namespace std {
template <> template <>
struct hash<LV::Server::ServerObjectPos> { struct hash<LV::Server::ServerObjectPos> {
std::size_t operator()(const LV::Server::ServerObjectPos& obj) const { std::size_t operator()(const LV::Server::ServerObjectPos& obj) const {
return std::hash<uint32_t>()(obj.WorldId) ^ std::hash<int32_t>()(obj.ObjectPos.x) ^ std::hash<int32_t>()(obj.ObjectPos.y) ^ std::hash<int32_t>()(obj.ObjectPos.z); return std::hash<uint32_t>()(obj.WorldId) ^ std::hash<LV::Pos::Object>()(obj.ObjectPos);
} }
}; };
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

@@ -4,6 +4,7 @@
#include "Common/Packets.hpp" #include "Common/Packets.hpp"
#include "Server/Abstract.hpp" #include "Server/Abstract.hpp"
#include "Server/ContentEventController.hpp" #include "Server/ContentEventController.hpp"
#include <algorithm>
#include <boost/json/parse.hpp> #include <boost/json/parse.hpp>
#include <chrono> #include <chrono>
#include <glm/geometric.hpp> #include <glm/geometric.hpp>
@@ -11,7 +12,6 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include "SaveBackends/Filesystem.hpp" #include "SaveBackends/Filesystem.hpp"
#include "Server/SaveBackend.hpp" #include "Server/SaveBackend.hpp"
#include "Server/World.hpp" #include "Server/World.hpp"
@@ -31,7 +31,7 @@ static thread_local std::vector<ContentViewCircle> TL_Circles;
std::vector<ContentViewCircle> GameServer::Expanse_t::accumulateContentViewCircles(ContentViewCircle circle, int depth) std::vector<ContentViewCircle> GameServer::Expanse_t::accumulateContentViewCircles(ContentViewCircle circle, int depth)
{ {
TL_Circles.clear(); TL_Circles.clear();
TL_Circles.reserve(256); TL_Circles.reserve(64);
TL_Circles.push_back(circle); TL_Circles.push_back(circle);
_accumulateContentViewCircles(circle, depth); _accumulateContentViewCircles(circle, depth);
return TL_Circles; return TL_Circles;
@@ -42,7 +42,7 @@ void GameServer::Expanse_t::_accumulateContentViewCircles(ContentViewCircle circ
auto &br = pair.second; auto &br = pair.second;
if(br.LeftWorld == circle.WorldId) { if(br.LeftWorld == circle.WorldId) {
glm::i32vec3 vec = circle.Pos-br.LeftPos; glm::i32vec3 vec = circle.Pos-br.LeftPos;
ContentViewCircle circleNew = {br.RightWorld, br.RightPos, circle.Range-(vec.x*vec.x+vec.y*vec.y+vec.z*vec.z+16)}; ContentViewCircle circleNew = {br.RightWorld, br.RightPos, static_cast<int16_t>(circle.Range-int16_t(vec.x*vec.x+vec.y*vec.y+vec.z*vec.z))};
if(circleNew.Range >= 0) { if(circleNew.Range >= 0) {
bool isIn = false; bool isIn = false;
@@ -69,7 +69,7 @@ void GameServer::Expanse_t::_accumulateContentViewCircles(ContentViewCircle circ
if(br.IsTwoWay && br.RightWorld == circle.WorldId) { if(br.IsTwoWay && br.RightWorld == circle.WorldId) {
glm::i32vec3 vec = circle.Pos-br.RightPos; glm::i32vec3 vec = circle.Pos-br.RightPos;
ContentViewCircle circleNew = {br.LeftWorld, br.LeftPos, circle.Range-(vec.x*vec.x+vec.y*vec.y+vec.z*vec.z+16)}; ContentViewCircle circleNew = {br.LeftWorld, br.LeftPos, static_cast<int16_t>(circle.Range-int16_t(vec.x*vec.x+vec.y*vec.y+vec.z*vec.z))};
if(circleNew.Range >= 0) { if(circleNew.Range >= 0) {
bool isIn = false; bool isIn = false;
@@ -107,34 +107,29 @@ void GameServer::Expanse_t::_accumulateContentViewCircles(ContentViewCircle circ
// } // }
ContentViewGlobal GameServer::Expanse_t::makeContentViewGlobal(const std::vector<ContentViewCircle> &views) { ContentViewInfo GameServer::Expanse_t::makeContentViewInfo(const std::vector<ContentViewCircle> &views) {
ContentViewGlobal cvg; ContentViewInfo cvi;
Pos::GlobalRegion posRegion, lastPosRegion;
std::bitset<64> *cache = nullptr;
for(const ContentViewCircle &circle : views) { for(const ContentViewCircle &circle : views) {
ContentViewWorld &cvw = cvg[circle.WorldId]; std::vector<Pos::GlobalRegion> &cvw = cvi.Regions[circle.WorldId];
uint16_t chunkRange = std::sqrt(circle.Range); int32_t regionRange = 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); cvw.reserve(cvw.size()+std::pow(regionRange*2+1, 3));
posRegion = posChunk >> 2;
if(!cache || lastPosRegion != posRegion) { for(int32_t z = -regionRange; z <= regionRange; z++)
lastPosRegion = posRegion; for(int32_t y = -regionRange; y <= regionRange; y++)
cache = &cvw[posRegion]; for(int32_t x = -regionRange; x <= regionRange; x++)
} cvw.push_back(Pos::GlobalRegion(x, y, z));
cache->_Unchecked_set(Pos::bvec4u(posChunk).pack());
}
} }
return cvg; for(auto& [worldId, regions] : cvi.Regions) {
std::sort(regions.begin(), regions.end());
auto eraseIter = std::unique(regions.begin(), regions.end());
regions.erase(eraseIter, regions.end());
regions.shrink_to_fit();
}
return cvi;
} }
coro<> GameServer::pushSocketConnect(tcp::socket socket) { coro<> GameServer::pushSocketConnect(tcp::socket socket) {
@@ -404,7 +399,102 @@ void GameServer::run() {
LOG.info() << "Сервер завершил работу"; LOG.info() << "Сервер завершил работу";
} }
void GameServer::stepContent() { void GameServer::stepConnections() {
// Подключить новых игроков
if(!External.NewConnectedPlayers.no_lock_readable().empty()) {
auto lock = External.NewConnectedPlayers.lock_write();
for(std::unique_ptr<RemoteClient>& client : *lock) {
co_spawn(client->run());
Game.CECs.push_back(std::make_unique<ContentEventController>(std::move(client)));
}
lock->clear();
}
// Отключение игроков
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
// Убрать отключившихся
if(!cec->Remote->isConnected()) {
// Отписываем наблюдателя от миров
for(auto wPair : cec->ContentViewState.Regions) {
auto wIter = Expanse.Worlds.find(wPair.first);
assert(wIter != Expanse.Worlds.end());
wIter->second->onCEC_RegionsLost(cec.get(), wPair.second);
}
std::string username = cec->Remote->Username;
External.ConnectedPlayersSet.lock_write()->erase(username);
cec.reset();
}
}
// Вычистить невалидные ссылки на игроков
Game.CECs.erase(std::remove_if(Game.CECs.begin(), Game.CECs.end(),
[](const std::unique_ptr<ContentEventController>& ptr) { return !ptr; }),
Game.CECs.end());
}
void GameServer::stepModInitializations() {
}
void GameServer::stepDatabase() {
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
assert(cec);
// Пересчитать зоны наблюдения
if(cec->CrossedBorder) {
cec->CrossedBorder = false;
}
}
}
void GameServer::stepLuaAsync() {
}
void GameServer::stepPlayerProceed() {
}
void GameServer::stepWorldPhysic() {
}
void GameServer::stepGlobalStep() {
}
void GameServer::stepSyncContent() {
// Сбор запросов на ресурсы и профили
ResourceRequest full;
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
full.insert(cec->Remote->pushPreparedPackets());
}
full.uniq();
if(!full.BinTexture.empty())
Content.Texture.needResourceResponse(full.BinTexture);
if(!full.BinAnimation.empty())
Content.Animation.needResourceResponse(full.BinAnimation);
if(!full.BinModel.empty())
Content.Model.needResourceResponse(full.BinModel);
if(!full.BinSound.empty())
Content.Sound.needResourceResponse(full.BinSound);
if(!full.BinFont.empty())
Content.Font.needResourceResponse(full.BinFont);
// Оповещения о ресурсах и профилях
Content.Texture.update(CurrentTickDuration); Content.Texture.update(CurrentTickDuration);
if(Content.Texture.hasPreparedInformation()) { if(Content.Texture.hasPreparedInformation()) {
auto table = Content.Texture.takePreparedInformation(); auto table = Content.Texture.takePreparedInformation();
@@ -450,7 +540,7 @@ void GameServer::stepSyncWithAsync() {
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) { for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
assert(cec); assert(cec);
for(const auto &[worldId, regions] : cec->ContentViewState) { for(const auto &[worldId, regions] : cec->ContentViewState.Regions) {
for(const auto &[regionPos, chunkBitfield] : regions) { for(const auto &[regionPos, chunkBitfield] : regions) {
forceGetRegion(worldId, regionPos); forceGetRegion(worldId, regionPos);
} }
@@ -469,53 +559,6 @@ void GameServer::stepSyncWithAsync() {
} }
} }
void GameServer::stepPlayers() {
// Подключить новых игроков
if(!External.NewConnectedPlayers.no_lock_readable().empty()) {
auto lock = External.NewConnectedPlayers.lock_write();
for(std::unique_ptr<RemoteClient> &client : *lock) {
co_spawn(client->run());
Game.CECs.push_back(std::make_unique<ContentEventController>(std::move(client)));
}
lock->clear();
}
// Обработка игроков
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
// Убрать отключившихся
if(!cec->Remote->isConnected()) {
// Отписываем наблюдателя от миров
for(auto wPair : cec->ContentViewState) {
auto wIter = Expanse.Worlds.find(wPair.first);
if(wIter == Expanse.Worlds.end())
continue;
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;
External.ConnectedPlayersSet.lock_write()->erase(username);
cec.reset();
}
}
// Вычистить невалидные ссылки на игроков
Game.CECs.erase(std::remove_if(Game.CECs.begin(), Game.CECs.end(),
[](const std::unique_ptr<ContentEventController>& ptr) { return !ptr; }),
Game.CECs.end());
}
void GameServer::stepWorlds() { void GameServer::stepWorlds() {
for(auto &pair : Expanse.Worlds) for(auto &pair : Expanse.Worlds)
pair.second->onUpdate(this, CurrentTickDuration); pair.second->onUpdate(this, CurrentTickDuration);
@@ -802,10 +845,6 @@ void GameServer::stepWorlds() {
// Отправка полной информации о новых наблюдаемых чанках // Отправка полной информации о новых наблюдаемых чанках
{ {
const std::bitset<64> *new_chunkBitset = nullptr;
try { new_chunkBitset = &cec->ContentView_NewView.View.at(pWorld.first).at(pRegion.first); } catch(...) {}
if(new_chunkBitset) {
//std::unordered_map<Pos::bvec4u, const LightPrism*> newLightPrism; //std::unordered_map<Pos::bvec4u, const LightPrism*> newLightPrism;
std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*> newVoxels; std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*> newVoxels;
std::unordered_map<Pos::bvec4u, const Node*> newNodes; std::unordered_map<Pos::bvec4u, const Node*> newNodes;
@@ -829,7 +868,7 @@ void GameServer::stepWorlds() {
//cec->onChunksUpdate_LightPrism(pWorld.first, pRegion.first, newLightPrism); //cec->onChunksUpdate_LightPrism(pWorld.first, pRegion.first, newLightPrism);
cec->onChunksUpdate_Voxels(pWorld.first, pRegion.first, newVoxels); cec->onChunksUpdate_Voxels(pWorld.first, pRegion.first, newVoxels);
cec->onChunksUpdate_Nodes(pWorld.first, pRegion.first, newNodes); cec->onChunksUpdate_Nodes(pWorld.first, pRegion.first, newNodes);
}
} }
// То, что уже отслеживает наблюдатель // То, что уже отслеживает наблюдатель
@@ -903,8 +942,12 @@ void GameServer::stepWorlds() {
// // Отправить полную информацию о новых наблюдаемых сущностях наблюдателю // // Отправить полную информацию о новых наблюдаемых сущностях наблюдателю
// } // }
if(!region.Entityes.empty()) if(!region.Entityes.empty()) {
cec->onEntityUpdates(pWorld.first, pRegion.first, region.Entityes); std::unordered_map<RegionEntityId_t, Entity*> entities;
for(size_t iter = 0; iter < region.Entityes.size(); iter++)
entities[iter] = &region.Entityes[iter];
cec->onEntityUpdates(pWorld.first, pRegion.first, entities);
}
} }
} }
@@ -971,101 +1014,39 @@ void GameServer::stepViewContent() {
if(Game.CECs.empty()) if(Game.CECs.empty())
return; return;
// Затереть изменения предыдущего такта for(auto &cec : Game.CECs) {
for(auto &cecPtr : Game.CECs) { assert(cec);
assert(cecPtr);
cecPtr->ContentView_NewView = {};
cecPtr->ContentView_LostView = {};
}
// Если наблюдаемая территория изменяется if(!cec->CrossedBorder)
// -> Новая увиденная + Старая потерянная continue;
std::unordered_map<ContentEventController*, ContentViewGlobal_DiffInfo> lost_CVG;
// Обновления поля зрения cec->CrossedBorder = false;
for(int iter = 0; iter < 1; iter++) {
if(++Game.CEC_NextRebuildViewCircles >= Game.CECs.size())
Game.CEC_NextRebuildViewCircles = 0;
ContentEventController &cec = *Game.CECs[Game.CEC_NextRebuildViewCircles]; // Пересчёт зон наблюдения
ServerObjectPos oPos = cec.getPos(); ServerObjectPos oPos = cec->getPos();
ContentViewCircle cvc; ContentViewCircle cvc;
cvc.WorldId = oPos.WorldId; cvc.WorldId = oPos.WorldId;
cvc.Pos = Pos::Object_t::asChunkPos(oPos.ObjectPos); cvc.Pos = Pos::Object_t::asChunkPos(oPos.ObjectPos);
cvc.Range = cec.getViewRangeActive(); cvc.Range = 2*2;
cvc.Range *= cvc.Range;
std::vector<ContentViewCircle> newCVCs = Expanse.accumulateContentViewCircles(cvc); std::vector<ContentViewCircle> newCVCs = Expanse.accumulateContentViewCircles(cvc);
//size_t hash = (std::hash<std::vector<ContentViewCircle>>{})(newCVCs); ContentViewInfo newCbg = Expanse_t::makeContentViewInfo(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}});
std::vector<WorldId_t> newWorlds, lostWorlds; ContentViewInfo_Diff diff = newCbg.diffWith(cec->ContentViewState);
if(!diff.WorldsNew.empty()) {
// Сообщить о новых мирах
for(const WorldId_t id : diff.WorldsNew) {
auto iter = Expanse.Worlds.find(id);
assert(iter != Expanse.Worlds.end());
// Поиск потерянных миров cec->onWorldUpdate(id, iter->second.get());
for(const auto& [key, _] : cec.ContentViewState) {
if(!newCbg.contains(key))
lostWorlds.push_back(key);
}
// Поиск новых увиденных миров
for(const auto& [key, _] : newCbg) {
if(!cec.ContentViewState.contains(key))
newWorlds.push_back(key);
}
cec.NewWorlds = std::move(newWorlds);
cec.ContentViewState = std::move(newCbg);
cec.ContentView_NewView = std::move(newView);
cec.ContentView_LostView = std::move(lostView);
} }
} }
cec->ContentViewState = newCbg;
cec->removeUnobservable(diff);
} }
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);
World &world = *worldIter->second;
world.onCEC_RegionsLost(cec, lostList);
}
cec->checkContentViewChanges();
}
}
void GameServer::stepSendPlayersPackets() {
ResourceRequest full;
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
full.insert(cec->Remote->pushPreparedPackets());
}
full.uniq();
if(!full.BinTexture.empty())
Content.Texture.needResourceResponse(full.BinTexture);
if(!full.BinAnimation.empty())
Content.Animation.needResourceResponse(full.BinAnimation);
if(!full.BinModel.empty())
Content.Model.needResourceResponse(full.BinModel);
if(!full.BinSound.empty())
Content.Sound.needResourceResponse(full.BinSound);
if(!full.BinFont.empty())
Content.Font.needResourceResponse(full.BinFont);
} }
void GameServer::stepLoadRegions() { void GameServer::stepLoadRegions() {

View File

@@ -86,9 +86,9 @@ class GameServer : public AsyncObject {
// depth ограничивает глубину входа в ContentBridges // depth ограничивает глубину входа в ContentBridges
std::vector<ContentViewCircle> accumulateContentViewCircles(ContentViewCircle circle, int depth = 2); std::vector<ContentViewCircle> accumulateContentViewCircles(ContentViewCircle circle, int depth = 2);
// Вынести в отдельный поток // Вынести в отдельный поток
static ContentViewGlobal makeContentViewGlobal(const std::vector<ContentViewCircle> &views); static ContentViewInfo makeContentViewInfo(const std::vector<ContentViewCircle> &views);
ContentViewGlobal makeContentViewGlobal(ContentViewCircle circle, int depth = 2) { ContentViewInfo makeContentViewInfo(ContentViewCircle circle, int depth = 2) {
return makeContentViewGlobal(accumulateContentViewCircles(circle, depth)); return makeContentViewInfo(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>> remapCVCsByWorld(const std::vector<ContentViewCircle> &list);
@@ -100,9 +100,14 @@ class GameServer : public AsyncObject {
/* /*
Регистрация миров по строке Регистрация миров по строке
*/ */
/*
*/
private: private:
void _accumulateContentViewCircles(ContentViewCircle circle, int depth); void _accumulateContentViewCircles(ContentViewCircle circle, int depth);
} Expanse; } Expanse;
@@ -154,29 +159,61 @@ private:
void prerun(); void prerun();
void run(); void run();
void stepContent();
/* /*
Дождаться и получить необходимые данные с бд или диска Подключение/отключение игроков
Получить несрочные данные
*/ */
void stepSyncWithAsync();
void stepPlayers();
void stepWorlds();
/*
Пересмотр наблюдаемых зон (чанки, регионы, миры)
Добавить требуемые регионы в список на предзагрузку с приоритетом
TODO: нужен механизм асинхронной загрузки регионов с бд
В начале следующего такта обязательное дожидание прогрузки активной зоны void stepConnections();
и
оповещение миров об изменениях в наблюдаемых регионах /*
Переинициализация модов, если требуется
*/ */
void stepViewContent();
void stepSendPlayersPackets(); void stepModInitializations();
void stepLoadRegions();
void stepGlobal(); /*
void stepSave(); Пересчёт зон видимости игроков, если необходимо
void save(); Выгрузить более не используемые регионы
Сохранение регионов
Создание списка регионов необходимых для загрузки (бд автоматически будет предзагружать)
<Синхронизация с модулем сохранений>
Очередь загрузки, выгрузка регионов и получение загруженных из бд регионов
Получить список регионов отсутствующих в сохранении и требующих генерации
Подпись на загруженные регионы (отправить полностью на клиент)
*/
void stepDatabase();
/*
Синхронизация с генератором карт (отправка запросов на генерацию и получение шума для обработки модами)
Синхронизация с потоками модов
*/
void stepLuaAsync();
/*
Получить пакеты с игроков
*/
void stepPlayerProceed();
/*
Физика
*/
void stepWorldPhysic();
/*
Глобальный такт
*/
void stepGlobalStep();
/*
Обработка запросов двоичных ресурсов и определений
Отправка пакетов игрокам
*/
void stepSyncContent();
}; };
} }

View File

@@ -112,12 +112,13 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk
// Исключим зависимости предыдущей версии чанка // Исключим зависимости предыдущей версии чанка
auto iterWorld = ResUses.RefChunk.find(worldId); auto iterWorld = ResUses.RefChunk.find(worldId);
assert(iterWorld != ResUses.RefChunk.end()); assert(iterWorld != ResUses.RefChunk.end());
Pos::bvec4u lChunk = (chunkPos & 0xf);
// Исключим зависимости предыдущей версии чанка
{ {
auto iterChunk = iterWorld->second.find(chunkPos); auto iterRegion = iterWorld->second.find(chunkPos);
if(iterChunk != iterWorld->second.end()) { if(iterRegion != iterWorld->second.end()) {
// Раньше этот чанк был, значит не новый для клиента
// Уменьшим счётчик зависимостей // Уменьшим счётчик зависимостей
for(const DefVoxelId_t& id : iterChunk->second.Voxel) { for(const DefVoxelId_t& id : iterRegion->second[lChunk.pack()].Voxel) {
auto iter = ResUses.DefVoxel.find(id); auto iter = ResUses.DefVoxel.find(id);
assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях
if(--iter->second == 0) { if(--iter->second == 0) {
@@ -129,7 +130,7 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk
} }
} }
iterWorld->second[chunkPos].Voxel = v; iterWorld->second[chunkPos][lChunk.pack()].Voxel = v;
if(!newTypes.empty()) { if(!newTypes.empty()) {
// Добавляем новые типы в запрос // Добавляем новые типы в запрос
@@ -184,14 +185,13 @@ void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk
auto iterWorld = ResUses.RefChunk.find(worldId); auto iterWorld = ResUses.RefChunk.find(worldId);
assert(iterWorld != ResUses.RefChunk.end()); assert(iterWorld != ResUses.RefChunk.end());
Pos::bvec4u lChunk = (chunkPos & 0xf);
// Исключим зависимости предыдущей версии чанка // Исключим зависимости предыдущей версии чанка
{ {
auto iterRegion = iterWorld->second.find(chunkPos);
auto iterChunk = iterWorld->second.find(chunkPos); if(iterRegion != iterWorld->second.end()) {
if(iterChunk != iterWorld->second.end()) {
// Раньше этот чанк был, значит не новый для клиента
// Уменьшим счётчик зависимостей // Уменьшим счётчик зависимостей
for(const DefNodeId_t& id : iterChunk->second.Node) { for(const DefNodeId_t& id : iterRegion->second[lChunk.pack()].Node) {
auto iter = ResUses.DefNode.find(id); auto iter = ResUses.DefNode.find(id);
assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях
if(--iter->second == 0) { if(--iter->second == 0) {
@@ -203,7 +203,7 @@ void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk
} }
} }
iterWorld->second[chunkPos].Node = n; iterWorld->second[chunkPos][lChunk.pack()].Node = n;
if(!newTypes.empty()) { if(!newTypes.empty()) {
// Добавляем новые типы в запрос // Добавляем новые типы в запрос
@@ -223,9 +223,7 @@ void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk
LOG.debug() << "Увидели " << chunkPos.x << ' ' << chunkPos.y << ' ' << chunkPos.z; LOG.debug() << "Увидели " << chunkPos.x << ' ' << chunkPos.y << ' ' << chunkPos.z;
} }
void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos) void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos) {
{
LOG.debug() << "Потеряли " << chunkPos.x << ' ' << chunkPos.y << ' ' << chunkPos.z;
std::vector<DefVoxelId_t> std::vector<DefVoxelId_t>
lostTypesV /* Потерянные типы вокселей */; lostTypesV /* Потерянные типы вокселей */;
std::vector<DefNodeId_t> std::vector<DefNodeId_t>
@@ -235,28 +233,29 @@ void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkP
{ {
auto iterWorld = ResUses.RefChunk.find(worldId); auto iterWorld = ResUses.RefChunk.find(worldId);
assert(iterWorld != ResUses.RefChunk.end()); assert(iterWorld != ResUses.RefChunk.end());
auto iterChunk = iterWorld->second.find(chunkPos);
assert(iterChunk != iterWorld->second.end());
// Уменьшим счётчики зависимостей
for(const DefVoxelId_t& id : iterChunk->second.Voxel) {
auto iter = ResUses.DefVoxel.find(id);
assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях
if(--iter->second == 0) {
// Ноды больше нет в зависимостях
lostTypesV.push_back(id);
ResUses.DefVoxel.erase(iter);
}
}
for(const DefNodeId_t& id : iterChunk->second.Node) { auto iterRegion = iterWorld->second.find(regionPos);
auto iter = ResUses.DefNode.find(id); assert(iterRegion != iterWorld->second.end());
assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях
if(--iter->second == 0) { for(const auto &iterChunk : iterRegion->second) {
// Ноды больше нет в зависимостях for(const DefVoxelId_t& id : iterChunk.Voxel) {
lostTypesN.push_back(id); auto iter = ResUses.DefVoxel.find(id);
ResUses.DefNode.erase(iter); assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях
if(--iter->second == 0) {
// Вокселя больше нет в зависимостях
lostTypesV.push_back(id);
ResUses.DefVoxel.erase(iter);
}
}
for(const DefNodeId_t& id : iterChunk.Node) {
auto iter = ResUses.DefNode.find(id);
assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях
if(--iter->second == 0) {
// Ноды больше нет в зависимостях
lostTypesN.push_back(id);
ResUses.DefNode.erase(iter);
}
} }
} }
} }
@@ -281,8 +280,8 @@ void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkP
checkPacketBorder(16); checkPacketBorder(16);
NextPacket << (uint8_t) ToClient::L1::Content NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::RemoveChunk << (uint8_t) ToClient::L2Content::RemoveRegion
<< worldId << chunkPos.pack(); << worldId << regionPos.pack();
} }
void RemoteClient::prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity) void RemoteClient::prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity)
@@ -366,87 +365,6 @@ void RemoteClient::prepareEntityRemove(ServerEntityId_t entityId)
<< cId; << cId;
} }
void RemoteClient::prepareFuncEntityUpdate(ServerFuncEntityId_t entityId, const FuncEntity *entity)
{
// Сопоставим с идентификатором клиента
ClientFuncEntityId_t ceId = ResRemap.FuncEntityes.toClient(entityId);
// Профиль новый
{
DefFuncEntityId_t profile = entity->getDefId();
auto iter = ResUses.DefFuncEntity.find(profile);
if(iter == ResUses.DefFuncEntity.end()) {
// Клиенту неизвестен профиль
NextRequest.FuncEntity.push_back(profile);
ResUses.DefFuncEntity[profile] = 1;
} else
iter->second++;
}
// Добавление модификационных зависимостей
// incrementBinary({}, {}, {}, {}, {});
// Старые данные
{
auto iterEntity = ResUses.RefFuncEntity.find(entityId);
if(iterEntity != ResUses.RefFuncEntity.end()) {
// Убавляем зависимость к старому профилю
auto iterProfile = ResUses.DefFuncEntity.find(iterEntity->second.Profile);
assert(iterProfile != ResUses.DefFuncEntity.end()); // Старый профиль должен быть
if(--iterProfile->second == 0) {
// Старый профиль больше не нужен
auto iterProfileRef = ResUses.RefDefFuncEntity.find(iterEntity->second.Profile);
decrementBinary(std::move(iterProfileRef->second.Texture), std::move(iterProfileRef->second.Animation), {},
std::move(iterProfileRef->second.Model), {});
ResUses.DefFuncEntity.erase(iterProfile);
}
// Убавляем зависимость к модификационным данным
// iterEntity->second.
// decrementBinary({}, {}, {}, {}, {});
}
}
// TODO: отправить клиенту
}
void RemoteClient::prepareFuncEntitySwap(ServerFuncEntityId_t prev, ServerFuncEntityId_t next)
{
ResRemap.FuncEntityes.rebindClientKey(prev, next);
}
void RemoteClient::prepareFuncEntityRemove(ServerFuncEntityId_t entityId)
{
ClientFuncEntityId_t cId = ResRemap.FuncEntityes.erase(entityId);
// Убавляем старые данные
{
auto iterEntity = ResUses.RefFuncEntity.find(entityId);
assert(iterEntity != ResUses.RefFuncEntity.end()); // Зависимости должны быть
// Убавляем модификационные заависимости
//decrementBinary(std::vector<BinTextureId_t> &&textures, std::vector<BinAnimationId_t> &&animation, std::vector<BinSoundId_t> &&sounds, std::vector<BinModelId_t> &&models, std::vector<BinFontId_t> &&fonts)
// Убавляем зависимость к профилю
auto iterProfile = ResUses.DefFuncEntity.find(iterEntity->second.Profile);
assert(iterProfile != ResUses.DefFuncEntity.end()); // Профиль должен быть
if(--iterProfile->second == 0) {
// Профиль больше не используется
auto iterProfileRef = ResUses.RefDefFuncEntity.find(iterEntity->second.Profile);
decrementBinary(std::move(iterProfileRef->second.Texture), std::move(iterProfileRef->second.Animation), {}, std::move(iterProfileRef->second.Model), {});
ResUses.RefDefFuncEntity.erase(iterProfileRef);
ResUses.DefFuncEntity.erase(iterProfile);
}
}
// checkPacketBorder(16);
// NextPacket << (uint8_t) ToClient::L1::Content
// << (uint8_t) ToClient::L2Content::RemoveEntity
// << cId;
}
void RemoteClient::prepareWorldUpdate(WorldId_t worldId, World* world) void RemoteClient::prepareWorldUpdate(WorldId_t worldId, World* world)
{ {
// Добавление зависимостей // Добавление зависимостей
@@ -687,19 +605,6 @@ void RemoteClient::informateDefEntity(const std::unordered_map<DefEntityId_t, vo
} }
} }
void RemoteClient::informateDefFuncEntity(const std::unordered_map<DefFuncEntityId_t, void*> &entityes)
{
for(auto pair : entityes) {
DefFuncEntityId_t id = pair.first;
if(!ResUses.DefFuncEntity.contains(id))
continue;
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::FuncEntity
<< id;
}
}
void RemoteClient::informateDefItem(const std::unordered_map<DefItemId_t, void*> &items) void RemoteClient::informateDefItem(const std::unordered_map<DefItemId_t, void*> &items)
{ {
for(auto pair : items) { for(auto pair : items) {

View File

@@ -149,7 +149,6 @@ struct ResourceRequest {
std::vector<DefWorldId_t> World; std::vector<DefWorldId_t> World;
std::vector<DefPortalId_t> Portal; std::vector<DefPortalId_t> Portal;
std::vector<DefEntityId_t> Entity; std::vector<DefEntityId_t> Entity;
std::vector<DefFuncEntityId_t> FuncEntity;
std::vector<DefItemId_t> Item; std::vector<DefItemId_t> Item;
void insert(const ResourceRequest &obj) { void insert(const ResourceRequest &obj) {
@@ -164,7 +163,6 @@ struct ResourceRequest {
World.insert(World.end(), obj.World.begin(), obj.World.end()); World.insert(World.end(), obj.World.begin(), obj.World.end());
Portal.insert(Portal.end(), obj.Portal.begin(), obj.Portal.end()); Portal.insert(Portal.end(), obj.Portal.begin(), obj.Portal.end());
Entity.insert(Entity.end(), obj.Entity.begin(), obj.Entity.end()); Entity.insert(Entity.end(), obj.Entity.begin(), obj.Entity.end());
FuncEntity.insert(FuncEntity.end(), obj.FuncEntity.begin(), obj.FuncEntity.end());
Item.insert(Item.end(), obj.Item.begin(), obj.Item.end()); Item.insert(Item.end(), obj.Item.begin(), obj.Item.end());
} }
@@ -172,7 +170,7 @@ struct ResourceRequest {
for(std::vector<ResourceId_t> *vec : { for(std::vector<ResourceId_t> *vec : {
&BinTexture, &BinAnimation, &BinModel, &BinSound, &BinTexture, &BinAnimation, &BinModel, &BinSound,
&BinFont, &Voxel, &Node, &World, &BinFont, &Voxel, &Node, &World,
&Portal, &Entity, &FuncEntity, &Item &Portal, &Entity, &Item
}) })
{ {
std::sort(vec->begin(), vec->end()); std::sort(vec->begin(), vec->end());
@@ -222,7 +220,6 @@ class RemoteClient {
std::map<DefWorldId_t, uint32_t> DefWorld; std::map<DefWorldId_t, uint32_t> DefWorld;
std::map<DefPortalId_t, uint32_t> DefPortal; std::map<DefPortalId_t, uint32_t> DefPortal;
std::map<DefEntityId_t, uint32_t> DefEntity; std::map<DefEntityId_t, uint32_t> DefEntity;
std::map<DefFuncEntityId_t, uint32_t> DefFuncEntity;
std::map<DefItemId_t, uint32_t> DefItem; // При передаче инвентарей? std::map<DefItemId_t, uint32_t> DefItem; // При передаче инвентарей?
// Зависимость профилей контента от профилей ресурсов // Зависимость профилей контента от профилей ресурсов
@@ -254,12 +251,6 @@ class RemoteClient {
std::vector<BinModelId_t> Model; std::vector<BinModelId_t> Model;
}; };
std::map<DefEntityId_t, RefDefEntity_t> RefDefEntity; std::map<DefEntityId_t, RefDefEntity_t> RefDefEntity;
struct RefDefFuncEntity_t {
std::vector<BinTextureId_t> Texture;
std::vector<BinAnimationId_t> Animation;
std::vector<BinModelId_t> Model;
};
std::map<DefFuncEntityId_t, RefDefFuncEntity_t> RefDefFuncEntity;
struct RefDefItem_t { struct RefDefItem_t {
std::vector<BinTextureId_t> Texture; std::vector<BinTextureId_t> Texture;
std::vector<BinAnimationId_t> Animation; std::vector<BinAnimationId_t> Animation;
@@ -273,7 +264,7 @@ class RemoteClient {
std::vector<DefVoxelId_t> Voxel; std::vector<DefVoxelId_t> Voxel;
std::vector<DefNodeId_t> Node; std::vector<DefNodeId_t> Node;
}; };
std::map<WorldId_t, std::map<Pos::GlobalChunk, ChunkRef>> RefChunk; std::map<WorldId_t, std::map<Pos::GlobalRegion, std::array<ChunkRef, 4*4*4>>> RefChunk;
struct RefWorld_t { struct RefWorld_t {
DefWorldId_t Profile; DefWorldId_t Profile;
}; };
@@ -286,10 +277,6 @@ class RemoteClient {
DefEntityId_t Profile; DefEntityId_t Profile;
}; };
std::map<ServerEntityId_t, RefEntity_t> RefEntity; std::map<ServerEntityId_t, RefEntity_t> RefEntity;
struct RefFuncEntity_t {
DefFuncEntityId_t Profile;
};
std::map<ServerFuncEntityId_t, RefFuncEntity_t> RefFuncEntity;
} ResUses; } ResUses;
@@ -336,8 +323,8 @@ public:
void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const Node* nodes); void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const Node* nodes);
void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::unordered_map<Pos::bvec16u, Node> &nodes); void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::unordered_map<Pos::bvec16u, Node> &nodes);
//void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights); //void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights);
// Чанк удалён из зоны видимости // Регион удалён из зоны видимости
void prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos); void prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos);
// В зоне видимости добавилась новая сущность или она изменилась // В зоне видимости добавилась новая сущность или она изменилась
void prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity); void prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity);
@@ -346,10 +333,6 @@ public:
// Клиент перестал наблюдать за сущностью // Клиент перестал наблюдать за сущностью
void prepareEntityRemove(ServerEntityId_t entityId); void prepareEntityRemove(ServerEntityId_t entityId);
void prepareFuncEntitySwap(ServerEntityId_t prevEntityId, ServerEntityId_t nextEntityId);
void prepareFuncEntityUpdate(ServerEntityId_t entityId, const FuncEntity *funcRntity);
void prepareFuncEntityRemove(ServerEntityId_t entityId);
// В зоне видимости добавился мир или он изменился // В зоне видимости добавился мир или он изменился
void prepareWorldUpdate(WorldId_t worldId, World* world); void prepareWorldUpdate(WorldId_t worldId, World* world);
// Клиент перестал наблюдать за миром // Клиент перестал наблюдать за миром
@@ -383,7 +366,6 @@ public:
void informateDefWorld(const std::unordered_map<DefWorldId_t, void*> &worlds); void informateDefWorld(const std::unordered_map<DefWorldId_t, void*> &worlds);
void informateDefPortal(const std::unordered_map<DefPortalId_t, void*> &portals); void informateDefPortal(const std::unordered_map<DefPortalId_t, void*> &portals);
void informateDefEntity(const std::unordered_map<DefEntityId_t, void*> &entityes); void informateDefEntity(const std::unordered_map<DefEntityId_t, void*> &entityes);
void informateDefFuncEntity(const std::unordered_map<DefFuncEntityId_t, void*> &funcEntityes);
void informateDefItem(const std::unordered_map<DefItemId_t, void*> &items); void informateDefItem(const std::unordered_map<DefItemId_t, void*> &items);
private: private:

View File

@@ -25,7 +25,6 @@ public:
Node Nodes[16][16][16][4][4][4]; Node Nodes[16][16][16][4][4][4];
std::vector<Entity> Entityes; std::vector<Entity> Entityes;
std::vector<FuncEntity> FuncEntityes;
std::vector<ContentEventController*> CECs; std::vector<ContentEventController*> CECs;
// Используется для прорежения количества проверок на наблюдаемые чанки и сущности // Используется для прорежения количества проверок на наблюдаемые чанки и сущности
// В одно обновление региона - проверка одного наблюдателя // В одно обновление региона - проверка одного наблюдателя