*
This commit is contained in:
@@ -385,7 +385,7 @@ struct Object_t {
|
||||
}
|
||||
|
||||
|
||||
using ResourceId_t = uint32_t;
|
||||
using ResourceId = uint32_t;
|
||||
|
||||
/*
|
||||
Объекты, собранные из папки assets или зарегистрированные модами.
|
||||
@@ -399,13 +399,13 @@ enum class EnumAssets {
|
||||
Nodestate, Patricle, Animation, Model, Texture, Sound, Font, MAX_ENUM
|
||||
};
|
||||
|
||||
using AssetsNodestate = ResourceId_t;
|
||||
using AssetsParticle = ResourceId_t;
|
||||
using AssetsAnimation = ResourceId_t;
|
||||
using AssetsModel = ResourceId_t;
|
||||
using AssetsTexture = ResourceId_t;
|
||||
using AssetsSound = ResourceId_t;
|
||||
using AssetsFont = ResourceId_t;
|
||||
using AssetsNodestate = ResourceId;
|
||||
using AssetsParticle = ResourceId;
|
||||
using AssetsAnimation = ResourceId;
|
||||
using AssetsModel = ResourceId;
|
||||
using AssetsTexture = ResourceId;
|
||||
using AssetsSound = ResourceId;
|
||||
using AssetsFont = ResourceId;
|
||||
|
||||
using BinaryResource = std::shared_ptr<const std::u8string>;
|
||||
|
||||
@@ -416,19 +416,19 @@ enum class EnumDefContent {
|
||||
Voxel, Node, World, Portal, Entity, Item, MAX_ENUM
|
||||
};
|
||||
|
||||
using DefVoxelId_t = ResourceId_t;
|
||||
using DefNodeId_t = ResourceId_t;
|
||||
using DefWorldId_t = ResourceId_t;
|
||||
using DefPortalId_t = ResourceId_t;
|
||||
using DefEntityId_t = ResourceId_t;
|
||||
using DefItemId_t = ResourceId_t;
|
||||
using DefVoxelId = ResourceId;
|
||||
using DefNodeId = ResourceId;
|
||||
using DefWorldId = ResourceId;
|
||||
using DefPortalId = ResourceId;
|
||||
using DefEntityId = ResourceId;
|
||||
using DefItemId = ResourceId;
|
||||
|
||||
/*
|
||||
Контент, основанный на определениях.
|
||||
Отдельные свойства могут менятся в самих объектах
|
||||
*/
|
||||
|
||||
using WorldId_t = ResourceId_t;
|
||||
using WorldId_t = ResourceId;
|
||||
|
||||
// struct LightPrism {
|
||||
// uint8_t R : 2, G : 2, B : 2;
|
||||
@@ -439,10 +439,10 @@ using WorldId_t = ResourceId_t;
|
||||
struct VoxelCube {
|
||||
union {
|
||||
struct {
|
||||
DefVoxelId_t VoxelId : 24, Meta : 8;
|
||||
DefVoxelId VoxelId : 24, Meta : 8;
|
||||
};
|
||||
|
||||
DefVoxelId_t Data = 0;
|
||||
DefVoxelId Data = 0;
|
||||
};
|
||||
|
||||
Pos::bvec256u Pos, Size; // Размер+1, 0 это единичный размер
|
||||
@@ -465,7 +465,7 @@ struct VoxelCube {
|
||||
struct CompressedVoxels {
|
||||
std::u8string Compressed;
|
||||
// Уникальный сортированный список идентификаторов вокселей
|
||||
std::vector<DefVoxelId_t> Defines;
|
||||
std::vector<DefVoxelId> Defines;
|
||||
};
|
||||
|
||||
CompressedVoxels compressVoxels(const std::vector<VoxelCube>& voxels, bool fast = true);
|
||||
@@ -474,16 +474,16 @@ std::vector<VoxelCube> unCompressVoxels(const std::u8string& compressed);
|
||||
struct Node {
|
||||
union {
|
||||
struct {
|
||||
DefNodeId_t NodeId : 24, Meta : 8;
|
||||
DefNodeId NodeId : 24, Meta : 8;
|
||||
};
|
||||
DefNodeId_t Data;
|
||||
DefNodeId Data;
|
||||
};
|
||||
};
|
||||
|
||||
struct CompressedNodes {
|
||||
std::u8string Compressed;
|
||||
// Уникальный сортированный список идентификаторов нод
|
||||
std::vector<DefNodeId_t> Defines;
|
||||
std::vector<DefNodeId> Defines;
|
||||
};
|
||||
|
||||
CompressedNodes compressNodes(const Node* nodes, bool fast = true);
|
||||
|
||||
@@ -388,5 +388,14 @@ NodeStateCondition nodestateExpression(const std::vector<NodestateEntry>& entrie
|
||||
|
||||
return ct;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -432,4 +432,105 @@ inline void convertChunkVoxelsToRegion(const std::unordered_map<Pos::bvec4u, std
|
||||
regions = VoxelCuboidsFuncs<VoxelCube_Region>::optimizeVoxelRegions(regions);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -22,9 +22,9 @@ AssetsManager::AssetsManager(asio::io_context& ioc)
|
||||
|
||||
AssetsManager::~AssetsManager() = default;
|
||||
|
||||
std::tuple<ResourceId_t, std::optional<AssetsManager::DataEntry>&> AssetsManager::Local::nextId(EnumAssets type) {
|
||||
std::tuple<ResourceId, std::optional<AssetsManager::DataEntry>&> AssetsManager::Local::nextId(EnumAssets type) {
|
||||
auto& table = Table[(int) type];
|
||||
ResourceId_t id = -1;
|
||||
ResourceId id = -1;
|
||||
std::optional<DataEntry> *data = nullptr;
|
||||
|
||||
for(size_t index = 0; index < table.size(); index++) {
|
||||
@@ -185,7 +185,7 @@ AssetsManager::Out_recheckResources AssetsManager::recheckResources(const Assets
|
||||
continue;
|
||||
else if(iterDomain != lock->KeyToId[type].end() && iterDomain->second.contains(key)) {
|
||||
// Ресурс уже есть, TODO: нужно проверить его изменение
|
||||
ResourceId_t id = iterDomain->second.at(key);
|
||||
ResourceId id = iterDomain->second.at(key);
|
||||
DataEntry& entry = *lock->Table[type][id / TableEntry::ChunkSize]->Entries[id % TableEntry::ChunkSize];
|
||||
|
||||
fs::file_time_type lwt = fs::last_write_time(file);
|
||||
@@ -229,7 +229,7 @@ AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const
|
||||
auto iter = keyToIdDomain.find(key);
|
||||
assert(iter != keyToIdDomain.end());
|
||||
|
||||
ResourceId_t resId = iter->second;
|
||||
ResourceId resId = iter->second;
|
||||
// keyToIdDomain.erase(iter);
|
||||
// lost[type].push_back(resId);
|
||||
|
||||
@@ -249,7 +249,7 @@ AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const
|
||||
auto& keyToIdDomain = lock->KeyToId[type][domain];
|
||||
|
||||
for(auto& [key, resource, lwt] : resources) {
|
||||
ResourceId_t id = -1;
|
||||
ResourceId id = -1;
|
||||
std::optional<DataEntry>* data = nullptr;
|
||||
|
||||
if(auto iterId = keyToIdDomain.find(key); iterId != keyToIdDomain.end()) {
|
||||
@@ -269,8 +269,8 @@ AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const
|
||||
}
|
||||
|
||||
// Удалённые идентификаторы не считаются удалёнными, если были изменены
|
||||
std::unordered_set<ResourceId_t> noc(result.NewOrChange[type].begin(), result.NewOrChange[type].end());
|
||||
std::unordered_set<ResourceId_t> l(result.Lost[type].begin(), result.Lost[type].end());
|
||||
std::unordered_set<ResourceId> noc(result.NewOrChange[type].begin(), result.NewOrChange[type].end());
|
||||
std::unordered_set<ResourceId> l(result.Lost[type].begin(), result.Lost[type].end());
|
||||
result.Lost[type].clear();
|
||||
std::set_difference(l.begin(), l.end(), noc.begin(), noc.end(), std::back_inserter(result.Lost[type]));
|
||||
}
|
||||
@@ -278,7 +278,7 @@ AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const
|
||||
return result;
|
||||
}
|
||||
|
||||
ResourceId_t AssetsManager::getId(EnumAssets type, const std::string& domain, const std::string& key) {
|
||||
ResourceId AssetsManager::getId(EnumAssets type, const std::string& domain, const std::string& key) {
|
||||
auto lock = LocalObj.lock();
|
||||
auto& keyToId = lock->KeyToId[(int) type];
|
||||
if(auto iterKTI = keyToId.find(domain); iterKTI != keyToId.end()) {
|
||||
|
||||
@@ -78,9 +78,9 @@ private:
|
||||
// Связь ресурсов по идентификаторам
|
||||
std::vector<std::unique_ptr<TableEntry>> Table[(int) EnumAssets::MAX_ENUM];
|
||||
// Связь домены -> {ключ -> идентификатор}
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, ResourceId_t>> KeyToId[(int) EnumAssets::MAX_ENUM];
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, ResourceId>> KeyToId[(int) EnumAssets::MAX_ENUM];
|
||||
|
||||
std::tuple<ResourceId_t, std::optional<DataEntry>&> nextId(EnumAssets type);
|
||||
std::tuple<ResourceId, std::optional<DataEntry>&> nextId(EnumAssets type);
|
||||
};
|
||||
|
||||
TOS::SpinlockObject<Local> LocalObj;
|
||||
@@ -133,8 +133,8 @@ public:
|
||||
Раздаёт идентификаторы ресурсам и записывает их в таблицу
|
||||
*/
|
||||
struct Out_applyResourceChange {
|
||||
std::vector<ResourceId_t> Lost[(int) EnumAssets::MAX_ENUM];
|
||||
std::vector<std::pair<ResourceId_t, Resource>> NewOrChange[(int) EnumAssets::MAX_ENUM];
|
||||
std::vector<ResourceId> Lost[(int) EnumAssets::MAX_ENUM];
|
||||
std::vector<std::pair<ResourceId, Resource>> NewOrChange[(int) EnumAssets::MAX_ENUM];
|
||||
};
|
||||
|
||||
Out_applyResourceChange applyResourceChange(const Out_recheckResources& orr);
|
||||
@@ -143,7 +143,7 @@ public:
|
||||
Выдаёт идентификатор ресурса, даже если он не существует или был удалён.
|
||||
resource должен содержать домен и путь
|
||||
*/
|
||||
ResourceId_t getId(EnumAssets type, const std::string& domain, const std::string& key);
|
||||
ResourceId getId(EnumAssets type, const std::string& domain, const std::string& key);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -20,124 +20,9 @@ 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;
|
||||
|
||||
ServerObjectPos Pos, LastPos;
|
||||
std::queue<Pos::GlobalNode> Build, Break;
|
||||
|
||||
public:
|
||||
@@ -172,12 +57,3 @@ 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<LV::Pos::Object>()(obj.ObjectPos);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,113 @@
|
||||
#include "ContentManager.hpp"
|
||||
#include "Common/Abstract.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace LV::Server {
|
||||
|
||||
|
||||
ContentManager::ContentManager(asio::io_context& ioc)
|
||||
{
|
||||
ContentManager::ContentManager(asio::io_context& ioc) {
|
||||
|
||||
}
|
||||
|
||||
ContentManager::~ContentManager() = default;
|
||||
|
||||
void ContentManager::registerBase_Node(ResourceId id, const sol::table& profile) {
|
||||
std::optional<DefNode>& node = getEntry_Node(id);
|
||||
if(!node)
|
||||
node.emplace();
|
||||
|
||||
DefNode& def = *node;
|
||||
|
||||
{
|
||||
std::variant<std::monostate, std::string, sol::table> parent = profile["parent"];
|
||||
if(const sol::table* table = std::get_if<sol::table>(&parent)) {
|
||||
// result = createNodeProfileByLua(*table);
|
||||
} else if(const std::string* key = std::get_if<std::string>(&parent)) {
|
||||
auto regResult = TOS::Str::match(*key, "(?:([\\w\\d_]+):)?([\\w\\d_]+)");
|
||||
if(!regResult)
|
||||
MAKE_ERROR("Недействительный ключ в определении parent");
|
||||
|
||||
std::string realKey;
|
||||
|
||||
if(regResult->at(1)) {
|
||||
realKey = *key;
|
||||
} else {
|
||||
realKey = "core:" + *regResult->at(2);
|
||||
}
|
||||
|
||||
DefNodeId_t parentId;
|
||||
|
||||
// {
|
||||
// auto& list = Content.ContentKeyToId[(int) EnumDefContent::Node];
|
||||
// auto iter = list.find(realKey);
|
||||
// if(iter == list.end())
|
||||
// MAKE_ERROR("Идентификатор parent не найден");
|
||||
|
||||
// parentId = iter->second;
|
||||
// }
|
||||
|
||||
// result = Content.ContentIdToDef_Node.at(parentId);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<sol::table> nodestate = profile["nodestate"];
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<sol::table> render = profile["render"];
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<sol::table> collision = profile["collision"];
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<sol::table> events = profile["events"];
|
||||
}
|
||||
|
||||
// result.NodeAdvancementFactory = profile["node_advancement_factory"];
|
||||
}
|
||||
|
||||
void ContentManager::registerBase(EnumDefContent type, const std::string& domain, const std::string& key, const sol::table& profile)
|
||||
{
|
||||
ResourceId id = getId(type, domain, key);
|
||||
ProfileChanges[(int) type].push_back(id);
|
||||
|
||||
if(type == EnumDefContent::Node)
|
||||
registerBase_Node(id, profile);
|
||||
else
|
||||
MAKE_ERROR("Не реализовано");
|
||||
}
|
||||
|
||||
void ContentManager::unRegisterBase(EnumDefContent type, const std::string& domain, const std::string& key)
|
||||
{
|
||||
ResourceId id = getId(type, domain, key);
|
||||
ProfileChanges[(int) type].push_back(id);
|
||||
}
|
||||
|
||||
void ContentManager::registerModifier(EnumDefContent type, const std::string& mod, const std::string& domain, const std::string& key, const sol::table& profile)
|
||||
{
|
||||
ResourceId id = getId(type, domain, key);
|
||||
ProfileChanges[(int) type].push_back(id);
|
||||
}
|
||||
|
||||
void ContentManager::unRegisterModifier(EnumDefContent type, const std::string& mod, const std::string& domain, const std::string& key)
|
||||
{
|
||||
ResourceId id = getId(type, domain, key);
|
||||
ProfileChanges[(int) type].push_back(id);
|
||||
}
|
||||
|
||||
ContentManager::Out_buildEndProfiles ContentManager::buildEndProfiles() {
|
||||
Out_buildEndProfiles result;
|
||||
|
||||
for(int type = 0; type < (int) EnumDefContent::MAX_ENUM; type++) {
|
||||
auto& keys = ProfileChanges[type];
|
||||
std::sort(keys.begin(), keys.end());
|
||||
auto iterErase = std::unique(keys.begin(), keys.end());
|
||||
keys.erase(iterErase, keys.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/Abstract.hpp"
|
||||
#include "Server/Abstract.hpp"
|
||||
#include "Server/AssetsManager.hpp"
|
||||
#include <sol/table.hpp>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace LV::Server {
|
||||
|
||||
struct DefVoxel {
|
||||
struct DefVoxel_Base { };
|
||||
struct DefNode_Base { };
|
||||
struct DefWorld_Base { };
|
||||
struct DefPortal_Base { };
|
||||
struct DefEntity_Base { };
|
||||
struct DefItem_Base { };
|
||||
|
||||
};
|
||||
struct DefVoxel_Mod { };
|
||||
struct DefNode_Mod { };
|
||||
struct DefWorld_Mod { };
|
||||
struct DefPortal_Mod { };
|
||||
struct DefEntity_Mod { };
|
||||
struct DefItem_Mod { };
|
||||
|
||||
struct DefNode {
|
||||
|
||||
};
|
||||
|
||||
struct DefWorld {
|
||||
|
||||
};
|
||||
|
||||
struct DefPortal {
|
||||
|
||||
};
|
||||
|
||||
struct DefEntity {
|
||||
|
||||
};
|
||||
|
||||
struct DefItem {
|
||||
|
||||
};
|
||||
struct DefVoxel { };
|
||||
struct DefNode { };
|
||||
struct DefWorld { };
|
||||
struct DefPortal { };
|
||||
struct DefEntity { };
|
||||
struct DefItem { };
|
||||
|
||||
class ContentManager {
|
||||
// Профили зарегистрированные модами
|
||||
|
||||
// Изменения, накладываемые на профили
|
||||
|
||||
// Следующие идентификаторы регистрации контента
|
||||
ResourceId_t NextId[(int) EnumDefContent::MAX_ENUM] = {0};
|
||||
// Домен -> {ключ -> идентификатор}
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, ResourceId_t>> ContentKeyToId[(int) EnumDefContent::MAX_ENUM];
|
||||
|
||||
template<typename T>
|
||||
struct TableEntry {
|
||||
static constexpr size_t ChunkSize = 4096;
|
||||
std::array<std::optional<T>, ChunkSize> Entries;
|
||||
};
|
||||
|
||||
|
||||
// Следующие идентификаторы регистрации контента
|
||||
ResourceId NextId[(int) EnumDefContent::MAX_ENUM] = {0};
|
||||
// Домен -> {ключ -> идентификатор}
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, ResourceId>> ContentKeyToId[(int) EnumDefContent::MAX_ENUM];
|
||||
|
||||
// Профили зарегистрированные модами
|
||||
std::vector<std::unique_ptr<TableEntry<DefVoxel>>> Profiles_Base_Voxel;
|
||||
std::vector<std::unique_ptr<TableEntry<DefNode>>> Profiles_Base_Node;
|
||||
std::vector<std::unique_ptr<TableEntry<DefWorld>>> Profiles_Base_World;
|
||||
std::vector<std::unique_ptr<TableEntry<DefPortal>>> Profiles_Base_Portal;
|
||||
std::vector<std::unique_ptr<TableEntry<DefEntity>>> Profiles_Base_Entity;
|
||||
std::vector<std::unique_ptr<TableEntry<DefItem>>> Profiles_Base_Item;
|
||||
|
||||
// Изменения, накладываемые на профили
|
||||
// Идентификатор [домен мода модификатора, модификатор]
|
||||
std::unordered_map<ResourceId, std::vector<std::tuple<std::string, DefVoxel_Mod>>> Profiles_Mod_Voxel;
|
||||
std::unordered_map<ResourceId, std::vector<std::tuple<std::string, DefNode_Mod>>> Profiles_Mod_Node;
|
||||
std::unordered_map<ResourceId, std::vector<std::tuple<std::string, DefWorld_Mod>>> Profiles_Mod_World;
|
||||
std::unordered_map<ResourceId, std::vector<std::tuple<std::string, DefPortal_Mod>>> Profiles_Mod_Portal;
|
||||
std::unordered_map<ResourceId, std::vector<std::tuple<std::string, DefEntity_Mod>>> Profiles_Mod_Entity;
|
||||
std::unordered_map<ResourceId, std::vector<std::tuple<std::string, DefItem_Mod>>> Profiles_Mod_Item;
|
||||
|
||||
// Затронутые профили в процессе регистраций
|
||||
// По ним будут пересобраны профили
|
||||
std::vector<ResourceId> ProfileChanges[(int) EnumDefContent::MAX_ENUM];
|
||||
|
||||
// Конечные профили контента
|
||||
std::vector<std::unique_ptr<TableEntry<DefVoxel>>> Profiles_Voxel;
|
||||
std::vector<std::unique_ptr<TableEntry<DefNode>>> Profiles_Node;
|
||||
@@ -53,10 +73,43 @@ class ContentManager {
|
||||
std::vector<std::unique_ptr<TableEntry<DefEntity>>> Profiles_Entity;
|
||||
std::vector<std::unique_ptr<TableEntry<DefItem>>> Profiles_Item;
|
||||
|
||||
std::optional<DefVoxel>& getEntry_Voxel(ResourceId resId) { return Profiles_Voxel[resId / TableEntry<DefVoxel>::ChunkSize]->Entries[resId % TableEntry<DefVoxel>::ChunkSize];}
|
||||
std::optional<DefNode>& getEntry_Node(ResourceId resId) { return Profiles_Node[resId / TableEntry<DefVoxel>::ChunkSize]->Entries[resId % TableEntry<DefVoxel>::ChunkSize];}
|
||||
std::optional<DefWorld>& getEntry_World(ResourceId resId) { return Profiles_World[resId / TableEntry<DefVoxel>::ChunkSize]->Entries[resId % TableEntry<DefVoxel>::ChunkSize];}
|
||||
std::optional<DefPortal>& getEntry_Portal(ResourceId resId) { return Profiles_Portal[resId / TableEntry<DefVoxel>::ChunkSize]->Entries[resId % TableEntry<DefVoxel>::ChunkSize];}
|
||||
std::optional<DefEntity>& getEntry_Entity(ResourceId resId) { return Profiles_Entity[resId / TableEntry<DefVoxel>::ChunkSize]->Entries[resId % TableEntry<DefVoxel>::ChunkSize];}
|
||||
std::optional<DefItem>& getEntry_Item(ResourceId resId) { return Profiles_Item[resId / TableEntry<DefVoxel>::ChunkSize]->Entries[resId % TableEntry<DefVoxel>::ChunkSize];}
|
||||
|
||||
ResourceId getId(EnumDefContent type, const std::string& domain, const std::string& key) {
|
||||
if(auto iterCKTI = ContentKeyToId[(int) type].find(domain); iterCKTI != ContentKeyToId[(int) type].end()) {
|
||||
if(auto iterKey = iterCKTI->second.find(key); iterKey != iterCKTI->second.end()) {
|
||||
return iterKey->second;
|
||||
}
|
||||
}
|
||||
|
||||
ResourceId resId = NextId[(int) type]++;
|
||||
ContentKeyToId[(int) type][domain][key] = resId;
|
||||
return resId;
|
||||
}
|
||||
|
||||
void registerBase_Node(ResourceId id, const sol::table& profile);
|
||||
|
||||
public:
|
||||
ContentManager(asio::io_context& ioc);
|
||||
~ContentManager();
|
||||
|
||||
// Регистрирует определение контента
|
||||
void registerBase(EnumDefContent type, const std::string& domain, const std::string& key, const sol::table& profile);
|
||||
void unRegisterBase(EnumDefContent type, const std::string& domain, const std::string& key);
|
||||
// Регистрация модификатора предмета модом
|
||||
void registerModifier(EnumDefContent type, const std::string& mod, const std::string& domain, const std::string& key, const sol::table& profile);
|
||||
void unRegisterModifier(EnumDefContent type, const std::string& mod, const std::string& domain, const std::string& key);
|
||||
// Компилирует изменённые профили
|
||||
struct Out_buildEndProfiles {
|
||||
std::vector<ResourceId> ChangedProfiles[(int) EnumDefContent::MAX_ENUM];
|
||||
};
|
||||
|
||||
Out_buildEndProfiles buildEndProfiles();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Common/Packets.hpp"
|
||||
#include "Server/Abstract.hpp"
|
||||
#include "Server/AssetsManager.hpp"
|
||||
#include "Server/ContentEventController.hpp"
|
||||
#include "Server/RemoteClient.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <boost/json/parse.hpp>
|
||||
@@ -526,7 +526,7 @@ void GameServer::BackingChunkPressure_t::run(int id) {
|
||||
size_t counter = 0;
|
||||
|
||||
struct Dump {
|
||||
std::vector<std::shared_ptr<ContentEventController>> CECs, NewCECs;
|
||||
std::vector<std::shared_ptr<RemoteClient>> CECs, NewCECs;
|
||||
std::unordered_map<Pos::bvec4u, std::vector<VoxelCube>> Voxels;
|
||||
std::unordered_map<Pos::bvec4u, std::array<Node, 16*16*16>> Nodes;
|
||||
uint64_t IsChunkChanged_Nodes, IsChunkChanged_Voxels;
|
||||
@@ -628,10 +628,10 @@ void GameServer::BackingChunkPressure_t::run(int id) {
|
||||
CompressedNodes Data;
|
||||
};
|
||||
|
||||
std::list<std::pair<PostponedV, std::vector<ContentEventController*>>> postponedVoxels;
|
||||
std::list<std::pair<PostponedN, std::vector<ContentEventController*>>> postponedNodes;
|
||||
std::list<std::pair<PostponedV, std::vector<RemoteClient*>>> postponedVoxels;
|
||||
std::list<std::pair<PostponedN, std::vector<RemoteClient*>>> postponedNodes;
|
||||
|
||||
std::vector<ContentEventController*> cecs;
|
||||
std::vector<RemoteClient*> cecs;
|
||||
|
||||
for(auto& [worldId, world] : dump) {
|
||||
for(auto& [regionPos, region] : world) {
|
||||
@@ -720,7 +720,7 @@ void GameServer::BackingChunkPressure_t::run(int id) {
|
||||
auto begin = postponedVoxels.begin(), end = postponedVoxels.end();
|
||||
while(begin != end) {
|
||||
auto& [worldId, chunkPos, cmp] = begin->first;
|
||||
for(ContentEventController* cec : begin->second) {
|
||||
for(RemoteClient* cec : begin->second) {
|
||||
bool accepted = cec->Remote->maybe_prepareChunkUpdate_Voxels(worldId, chunkPos, cmp.Compressed, cmp.Defines);
|
||||
if(!accepted)
|
||||
cecs.push_back(cec);
|
||||
@@ -740,7 +740,7 @@ void GameServer::BackingChunkPressure_t::run(int id) {
|
||||
auto begin = postponedNodes.begin(), end = postponedNodes.end();
|
||||
while(begin != end) {
|
||||
auto& [worldId, chunkPos, cmp] = begin->first;
|
||||
for(ContentEventController* cec : begin->second) {
|
||||
for(RemoteClient* cec : begin->second) {
|
||||
bool accepted = cec->Remote->maybe_prepareChunkUpdate_Nodes(worldId, chunkPos, cmp.Compressed, cmp.Defines);
|
||||
if(!accepted)
|
||||
cecs.push_back(cec);
|
||||
@@ -1138,8 +1138,8 @@ TexturePipeline GameServer::buildTexturePipeline(const std::string& pl) {
|
||||
default:empty^(default:dirt.png^our_tech:machine.png)
|
||||
*/
|
||||
|
||||
std::map<std::string, BinTextureId_t> stbt;
|
||||
std::unordered_set<BinTextureId_t> btis;
|
||||
std::map<std::string, AssetsTexture> stbt;
|
||||
std::unordered_set<AssetsTexture> btis;
|
||||
std::string alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
// Парсер группы. Возвращает позицию на которой закончил и скомпилированный код
|
||||
@@ -1236,7 +1236,7 @@ TexturePipeline GameServer::buildTexturePipeline(const std::string& pl) {
|
||||
texture_name += pl[pos];
|
||||
}
|
||||
|
||||
BinTextureId_t id = stbt[texture_name];
|
||||
AssetsTexture id = stbt[texture_name];
|
||||
btis.insert(id);
|
||||
|
||||
for(int iter = 0; iter < 4; iter++)
|
||||
@@ -1256,7 +1256,7 @@ TexturePipeline GameServer::buildTexturePipeline(const std::string& pl) {
|
||||
MAKE_ERROR("Неожиданное продолжение " << pos);
|
||||
}
|
||||
|
||||
return {std::vector<BinTextureId_t>(btis.begin(), btis.end()), cmd};
|
||||
return {std::vector<AssetsTexture>(btis.begin(), btis.end()), cmd};
|
||||
}
|
||||
|
||||
std::string GameServer::deBuildTexturePipeline(const TexturePipeline& pipeline) {
|
||||
@@ -1513,7 +1513,7 @@ void GameServer::run() {
|
||||
|
||||
if(IsGoingShutdown) {
|
||||
// Отключить игроков
|
||||
for(std::shared_ptr<ContentEventController> &cec : Game.CECs) {
|
||||
for(std::shared_ptr<RemoteClient> &cec : Game.CECs) {
|
||||
cec->Remote->shutdown(EnumDisconnect::ByInterface, ShutdownReason);
|
||||
}
|
||||
|
||||
@@ -1576,62 +1576,6 @@ void GameServer::run() {
|
||||
LOG.info() << "Сервер завершил работу";
|
||||
}
|
||||
|
||||
DefNode_t GameServer::createNodeProfileByLua(const sol::table& profile) {
|
||||
DefNode_t result;
|
||||
|
||||
{
|
||||
std::variant<std::monostate, std::string, sol::table> parent = profile["parent"];
|
||||
if(const sol::table* table = std::get_if<sol::table>(&parent)) {
|
||||
result = createNodeProfileByLua(*table);
|
||||
} else if(const std::string* key = std::get_if<std::string>(&parent)) {
|
||||
auto regResult = TOS::Str::match(*key, "(?:([\\w\\d_]+):)?([\\w\\d_]+)");
|
||||
if(!regResult)
|
||||
MAKE_ERROR("Недействительный ключ в определении parent");
|
||||
|
||||
std::string realKey;
|
||||
|
||||
if(regResult->at(1)) {
|
||||
realKey = *key;
|
||||
} else {
|
||||
realKey = "core:" + *regResult->at(2);
|
||||
}
|
||||
|
||||
DefNodeId_t parentId;
|
||||
|
||||
{
|
||||
auto& list = Content.ContentKeyToId[(int) EnumDefContent::Node];
|
||||
auto iter = list.find(realKey);
|
||||
if(iter == list.end())
|
||||
MAKE_ERROR("Идентификатор parent не найден");
|
||||
|
||||
parentId = iter->second;
|
||||
}
|
||||
|
||||
result = Content.ContentIdToDef_Node.at(parentId);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<sol::table> nodestate = profile["nodestate"];
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<sol::table> render = profile["render"];
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<sol::table> collision = profile["collision"];
|
||||
}
|
||||
|
||||
{
|
||||
std::optional<sol::table> events = profile["events"];
|
||||
}
|
||||
|
||||
result.NodeAdvancementFactory = profile["node_advancement_factory"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void GameServer::initLuaAssets() {
|
||||
auto &lua = LuaMainState;
|
||||
std::optional<sol::table> core = lua["core"];
|
||||
@@ -1644,7 +1588,7 @@ void GameServer::initLuaAssets() {
|
||||
std::optional<std::vector<std::optional<std::string>>> result_o = TOS::Str::match(key, "^(?:([\\w\\d_]+):)?([\\w\\d_]+)$");
|
||||
|
||||
if(!result_o) {
|
||||
MAKE_ERROR("Недействительный идентификатор ноды: " << key);
|
||||
MAKE_ERROR("Недействительный идентификатор: " << key);
|
||||
}
|
||||
|
||||
auto &result = *result_o;
|
||||
@@ -1661,8 +1605,6 @@ void GameServer::initLuaAssets() {
|
||||
core->set_function("register_texture", std::bind(reg, EnumAssets::Texture, std::placeholders::_1, std::placeholders::_2));
|
||||
core->set_function("register_sound", std::bind(reg, EnumAssets::Sound, std::placeholders::_1, std::placeholders::_2));
|
||||
core->set_function("register_font", std::bind(reg, EnumAssets::Font, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
void GameServer::initLuaPre() {
|
||||
@@ -1677,25 +1619,28 @@ void GameServer::initLuaPre() {
|
||||
"register_model", "register_texture", "register_sound", "register_font"})
|
||||
core.set_function(name, lambdaError);
|
||||
|
||||
|
||||
|
||||
core.set_function("register_node", [&](const std::string& id, const sol::table& profile) {
|
||||
std::optional<std::vector<std::optional<std::string>>> result_o = TOS::Str::match(id, "^(?:([\\w\\d_]+):)?([\\w\\d_]+)$");
|
||||
std::function<void(EnumDefContent, const std::string&, const sol::table&)> reg
|
||||
= [this](EnumDefContent type, const std::string& key, const sol::table& profile)
|
||||
{
|
||||
std::optional<std::vector<std::optional<std::string>>> result_o = TOS::Str::match(key, "^(?:([\\w\\d_]+):)?([\\w\\d_]+)$");
|
||||
|
||||
if(!result_o) {
|
||||
MAKE_ERROR("Недействительный идентификатор ноды: " << id);
|
||||
MAKE_ERROR("Недействительный идентификатор: " << key);
|
||||
}
|
||||
|
||||
auto &result = *result_o;
|
||||
ResourceId_t sId;
|
||||
|
||||
if(result[1])
|
||||
sId = Content.registerContent(id, EnumDefContent::Node);
|
||||
Content.CM.registerBase(type, *result[1], *result[2], profile);
|
||||
else
|
||||
sId = Content.registerContent(CurrentModId+":"+*result[2], EnumDefContent::Node);
|
||||
Content.CM.registerBase(type, CurrentModId, *result[2], profile);
|
||||
};
|
||||
|
||||
Content.ContentIdToDef_Node.insert({sId, createNodeProfileByLua(profile)});
|
||||
});
|
||||
core.set_function("register_voxel", std::bind(reg, EnumDefContent::Voxel, std::placeholders::_1, std::placeholders::_2));
|
||||
core.set_function("register_node", std::bind(reg, EnumDefContent::Node, std::placeholders::_1, std::placeholders::_2));
|
||||
core.set_function("register_world", std::bind(reg, EnumDefContent::World, std::placeholders::_1, std::placeholders::_2));
|
||||
core.set_function("register_portal", std::bind(reg, EnumDefContent::Portal, std::placeholders::_1, std::placeholders::_2));
|
||||
core.set_function("register_entity", std::bind(reg, EnumDefContent::Entity, std::placeholders::_1, std::placeholders::_2));
|
||||
core.set_function("register_item", std::bind(reg, EnumDefContent::Item, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void GameServer::initLua() {
|
||||
@@ -1707,8 +1652,10 @@ void GameServer::initLua() {
|
||||
luaL_error(L.lua_state(), "Данная функция может использоваться только в стадии [preInit]");
|
||||
};
|
||||
|
||||
for(const char* name : {"register_node"})
|
||||
for(const char* name : {"register_voxel", "register_node", "register_world", "register_portal", "register_entity", "register_item"})
|
||||
core.set_function(name, lambdaError);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GameServer::initLuaPost() {
|
||||
@@ -1722,7 +1669,7 @@ void GameServer::stepConnections() {
|
||||
|
||||
for(std::unique_ptr<RemoteClient>& client : *lock) {
|
||||
co_spawn(client->run());
|
||||
Game.CECs.push_back(std::make_unique<ContentEventController>(std::move(client)));
|
||||
Game.CECs.push_back(std::make_unique<RemoteClient>(std::move(client)));
|
||||
}
|
||||
|
||||
lock->clear();
|
||||
@@ -1731,7 +1678,7 @@ void GameServer::stepConnections() {
|
||||
BackingChunkPressure.endCollectChanges();
|
||||
|
||||
// Отключение игроков
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// Убрать отключившихся
|
||||
if(!cec->Remote->isConnected()) {
|
||||
// Отписываем наблюдателя от миров
|
||||
@@ -1751,7 +1698,7 @@ void GameServer::stepConnections() {
|
||||
|
||||
// Вычистить невалидные ссылки на игроков
|
||||
Game.CECs.erase(std::remove_if(Game.CECs.begin(), Game.CECs.end(),
|
||||
[](const std::shared_ptr<ContentEventController>& ptr) { return !ptr; }),
|
||||
[](const std::shared_ptr<RemoteClient>& ptr) { return !ptr; }),
|
||||
Game.CECs.end());
|
||||
}
|
||||
|
||||
@@ -1762,7 +1709,7 @@ void GameServer::stepModInitializations() {
|
||||
IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
|
||||
IWorldSaveBackend::TickSyncInfo_In toDB;
|
||||
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
assert(cec);
|
||||
// Пересчитать зоны наблюдения
|
||||
if(cec->CrossedBorder) {
|
||||
@@ -1906,7 +1853,7 @@ void GameServer::stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db
|
||||
newRegions.push_back(pos);
|
||||
std::sort(newRegions.begin(), newRegions.end());
|
||||
|
||||
std::unordered_map<std::shared_ptr<ContentEventController>, std::vector<Pos::GlobalRegion>> toSubscribe;
|
||||
std::unordered_map<std::shared_ptr<RemoteClient>, std::vector<Pos::GlobalRegion>> toSubscribe;
|
||||
|
||||
for(auto& cec : Game.CECs) {
|
||||
auto iterViewWorld = cec->ContentViewState.Regions.find(worldId);
|
||||
@@ -2117,7 +2064,7 @@ void GameServer::stepWorldPhysic() {
|
||||
// } else {
|
||||
// entity.IsRemoved = true;
|
||||
// // Сообщаем о перемещении сущности
|
||||
// for(ContentEventController *cec : region.CECs) {
|
||||
// for(RemoteClient *cec : region.CECs) {
|
||||
// cec->onEntitySwap(pWorld.first, pRegion.first, entityIndex, entity.WorldId, rPos, newId);
|
||||
// }
|
||||
// }
|
||||
@@ -2177,7 +2124,7 @@ void GameServer::stepWorldPhysic() {
|
||||
// // Пробегаемся по всем наблюдателям
|
||||
// {
|
||||
// size_t cecIndex = 0;
|
||||
// for(ContentEventController *cec : region.CECs) {
|
||||
// for(RemoteClient *cec : region.CECs) {
|
||||
// cecIndex++;
|
||||
|
||||
// auto cvwIter = cec->ContentViewState.find(pWorld.first);
|
||||
@@ -2377,7 +2324,7 @@ void GameServer::stepGlobalStep() {
|
||||
}
|
||||
|
||||
void GameServer::stepSyncContent() {
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
cec->onUpdate();
|
||||
|
||||
// Это для пробы строительства и ломания нод
|
||||
@@ -2411,124 +2358,124 @@ void GameServer::stepSyncContent() {
|
||||
|
||||
// Сбор запросов на ресурсы и профили + отправка пакетов игрокам
|
||||
ResourceRequest full = std::move(Content.OnContentChanges);
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
full.insert(cec->Remote->pushPreparedPackets());
|
||||
}
|
||||
|
||||
|
||||
full.uniq();
|
||||
// Запрашиваем двоичные ресурсы
|
||||
Content.BRM.needResourceResponse(full);
|
||||
Content.BRM.update(CurrentTickDuration);
|
||||
// Content.BRM.needResourceResponse(full);
|
||||
// Content.BRM.update(CurrentTickDuration);
|
||||
// Получаем готовую информацию
|
||||
{
|
||||
BinaryResourceManager::OutObj_t out = Content.BRM.takePreparedInformation();
|
||||
bool hasData = false;
|
||||
for(int iter = 0; iter < (int) EnumBinResource::MAX_ENUM; iter++)
|
||||
hasData |= !out.BinToHash[iter].empty();
|
||||
// {
|
||||
// BinaryResourceManager::OutObj_t out = Content.BRM.takePreparedInformation();
|
||||
// bool hasData = false;
|
||||
// for(int iter = 0; iter < (int) EnumBinResource::MAX_ENUM; iter++)
|
||||
// hasData |= !out.BinToHash[iter].empty();
|
||||
|
||||
if(hasData) {
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
cec->Remote->informateIdToHash(out.BinToHash);
|
||||
}
|
||||
}
|
||||
// if(hasData) {
|
||||
// for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// cec->Remote->informateIdToHash(out.BinToHash);
|
||||
// }
|
||||
// }
|
||||
|
||||
if(!out.HashToResource.empty())
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
cec->Remote->informateBinary(out.HashToResource);
|
||||
}
|
||||
}
|
||||
// if(!out.HashToResource.empty())
|
||||
// for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// cec->Remote->informateBinary(out.HashToResource);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Оповещаем об игровых профилях
|
||||
if(!full.Voxel.empty()) {
|
||||
std::unordered_map<DefVoxelId_t, DefVoxel_t*> defines;
|
||||
// if(!full.Voxel.empty()) {
|
||||
// std::unordered_map<DefVoxelId_t, DefVoxel_t*> defines;
|
||||
|
||||
for(DefVoxelId_t id : full.Node) {
|
||||
auto iter = Content.ContentIdToDef_Voxel.find(id);
|
||||
if(iter != Content.ContentIdToDef_Voxel.end()) {
|
||||
defines[id] = &iter->second;
|
||||
}
|
||||
}
|
||||
// for(DefVoxelId_t id : full.Node) {
|
||||
// auto iter = Content.ContentIdToDef_Voxel.find(id);
|
||||
// if(iter != Content.ContentIdToDef_Voxel.end()) {
|
||||
// defines[id] = &iter->second;
|
||||
// }
|
||||
// }
|
||||
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
cec->Remote->informateDefVoxel(defines);
|
||||
}
|
||||
}
|
||||
// for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// cec->Remote->informateDefVoxel(defines);
|
||||
// }
|
||||
// }
|
||||
|
||||
if(!full.Node.empty()) {
|
||||
std::unordered_map<DefNodeId_t, DefNode_t*> defines;
|
||||
// if(!full.Node.empty()) {
|
||||
// std::unordered_map<DefNodeId_t, DefNode_t*> defines;
|
||||
|
||||
for(DefNodeId_t id : full.Node) {
|
||||
auto iter = Content.ContentIdToDef_Node.find(id);
|
||||
if(iter != Content.ContentIdToDef_Node.end()) {
|
||||
defines[id] = &iter->second;
|
||||
}
|
||||
}
|
||||
// for(DefNodeId_t id : full.Node) {
|
||||
// auto iter = Content.ContentIdToDef_Node.find(id);
|
||||
// if(iter != Content.ContentIdToDef_Node.end()) {
|
||||
// defines[id] = &iter->second;
|
||||
// }
|
||||
// }
|
||||
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
cec->Remote->informateDefNode(defines);
|
||||
}
|
||||
}
|
||||
// for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// cec->Remote->informateDefNode(defines);
|
||||
// }
|
||||
// }
|
||||
|
||||
if(!full.World.empty()) {
|
||||
std::unordered_map<DefWorldId_t, DefWorld_t*> defines;
|
||||
// if(!full.World.empty()) {
|
||||
// std::unordered_map<DefWorldId_t, DefWorld_t*> defines;
|
||||
|
||||
for(DefWorldId_t id : full.Node) {
|
||||
auto iter = Content.ContentIdToDef_World.find(id);
|
||||
if(iter != Content.ContentIdToDef_World.end()) {
|
||||
defines[id] = &iter->second;
|
||||
}
|
||||
}
|
||||
// for(DefWorldId_t id : full.Node) {
|
||||
// auto iter = Content.ContentIdToDef_World.find(id);
|
||||
// if(iter != Content.ContentIdToDef_World.end()) {
|
||||
// defines[id] = &iter->second;
|
||||
// }
|
||||
// }
|
||||
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
cec->Remote->informateDefWorld(defines);
|
||||
}
|
||||
}
|
||||
// for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// cec->Remote->informateDefWorld(defines);
|
||||
// }
|
||||
// }
|
||||
|
||||
if(!full.Portal.empty()) {
|
||||
std::unordered_map<DefPortalId_t, DefPortal_t*> defines;
|
||||
// if(!full.Portal.empty()) {
|
||||
// std::unordered_map<DefPortalId_t, DefPortal_t*> defines;
|
||||
|
||||
for(DefPortalId_t id : full.Node) {
|
||||
auto iter = Content.ContentIdToDef_Portal.find(id);
|
||||
if(iter != Content.ContentIdToDef_Portal.end()) {
|
||||
defines[id] = &iter->second;
|
||||
}
|
||||
}
|
||||
// for(DefPortalId_t id : full.Node) {
|
||||
// auto iter = Content.ContentIdToDef_Portal.find(id);
|
||||
// if(iter != Content.ContentIdToDef_Portal.end()) {
|
||||
// defines[id] = &iter->second;
|
||||
// }
|
||||
// }
|
||||
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
cec->Remote->informateDefPortal(defines);
|
||||
}
|
||||
}
|
||||
// for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// cec->Remote->informateDefPortal(defines);
|
||||
// }
|
||||
// }
|
||||
|
||||
if(!full.Entity.empty()) {
|
||||
std::unordered_map<DefEntityId_t, DefEntity_t*> defines;
|
||||
// if(!full.Entity.empty()) {
|
||||
// std::unordered_map<DefEntityId_t, DefEntity_t*> defines;
|
||||
|
||||
for(DefEntityId_t id : full.Node) {
|
||||
auto iter = Content.ContentIdToDef_Entity.find(id);
|
||||
if(iter != Content.ContentIdToDef_Entity.end()) {
|
||||
defines[id] = &iter->second;
|
||||
}
|
||||
}
|
||||
// for(DefEntityId_t id : full.Node) {
|
||||
// auto iter = Content.ContentIdToDef_Entity.find(id);
|
||||
// if(iter != Content.ContentIdToDef_Entity.end()) {
|
||||
// defines[id] = &iter->second;
|
||||
// }
|
||||
// }
|
||||
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
cec->Remote->informateDefEntity(defines);
|
||||
}
|
||||
}
|
||||
// for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// cec->Remote->informateDefEntity(defines);
|
||||
// }
|
||||
// }
|
||||
|
||||
if(!full.Item.empty()) {
|
||||
std::unordered_map<DefItemId_t, DefItem_t*> defines;
|
||||
// if(!full.Item.empty()) {
|
||||
// std::unordered_map<DefItemId_t, DefItem_t*> defines;
|
||||
|
||||
for(DefItemId_t id : full.Node) {
|
||||
auto iter = Content.ContentIdToDef_Item.find(id);
|
||||
if(iter != Content.ContentIdToDef_Item.end()) {
|
||||
defines[id] = &iter->second;
|
||||
}
|
||||
}
|
||||
// for(DefItemId_t id : full.Node) {
|
||||
// auto iter = Content.ContentIdToDef_Item.find(id);
|
||||
// if(iter != Content.ContentIdToDef_Item.end()) {
|
||||
// defines[id] = &iter->second;
|
||||
// }
|
||||
// }
|
||||
|
||||
for(std::shared_ptr<ContentEventController>& cec : Game.CECs) {
|
||||
cec->Remote->informateDefItem(defines);
|
||||
}
|
||||
}
|
||||
// for(std::shared_ptr<RemoteClient>& cec : Game.CECs) {
|
||||
// cec->Remote->informateDefItem(defines);
|
||||
// }
|
||||
// }
|
||||
|
||||
BackingChunkPressure.startCollectChanges();
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
#include <sol/state.hpp>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include "ContentEventController.hpp"
|
||||
|
||||
#include "WorldDefManager.hpp"
|
||||
#include "AssetsManager.hpp"
|
||||
#include "ContentManager.hpp"
|
||||
#include "World.hpp"
|
||||
|
||||
#include "SaveBackend.hpp"
|
||||
@@ -73,57 +73,19 @@ class GameServer : public AsyncObject {
|
||||
struct ContentObj {
|
||||
public:
|
||||
AssetsManager AM;
|
||||
|
||||
ResourceId_t NextId[(int) EnumDefContent::MAX_ENUM] = {0};
|
||||
std::unordered_map<std::string, ResourceId_t> ContentKeyToId[(int) EnumDefContent::MAX_ENUM]; // EnumDefContent
|
||||
|
||||
std::unordered_map<DefVoxelId_t, DefVoxel_t> ContentIdToDef_Voxel;
|
||||
std::unordered_map<DefNodeId_t, DefNode_t> ContentIdToDef_Node;
|
||||
std::unordered_map<DefWorldId_t, DefWorld_t> ContentIdToDef_World;
|
||||
std::unordered_map<DefPortalId_t, DefPortal_t> ContentIdToDef_Portal;
|
||||
std::unordered_map<DefEntityId_t, DefEntity_t> ContentIdToDef_Entity;
|
||||
std::unordered_map<DefItemId_t, DefItem_t> ContentIdToDef_Item;
|
||||
|
||||
ResourceId_t registerContent(const std::string& key, EnumDefContent def) {
|
||||
int index = int(def);
|
||||
assert(index < (int) EnumDefContent::MAX_ENUM);
|
||||
|
||||
auto &container = ContentKeyToId[index];
|
||||
auto iter = container.find(key);
|
||||
if(iter == container.end()) {
|
||||
assert(NextId[index] != ResourceId_t(-1));
|
||||
ResourceId_t nextId = NextId[index]++;
|
||||
container.insert(iter, {key, nextId});
|
||||
return nextId;
|
||||
}
|
||||
|
||||
MAKE_ERROR("Повторная регистрация контента: " << key << " тип " << int(def));
|
||||
}
|
||||
|
||||
ResourceId_t getContentDefId(const std::string& key, EnumDefContent def) {
|
||||
int index = int(def);
|
||||
assert(index < (int) EnumDefContent::MAX_ENUM);
|
||||
|
||||
auto &container = ContentKeyToId[index];
|
||||
auto iter = container.find(key);
|
||||
if(iter == container.end()) {
|
||||
return ResourceId_t(-1);
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
ContentManager CM;
|
||||
|
||||
// Если контент был перерегистрирован (исключая двоичные ресурсы), то профили будут повторно разосланы
|
||||
ResourceRequest OnContentChanges;
|
||||
|
||||
|
||||
ContentObj(asio::io_context& ioc)
|
||||
: AM(ioc)
|
||||
: AM(ioc), CM(ioc)
|
||||
{}
|
||||
} Content;
|
||||
|
||||
struct {
|
||||
std::vector<std::shared_ptr<ContentEventController>> CECs;
|
||||
std::vector<std::shared_ptr<RemoteClient>> RemoteClients;
|
||||
ServerTime AfterStartTime = {0, 0};
|
||||
|
||||
} Game;
|
||||
@@ -337,9 +299,6 @@ private:
|
||||
void prerun();
|
||||
void run();
|
||||
|
||||
DefNode_t createNodeProfileByLua(const sol::table& profile);
|
||||
|
||||
|
||||
void initLuaAssets();
|
||||
void initLuaPre();
|
||||
void initLua();
|
||||
|
||||
@@ -279,6 +279,11 @@ public:
|
||||
TOS::SpinlockObject<std::queue<uint8_t>> Actions;
|
||||
ResourceId_t RecievedAssets[(int) EnumAssets::MAX_ENUM] = {0};
|
||||
|
||||
// Регионы, наблюдаемые клиентом
|
||||
ContentViewInfo ContentViewState;
|
||||
// Если игрок пересекал границы региона (для перерасчёта ContentViewState)
|
||||
bool CrossedRegion = true;
|
||||
|
||||
public:
|
||||
RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username, std::vector<ResourceFile::Hash_t> &&client_cache)
|
||||
: LOG("RemoteClient " + username), Socket(ioc, std::move(socket)), Username(username), ClientBinaryCache(std::move(client_cache))
|
||||
@@ -300,24 +305,46 @@ public:
|
||||
|
||||
/*
|
||||
Сервер собирает изменения миров, сжимает их и раздаёт на отправку игрокам
|
||||
|
||||
*/
|
||||
|
||||
// Функции подготавливают пакеты к отправке
|
||||
// Отслеживаемое игроком использование контента
|
||||
|
||||
// maybe созданны для использования в многопотоке, если ресурс сейчас занят вернёт false, потом нужно повторить запрос
|
||||
TOS::Spinlock MurkyLock;
|
||||
// marky используются в BackingChunkPressure_t в GameServer во время заморозки мира от записи.
|
||||
// В это время просматриваются изменённые объекты и рассылаются изменения клиентам
|
||||
|
||||
/*
|
||||
Все пробегаются по игрокам, и смотрят наблюдаемые миры.
|
||||
Если идентификатор мира % количество потоков == 0, то проверяем что
|
||||
этот мир наблюдается игроком и готовим информацию о нём для отправки,
|
||||
отправляем
|
||||
|
||||
Синхронизация этапа с группой
|
||||
|
||||
Потоки рассылки изменений соблюдают пакетность изменений.
|
||||
Изменение чанков, потеря регионов, изменения чанков, потеря регионов
|
||||
|
||||
игрок % потоки == 0
|
||||
Информируем о потерянных регионах
|
||||
Информируем о потерянных мирах
|
||||
|
||||
Синхронизация этапа с группой
|
||||
*/
|
||||
|
||||
// В зоне видимости добавился чанк или изменились его воксели
|
||||
bool maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels,
|
||||
void murky_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels,
|
||||
const std::vector<DefVoxelId_t>& uniq_sorted_defines);
|
||||
// В зоне видимости добавился чанк или изменились его ноды
|
||||
bool maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
|
||||
void murky_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
|
||||
const std::vector<DefNodeId_t>& uniq_sorted_defines);
|
||||
// void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights);
|
||||
|
||||
|
||||
// Регион удалён из зоны видимости
|
||||
void prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos);
|
||||
void murky_prepareRegionRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses);
|
||||
// Мир появился в зоне видимости
|
||||
void murky_prepareWorldUpdate(WorldId_t worldId, World* world);
|
||||
// Мир удалён из зоны видимости
|
||||
void murky_prepareWorldRemove(WorldId_t worldId);
|
||||
|
||||
// В зоне видимости добавилась новая сущность или она изменилась
|
||||
void prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity);
|
||||
@@ -326,15 +353,10 @@ public:
|
||||
// Клиент перестал наблюдать за сущностью
|
||||
void prepareEntityRemove(ServerEntityId_t entityId);
|
||||
|
||||
// В зоне видимости добавился мир или он изменился
|
||||
void prepareWorldUpdate(WorldId_t worldId, World* world);
|
||||
// Клиент перестал наблюдать за миром
|
||||
void prepareWorldRemove(WorldId_t worldId);
|
||||
|
||||
// В зоне видимости добавился порта или он изменился
|
||||
void preparePortalUpdate(PortalId_t portalId, void* portal);
|
||||
// void preparePortalUpdate(PortalId_t portalId, void* portal);
|
||||
// Клиент перестал наблюдать за порталом
|
||||
void preparePortalRemove(PortalId_t portalId);
|
||||
// void preparePortalRemove(PortalId_t portalId);
|
||||
|
||||
// Прочие моменты
|
||||
void prepareCameraSetEntity(ServerEntityId_t entityId);
|
||||
|
||||
@@ -107,13 +107,15 @@ class SpinlockObject {
|
||||
public:
|
||||
template<typename... Args>
|
||||
explicit SpinlockObject(Args&&... args)
|
||||
: value(std::forward<Args>(args)...) {}
|
||||
: Value(std::forward<Args>(args)...) {}
|
||||
|
||||
class Lock {
|
||||
public:
|
||||
Lock(SpinlockObject* obj, std::atomic_flag& flag)
|
||||
: Obj(obj), Flag(&flag) {
|
||||
while (flag.test_and_set(std::memory_order_acquire));
|
||||
Lock(SpinlockObject* obj, std::atomic_flag& flag, bool locked = false)
|
||||
: Obj(obj), Flag(&flag)
|
||||
{
|
||||
if(obj && !locked)
|
||||
while(flag.test_and_set(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
~Lock() {
|
||||
@@ -143,9 +145,9 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& get() const { assert(Obj); return Obj->value; }
|
||||
T* operator->() const { assert(Obj); return &Obj->value; }
|
||||
T& operator*() const { assert(Obj); return Obj->value; }
|
||||
T& get() const { assert(Obj); return Obj->Value; }
|
||||
T* operator->() const { assert(Obj); return &Obj->Value; }
|
||||
T& operator*() const { assert(Obj); return Obj->Value; }
|
||||
|
||||
void unlock() { assert(Obj); Obj = nullptr; Flag->clear(std::memory_order_release);}
|
||||
|
||||
@@ -155,14 +157,83 @@ public:
|
||||
};
|
||||
|
||||
Lock lock() {
|
||||
return Lock(this, mutex);
|
||||
return Lock(this, Flag);
|
||||
}
|
||||
|
||||
const T& get_read() { return value; }
|
||||
Lock tryLock() {
|
||||
if(Flag.test_and_set(std::memory_order_acquire))
|
||||
return Lock(nullptr, Flag);
|
||||
else
|
||||
return Lock(this, Flag, true);
|
||||
}
|
||||
|
||||
const T& get_read() { return Value; }
|
||||
|
||||
private:
|
||||
T value;
|
||||
std::atomic_flag mutex = ATOMIC_FLAG_INIT;
|
||||
T Value;
|
||||
std::atomic_flag Flag = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
class Spinlock {
|
||||
public:
|
||||
Spinlock() {}
|
||||
|
||||
class Lock {
|
||||
public:
|
||||
Lock(Spinlock* obj, std::atomic_flag& flag, bool locked = false)
|
||||
: Obj(obj), Flag(&flag)
|
||||
{
|
||||
if(obj && !locked)
|
||||
while(flag.test_and_set(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
~Lock() {
|
||||
if(Obj)
|
||||
Flag->clear(std::memory_order_release);
|
||||
}
|
||||
|
||||
Lock(const Lock&) = delete;
|
||||
Lock(Lock&& obj)
|
||||
: Obj(obj.Obj), Flag(obj.Flag)
|
||||
{
|
||||
obj.Obj = nullptr;
|
||||
}
|
||||
|
||||
Lock& operator=(const Lock&) = delete;
|
||||
Lock& operator=(Lock&& obj) {
|
||||
if(this == &obj)
|
||||
return *this;
|
||||
|
||||
if(Obj)
|
||||
unlock();
|
||||
|
||||
Obj = obj.Obj;
|
||||
obj.Obj = nullptr;
|
||||
Flag = obj.Flag;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void unlock() { assert(Obj); Obj = nullptr; Flag->clear(std::memory_order_release);}
|
||||
|
||||
private:
|
||||
Spinlock *Obj;
|
||||
std::atomic_flag *Flag;
|
||||
};
|
||||
|
||||
Lock lock() {
|
||||
return Lock(this, Flag);
|
||||
}
|
||||
|
||||
Lock tryLock() {
|
||||
if(Flag.test_and_set(std::memory_order_acquire))
|
||||
return Lock(nullptr, Flag);
|
||||
else
|
||||
return Lock(this, Flag, true);
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic_flag Flag = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
|
||||
Reference in New Issue
Block a user