stepDatabaseSync stepGeneratorAndLuaAsync
This commit is contained in:
@@ -349,6 +349,8 @@ using bvec4i = BitVec3<int8_t, 2>;
|
||||
using bvec4u = BitVec3<uint8_t, 2>;
|
||||
using bvec16i = BitVec3<int8_t, 4>;
|
||||
using bvec16u = BitVec3<uint8_t, 4>;
|
||||
using bvec64i = BitVec3<int8_t, 6>;
|
||||
using bvec64u = BitVec3<uint8_t, 6>;
|
||||
using bvec256i = BitVec3<int8_t, 8>;
|
||||
using bvec256u = BitVec3<uint8_t, 8>;
|
||||
using bvec1024i = BitVec3<int16_t, 10>;
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
#include "Server/Abstract.hpp"
|
||||
#include "Server/ContentEventController.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <chrono>
|
||||
#include <glm/geometric.hpp>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -15,6 +17,7 @@
|
||||
#include "SaveBackends/Filesystem.hpp"
|
||||
#include "Server/SaveBackend.hpp"
|
||||
#include "Server/World.hpp"
|
||||
#include "glm/gtc/noise.hpp"
|
||||
|
||||
namespace LV::Server {
|
||||
|
||||
@@ -441,20 +444,189 @@ void GameServer::stepModInitializations() {
|
||||
|
||||
}
|
||||
|
||||
void GameServer::stepDatabase() {
|
||||
|
||||
IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
|
||||
IWorldSaveBackend::TickSyncInfo_In toDB;
|
||||
|
||||
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
|
||||
assert(cec);
|
||||
// Пересчитать зоны наблюдения
|
||||
if(cec->CrossedBorder) {
|
||||
cec->CrossedBorder = false;
|
||||
|
||||
// Пересчёт зон наблюдения
|
||||
ServerObjectPos oPos = cec->getPos();
|
||||
|
||||
ContentViewCircle cvc;
|
||||
cvc.WorldId = oPos.WorldId;
|
||||
cvc.Pos = Pos::Object_t::asChunkPos(oPos.ObjectPos);
|
||||
cvc.Range = 2*2;
|
||||
|
||||
std::vector<ContentViewCircle> newCVCs = Expanse.accumulateContentViewCircles(cvc);
|
||||
ContentViewInfo newCbg = Expanse_t::makeContentViewInfo(newCVCs);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
cec->ContentViewState = newCbg;
|
||||
// Вычистка не наблюдаемых регионов
|
||||
cec->removeUnobservable(diff);
|
||||
|
||||
// Подписываем игрока на наблюдение за регионами
|
||||
for(const auto& [worldId, regions] : diff.RegionsNew) {
|
||||
auto iterWorld = Expanse.Worlds.find(worldId);
|
||||
assert(iterWorld != Expanse.Worlds.end());
|
||||
|
||||
std::vector<Pos::GlobalRegion> notLoaded = iterWorld->second->onCEC_RegionsEnter(cec.get(), regions);
|
||||
if(!notLoaded.empty()) {
|
||||
// Добавляем к списку на загрузку
|
||||
std::vector<Pos::GlobalRegion> &tl = toDB.Load[worldId];
|
||||
tl.insert(tl.end(), notLoaded.begin(), notLoaded.end());
|
||||
}
|
||||
}
|
||||
|
||||
// Отписываем то, что игрок больше не наблюдает
|
||||
for(const auto& [worldId, regions] : diff.RegionsLost) {
|
||||
auto iterWorld = Expanse.Worlds.find(worldId);
|
||||
assert(iterWorld != Expanse.Worlds.end());
|
||||
|
||||
iterWorld->second->onCEC_RegionsLost(cec.get(), regions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& [worldId, regions] : toDB.Load) {
|
||||
std::sort(regions.begin(), regions.end());
|
||||
auto eraseIter = std::unique(regions.begin(), regions.end());
|
||||
regions.erase(eraseIter, regions.end());
|
||||
}
|
||||
|
||||
// Обзавелись списком на прогрузку регионов
|
||||
// Теперь узнаем что нужно сохранить и что из регионов было выгружено
|
||||
for(auto& [worldId, world] : Expanse.Worlds) {
|
||||
World::SaveUnloadInfo info = world->onStepDatabaseSync();
|
||||
|
||||
if(!info.ToSave.empty()) {
|
||||
auto &obj = toDB.ToSave[worldId];
|
||||
obj.insert(obj.end(), std::make_move_iterator(info.ToSave.begin()), std::make_move_iterator(info.ToSave.end()));
|
||||
}
|
||||
|
||||
if(!info.ToUnload.empty()) {
|
||||
auto &obj = toDB.Unload[worldId];
|
||||
obj.insert(obj.end(), info.ToUnload.begin(), info.ToUnload.end());
|
||||
}
|
||||
}
|
||||
|
||||
// Синхронизируемся с базой
|
||||
return SaveBackend.World->tickSync(std::move(toDB));
|
||||
}
|
||||
|
||||
void GameServer::stepLuaAsync() {
|
||||
void GameServer::stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db) {
|
||||
// 1. Получили сырые регионы и те регионы, что не существуют
|
||||
// 2.1 Те регионы, что не существуют отправляются на расчёт шума
|
||||
// 2.2 Далее в луа для обработки шума
|
||||
// 3.1 Нужно прогнать идентификаторы через обработчики lua
|
||||
// 3.2 Полученный регион связать с существующими профилями сервера
|
||||
// 4. Полученные регионы раздать мирам и попробовать по новой подписать к ним игроков, если они всё ещё должны наблюдать эти регионы
|
||||
|
||||
|
||||
// Синхронизация с генератором шума
|
||||
std::unordered_map<WorldId_t, std::vector<std::pair<Pos::GlobalRegion, std::array<float, 64*64*64>>>> calculatedNoise;
|
||||
for(auto& [worldId, regions] : db.NotExisten) {
|
||||
auto &r = calculatedNoise[worldId];
|
||||
for(Pos::GlobalRegion pos : regions) {
|
||||
r.emplace_back();
|
||||
std::get<0>(r.back()) = pos;
|
||||
auto ®ion = std::get<1>(r.back());
|
||||
Pos::GlobalNode posNode = pos;
|
||||
posNode <<= 6;
|
||||
|
||||
float *ptr = ®ion[0];
|
||||
|
||||
for(int z = 0; z < 64; z++)
|
||||
for(int y = 0; y < 64; y++)
|
||||
for(int x = 0; x < 64; x++, ptr++) {
|
||||
*ptr = glm::perlin(glm::dvec3(posNode.x+x, posNode.y+y, posNode.z+z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<WorldId_t, std::vector<std::pair<Pos::GlobalRegion, World::RegionIn>>> toLoadRegions;
|
||||
|
||||
// Синхронизация с контроллером асинхронных обработчиков луа
|
||||
// 2.2 и 3.1
|
||||
// Обработка шума
|
||||
for(auto& [WorldId_t, regions] : calculatedNoise) {
|
||||
auto &list = toLoadRegions[WorldId_t];
|
||||
|
||||
for(auto& [pos, noise] : regions) {
|
||||
auto &obj = list.emplace_back(pos, World::RegionIn()).second;
|
||||
|
||||
float *ptr = &noise[0];
|
||||
|
||||
for(int z = 0; z < 64; z++)
|
||||
for(int y = 0; y < 64; y++)
|
||||
for(int x = 0; x < 64; x++, ptr++) {
|
||||
DefVoxelId_t id = *ptr > 0.5 ? 1 : 0;
|
||||
Pos::bvec64u nodePos(x, y, z);
|
||||
auto &node = obj.Nodes[Pos::bvec4u(nodePos >> 4).pack()][Pos::bvec16u(nodePos & 0xf).pack()];
|
||||
node.NodeId = id;
|
||||
node.Meta = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Обработка идентификаторов на стороне луа
|
||||
|
||||
// Трансформация полученных ключей в профили сервера
|
||||
for(auto& [WorldId_t, regions] : db.LoadedRegions) {
|
||||
auto &list = toLoadRegions[WorldId_t];
|
||||
|
||||
for(auto& [pos, region] : regions) {
|
||||
auto &obj = list.emplace_back(pos, World::RegionIn()).second;
|
||||
convertRegionVoxelsToChunks(region.Voxels, obj.Voxels);
|
||||
obj.Nodes = std::move(region.Nodes);
|
||||
obj.Entityes = std::move(region.Entityes);
|
||||
}
|
||||
}
|
||||
|
||||
// Раздадим полученные регионы мирам и попробуем подписать на них наблюдателей
|
||||
for(auto& [worldId, regions] : toLoadRegions) {
|
||||
auto iterWorld = Expanse.Worlds.find(worldId);
|
||||
assert(iterWorld != Expanse.Worlds.end());
|
||||
|
||||
std::vector<Pos::GlobalRegion> newRegions;
|
||||
newRegions.reserve(regions.size());
|
||||
for(auto& [pos, _] : regions)
|
||||
newRegions.push_back(pos);
|
||||
std::sort(newRegions.begin(), newRegions.end());
|
||||
|
||||
std::unordered_map<ContentEventController*, std::vector<Pos::GlobalRegion>> toSubscribe;
|
||||
|
||||
for(auto& cec : Game.CECs) {
|
||||
auto iterViewWorld = cec->ContentViewState.Regions.find(worldId);
|
||||
if(iterViewWorld == cec->ContentViewState.Regions.end())
|
||||
continue;
|
||||
|
||||
for(auto& pos : iterViewWorld->second) {
|
||||
if(std::binary_search(newRegions.begin(), newRegions.end(), pos))
|
||||
toSubscribe[cec.get()].push_back(pos);
|
||||
}
|
||||
}
|
||||
|
||||
iterWorld->second->pushRegions(std::move(regions));
|
||||
for(auto& [cec, poses] : toSubscribe) {
|
||||
iterWorld->second->onCEC_RegionsEnter(cec, poses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameServer::stepPlayerProceed() {
|
||||
@@ -466,34 +638,11 @@ void GameServer::stepWorldPhysic() {
|
||||
}
|
||||
|
||||
void GameServer::stepGlobalStep() {
|
||||
|
||||
for(auto &pair : Expanse.Worlds)
|
||||
pair.second->onUpdate(this, CurrentTickDuration);
|
||||
}
|
||||
|
||||
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()) {
|
||||
@@ -534,45 +683,32 @@ void GameServer::stepSyncContent() {
|
||||
cec->Remote->informateBinFont(table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameServer::stepSyncWithAsync() {
|
||||
// Сбор запросов на ресурсы и профили + отправка пакетов игрокам
|
||||
ResourceRequest full;
|
||||
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
|
||||
assert(cec);
|
||||
|
||||
for(const auto &[worldId, regions] : cec->ContentViewState.Regions) {
|
||||
for(const auto &[regionPos, chunkBitfield] : regions) {
|
||||
forceGetRegion(worldId, regionPos);
|
||||
}
|
||||
}
|
||||
|
||||
// Подпись на регионы
|
||||
for(const auto &[worldId, newRegions] : cec->ContentView_NewView.Regions) {
|
||||
auto worldIter = Expanse.Worlds.find(worldId);
|
||||
assert(worldIter != Expanse.Worlds.end() && "TODO: Логика не определена");
|
||||
assert(worldIter->second);
|
||||
World &world = *worldIter->second;
|
||||
|
||||
// Подписать наблюдателей на регионы миров
|
||||
world.onCEC_RegionsEnter(cec.get(), newRegions);
|
||||
}
|
||||
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::stepWorlds() {
|
||||
for(auto &pair : Expanse.Worlds)
|
||||
pair.second->onUpdate(this, CurrentTickDuration);
|
||||
|
||||
// Оповещаем о новых мирах
|
||||
for(const std::unique_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
for(WorldId_t id : cec->NewWorlds) {
|
||||
auto iter = Expanse.Worlds.find(id);
|
||||
assert(iter != Expanse.Worlds.end());
|
||||
cec->onWorldUpdate(id, iter->second.get());
|
||||
}
|
||||
|
||||
cec->NewWorlds.clear();
|
||||
}
|
||||
|
||||
for(auto &pWorld : Expanse.Worlds) {
|
||||
World &wobj = *pWorld.second;
|
||||
@@ -1010,65 +1146,5 @@ void GameServer::stepWorlds() {
|
||||
|
||||
}
|
||||
|
||||
void GameServer::stepViewContent() {
|
||||
if(Game.CECs.empty())
|
||||
return;
|
||||
|
||||
for(auto &cec : Game.CECs) {
|
||||
assert(cec);
|
||||
|
||||
if(!cec->CrossedBorder)
|
||||
continue;
|
||||
|
||||
cec->CrossedBorder = false;
|
||||
|
||||
// Пересчёт зон наблюдения
|
||||
ServerObjectPos oPos = cec->getPos();
|
||||
|
||||
ContentViewCircle cvc;
|
||||
cvc.WorldId = oPos.WorldId;
|
||||
cvc.Pos = Pos::Object_t::asChunkPos(oPos.ObjectPos);
|
||||
cvc.Range = 2*2;
|
||||
|
||||
std::vector<ContentViewCircle> newCVCs = Expanse.accumulateContentViewCircles(cvc);
|
||||
ContentViewInfo newCbg = Expanse_t::makeContentViewInfo(newCVCs);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
cec->ContentViewState = newCbg;
|
||||
cec->removeUnobservable(diff);
|
||||
}
|
||||
}
|
||||
|
||||
void GameServer::stepLoadRegions() {
|
||||
for(auto &iterWorld : Expanse.Worlds) {
|
||||
for(Pos::GlobalRegion pos : iterWorld.second->NeedToLoad) {
|
||||
forceGetRegion(iterWorld.first, pos);
|
||||
}
|
||||
|
||||
iterWorld.second->NeedToLoad.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void GameServer::stepGlobal() {
|
||||
|
||||
}
|
||||
|
||||
void GameServer::stepSave() {
|
||||
|
||||
}
|
||||
|
||||
void GameServer::save() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -181,14 +181,15 @@ private:
|
||||
Подпись на загруженные регионы (отправить полностью на клиент)
|
||||
*/
|
||||
|
||||
void stepDatabase();
|
||||
IWorldSaveBackend::TickSyncInfo_Out stepDatabaseSync();
|
||||
|
||||
/*
|
||||
Синхронизация с генератором карт (отправка запросов на генерацию и получение шума для обработки модами)
|
||||
Обработка модами сырых регионов полученных с бд
|
||||
Синхронизация с потоками модов
|
||||
*/
|
||||
|
||||
void stepLuaAsync();
|
||||
void stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db);
|
||||
|
||||
/*
|
||||
Получить пакеты с игроков
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace LV::Server {
|
||||
/*
|
||||
Обменная единица мира
|
||||
*/
|
||||
struct SB_Region {
|
||||
struct SB_Region_In {
|
||||
// Список вокселей всех чанков
|
||||
std::unordered_map<Pos::bvec4u, VoxelCube> Voxels;
|
||||
// Привязка вокселей к ключу профиля
|
||||
@@ -29,20 +29,28 @@ struct SB_Region {
|
||||
std::vector<std::pair<DefEntityId_t, std::string>> EntityMap;
|
||||
};
|
||||
|
||||
struct DB_Region_Out {
|
||||
std::vector<VoxelCube_Region> Voxels;
|
||||
std::array<std::array<Node, 16*16*16>, 4*4*4> Nodes;
|
||||
std::vector<Entity> Entityes;
|
||||
|
||||
std::vector<std::string> VoxelIdToKey, NodeIdToKey, EntityToKey;
|
||||
};
|
||||
|
||||
class IWorldSaveBackend {
|
||||
public:
|
||||
virtual ~IWorldSaveBackend();
|
||||
|
||||
struct TickSyncInfo_In {
|
||||
// Для загрузки и более не используемые (регионы автоматически подгружаются по списку загруженных)
|
||||
std::vector<Pos::GlobalRegion> Load, Unload;
|
||||
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> Load, Unload;
|
||||
// Регионы для сохранения
|
||||
std::vector<std::pair<Pos::GlobalRegion, std::unique_ptr<SB_Region>>> ToSave;
|
||||
std::unordered_map<WorldId_t, std::vector<std::pair<Pos::GlobalRegion, SB_Region_In>>> ToSave;
|
||||
};
|
||||
|
||||
struct TickSyncInfo_Out {
|
||||
std::vector<Pos::GlobalRegion> NotExisten;
|
||||
std::vector<std::pair<Pos::GlobalRegion, std::unique_ptr<SB_Region>>> LoadedRegions;
|
||||
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> NotExisten;
|
||||
std::unordered_map<WorldId_t, std::vector<std::pair<Pos::GlobalRegion, DB_Region_Out>>> LoadedRegions;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -30,7 +30,6 @@ public:
|
||||
// В одно обновление региона - проверка одного наблюдателя
|
||||
uint16_t CEC_NextChunkAndEntityesViewCheck = 0;
|
||||
|
||||
bool IsLoaded = false;
|
||||
float LastSaveTime = 0;
|
||||
|
||||
void getCollideBoxes(Pos::GlobalRegion rPos, AABB aabb, std::vector<CollisionAABB> &boxes) {
|
||||
@@ -40,12 +39,6 @@ public:
|
||||
// Бокс региона
|
||||
AABB regionAABB(raPos, raPos+Pos::Object(Pos::Object_t::BS*64));
|
||||
|
||||
// Если регион не загружен, то он весь непроходим
|
||||
if(!IsLoaded) {
|
||||
boxes.emplace_back(regionAABB);
|
||||
return;
|
||||
}
|
||||
|
||||
// Собираем коробки сущностей
|
||||
for(size_t iter = 0; iter < Entityes.size(); iter++) {
|
||||
Entity &entity = Entityes[iter];
|
||||
@@ -92,13 +85,13 @@ public:
|
||||
aabbInfo.VecMin.set(axis, aabbInfo.VecMin[axis] & ~0xff00);
|
||||
aabbInfo.VecMax = aabbInfo.VecMin;
|
||||
|
||||
aabbInfo.VecMin.x |= int(cube.Left.x) << 8;
|
||||
aabbInfo.VecMin.y |= int(cube.Left.y) << 8;
|
||||
aabbInfo.VecMin.z |= int(cube.Left.z) << 8;
|
||||
aabbInfo.VecMin.x |= int(cube.Left.x) << 6;
|
||||
aabbInfo.VecMin.y |= int(cube.Left.y) << 6;
|
||||
aabbInfo.VecMin.z |= int(cube.Left.z) << 6;
|
||||
|
||||
aabbInfo.VecMax.x |= int(cube.Right.x) << 8;
|
||||
aabbInfo.VecMax.y |= int(cube.Right.y) << 8;
|
||||
aabbInfo.VecMax.z |= int(cube.Right.z) << 8;
|
||||
aabbInfo.VecMax.x |= int(cube.Right.x) << 6;
|
||||
aabbInfo.VecMax.y |= int(cube.Right.y) << 6;
|
||||
aabbInfo.VecMax.z |= int(cube.Right.z) << 6;
|
||||
|
||||
if(aabb.isCollideWith(aabbInfo)) {
|
||||
aabbInfo = {
|
||||
@@ -139,22 +132,12 @@ public:
|
||||
// В регионе не осталось места
|
||||
return RegionEntityId_t(-1);
|
||||
}
|
||||
|
||||
void load(SB_Region *data) {
|
||||
convertRegionVoxelsToChunks(data->Voxels, Voxels);
|
||||
}
|
||||
|
||||
void save(SB_Region *data) {
|
||||
data->Voxels.clear();
|
||||
convertChunkVoxelsToRegion(Voxels, data->Voxels);
|
||||
}
|
||||
};
|
||||
|
||||
class World {
|
||||
DefWorldId_t DefId;
|
||||
|
||||
public:
|
||||
std::vector<Pos::GlobalRegion> NeedToLoad;
|
||||
std::unordered_map<Pos::GlobalRegion, std::unique_ptr<Region>> Regions;
|
||||
|
||||
public:
|
||||
@@ -166,9 +149,24 @@ public:
|
||||
*/
|
||||
void onUpdate(GameServer *server, float dtime);
|
||||
|
||||
// Игрок начал отслеживать регионы
|
||||
void onCEC_RegionsEnter(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &enter);
|
||||
void onCEC_RegionsLost(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &lost);
|
||||
/*
|
||||
Подписывает игрока на отслеживаемые им регионы
|
||||
Возвращает список не загруженных регионов, на которые соответственно игрока не получилось подписать
|
||||
*/
|
||||
std::vector<Pos::GlobalRegion> onCEC_RegionsEnter(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &enter);
|
||||
void onCEC_RegionsLost(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &lost);
|
||||
struct SaveUnloadInfo {
|
||||
std::vector<Pos::GlobalRegion> ToUnload;
|
||||
std::vector<std::pair<Pos::GlobalRegion, SB_Region_In>> ToSave;
|
||||
};
|
||||
SaveUnloadInfo onStepDatabaseSync();
|
||||
|
||||
struct RegionIn {
|
||||
std::unordered_map<Pos::bvec4u, std::vector<VoxelCube>> Voxels;
|
||||
std::array<std::array<Node, 16*16*16>, 4*4*4> Nodes;
|
||||
std::vector<Entity> Entityes;
|
||||
};
|
||||
void pushRegions(std::vector<std::pair<Pos::GlobalRegion, RegionIn>>);
|
||||
|
||||
DefWorldId_t getDefId() const { return DefId; }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user