diff --git a/Src/Server/Abstract.cpp b/Src/Server/Abstract.cpp new file mode 100644 index 0000000..e3bc5ec --- /dev/null +++ b/Src/Server/Abstract.cpp @@ -0,0 +1,392 @@ +#include "Abstract.hpp" + + +namespace LV::Server { + +NodeStateCondition nodestateExpression(const std::vector& entries, const std::string& expression) { + // Скомпилировать выражение и просчитать таблицу CT + + std::unordered_map valueToInt; + for(const NodestateEntry& entry : entries) { + for(size_t index = 0; index < entry.ValueNames.size(); index++) { + valueToInt[entry.ValueNames[index]] = index; + } + } + + // Парсинг токенов + enum class EnumTokenKind { + LParen, RParen, + Plus, Minus, Star, Slash, Percent, + Not, And, Or, + LT, LE, GT, GE, EQ, NE + }; + + std::vector> tokens; + ssize_t pos = 0; + auto skipWS = [&](){ while(pos' && n == '=') { + tokens.push_back(EnumTokenKind::GE); + pos++; + continue; + } else if(c == '=' && n == '=') { + tokens.push_back(EnumTokenKind::EQ); + pos++; + continue; + } else if(c == '!' && n == '=') { + tokens.push_back(EnumTokenKind::NE); + pos++; + continue; + } + } + + // Операторы + switch(c) { + case '(': tokens.push_back(EnumTokenKind::LParen); + case ')': tokens.push_back(EnumTokenKind::RParen); + case '+': tokens.push_back(EnumTokenKind::Plus); + case '-': tokens.push_back(EnumTokenKind::Minus); + case '*': tokens.push_back(EnumTokenKind::Star); + case '/': tokens.push_back(EnumTokenKind::Slash); + case '%': tokens.push_back(EnumTokenKind::Percent); + case '!': tokens.push_back(EnumTokenKind::Not); + case '&': tokens.push_back(EnumTokenKind::And); + case '|': tokens.push_back(EnumTokenKind::Or); + case '<': tokens.push_back(EnumTokenKind::LT); + case '>': tokens.push_back(EnumTokenKind::GT); + } + + MAKE_ERROR("Недопустимый символ: " << c); + } + + // Разбор токенов + enum class Op { + Add, Sub, Mul, Div, Mod, + LT, LE, GT, GE, EQ, NE, + And, Or, + Pos, Neg, Not + }; + + struct Node { + struct Num { int v; }; + struct Var { std::string name; }; + struct Unary { Op op; uint16_t rhs; }; + struct Binary { Op op; uint16_t lhs, rhs; }; + std::variant v; + }; + + std::vector nodes; + + for(size_t index = 0; index < tokens.size(); index++) { + auto &token = tokens[index]; + + if(std::string* value = std::get_if(&token)) { + if(*value == "false") { + token = 0; + } else if(*value == "true") { + token = 1; + } else if(auto iter = valueToInt.find(*value); iter != valueToInt.end()) { + token = iter->second; // TODO: + } else { + Node node; + node.v = Node::Var(*value); + nodes.emplace_back(std::move(node)); + assert(nodes.size() < std::pow(2, 16)-64); + token = uint16_t(nodes.size()-1); + } + } + } + + // Рекурсивный разбор выражений в скобках + std::function lambdaParse = [&](size_t pos) -> uint16_t { + size_t end = tokens.size(); + + // Парсим выражения в скобках + for(size_t index = pos; index < tokens.size(); index++) { + if(EnumTokenKind* kind = std::get_if(&tokens[index])) { + if(*kind == EnumTokenKind::LParen) { + uint16_t node = lambdaParse(index+1); + tokens.insert(tokens.begin()+index, node); + tokens.erase(tokens.begin()+index+1, tokens.begin()+index+3); + end = tokens.size(); + } else if(*kind == EnumTokenKind::RParen) { + end = index; + break; + } + } + } + + // Обрабатываем унарные операции + for(ssize_t index = end; index >= pos; index--) { + if(EnumTokenKind *kind = std::get_if(&tokens[index])) { + if(*kind != EnumTokenKind::Not && *kind != EnumTokenKind::Plus && *kind != EnumTokenKind::Minus) + continue; + + if(index+1 >= end) + MAKE_ERROR("Отсутствует операнд"); + + auto rightToken = tokens[index+1]; + if(std::holds_alternative(rightToken)) + MAKE_ERROR("Недопустимый операнд"); + + if(int* value = std::get_if(&rightToken)) { + if(*kind == EnumTokenKind::Not) + tokens[index] = *value ? 0 : 1; + else if(*kind == EnumTokenKind::Plus) + tokens[index] = +*value; + else if(*kind == EnumTokenKind::Minus) + tokens[index] = -*value; + + } else if(uint16_t* value = std::get_if(&rightToken)) { + Node node; + Node::Unary un; + un.rhs = *value; + + if(*kind == EnumTokenKind::Not) + un.op = Op::Not; + else if(*kind == EnumTokenKind::Plus) + un.op = Op::Pos; + else if(*kind == EnumTokenKind::Minus) + un.op = Op::Neg; + + node.v = un; + nodes.emplace_back(std::move(node)); + assert(nodes.size() < std::pow(2, 16)-64); + tokens[index] = uint16_t(nodes.size()-1); + } + + end--; + tokens.erase(tokens.begin()+index+1); + } + } + + // Бинарные в порядке приоритета + for(int priority = 0; priority < 6; priority++) + for(size_t index = pos; index < end; index++) { + EnumTokenKind *kind = std::get_if(&tokens[index]); + + if(!kind) + continue; + + if(priority == 0 && *kind != EnumTokenKind::Star && *kind != EnumTokenKind::Slash && *kind != EnumTokenKind::Percent) + continue; + if(priority == 1 && *kind != EnumTokenKind::Plus && *kind != EnumTokenKind::Minus) + continue; + if(priority == 2 && *kind != EnumTokenKind::LT && *kind != EnumTokenKind::GT && *kind != EnumTokenKind::LE && *kind != EnumTokenKind::GE) + continue; + if(priority == 3 && *kind != EnumTokenKind::EQ && *kind != EnumTokenKind::NE) + continue; + if(priority == 4 && *kind != EnumTokenKind::And) + continue; + if(priority == 5 && *kind != EnumTokenKind::Or) + continue; + + if(index == pos) + MAKE_ERROR("Отсутствует операнд перед"); + else if(index == end-1) + MAKE_ERROR("Отсутствует операнд после"); + + auto &leftToken = tokens[index-1]; + auto &rightToken = tokens[index+1]; + + if(std::holds_alternative(leftToken)) + MAKE_ERROR("Недопустимый операнд"); + + if(std::holds_alternative(rightToken)) + MAKE_ERROR("Недопустимый операнд"); + + if(std::holds_alternative(leftToken) && std::holds_alternative(rightToken)) { + int value; + + switch(*kind) { + case EnumTokenKind::Plus: value = std::get(leftToken) + std::get(rightToken); break; + case EnumTokenKind::Minus: value = std::get(leftToken) - std::get(rightToken); break; + case EnumTokenKind::Star: value = std::get(leftToken) * std::get(rightToken); break; + case EnumTokenKind::Slash: value = std::get(leftToken) / std::get(rightToken); break; + case EnumTokenKind::Percent: value = std::get(leftToken) % std::get(rightToken); break; + case EnumTokenKind::And: value = std::get(leftToken) && std::get(rightToken); break; + case EnumTokenKind::Or: value = std::get(leftToken) || std::get(rightToken); break; + case EnumTokenKind::LT: value = std::get(leftToken) < std::get(rightToken); break; + case EnumTokenKind::LE: value = std::get(leftToken) <= std::get(rightToken); break; + case EnumTokenKind::GT: value = std::get(leftToken) > std::get(rightToken); break; + case EnumTokenKind::GE: value = std::get(leftToken) >= std::get(rightToken); break; + case EnumTokenKind::EQ: value = std::get(leftToken) == std::get(rightToken); break; + case EnumTokenKind::NE: value = std::get(leftToken) != std::get(rightToken); break; + + default: assert(false); + } + + + leftToken = value; + } else { + Node node; + Node::Binary bin; + + switch(*kind) { + case EnumTokenKind::Plus: bin.op = Op::Add; break; + case EnumTokenKind::Minus: bin.op = Op::Sub; break; + case EnumTokenKind::Star: bin.op = Op::Mul; break; + case EnumTokenKind::Slash: bin.op = Op::Div; break; + case EnumTokenKind::Percent: bin.op = Op::Mod; break; + case EnumTokenKind::And: bin.op = Op::And; break; + case EnumTokenKind::Or: bin.op = Op::Or; break; + case EnumTokenKind::LT: bin.op = Op::LT; break; + case EnumTokenKind::LE: bin.op = Op::LE; break; + case EnumTokenKind::GT: bin.op = Op::GT; break; + case EnumTokenKind::GE: bin.op = Op::GE; break; + case EnumTokenKind::EQ: bin.op = Op::EQ; break; + case EnumTokenKind::NE: bin.op = Op::NE; break; + + default: assert(false); + } + + if(int* value = std::get_if(&leftToken)) { + Node valueNode; + valueNode.v = Node::Num(*value); + nodes.emplace_back(std::move(valueNode)); + assert(nodes.size() < std::pow(2, 16)-64); + bin.lhs = uint16_t(nodes.size()-1); + } else if(uint16_t* nodeId = std::get_if(&leftToken)) { + bin.lhs = *nodeId; + } + + if(int* value = std::get_if(&rightToken)) { + Node valueNode; + valueNode.v = Node::Num(*value); + nodes.emplace_back(std::move(valueNode)); + assert(nodes.size() < std::pow(2, 16)-64); + bin.rhs = uint16_t(nodes.size()-1); + } else if(uint16_t* nodeId = std::get_if(&rightToken)) { + bin.rhs = *nodeId; + } + + nodes.emplace_back(std::move(node)); + assert(nodes.size() < std::pow(2, 16)-64); + leftToken = uint16_t(nodes.size()-1); + } + + tokens.erase(tokens.begin()+index, tokens.begin()+index+2); + end -= 2; + index--; + } + + if(tokens.size() != 1) + MAKE_ERROR("Выражение не корректно"); + + if(uint16_t* nodeId = std::get_if(&tokens[0])) { + return *nodeId; + } else if(int* value = std::get_if(&tokens[0])) { + Node node; + node.v = Node::Num(*value); + nodes.emplace_back(std::move(node)); + assert(nodes.size() < std::pow(2, 16)-64); + return uint16_t(nodes.size()-1); + } else { + MAKE_ERROR("Выражение не корректно"); + } + }; + + uint16_t nodeId = lambdaParse(0); + if(!tokens.empty()) + MAKE_ERROR("Выражение не действительно"); + + std::unordered_map vars; + std::function lambdaCalcNode = [&](uint16_t nodeId) -> int { + const Node& node = nodes[nodeId]; + if(const Node::Num* value = std::get_if(&node.v)) { + return value->v; + } else if(const Node::Var* value = std::get_if(&node.v)) { + auto iter = vars.find(value->name); + if(iter == vars.end()) + MAKE_ERROR("Неопознанное состояние"); + + return iter->second; + } else if(const Node::Unary* value = std::get_if(&node.v)) { + int rNodeValue = lambdaCalcNode(value->rhs); + switch(value->op) { + case Op::Not: return !rNodeValue; + case Op::Pos: return +rNodeValue; + case Op::Neg: return -rNodeValue; + default: + assert(false); + } + } else if(const Node::Binary* value = std::get_if(&node.v)) { + int lNodeValue = lambdaCalcNode(value->lhs); + int rNodeValue = lambdaCalcNode(value->rhs); + + switch(value->op) { + case Op::Add: return lNodeValue+rNodeValue; + case Op::Sub: return lNodeValue-rNodeValue; + case Op::Mul: return lNodeValue*rNodeValue; + case Op::Div: return lNodeValue/rNodeValue; + case Op::Mod: return lNodeValue%rNodeValue; + case Op::LT: return lNodeValuerNodeValue; + case Op::GE: return lNodeValue>=rNodeValue; + case Op::EQ: return lNodeValue==rNodeValue; + case Op::NE: return lNodeValue!=rNodeValue; + case Op::And: return lNodeValue&&rNodeValue; + case Op::Or: return lNodeValue||rNodeValue; + default: + assert(false); + } + } else { + assert(false); + } + }; + + NodeStateCondition ct; + for(int meta = 0; meta < 256; meta++) { + int meta_temp = meta; + + for(size_t index = 0; index < entries.size(); index++) { + const auto& entry = entries[index]; + + vars[entry.Name] = meta_temp % entry.Variability; + meta_temp /= entry.Variability; + } + + ct[meta] = (bool) lambdaCalcNode(nodeId); + } + + return ct; +} + +} \ No newline at end of file diff --git a/Src/Server/Abstract.hpp b/Src/Server/Abstract.hpp index 0254797..f2cf1b0 100644 --- a/Src/Server/Abstract.hpp +++ b/Src/Server/Abstract.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace LV::Server { @@ -167,167 +168,46 @@ struct NodestateEntry { std::vector ValueNames; }; -struct NodestateExpression { - std::bitset<256> CT; +using NodeStateCondition = std::bitset<256>; +NodeStateCondition nodestateExpression(const std::vector& entries, const std::string& expression); - NodestateExpression(std::vector, const std::string& expression) { - // Скомпилировать выражение и просчитать таблицу CT - - // Парсинг токенов - enum class EnumTokenKind { - LParen, RParen, - Plus, Minus, Star, Slash, Percent, - Not, And, Or, - LT, LE, GT, GE, EQ, NE - }; - - std::vector> tokens; - ssize_t pos = 0; - auto skipWS = [&](){ while(pos' && n == '=') { - tokens.push_back(EnumTokenKind::GE); - pos++; - continue; - } else if(c == '=' && n == '=') { - tokens.push_back(EnumTokenKind::EQ); - pos++; - continue; - } else if(c == '!' && n == '=') { - tokens.push_back(EnumTokenKind::NE); - pos++; - continue; - } - } - - // Операторы - switch(c) { - case '(': tokens.push_back(EnumTokenKind::LParen); - case ')': tokens.push_back(EnumTokenKind::RParen); - case '+': tokens.push_back(EnumTokenKind::Plus); - case '-': tokens.push_back(EnumTokenKind::Minus); - case '*': tokens.push_back(EnumTokenKind::Star); - case '/': tokens.push_back(EnumTokenKind::Slash); - case '%': tokens.push_back(EnumTokenKind::Percent); - case '!': tokens.push_back(EnumTokenKind::Not); - case '&': tokens.push_back(EnumTokenKind::And); - case '|': tokens.push_back(EnumTokenKind::Or); - case '<': tokens.push_back(EnumTokenKind::LT); - case '>': tokens.push_back(EnumTokenKind::GT); - } - - MAKE_ERROR("Недопустимый символ: " << c); - } - - // Разбор токенов - enum class Op { - Add, Sub, Mul, Div, Mod, - LT, LE, GT, GE, EQ, NE, - And, Or, - Pos, Neg, Not - }; - - std::vector nodes; - - struct Node { - struct Num { int v; }; - struct Var { std::string name; }; - struct Unary { Op op; uint16_t rhs; }; - struct Binary { Op op; uint16_t lhs, rhs; }; - std::variant v; - }; - - // Рекурсивный разбор выражений в скобках - std::function lambda = [&](const std::string_view& exp) -> uint16_t { - - }; - - const std::string exp = TOS::Str::replace(expression, " ", ""); - // exp = TOS::Str::replace(expression, "\0", ""); - std::variant result = lambda(exp); - - for(int meta = 0; meta < 256; meta++) { - CT[meta] = true; - } - } - - bool operator()(uint8_t meta) { - return CT[meta]; - } +struct ModelTransform { + std::vector Ids; + uint16_t Weight = 1; + bool UVLock = false; }; -struct DefNodeStates_t { - /* - Указать модель, текстуры и поворот по конкретным осям. - Может быть вариативность моделей относительно одного условия (случайность в зависимости от координат?) - Допускается активация нескольких условий одновременно +/* + Указать модель, текстуры и поворот по конкретным осям. + Может быть вариативность моделей относительно одного условия (случайность в зависимости от координат?) + Допускается активация нескольких условий одновременно - условия snowy=false + условия snowy=false - "snowy=false": [{"model": "node/grass_node"}, {"model": "node/grass_node", transformations: ["y=90", "x=67"]}] <- модель будет выбрана случайно - или - : [{models: [], weight: 1}, {}] <- в models можно перечислить сразу несколько моделей, и они будут использоваться одновременно - или - "": {"model": "node/grass", weight <вес влияющий на шанс отображения именно этой модели>} - или просто - "model": "node/grass_node" - В условия добавить простые проверки !><=&|() in ['1', 2] - в задании параметров модели использовать формулы с применением состояний + "snowy=false": [{"model": "node/grass_node"}, {"model": "node/grass_node", transformations: ["y=90", "x=67"]}] <- модель будет выбрана случайно + или + : [{models: [], weight: 1}, {}] <- в models можно перечислить сразу несколько моделей, и они будут использоваться одновременно + или + "": {"model": "node/grass", weight <вес влияющий на шанс отображения именно этой модели>} + или просто + "model": "node/grass_node" + В условия добавить простые проверки !><=&|() + в задании параметров модели использовать формулы с применением состояний - uvlock ? https://minecraft.wiki/w/Blockstates_definition/format - */ + uvlock ? https://minecraft.wiki/w/Blockstates_definition/format +*/ - -}; +using DefNodestates_t = std::unordered_map>; // Скомпилированный профиль ноды struct DefNode_t { // Зарегистрированные состояния (мета) - struct { - // Подгружается с файла assets//blockstate/node/nodeId.json - DefNodeStates_t StateRouter; - - } States; + // Подгружается с файла assets//nodestate/node/nodeId.json + std::variant> StatesRouter; // Параметры рендера struct { - bool hasHalfTransparency = false; + bool HasHalfTransparency = false; } Render; // Параметры коллизии @@ -345,9 +225,7 @@ struct DefNode_t { } Events; // Если нода умная, то для неё будет создаваться дополнительный более активный объект - sol::protected_function NodeAdvancementFactory; - - + std::optional NodeAdvancementFactory; }; class Entity { diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index 2a4a66e..c04e612 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -1596,6 +1596,62 @@ 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::initLuaPre() { auto &lua = LuaMainState; @@ -1608,9 +1664,6 @@ void GameServer::initLuaPre() { } auto &result = *result_o; - - DefNode_t node; - ResourceId_t sId; if(result[1]) @@ -1618,7 +1671,7 @@ void GameServer::initLuaPre() { else sId = Content.registerContent(CurrentModId+":"+*result[2], EnumDefContent::Node); - Content.ContentIdToDef_Node.insert({sId, std::move(node)}); + Content.ContentIdToDef_Node.insert({sId, createNodeProfileByLua(profile)}); }); } diff --git a/Src/Server/GameServer.hpp b/Src/Server/GameServer.hpp index b5cdf89..6c90f95 100644 --- a/Src/Server/GameServer.hpp +++ b/Src/Server/GameServer.hpp @@ -38,6 +38,7 @@ struct ModRequest { struct ModInfo { std::string Id, Name, Description, Author; + std::vector AlternativeIds; std::array Version; std::vector Dependencies, Optional; float LoadPriority; @@ -301,10 +302,6 @@ class GameServer : public AsyncObject { // Идентификатор текущегго мода, находящевося в обработке std::string CurrentModId; - struct { - - }; - public: GameServer(asio::io_context &ioc, fs::path worldPath); virtual ~GameServer(); @@ -338,6 +335,8 @@ private: void prerun(); void run(); + DefNode_t createNodeProfileByLua(const sol::table& profile); + void initLuaPre(); void initLua();