stepDatabaseSync stepGeneratorAndLuaAsync
This commit is contained in:
@@ -349,6 +349,8 @@ using bvec4i = BitVec3<int8_t, 2>;
|
|||||||
using bvec4u = BitVec3<uint8_t, 2>;
|
using bvec4u = BitVec3<uint8_t, 2>;
|
||||||
using bvec16i = BitVec3<int8_t, 4>;
|
using bvec16i = BitVec3<int8_t, 4>;
|
||||||
using bvec16u = BitVec3<uint8_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 bvec256i = BitVec3<int8_t, 8>;
|
||||||
using bvec256u = BitVec3<uint8_t, 8>;
|
using bvec256u = BitVec3<uint8_t, 8>;
|
||||||
using bvec1024i = BitVec3<int16_t, 10>;
|
using bvec1024i = BitVec3<int16_t, 10>;
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
#include "Server/Abstract.hpp"
|
#include "Server/Abstract.hpp"
|
||||||
#include "Server/ContentEventController.hpp"
|
#include "Server/ContentEventController.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <boost/json/parse.hpp>
|
#include <boost/json/parse.hpp>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <glm/geometric.hpp>
|
#include <glm/geometric.hpp>
|
||||||
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -15,6 +17,7 @@
|
|||||||
#include "SaveBackends/Filesystem.hpp"
|
#include "SaveBackends/Filesystem.hpp"
|
||||||
#include "Server/SaveBackend.hpp"
|
#include "Server/SaveBackend.hpp"
|
||||||
#include "Server/World.hpp"
|
#include "Server/World.hpp"
|
||||||
|
#include "glm/gtc/noise.hpp"
|
||||||
|
|
||||||
namespace LV::Server {
|
namespace LV::Server {
|
||||||
|
|
||||||
@@ -441,7 +444,8 @@ void GameServer::stepModInitializations() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameServer::stepDatabase() {
|
IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
|
||||||
|
IWorldSaveBackend::TickSyncInfo_In toDB;
|
||||||
|
|
||||||
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
|
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
|
||||||
assert(cec);
|
assert(cec);
|
||||||
@@ -449,12 +453,180 @@ void GameServer::stepDatabase() {
|
|||||||
if(cec->CrossedBorder) {
|
if(cec->CrossedBorder) {
|
||||||
cec->CrossedBorder = false;
|
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() {
|
void GameServer::stepPlayerProceed() {
|
||||||
@@ -466,34 +638,11 @@ void GameServer::stepWorldPhysic() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GameServer::stepGlobalStep() {
|
void GameServer::stepGlobalStep() {
|
||||||
|
for(auto &pair : Expanse.Worlds)
|
||||||
|
pair.second->onUpdate(this, CurrentTickDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameServer::stepSyncContent() {
|
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()) {
|
||||||
@@ -534,45 +683,32 @@ void GameServer::stepSyncContent() {
|
|||||||
cec->Remote->informateBinFont(table);
|
cec->Remote->informateBinFont(table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void GameServer::stepSyncWithAsync() {
|
// Сбор запросов на ресурсы и профили + отправка пакетов игрокам
|
||||||
|
ResourceRequest full;
|
||||||
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
|
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
|
||||||
assert(cec);
|
full.insert(cec->Remote->pushPreparedPackets());
|
||||||
|
|
||||||
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.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() {
|
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) {
|
for(auto &pWorld : Expanse.Worlds) {
|
||||||
World &wobj = *pWorld.second;
|
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;
|
std::unordered_map<Pos::bvec4u, VoxelCube> Voxels;
|
||||||
// Привязка вокселей к ключу профиля
|
// Привязка вокселей к ключу профиля
|
||||||
@@ -29,20 +29,28 @@ struct SB_Region {
|
|||||||
std::vector<std::pair<DefEntityId_t, std::string>> EntityMap;
|
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 {
|
class IWorldSaveBackend {
|
||||||
public:
|
public:
|
||||||
virtual ~IWorldSaveBackend();
|
virtual ~IWorldSaveBackend();
|
||||||
|
|
||||||
struct TickSyncInfo_In {
|
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 {
|
struct TickSyncInfo_Out {
|
||||||
std::vector<Pos::GlobalRegion> NotExisten;
|
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> NotExisten;
|
||||||
std::vector<std::pair<Pos::GlobalRegion, std::unique_ptr<SB_Region>>> LoadedRegions;
|
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;
|
uint16_t CEC_NextChunkAndEntityesViewCheck = 0;
|
||||||
|
|
||||||
bool IsLoaded = false;
|
|
||||||
float LastSaveTime = 0;
|
float LastSaveTime = 0;
|
||||||
|
|
||||||
void getCollideBoxes(Pos::GlobalRegion rPos, AABB aabb, std::vector<CollisionAABB> &boxes) {
|
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));
|
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++) {
|
for(size_t iter = 0; iter < Entityes.size(); iter++) {
|
||||||
Entity &entity = Entityes[iter];
|
Entity &entity = Entityes[iter];
|
||||||
@@ -92,13 +85,13 @@ public:
|
|||||||
aabbInfo.VecMin.set(axis, aabbInfo.VecMin[axis] & ~0xff00);
|
aabbInfo.VecMin.set(axis, aabbInfo.VecMin[axis] & ~0xff00);
|
||||||
aabbInfo.VecMax = aabbInfo.VecMin;
|
aabbInfo.VecMax = aabbInfo.VecMin;
|
||||||
|
|
||||||
aabbInfo.VecMin.x |= int(cube.Left.x) << 8;
|
aabbInfo.VecMin.x |= int(cube.Left.x) << 6;
|
||||||
aabbInfo.VecMin.y |= int(cube.Left.y) << 8;
|
aabbInfo.VecMin.y |= int(cube.Left.y) << 6;
|
||||||
aabbInfo.VecMin.z |= int(cube.Left.z) << 8;
|
aabbInfo.VecMin.z |= int(cube.Left.z) << 6;
|
||||||
|
|
||||||
aabbInfo.VecMax.x |= int(cube.Right.x) << 8;
|
aabbInfo.VecMax.x |= int(cube.Right.x) << 6;
|
||||||
aabbInfo.VecMax.y |= int(cube.Right.y) << 8;
|
aabbInfo.VecMax.y |= int(cube.Right.y) << 6;
|
||||||
aabbInfo.VecMax.z |= int(cube.Right.z) << 8;
|
aabbInfo.VecMax.z |= int(cube.Right.z) << 6;
|
||||||
|
|
||||||
if(aabb.isCollideWith(aabbInfo)) {
|
if(aabb.isCollideWith(aabbInfo)) {
|
||||||
aabbInfo = {
|
aabbInfo = {
|
||||||
@@ -139,22 +132,12 @@ public:
|
|||||||
// В регионе не осталось места
|
// В регионе не осталось места
|
||||||
return RegionEntityId_t(-1);
|
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 {
|
class World {
|
||||||
DefWorldId_t DefId;
|
DefWorldId_t DefId;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::vector<Pos::GlobalRegion> NeedToLoad;
|
|
||||||
std::unordered_map<Pos::GlobalRegion, std::unique_ptr<Region>> Regions;
|
std::unordered_map<Pos::GlobalRegion, std::unique_ptr<Region>> Regions;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -166,9 +149,24 @@ public:
|
|||||||
*/
|
*/
|
||||||
void onUpdate(GameServer *server, float dtime);
|
void onUpdate(GameServer *server, float dtime);
|
||||||
|
|
||||||
// Игрок начал отслеживать регионы
|
/*
|
||||||
void onCEC_RegionsEnter(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &enter);
|
Подписывает игрока на отслеживаемые им регионы
|
||||||
|
Возвращает список не загруженных регионов, на которые соответственно игрока не получилось подписать
|
||||||
|
*/
|
||||||
|
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);
|
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; }
|
DefWorldId_t getDefId() const { return DefId; }
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user