diff --git a/Src/Common/Abstract.hpp b/Src/Common/Abstract.hpp index f3080db..528a083 100644 --- a/Src/Common/Abstract.hpp +++ b/Src/Common/Abstract.hpp @@ -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; @@ -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 Defines; + std::vector Defines; }; CompressedVoxels compressVoxels(const std::vector& voxels, bool fast = true); @@ -474,16 +474,16 @@ std::vector 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 Defines; + std::vector Defines; }; CompressedNodes compressNodes(const Node* nodes, bool fast = true); diff --git a/Src/Server/Abstract.cpp b/Src/Server/Abstract.cpp index e3bc5ec..59dae1d 100644 --- a/Src/Server/Abstract.cpp +++ b/Src/Server/Abstract.cpp @@ -388,5 +388,14 @@ NodeStateCondition nodestateExpression(const std::vector& entrie return ct; } +} +namespace std { + +template <> +struct hash { + std::size_t operator()(const LV::Server::ServerObjectPos& obj) const { + return std::hash()(obj.WorldId) ^ std::hash()(obj.ObjectPos); + } +}; } \ No newline at end of file diff --git a/Src/Server/Abstract.hpp b/Src/Server/Abstract.hpp index 6a6a2d1..2219278 100644 --- a/Src/Server/Abstract.hpp +++ b/Src/Server/Abstract.hpp @@ -432,4 +432,105 @@ inline void convertChunkVoxelsToRegion(const std::unordered_map::optimizeVoxelRegions(regions); } + +struct ServerObjectPos { + WorldId_t WorldId; + Pos::Object ObjectPos; +}; + +/* + Разница между информацией о наблюдаемых регионах +*/ +struct ContentViewInfo_Diff { + // Изменения на уровне миров (увиден или потерян) + std::vector WorldsNew, WorldsLost; + // Изменения на уровне регионов + std::unordered_map> RegionsNew, RegionsLost; + + bool empty() const { + return WorldsNew.empty() && WorldsLost.empty() && RegionsNew.empty() && RegionsLost.empty(); + } +}; + +/* + То, какие регионы наблюдает игрок +*/ +struct ContentViewInfo { + // std::vector - сортированный и с уникальными значениями + std::unordered_map> 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; +}; + } \ No newline at end of file diff --git a/Src/Server/AssetsManager.cpp b/Src/Server/AssetsManager.cpp index f276dc4..f9706c2 100644 --- a/Src/Server/AssetsManager.cpp +++ b/Src/Server/AssetsManager.cpp @@ -22,9 +22,9 @@ AssetsManager::AssetsManager(asio::io_context& ioc) AssetsManager::~AssetsManager() = default; -std::tuple&> AssetsManager::Local::nextId(EnumAssets type) { +std::tuple&> AssetsManager::Local::nextId(EnumAssets type) { auto& table = Table[(int) type]; - ResourceId_t id = -1; + ResourceId id = -1; std::optional *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* data = nullptr; if(auto iterId = keyToIdDomain.find(key); iterId != keyToIdDomain.end()) { @@ -269,8 +269,8 @@ AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const } // Удалённые идентификаторы не считаются удалёнными, если были изменены - std::unordered_set noc(result.NewOrChange[type].begin(), result.NewOrChange[type].end()); - std::unordered_set l(result.Lost[type].begin(), result.Lost[type].end()); + std::unordered_set noc(result.NewOrChange[type].begin(), result.NewOrChange[type].end()); + std::unordered_set 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()) { diff --git a/Src/Server/AssetsManager.hpp b/Src/Server/AssetsManager.hpp index 7e360d4..f5ca891 100644 --- a/Src/Server/AssetsManager.hpp +++ b/Src/Server/AssetsManager.hpp @@ -78,9 +78,9 @@ private: // Связь ресурсов по идентификаторам std::vector> Table[(int) EnumAssets::MAX_ENUM]; // Связь домены -> {ключ -> идентификатор} - std::unordered_map> KeyToId[(int) EnumAssets::MAX_ENUM]; + std::unordered_map> KeyToId[(int) EnumAssets::MAX_ENUM]; - std::tuple&> nextId(EnumAssets type); + std::tuple&> nextId(EnumAssets type); }; TOS::SpinlockObject LocalObj; @@ -133,8 +133,8 @@ public: Раздаёт идентификаторы ресурсам и записывает их в таблицу */ struct Out_applyResourceChange { - std::vector Lost[(int) EnumAssets::MAX_ENUM]; - std::vector> NewOrChange[(int) EnumAssets::MAX_ENUM]; + std::vector Lost[(int) EnumAssets::MAX_ENUM]; + std::vector> 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); }; } \ No newline at end of file diff --git a/Src/Server/ContentEventController.hpp b/Src/Server/ContentEventController.hpp index 7b113d2..f791aed 100644 --- a/Src/Server/ContentEventController.hpp +++ b/Src/Server/ContentEventController.hpp @@ -20,124 +20,9 @@ class GameServer; class World; -struct ServerObjectPos { - WorldId_t WorldId; - Pos::Object ObjectPos; -}; - -/* - Разница между информацией о наблюдаемых регионах -*/ -struct ContentViewInfo_Diff { - // Изменения на уровне миров (увиден или потерян) - std::vector WorldsNew, WorldsLost; - // Изменения на уровне регионов - std::unordered_map> RegionsNew, RegionsLost; - - bool empty() const { - return WorldsNew.empty() && WorldsLost.empty() && RegionsNew.empty() && RegionsLost.empty(); - } -}; - -/* - То, какие регионы наблюдает игрок -*/ -struct ContentViewInfo { - // std::vector - сортированный и с уникальными значениями - std::unordered_map> 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 Portals; - } Subscribed; - public: - // Управляется сервером - std::unique_ptr Remote; - // Что сейчас наблюдает игрок - ContentViewInfo ContentViewState; - // Если игрок пересекал границы чанка (для перерасчёта ContentViewState) - bool CrossedBorder = true; - - ServerObjectPos Pos, LastPos; std::queue Build, Break; public: @@ -172,12 +57,3 @@ public: } -namespace std { - -template <> -struct hash { - std::size_t operator()(const LV::Server::ServerObjectPos& obj) const { - return std::hash()(obj.WorldId) ^ std::hash()(obj.ObjectPos); - } -}; -} diff --git a/Src/Server/ContentManager.cpp b/Src/Server/ContentManager.cpp index cb80af8..f702c87 100644 --- a/Src/Server/ContentManager.cpp +++ b/Src/Server/ContentManager.cpp @@ -1,15 +1,113 @@ #include "ContentManager.hpp" +#include "Common/Abstract.hpp" +#include 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& node = getEntry_Node(id); + if(!node) + node.emplace(); + DefNode& def = *node; + + { + std::variant parent = profile["parent"]; + if(const sol::table* table = std::get_if(&parent)) { + // result = createNodeProfileByLua(*table); + } else if(const std::string* key = std::get_if(&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 nodestate = profile["nodestate"]; + } + + { + std::optional render = profile["render"]; + } + + { + std::optional collision = profile["collision"]; + } + + { + std::optional 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; +} } \ No newline at end of file diff --git a/Src/Server/ContentManager.hpp b/Src/Server/ContentManager.hpp index 868a88f..a2572c9 100644 --- a/Src/Server/ContentManager.hpp +++ b/Src/Server/ContentManager.hpp @@ -1,50 +1,70 @@ #pragma once +#include "Common/Abstract.hpp" #include "Server/Abstract.hpp" +#include "Server/AssetsManager.hpp" +#include +#include +#include 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> ContentKeyToId[(int) EnumDefContent::MAX_ENUM]; - template struct TableEntry { static constexpr size_t ChunkSize = 4096; std::array, ChunkSize> Entries; }; + + // Следующие идентификаторы регистрации контента + ResourceId NextId[(int) EnumDefContent::MAX_ENUM] = {0}; + // Домен -> {ключ -> идентификатор} + std::unordered_map> ContentKeyToId[(int) EnumDefContent::MAX_ENUM]; + + // Профили зарегистрированные модами + std::vector>> Profiles_Base_Voxel; + std::vector>> Profiles_Base_Node; + std::vector>> Profiles_Base_World; + std::vector>> Profiles_Base_Portal; + std::vector>> Profiles_Base_Entity; + std::vector>> Profiles_Base_Item; + + // Изменения, накладываемые на профили + // Идентификатор [домен мода модификатора, модификатор] + std::unordered_map>> Profiles_Mod_Voxel; + std::unordered_map>> Profiles_Mod_Node; + std::unordered_map>> Profiles_Mod_World; + std::unordered_map>> Profiles_Mod_Portal; + std::unordered_map>> Profiles_Mod_Entity; + std::unordered_map>> Profiles_Mod_Item; + + // Затронутые профили в процессе регистраций + // По ним будут пересобраны профили + std::vector ProfileChanges[(int) EnumDefContent::MAX_ENUM]; + // Конечные профили контента std::vector>> Profiles_Voxel; std::vector>> Profiles_Node; @@ -53,10 +73,43 @@ class ContentManager { std::vector>> Profiles_Entity; std::vector>> Profiles_Item; + std::optional& getEntry_Voxel(ResourceId resId) { return Profiles_Voxel[resId / TableEntry::ChunkSize]->Entries[resId % TableEntry::ChunkSize];} + std::optional& getEntry_Node(ResourceId resId) { return Profiles_Node[resId / TableEntry::ChunkSize]->Entries[resId % TableEntry::ChunkSize];} + std::optional& getEntry_World(ResourceId resId) { return Profiles_World[resId / TableEntry::ChunkSize]->Entries[resId % TableEntry::ChunkSize];} + std::optional& getEntry_Portal(ResourceId resId) { return Profiles_Portal[resId / TableEntry::ChunkSize]->Entries[resId % TableEntry::ChunkSize];} + std::optional& getEntry_Entity(ResourceId resId) { return Profiles_Entity[resId / TableEntry::ChunkSize]->Entries[resId % TableEntry::ChunkSize];} + std::optional& getEntry_Item(ResourceId resId) { return Profiles_Item[resId / TableEntry::ChunkSize]->Entries[resId % TableEntry::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 ChangedProfiles[(int) EnumDefContent::MAX_ENUM]; + }; + + Out_buildEndProfiles buildEndProfiles(); }; } \ No newline at end of file diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index fc68c3f..38f5a93 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -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 #include #include @@ -526,7 +526,7 @@ void GameServer::BackingChunkPressure_t::run(int id) { size_t counter = 0; struct Dump { - std::vector> CECs, NewCECs; + std::vector> CECs, NewCECs; std::unordered_map> Voxels; std::unordered_map> Nodes; uint64_t IsChunkChanged_Nodes, IsChunkChanged_Voxels; @@ -628,10 +628,10 @@ void GameServer::BackingChunkPressure_t::run(int id) { CompressedNodes Data; }; - std::list>> postponedVoxels; - std::list>> postponedNodes; + std::list>> postponedVoxels; + std::list>> postponedNodes; - std::vector cecs; + std::vector 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 stbt; - std::unordered_set btis; + std::map stbt; + std::unordered_set 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(btis.begin(), btis.end()), cmd}; + return {std::vector(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 &cec : Game.CECs) { + for(std::shared_ptr &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 parent = profile["parent"]; - if(const sol::table* table = std::get_if(&parent)) { - result = createNodeProfileByLua(*table); - } else if(const std::string* key = std::get_if(&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 nodestate = profile["nodestate"]; - } - - { - std::optional render = profile["render"]; - } - - { - std::optional collision = profile["collision"]; - } - - { - std::optional events = profile["events"]; - } - - result.NodeAdvancementFactory = profile["node_advancement_factory"]; - - return result; -} - void GameServer::initLuaAssets() { auto &lua = LuaMainState; std::optional core = lua["core"]; @@ -1644,7 +1588,7 @@ void GameServer::initLuaAssets() { std::optional>> 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>> result_o = TOS::Str::match(id, "^(?:([\\w\\d_]+):)?([\\w\\d_]+)$"); + std::function reg + = [this](EnumDefContent type, const std::string& key, const sol::table& profile) + { + std::optional>> 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& client : *lock) { co_spawn(client->run()); - Game.CECs.push_back(std::make_unique(std::move(client))); + Game.CECs.push_back(std::make_unique(std::move(client))); } lock->clear(); @@ -1731,7 +1678,7 @@ void GameServer::stepConnections() { BackingChunkPressure.endCollectChanges(); // Отключение игроков - for(std::shared_ptr& cec : Game.CECs) { + for(std::shared_ptr& 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& ptr) { return !ptr; }), + [](const std::shared_ptr& 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& cec : Game.CECs) { + for(std::shared_ptr& 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::vector> toSubscribe; + std::unordered_map, std::vector> 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& cec : Game.CECs) { + for(std::shared_ptr& cec : Game.CECs) { cec->onUpdate(); // Это для пробы строительства и ломания нод @@ -2411,124 +2358,124 @@ void GameServer::stepSyncContent() { // Сбор запросов на ресурсы и профили + отправка пакетов игрокам ResourceRequest full = std::move(Content.OnContentChanges); - for(std::shared_ptr& cec : Game.CECs) { + for(std::shared_ptr& 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& cec : Game.CECs) { - cec->Remote->informateIdToHash(out.BinToHash); - } - } + // if(hasData) { + // for(std::shared_ptr& cec : Game.CECs) { + // cec->Remote->informateIdToHash(out.BinToHash); + // } + // } - if(!out.HashToResource.empty()) - for(std::shared_ptr& cec : Game.CECs) { - cec->Remote->informateBinary(out.HashToResource); - } - } + // if(!out.HashToResource.empty()) + // for(std::shared_ptr& cec : Game.CECs) { + // cec->Remote->informateBinary(out.HashToResource); + // } + // } // Оповещаем об игровых профилях - if(!full.Voxel.empty()) { - std::unordered_map defines; + // if(!full.Voxel.empty()) { + // std::unordered_map 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& cec : Game.CECs) { - cec->Remote->informateDefVoxel(defines); - } - } + // for(std::shared_ptr& cec : Game.CECs) { + // cec->Remote->informateDefVoxel(defines); + // } + // } - if(!full.Node.empty()) { - std::unordered_map defines; + // if(!full.Node.empty()) { + // std::unordered_map 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& cec : Game.CECs) { - cec->Remote->informateDefNode(defines); - } - } + // for(std::shared_ptr& cec : Game.CECs) { + // cec->Remote->informateDefNode(defines); + // } + // } - if(!full.World.empty()) { - std::unordered_map defines; + // if(!full.World.empty()) { + // std::unordered_map 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& cec : Game.CECs) { - cec->Remote->informateDefWorld(defines); - } - } + // for(std::shared_ptr& cec : Game.CECs) { + // cec->Remote->informateDefWorld(defines); + // } + // } - if(!full.Portal.empty()) { - std::unordered_map defines; + // if(!full.Portal.empty()) { + // std::unordered_map 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& cec : Game.CECs) { - cec->Remote->informateDefPortal(defines); - } - } + // for(std::shared_ptr& cec : Game.CECs) { + // cec->Remote->informateDefPortal(defines); + // } + // } - if(!full.Entity.empty()) { - std::unordered_map defines; + // if(!full.Entity.empty()) { + // std::unordered_map 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& cec : Game.CECs) { - cec->Remote->informateDefEntity(defines); - } - } + // for(std::shared_ptr& cec : Game.CECs) { + // cec->Remote->informateDefEntity(defines); + // } + // } - if(!full.Item.empty()) { - std::unordered_map defines; + // if(!full.Item.empty()) { + // std::unordered_map 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& cec : Game.CECs) { - cec->Remote->informateDefItem(defines); - } - } + // for(std::shared_ptr& cec : Game.CECs) { + // cec->Remote->informateDefItem(defines); + // } + // } BackingChunkPressure.startCollectChanges(); } diff --git a/Src/Server/GameServer.hpp b/Src/Server/GameServer.hpp index b61724a..26a5e75 100644 --- a/Src/Server/GameServer.hpp +++ b/Src/Server/GameServer.hpp @@ -21,10 +21,10 @@ #include #include #include -#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 ContentKeyToId[(int) EnumDefContent::MAX_ENUM]; // EnumDefContent - - std::unordered_map ContentIdToDef_Voxel; - std::unordered_map ContentIdToDef_Node; - std::unordered_map ContentIdToDef_World; - std::unordered_map ContentIdToDef_Portal; - std::unordered_map ContentIdToDef_Entity; - std::unordered_map 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> CECs; + std::vector> 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(); diff --git a/Src/Server/RemoteClient.hpp b/Src/Server/RemoteClient.hpp index 36831c5..8b65ca0 100644 --- a/Src/Server/RemoteClient.hpp +++ b/Src/Server/RemoteClient.hpp @@ -279,6 +279,11 @@ public: TOS::SpinlockObject> 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 &&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& 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& 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 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); diff --git a/Src/TOSLib.hpp b/Src/TOSLib.hpp index 8e837b1..b4828e0 100644 --- a/Src/TOSLib.hpp +++ b/Src/TOSLib.hpp @@ -107,13 +107,15 @@ class SpinlockObject { public: template explicit SpinlockObject(Args&&... args) - : value(std::forward(args)...) {} + : Value(std::forward(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