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

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

View File

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

View File

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

View File

@@ -224,16 +224,6 @@ public:
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>
struct VoxelCuboidsFuncs {

View File

@@ -3,6 +3,7 @@
#include "RemoteClient.hpp"
#include "Server/Abstract.hpp"
#include "World.hpp"
#include <algorithm>
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 {
return 0;
}
@@ -28,31 +25,20 @@ ServerObjectPos ContentEventController::getPos() const {
return {0, Remote->CameraPos};
}
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::bvec4u chunkPosLocal;
chunkPosLocal.unpack(bitPos);
Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + chunkPosLocal;
Remote->prepareChunkRemove(worldId, chunkPos);
bitPos = chunks._Find_next(bitPos);
}
}
void ContentEventController::removeUnobservable(const ContentViewInfo_Diff& diff) {
for(const auto& [worldId, regions] : diff.RegionsLost) {
for(const Pos::GlobalRegion region : regions)
Remote->prepareRegionRemove(worldId, region);
}
// Очистка миров
for(WorldId_t worldId : ContentView_LostView.Worlds) {
for(const WorldId_t worldId : diff.WorldsLost)
Remote->prepareWorldRemove(worldId);
}
}
void ContentEventController::onWorldUpdate(WorldId_t worldId, World *worldObj)
{
auto pWorld = ContentViewState.find(worldId);
if(pWorld == ContentViewState.end())
auto pWorld = ContentViewState.Regions.find(worldId);
if(pWorld == ContentViewState.Regions.end())
return;
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,
const std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*>& chunks)
{
auto pWorld = ContentViewState.find(worldId);
if(pWorld == ContentViewState.end())
auto pWorld = ContentViewState.Regions.find(worldId);
if(pWorld == ContentViewState.Regions.end())
return;
auto pRegion = pWorld->second.find(regionPos);
if(pRegion == pWorld->second.end())
if(!std::binary_search(pWorld->second.begin(), pWorld->second.end(), regionPos))
return;
const std::bitset<64> &chunkBitset = pRegion->second;
for(auto pChunk : chunks) {
if(!chunkBitset.test(pChunk.first.pack()))
continue;
Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + pChunk.first;
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,
const std::unordered_map<Pos::bvec4u, const Node*> &chunks)
{
auto pWorld = ContentViewState.find(worldId);
if(pWorld == ContentViewState.end())
auto pWorld = ContentViewState.Regions.find(worldId);
if(pWorld == ContentViewState.Regions.end())
return;
auto pRegion = pWorld->second.find(regionPos);
if(pRegion == pWorld->second.end())
if(!std::binary_search(pWorld->second.begin(), pWorld->second.end(), regionPos))
return;
const std::bitset<64> &chunkBitset = pRegion->second;
for(auto pChunk : chunks) {
if(!chunkBitset.test(pChunk.first.pack()))
continue;
Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + Pos::GlobalChunk(pChunk.first);
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,
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
for(RegionEntityId_t eId : lost) {
Remote->prepareEntityRemove({worldId, regionPos, eId});
}
}
void ContentEventController::onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos,
RegionEntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, RegionEntityId_t newId)
void ContentEventController::onEntitySwap(ServerEntityId_t prevId, ServerEntityId_t newId)
{
// Проверим отслеживается ли эта сущность нами
auto lpWorld = Subscribed.Entities.find(lastWorldId);
if(lpWorld == Subscribed.Entities.end())
// Исходный мир нами не отслеживается
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;
}
{
auto pWorld = ContentViewState.Regions.find(std::get<0>(prevId));
assert(pWorld != ContentViewState.Regions.end());
assert(std::binary_search(pWorld->second.begin(), pWorld->second.end(), std::get<1>(prevId)));
}
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:
return;
Remote->prepareEntitySwap(prevId, newId);
}
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);
if(lpWorld == Subscribed.Entities.end())
auto pWorld = ContentViewState.Regions.find(worldId);
if(pWorld == ContentViewState.Regions.end())
// Исходный мир нами не отслеживается
return;
auto lpRegion = lpWorld->second.find(regionPos);
if(lpRegion == lpWorld->second.end())
if(!std::binary_search(pWorld->second.begin(), pWorld->second.end(), regionPos))
// Исходный регион нами не отслеживается
return;
for(size_t eId = 0; eId < entities.size(); eId++) {
if(!lpRegion->second.contains(eId))
continue;
Remote->prepareEntityUpdate({worldId, regionPos, eId}, &entities[eId]);
for(const auto& [id, entity] : entities) {
Remote->prepareEntityUpdate({worldId, regionPos, id}, entity);
}
}

View File

@@ -2,6 +2,7 @@
#include <Common/Abstract.hpp>
#include "Abstract.hpp"
#include <algorithm>
#include <bitset>
#include <map>
#include <memory>
@@ -22,171 +23,130 @@ struct ServerObjectPos {
Pos::Object ObjectPos;
};
/*
Сфера в которой отслеживаются события игроком
Разница между информацией о наблюдаемых регионах
*/
struct ContentViewCircle {
WorldId_t WorldId;
// Позиция в чанках
glm::i16vec3 Pos;
// (Единица равна размеру чанка) в квадрате
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;
struct ContentViewInfo_Diff {
// Изменения на уровне миров (увиден или потерян)
std::vector<WorldId_t> WorldsNew, WorldsLost;
// Изменения на уровне регионов
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> RegionsNew, RegionsLost;
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;
// Рассматриваем разницу меж мирами
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<64>> &newRegions = newWorldView;
const std::map<Pos::GlobalRegion, std::bitset<64>> &oldRegions = oldWorldIter->second;
std::map<Pos::GlobalRegion, std::bitset<64>> *diffRegions = nullptr;
// Что изменилось относительно obj
// Перерасчёт должен проводится при смещении игрока или ContentBridge за границу региона
ContentViewInfo_Diff diffWith(const ContentViewInfo& obj) const {
ContentViewInfo_Diff out;
// Рассматриваем разницу меж регионами
for(const auto &[newRegionPos, newRegionBitField] : newRegions) {
auto oldRegionIter = oldRegions.find(newRegionPos);
if(oldRegionIter == oldRegions.end()) { // В старой описи мира нет региона
if(!diffRegions)
diffRegions = &newView.View[newWorldId];
// Проверяем новые миры и регионы
for(const auto& [key, regions] : Regions) {
auto iterWorld = obj.Regions.find(key);
(*diffRegions)[newRegionPos] = newRegionBitField;
newView.Regions[newWorldId].push_back(newRegionPos);
} else {
const std::bitset<64> &oldChunks = oldRegionIter->second;
std::bitset<64> chunks = (~oldChunks) & newRegionBitField; // Останется поле с новыми чанками
if(chunks._Find_first() != chunks.size()) {
// Есть новые чанки
if(!diffRegions)
diffRegions = &newView.View[newWorldId];
(*diffRegions)[newRegionPos] = chunks;
}
}
if(iterWorld == obj.Regions.end()) {
out.WorldsNew.push_back(key);
out.RegionsNew[key] = regions;
} else {
auto &vec = out.RegionsNew[key];
vec.reserve(8*8);
std::set_difference(
regions.begin(), regions.end(),
iterWorld->second.begin(), iterWorld->second.end(),
std::back_inserter(vec)
);
}
}
// Проверяем потерянные миры и регионы
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 {
/*
false -> Из точки From видно контент из точки To
false -> Из точки Left видно контент в точки Right
true -> Контент виден в обе стороны
*/
bool IsTwoWay = false;
WorldId_t LeftWorld;
// Позиция в чанках
glm::i16vec3 LeftPos;
Pos::GlobalRegion LeftPos;
WorldId_t RightWorld;
// Позиция в чанках
glm::i16vec3 RightPos;
Pos::GlobalRegion RightPos;
};
struct ContentViewCircle {
WorldId_t WorldId;
Pos::GlobalRegion Pos;
// Радиус в регионах в квадрате
int16_t Range;
};
/* Игрок */
class ContentEventController {
private:
struct SubscribedObj {
// Используется регионами
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;
public:
// Управляется сервером
std::unique_ptr<RemoteClient> Remote;
// Регионы сюда заглядывают
// Каждый такт значения изменений обновляются GameServer'ом
// Объявленная в чанках территория точно отслеживается (активная зона)
ContentViewGlobal ContentViewState;
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;
// Что сейчас наблюдает игрок
ContentViewInfo ContentViewState;
// Если игрок пересекал границы чанка (для перерасчёта ContentViewState)
bool CrossedBorder = true;
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()
uint16_t getViewRangeBackground() const;
ServerObjectPos getLastPos() const;
ServerObjectPos getPos() const;
// Проверка на необходимость подгрузки новых определений миров
// и очистка клиента от не наблюдаемых данных
void checkContentViewChanges();
// Очищает более не наблюдаемые чанки и миры
void removeUnobservable(const ContentViewInfo_Diff& diff);
// Здесь приходят частично фильтрованные события
// Фильтровать не отслеживаемые миры
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 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 onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::vector<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 onEntitySwap(ServerEntityId_t prevId, ServerEntityId_t newId);
void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<RegionEntityId_t, Entity*> &entities);
void onPortalEnterLost(const std::vector<void*> &enter, const std::vector<void*> &lost);
void onPortalUpdates(const std::vector<void*> &portals);
@@ -211,33 +167,15 @@ public:
};
}
namespace std {
template <>
struct hash<LV::Server::ServerObjectPos> {
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 "Server/Abstract.hpp"
#include "Server/ContentEventController.hpp"
#include <algorithm>
#include <boost/json/parse.hpp>
#include <chrono>
#include <glm/geometric.hpp>
@@ -11,7 +12,6 @@
#include <string>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include "SaveBackends/Filesystem.hpp"
#include "Server/SaveBackend.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)
{
TL_Circles.clear();
TL_Circles.reserve(256);
TL_Circles.reserve(64);
TL_Circles.push_back(circle);
_accumulateContentViewCircles(circle, depth);
return TL_Circles;
@@ -42,7 +42,7 @@ void GameServer::Expanse_t::_accumulateContentViewCircles(ContentViewCircle circ
auto &br = pair.second;
if(br.LeftWorld == circle.WorldId) {
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) {
bool isIn = false;
@@ -69,7 +69,7 @@ void GameServer::Expanse_t::_accumulateContentViewCircles(ContentViewCircle circ
if(br.IsTwoWay && br.RightWorld == circle.WorldId) {
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) {
bool isIn = false;
@@ -107,34 +107,29 @@ void GameServer::Expanse_t::_accumulateContentViewCircles(ContentViewCircle circ
// }
ContentViewGlobal GameServer::Expanse_t::makeContentViewGlobal(const std::vector<ContentViewCircle> &views) {
ContentViewGlobal cvg;
Pos::GlobalRegion posRegion, lastPosRegion;
std::bitset<64> *cache = nullptr;
ContentViewInfo GameServer::Expanse_t::makeContentViewInfo(const std::vector<ContentViewCircle> &views) {
ContentViewInfo cvi;
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;
std::vector<Pos::GlobalRegion> &cvw = cvi.Regions[circle.WorldId];
int32_t regionRange = std::sqrt(circle.Range);
Pos::GlobalChunk posChunk(x+circle.Pos.x, y+circle.Pos.y, z+circle.Pos.z);
posRegion = posChunk >> 2;
cvw.reserve(cvw.size()+std::pow(regionRange*2+1, 3));
if(!cache || lastPosRegion != posRegion) {
lastPosRegion = posRegion;
cache = &cvw[posRegion];
}
cache->_Unchecked_set(Pos::bvec4u(posChunk).pack());
}
for(int32_t z = -regionRange; z <= regionRange; z++)
for(int32_t y = -regionRange; y <= regionRange; y++)
for(int32_t x = -regionRange; x <= regionRange; x++)
cvw.push_back(Pos::GlobalRegion(x, y, z));
}
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) {
@@ -404,7 +399,102 @@ void GameServer::run() {
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);
if(Content.Texture.hasPreparedInformation()) {
auto table = Content.Texture.takePreparedInformation();
@@ -450,7 +540,7 @@ void GameServer::stepSyncWithAsync() {
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
assert(cec);
for(const auto &[worldId, regions] : cec->ContentViewState) {
for(const auto &[worldId, regions] : cec->ContentViewState.Regions) {
for(const auto &[regionPos, chunkBitfield] : regions) {
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() {
for(auto &pair : Expanse.Worlds)
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 std::vector<VoxelCube>*> newVoxels;
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_Voxels(pWorld.first, pRegion.first, newVoxels);
cec->onChunksUpdate_Nodes(pWorld.first, pRegion.first, newNodes);
}
}
// То, что уже отслеживает наблюдатель
@@ -903,8 +942,12 @@ void GameServer::stepWorlds() {
// // Отправить полную информацию о новых наблюдаемых сущностях наблюдателю
// }
if(!region.Entityes.empty())
cec->onEntityUpdates(pWorld.first, pRegion.first, region.Entityes);
if(!region.Entityes.empty()) {
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())
return;
// Затереть изменения предыдущего такта
for(auto &cecPtr : Game.CECs) {
assert(cecPtr);
cecPtr->ContentView_NewView = {};
cecPtr->ContentView_LostView = {};
}
for(auto &cec : Game.CECs) {
assert(cec);
// Если наблюдаемая территория изменяется
// -> Новая увиденная + Старая потерянная
std::unordered_map<ContentEventController*, ContentViewGlobal_DiffInfo> lost_CVG;
if(!cec->CrossedBorder)
continue;
// Обновления поля зрения
for(int iter = 0; iter < 1; iter++) {
if(++Game.CEC_NextRebuildViewCircles >= Game.CECs.size())
Game.CEC_NextRebuildViewCircles = 0;
cec->CrossedBorder = false;
ContentEventController &cec = *Game.CECs[Game.CEC_NextRebuildViewCircles];
ServerObjectPos oPos = cec.getPos();
// Пересчёт зон наблюдения
ServerObjectPos oPos = cec->getPos();
ContentViewCircle cvc;
cvc.WorldId = oPos.WorldId;
cvc.Pos = Pos::Object_t::asChunkPos(oPos.ObjectPos);
cvc.Range = cec.getViewRangeActive();
cvc.Range *= cvc.Range;
cvc.Range = 2*2;
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}});
ContentViewInfo newCbg = Expanse_t::makeContentViewInfo(newCVCs);
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());
// Поиск потерянных миров
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->onWorldUpdate(id, iter->second.get());
}
}
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() {

View File

@@ -86,9 +86,9 @@ class GameServer : public AsyncObject {
// 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));
static ContentViewInfo makeContentViewInfo(const std::vector<ContentViewCircle> &views);
ContentViewInfo makeContentViewInfo(ContentViewCircle circle, int depth = 2) {
return makeContentViewInfo(accumulateContentViewCircles(circle, depth));
}
// std::unordered_map<WorldId_t, std::vector<ContentViewCircle>> remapCVCsByWorld(const std::vector<ContentViewCircle> &list);
@@ -100,9 +100,14 @@ class GameServer : public AsyncObject {
/*
Регистрация миров по строке
*/
/*
*/
private:
void _accumulateContentViewCircles(ContentViewCircle circle, int depth);
} Expanse;
@@ -154,29 +159,61 @@ private:
void prerun();
void run();
void stepContent();
/*
Дождаться и получить необходимые данные с бд или диска
Получить несрочные данные
Подключение/отключение игроков
*/
void stepSyncWithAsync();
void stepPlayers();
void stepWorlds();
/*
Пересмотр наблюдаемых зон (чанки, регионы, миры)
Добавить требуемые регионы в список на предзагрузку с приоритетом
TODO: нужен механизм асинхронной загрузки регионов с бд
В начале следующего такта обязательное дожидание прогрузки активной зоны
и
оповещение миров об изменениях в наблюдаемых регионах
void stepConnections();
/*
Переинициализация модов, если требуется
*/
void stepViewContent();
void stepSendPlayersPackets();
void stepLoadRegions();
void stepGlobal();
void stepSave();
void save();
void stepModInitializations();
/*
Пересчёт зон видимости игроков, если необходимо
Выгрузить более не используемые регионы
Сохранение регионов
Создание списка регионов необходимых для загрузки (бд автоматически будет предзагружать)
<Синхронизация с модулем сохранений>
Очередь загрузки, выгрузка регионов и получение загруженных из бд регионов
Получить список регионов отсутствующих в сохранении и требующих генерации
Подпись на загруженные регионы (отправить полностью на клиент)
*/
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);
assert(iterWorld != ResUses.RefChunk.end());
Pos::bvec4u lChunk = (chunkPos & 0xf);
// Исключим зависимости предыдущей версии чанка
{
auto iterChunk = iterWorld->second.find(chunkPos);
if(iterChunk != iterWorld->second.end()) {
// Раньше этот чанк был, значит не новый для клиента
auto iterRegion = iterWorld->second.find(chunkPos);
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);
assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях
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()) {
// Добавляем новые типы в запрос
@@ -184,14 +185,13 @@ void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk
auto iterWorld = ResUses.RefChunk.find(worldId);
assert(iterWorld != ResUses.RefChunk.end());
Pos::bvec4u lChunk = (chunkPos & 0xf);
// Исключим зависимости предыдущей версии чанка
{
auto iterChunk = iterWorld->second.find(chunkPos);
if(iterChunk != iterWorld->second.end()) {
// Раньше этот чанк был, значит не новый для клиента
auto iterRegion = iterWorld->second.find(chunkPos);
if(iterRegion != 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);
assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях
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()) {
// Добавляем новые типы в запрос
@@ -223,9 +223,7 @@ void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk
LOG.debug() << "Увидели " << chunkPos.x << ' ' << chunkPos.y << ' ' << chunkPos.z;
}
void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos)
{
LOG.debug() << "Потеряли " << chunkPos.x << ' ' << chunkPos.y << ' ' << chunkPos.z;
void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos) {
std::vector<DefVoxelId_t>
lostTypesV /* Потерянные типы вокселей */;
std::vector<DefNodeId_t>
@@ -235,28 +233,29 @@ void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkP
{
auto iterWorld = ResUses.RefChunk.find(worldId);
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 iter = ResUses.DefNode.find(id);
assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях
if(--iter->second == 0) {
// Ноды больше нет в зависимостях
lostTypesN.push_back(id);
ResUses.DefNode.erase(iter);
auto iterRegion = iterWorld->second.find(regionPos);
assert(iterRegion != iterWorld->second.end());
for(const auto &iterChunk : iterRegion->second) {
for(const DefVoxelId_t& id : iterChunk.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.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);
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::RemoveChunk
<< worldId << chunkPos.pack();
<< (uint8_t) ToClient::L2Content::RemoveRegion
<< worldId << regionPos.pack();
}
void RemoteClient::prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity)
@@ -366,87 +365,6 @@ void RemoteClient::prepareEntityRemove(ServerEntityId_t entityId)
<< 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)
{
// Добавление зависимостей
@@ -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)
{
for(auto pair : items) {

View File

@@ -149,7 +149,6 @@ struct ResourceRequest {
std::vector<DefWorldId_t> World;
std::vector<DefPortalId_t> Portal;
std::vector<DefEntityId_t> Entity;
std::vector<DefFuncEntityId_t> FuncEntity;
std::vector<DefItemId_t> Item;
void insert(const ResourceRequest &obj) {
@@ -164,7 +163,6 @@ struct ResourceRequest {
World.insert(World.end(), obj.World.begin(), obj.World.end());
Portal.insert(Portal.end(), obj.Portal.begin(), obj.Portal.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());
}
@@ -172,7 +170,7 @@ struct ResourceRequest {
for(std::vector<ResourceId_t> *vec : {
&BinTexture, &BinAnimation, &BinModel, &BinSound,
&BinFont, &Voxel, &Node, &World,
&Portal, &Entity, &FuncEntity, &Item
&Portal, &Entity, &Item
})
{
std::sort(vec->begin(), vec->end());
@@ -222,7 +220,6 @@ class RemoteClient {
std::map<DefWorldId_t, uint32_t> DefWorld;
std::map<DefPortalId_t, uint32_t> DefPortal;
std::map<DefEntityId_t, uint32_t> DefEntity;
std::map<DefFuncEntityId_t, uint32_t> DefFuncEntity;
std::map<DefItemId_t, uint32_t> DefItem; // При передаче инвентарей?
// Зависимость профилей контента от профилей ресурсов
@@ -254,12 +251,6 @@ class RemoteClient {
std::vector<BinModelId_t> Model;
};
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 {
std::vector<BinTextureId_t> Texture;
std::vector<BinAnimationId_t> Animation;
@@ -273,7 +264,7 @@ class RemoteClient {
std::vector<DefVoxelId_t> Voxel;
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 {
DefWorldId_t Profile;
};
@@ -286,10 +277,6 @@ class RemoteClient {
DefEntityId_t Profile;
};
std::map<ServerEntityId_t, RefEntity_t> RefEntity;
struct RefFuncEntity_t {
DefFuncEntityId_t Profile;
};
std::map<ServerFuncEntityId_t, RefFuncEntity_t> RefFuncEntity;
} 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 std::unordered_map<Pos::bvec16u, Node> &nodes);
//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);
@@ -346,10 +333,6 @@ public:
// Клиент перестал наблюдать за сущностью
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);
// Клиент перестал наблюдать за миром
@@ -383,7 +366,6 @@ public:
void informateDefWorld(const std::unordered_map<DefWorldId_t, void*> &worlds);
void informateDefPortal(const std::unordered_map<DefPortalId_t, void*> &portals);
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);
private:

View File

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