Переделка ресурсов и этапов обновения
This commit is contained in:
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user