Files
LuaVox/Src/Server/ContentEventController.hpp

182 lines
6.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <Common/Abstract.hpp>
#include "Abstract.hpp"
#include <algorithm>
#include <bitset>
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace LV::Server {
class RemoteClient;
class GameServer;
class World;
struct ServerObjectPos {
WorldId_t WorldId;
Pos::Object ObjectPos;
};
/*
Разница между информацией о наблюдаемых регионах
*/
struct ContentViewInfo_Diff {
// Изменения на уровне миров (увиден или потерян)
std::vector<WorldId_t> WorldsNew, WorldsLost;
// Изменения на уровне регионов
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> RegionsNew, RegionsLost;
bool empty() const {
return WorldsNew.empty() && WorldsLost.empty() && RegionsNew.empty() && RegionsLost.empty();
}
};
/*
То, какие регионы наблюдает игрок
*/
struct ContentViewInfo {
// std::vector<Pos::GlobalRegion> - сортированный и с уникальными значениями
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> Regions;
// Что изменилось относительно obj
// Перерасчёт должен проводится при смещении игрока или ContentBridge за границу региона
ContentViewInfo_Diff diffWith(const ContentViewInfo& obj) const {
ContentViewInfo_Diff out;
// Проверяем новые миры и регионы
for(const auto& [key, regions] : Regions) {
auto iterWorld = obj.Regions.find(key);
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;
}
};
/*
Мост контента, для отслеживания событий из удалённых точек
По типу портала, через который можно видеть контент на расстоянии
*/
struct ContentBridge {
/*
false -> Из точки Left видно контент в точки Right
true -> Контент виден в обе стороны
*/
bool IsTwoWay = false;
WorldId_t LeftWorld;
Pos::GlobalRegion LeftPos;
WorldId_t RightWorld;
Pos::GlobalRegion RightPos;
};
struct ContentViewCircle {
WorldId_t WorldId;
Pos::GlobalRegion Pos;
// Радиус в регионах в квадрате
int16_t Range;
};
/* Игрок */
class ContentEventController {
private:
struct SubscribedObj {
// Используется регионами
std::vector<PortalId_t> Portals;
} Subscribed;
public:
// Управляется сервером
std::unique_ptr<RemoteClient> Remote;
// Что сейчас наблюдает игрок
ContentViewInfo ContentViewState;
// Если игрок пересекал границы чанка (для перерасчёта ContentViewState)
bool CrossedBorder = true;
public:
ContentEventController(std::unique_ptr<RemoteClient>&& remote);
// Измеряется в чанках в регионах (активная зона)
static constexpr uint16_t getViewRangeActive() { return 2; }
// Измеряется в чанках в радиусе (Декоративная зона) + getViewRangeActive()
uint16_t getViewRangeBackground() const;
ServerObjectPos getLastPos() const;
ServerObjectPos getPos() const;
// Очищает более не наблюдаемые чанки и миры
void removeUnobservable(const ContentViewInfo_Diff& diff);
// Здесь приходят частично фильтрованные события
// Фильтровать не отслеживаемые миры
void onWorldUpdate(WorldId_t worldId, World *worldObj);
// Нужно фильтровать неотслеживаемые чанки
void onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*> &chunks);
void onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::bvec4u, const Node*> &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 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);
inline const SubscribedObj& getSubscribed() { return Subscribed; };
};
}
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<LV::Pos::Object>()(obj.ObjectPos);
}
};
}