From d0d925de05eb61179d22477e554a47cb9ca9ff29 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Wed, 20 Aug 2025 17:53:35 +0600 Subject: [PATCH] * --- Src/Client/Vulkan/Vulkan.cpp | 4 +- Src/Common/Abstract.cpp | 845 +++++++++++++++++++++++++++++++++- Src/Common/Abstract.hpp | 63 ++- Src/Server/Abstract.cpp | 385 ---------------- Src/Server/Abstract.hpp | 28 +- Src/Server/ContentManager.cpp | 2 +- 6 files changed, 900 insertions(+), 427 deletions(-) diff --git a/Src/Client/Vulkan/Vulkan.cpp b/Src/Client/Vulkan/Vulkan.cpp index cf61873..2f988b7 100644 --- a/Src/Client/Vulkan/Vulkan.cpp +++ b/Src/Client/Vulkan/Vulkan.cpp @@ -2229,7 +2229,7 @@ void Vulkan::gui_ConnectedToServer() { if(ImGui::Begin("MainMenu", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { std::string text = std::to_string(ImGui::GetIO().Framerate); - ImGui::Text(text.c_str()); + ImGui::Text("%s", text.c_str()); if(ImGui::Button("Выйти")) { try { if(Game.Session) @@ -2238,7 +2238,9 @@ void Vulkan::gui_ConnectedToServer() { LOG.error() << "Game.Session->shutdown: " << exc.what(); } + Game.RSession->pushStage(EnumRenderStage::Shutdown); Game.RSession = nullptr; + Game.Session = nullptr; Game.ImGuiInterfaces.pop_back(); int mode = glfwGetInputMode(Graphics.Window, GLFW_CURSOR); if(mode != GLFW_CURSOR_NORMAL) diff --git a/Src/Common/Abstract.cpp b/Src/Common/Abstract.cpp index a187248..821a876 100644 --- a/Src/Common/Abstract.cpp +++ b/Src/Common/Abstract.cpp @@ -1,10 +1,15 @@ #include "Abstract.hpp" +#include "boost/json.hpp" +#include "boost/json/string_view.hpp" #include #include #include #include #include #include +#include +#include +#include namespace LV { @@ -823,18 +828,848 @@ std::u8string unCompressLinear(const std::u8string& data) { return *(std::u8string*) &outString; } -PreparedNodeState::PreparedNodeState(const js::object& profile) { - for(auto& [key, value] : profile) { - +PreparedNodeState::PreparedNodeState(const std::string_view modid, const js::object& profile) { + for(auto& [condition, variability] : profile) { + // Распарсить условие + uint16_t node = parseCondition(condition); + + boost::container::small_vector< + std::pair>, + 1 + > models; + + if(variability.is_array()) { + // Варианты условия + for(const js::value& model : variability.as_array()) { + models.push_back(parseModel(modid, model.as_object())); + } + + HasVariability = true; + } else if (variability.is_object()) { + // Один список моделей на условие + models.push_back(parseModel(modid, variability.as_object())); + } else { + MAKE_ERROR("Условию должен соответствовать список или объект"); + } + + Routes.emplace_back(node, std::move(models)); } } -PreparedNodeState::PreparedNodeState(const sol::table& profile) { +PreparedNodeState::PreparedNodeState(const std::string_view modid, const sol::table& profile) { } -PreparedNodeState::PreparedNodeState(const std::u8string& data) { +PreparedNodeState::PreparedNodeState(const std::string_view modid, const std::u8string& data) { } +std::u8string PreparedNodeState::dump() const { + std::basic_stringstream result; + uint16_t v16; + + // ResourceToLocalId + assert(ResourceToLocalId.size() < (1 << 16)); + v16 = ResourceToLocalId.size(); + result.put(uint8_t(v16 & 0xff)); + result.put(uint8_t((v16 >> 8) & 0xff)); + + for(const auto& [domain, key] : ResourceToLocalId) { + assert(domain.size() < 32); + result.put(uint8_t(domain.size() & 0xff)); + result << (const std::u8string&) domain; + assert(key.size() < 32); + result.put(uint8_t(key.size() & 0xff)); + result << (const std::u8string&) key; + } + + // Nodes + assert(Nodes.size() < (1 << 16)); + v16 = Nodes.size(); + result.put(uint8_t(v16 & 0xff)); + result.put(uint8_t((v16 >> 8) & 0xff)); + + for(const Node& node : Nodes) { + result.put(uint8_t(node.v.index())); + + if(const Node::Num* val = std::get_if(&node.v)) { + for(int iter = 0; iter < 4; iter++) { + result.put((val->v >> 8*iter) & 0xff); + } + } else if(const Node::Var* val = std::get_if(&node.v)) { + assert(val->name.size() < 32); + result << (const std::u8string&) val->name; + } else if(const Node::Unary* val = std::get_if(&node.v)) { + result.put(uint8_t(val->op)); + result.put(uint8_t(val->rhs & 0xff)); + result.put(uint8_t((val->rhs >> 8) & 0xff)); + } else if(const Node::Binary* val = std::get_if(&node.v)) { + result.put(uint8_t(val->op)); + result.put(uint8_t(val->lhs & 0xff)); + result.put(uint8_t((val->lhs >> 8) & 0xff)); + result.put(uint8_t(val->rhs & 0xff)); + result.put(uint8_t((val->rhs >> 8) & 0xff)); + } else { + std::unreachable(); + } + } + + // Routes + assert(Routes.size() < (1 << 16)); + v16 = Routes.size(); + result.put(uint8_t(v16 & 0xff)); + result.put(uint8_t((v16 >> 8) & 0xff)); + + for(const auto& [nodeId, variants] : Routes) { + result.put(uint8_t(nodeId & 0xff)); + result.put(uint8_t((nodeId >> 8) & 0xff)); + + assert(variants.size() < (1 << 16)); + v16 = variants.size(); + result.put(uint8_t(v16 & 0xff)); + result.put(uint8_t((v16 >> 8) & 0xff)); + + for(const auto& [weight, model] : variants) { + union { + float f_val; + uint32_t i_val; + }; + + f_val = weight; + + for(int iter = 0; iter < 4; iter++) { + result.put((i_val >> 8*iter) & 0xff); + } + + result.put(uint8_t(model.index())); + if(const Model* val = std::get_if(&model)) { + result.put(uint8_t(val->Id & 0xff)); + result.put(uint8_t((val->Id >> 8) & 0xff)); + + result.put(uint8_t(val->UVLock)); + + assert(val->Transforms.size() < (1 << 16)); + v16 = val->Transforms.size(); + result.put(uint8_t(v16 & 0xff)); + result.put(uint8_t((v16 >> 8) & 0xff)); + + for(const Transformation& val : val->Transforms) { + result.put(uint8_t(val.Op)); + f_val = val.Value; + for(int iter = 0; iter < 4; iter++) + result.put((i_val >> 8*iter) & 0xff); + } + } else if(const VectorModel* val = std::get_if(&model)) { + assert(val->Models.size() < (1 << 16)); + v16 = val->Models.size(); + for(const Model& subModel : val->Models) { + result.put(uint8_t(subModel.Id & 0xff)); + result.put(uint8_t((subModel.Id >> 8) & 0xff)); + + result.put(uint8_t(subModel.UVLock)); + + assert(subModel.Transforms.size() < (1 << 16)); + v16 = subModel.Transforms.size(); + result.put(uint8_t(v16 & 0xff)); + result.put(uint8_t((v16 >> 8) & 0xff)); + + for(const Transformation& val : subModel.Transforms) { + result.put(uint8_t(val.Op)); + f_val = val.Value; + for(int iter = 0; iter < 4; iter++) + result.put((i_val >> 8*iter) & 0xff); + } + } + + result.put(uint8_t(val->UVLock)); + + assert(val->Transforms.size() < (1 << 16)); + v16 = val->Transforms.size(); + result.put(uint8_t(v16 & 0xff)); + result.put(uint8_t((v16 >> 8) & 0xff)); + + for(const Transformation& val : val->Transforms) { + result.put(uint8_t(val.Op)); + f_val = val.Value; + for(int iter = 0; iter < 4; iter++) + result.put((i_val >> 8*iter) & 0xff); + } + } + } + } + + return result.str(); +} + +bool PreparedNodeState::read_uint16(std::basic_istream& stream, uint16_t& value) noexcept { + char8_t lo, hi; + if (!(stream >> lo)) return false; + if (!(stream >> hi)) return false; + value = (static_cast(hi) << 8) | lo; + return true; +} + +bool PreparedNodeState::load(const std::u8string& data) noexcept { +std::basic_istringstream stream(data); + char8_t byte; + uint16_t size, v16; + char8_t buffer[32]; + + // Читаем ResourceToLocalId + if (!read_uint16(stream, size)) return false; + ResourceToLocalId.clear(); + for (uint16_t i = 0; i < size; ++i) { + if (!(stream >> byte)) return false; + size_t domain_len = byte & 0xff; + if (domain_len >= 32) return false; + if (!stream.read(buffer, domain_len)) return false; + std::string domain((const char*) buffer, domain_len); + + if (!(stream >> byte)) return false; + size_t key_len = byte & 0xff; + if (key_len >= 32) return false; + if (!stream.read(buffer, key_len)) return false; + std::string key((const char*) buffer, key_len); + + ResourceToLocalId.emplace_back(std::move(domain), std::move(key)); + } + + // Читаем Nodes + if (!read_uint16(stream, size)) return false; + Nodes.clear(); + Nodes.reserve(size); + for (uint16_t i = 0; i < size; ++i) { + if (!(stream >> byte)) return false; + uint8_t tag = byte; + + Node node; + switch (tag) { + case 0: { // Node::Num + uint32_t val = 0; + for (int iter = 0; iter < 4; ++iter) { + if (!(stream >> byte)) return false; + val |= (static_cast(byte) << (8 * iter)); + } + node.v = Node::Num{ int32_t(val) }; + break; + } + case 1: { // Node::Var + if (!(stream >> byte)) return false; + size_t len = byte & 0xff; + if (len >= 32) return false; + if (!stream.read(buffer, len)) return false; + node.v = Node::Var{ std::string((const char*) buffer, len) }; + break; + } + case 2: { // Node::Unary + if (!(stream >> byte)) return false; + uint8_t op = byte; + if (!read_uint16(stream, v16)) return false; + node.v = Node::Unary{ op, v16 }; + break; + } + case 3: { // Node::Binary + if (!(stream >> byte)) return false; + uint8_t op = byte; + if (!read_uint16(stream, v16)) return false; + uint16_t lhs = v16; + if (!read_uint16(stream, v16)) return false; + uint16_t rhs = v16; + node.v = Node::Binary{ op, lhs, rhs }; + break; + } + default: + return false; // неизвестный тип + } + Nodes.push_back(std::move(node)); + } + + // Читаем Routes + if (!read_uint16(stream, size)) return false; + Routes.clear(); + for (uint16_t i = 0; i < size; ++i) { + if (!read_uint16(stream, v16)) return false; + uint16_t nodeId = v16; + + if (!read_uint16(stream, size)) return false; + std::vector>> variants; + variants.reserve(size); + + for (uint16_t j = 0; j < size; ++j) { + // Читаем вес (float) + uint32_t f_bits = 0; + for (int iter = 0; iter < 4; ++iter) { + if (!(stream >> byte)) return false; + f_bits |= (static_cast(byte) << (8 * iter)); + } + float weight; + std::memcpy(&weight, &f_bits, 4); + + if (!(stream >> byte)) return false; + uint8_t model_tag = byte; + + if (model_tag == 0) { // Model + Model model; + if (!read_uint16(stream, v16)) return false; + model.Id = v16; + + if (!(stream >> byte)) return false; + model.UVLock = static_cast(byte & 1); + + if (!read_uint16(stream, v16)) return false; + model.Transforms.clear(); + for (uint16_t k = 0; k < v16; ++k) { + if (!(stream >> byte)) return false; + uint8_t op = byte; + + uint32_t val_bits = 0; + for (int iter = 0; iter < 4; ++iter) { + if (!(stream >> byte)) return false; + val_bits |= (static_cast(byte) << (8 * iter)); + } + float f_val; + std::memcpy(&f_val, &val_bits, 4); + + model.Transforms.push_back({ op, f_val }); + } + variants.emplace_back(weight, std::move(model)); + } else if (model_tag == 1) { // VectorModel + VectorModel vecModel; + if (!read_uint16(stream, v16)) return false; + size_t num_models = v16; + vecModel.Models.clear(); + vecModel.Models.reserve(num_models); + + for (size_t m = 0; m < num_models; ++m) { + Model subModel; + if (!read_uint16(stream, v16)) return false; + subModel.Id = v16; + + if (!(stream >> byte)) return false; + subModel.UVLock = static_cast(byte & 1); + + if (!read_uint16(stream, v16)) return false; + subModel.Transforms.clear(); + for (uint16_t k = 0; k < v16; ++k) { + if (!(stream >> byte)) return false; + uint8_t op = byte; + + uint32_t val_bits = 0; + for (int iter = 0; iter < 4; ++iter) { + if (!(stream >> byte)) return false; + val_bits |= (static_cast(byte) << (8 * iter)); + } + float f_val; + std::memcpy(&f_val, &val_bits, 4); + + subModel.Transforms.push_back({ op, f_val }); + } + vecModel.Models.push_back(std::move(subModel)); + } + + if (!(stream >> byte)) return false; + vecModel.UVLock = static_cast(byte & 1); + + if (!read_uint16(stream, v16)) return false; + vecModel.Transforms.clear(); + for (uint16_t k = 0; k < v16; ++k) { + if (!(stream >> byte)) return false; + uint8_t op = byte; + + uint32_t val_bits = 0; + for (int iter = 0; iter < 4; ++iter) { + if (!(stream >> byte)) return false; + val_bits |= (static_cast(byte) << (8 * iter)); + } + float f_val; + std::memcpy(&f_val, &val_bits, 4); + + vecModel.Transforms.push_back({ op, f_val }); + } + variants.emplace_back(weight, std::move(vecModel)); + } else { + return false; // неизвестный тип модели + } + } + Routes[nodeId] = std::move(variants); + } + + return true; +} + +uint16_t PreparedNodeState::parseCondition(const std::string_view expression) { + 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); + } + + + for(size_t index = 0; index < tokens.size(); index++) { + auto &token = tokens[index]; + + if(std::string_view* value = std::get_if(&token)) { + if(*value == "false") { + token = 0; + } else if(*value == "true") { + token = 1; + } else { + Node node; + node.v = Node::Var((std::string) *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: std::unreachable(); + } + + + 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: std::unreachable(); + } + + 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("Выражение не действительно"); + + return nodeId; + + // 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: + // std::unreachable(); + // } + // } 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: + // std::unreachable(); + // } + // } else { + // std::unreachable(); + // } + // }; +} + +std::pair> PreparedNodeState::parseModel(const std::string_view modid, const js::object& obj) { + // ResourceToLocalId + + bool uvlock; + float weight = 1; + std::vector transforms; + + if(const auto weight_val = obj.try_at("weight")) { + weight = weight_val->as_double(); + } + + if(const auto uvlock_val = obj.try_at("uvlock")) { + uvlock = uvlock_val->as_bool(); + } + + if(const auto transformations_val = obj.try_at("transformations")) { + transforms = parseTransormations(transformations_val->as_array()); + } + + const js::value& model = obj.at("model"); + if(const auto model_key = model.try_as_string()) { + // Одна модель + Model result; + result.UVLock = uvlock; + result.Transforms = std::move(transforms); + + auto [domain, key] = parseDomainKey((std::string) *model_key, modid); + + uint16_t resId = 0; + for(auto& [lDomain, lKey] : ResourceToLocalId) { + if(lDomain == domain && lKey == key) + break; + + resId++; + } + + if(resId == ResourceToLocalId.size()) { + ResourceToLocalId.emplace_back(domain, key); + } + + result.Id = resId; + + return {weight, result}; + } else if(model.is_array()) { + // Множество моделей + VectorModel result; + result.UVLock = uvlock; + result.Transforms = std::move(transforms); + + for(const js::value& js_value : model.as_array()) { + const js::object& js_obj = js_value.as_object(); + + Model subModel; + if(const auto uvlock_val = js_obj.try_at("uvlock")) { + subModel.UVLock = uvlock_val->as_bool(); + } + + if(const auto transformations_val = js_obj.try_at("transformations")) { + subModel.Transforms = parseTransormations(transformations_val->as_array()); + } + + auto [domain, key] = parseDomainKey((std::string) js_obj.at("model").as_string(), modid); + + uint16_t resId = 0; + for(auto& [lDomain, lKey] : ResourceToLocalId) { + if(lDomain == domain && lKey == key) + break; + + resId++; + } + + if(resId == ResourceToLocalId.size()) { + ResourceToLocalId.emplace_back(domain, key); + } + + subModel.Id = resId; + result.Models.push_back(std::move(subModel)); + } + + return {weight, result}; + } else { + MAKE_ERROR(""); + } +} + +std::vector PreparedNodeState::parseTransormations(const js::array& arr) { + std::vector result; + + for(const js::value& js_value : arr) { + const js::string_view transform = js_value.as_string(); + + auto pos = transform.find('='); + std::string_view key = transform.substr(0, pos); + std::string_view value = transform.substr(pos+1); + + float f_value; + auto [partial_ptr, partial_ec] = std::from_chars(value.data(), value.data() + value.size(), f_value); + + if(partial_ec == std::errc{} && partial_ptr != value.data() + value.size()) { + MAKE_ERROR("Converted part of the string: " << value << " (remaining: " << std::string_view(partial_ptr, value.data() + value.size() - partial_ptr) << ")"); + } else if(partial_ec != std::errc{}) { + MAKE_ERROR("Error converting partial string: " << value); + } + + if(key == "x") + result.emplace_back(Transformation::MoveX, f_value); + else if(key == "y") + result.emplace_back(Transformation::MoveY, f_value); + else if(key == "z") + result.emplace_back(Transformation::MoveZ, f_value); + else if(key == "rx") + result.emplace_back(Transformation::RotateX, f_value); + else if(key == "ry") + result.emplace_back(Transformation::RotateY, f_value); + else if(key == "rz") + result.emplace_back(Transformation::RotateZ, f_value); + else + MAKE_ERROR("Неизвестный ключ трансформации"); + } + + return result; +} + } \ No newline at end of file diff --git a/Src/Common/Abstract.hpp b/Src/Common/Abstract.hpp index 2a435ca..7896cea 100644 --- a/Src/Common/Abstract.hpp +++ b/Src/Common/Abstract.hpp @@ -2,6 +2,7 @@ #include "Common/Net.hpp" #include "TOSLib.hpp" +#include "boost/json/array.hpp" #include #include #include @@ -504,6 +505,13 @@ enum struct TexturePipelineCMD : uint8_t { }; + +struct NodestateEntry { + std::string Name; + int Variability = 0; // Количество возможный значений состояния + std::vector ValueNames; // Имена состояний, если имеются +}; + /* Хранит распаршенное определение состояний нод. Не привязано ни к какому окружению. @@ -517,7 +525,7 @@ struct PreparedNodeState { }; struct Node { - struct Num { int v; }; + struct Num { int32_t v; }; struct Var { std::string name; }; struct Unary { Op op; uint16_t rhs; }; struct Binary { Op op; uint16_t lhs, rhs; }; @@ -525,7 +533,13 @@ struct PreparedNodeState { }; struct Transformation { - int a; float b; + enum EnumTransform { + MoveX, MoveY, MoveZ, + RotateX, RotateY, RotateZ, + MAX_ENUM + } Op; + + float Value; }; struct Model { @@ -546,18 +560,18 @@ struct PreparedNodeState { // Ноды выражений std::vector Nodes; // Условия -> вариации модели + веса - std::vector< + boost::container::small_vector< std::pair, uint16_t>, + std::pair>, 1 > > - > Routes; + , 1> Routes; - PreparedNodeState(const js::object& profile); - PreparedNodeState(const sol::table& profile); - PreparedNodeState(const std::u8string& data); + PreparedNodeState(const std::string_view modid, const js::object& profile); + PreparedNodeState(const std::string_view modid, const sol::table& profile); + PreparedNodeState(const std::string_view modid, const std::u8string& data); PreparedNodeState() = default; PreparedNodeState(const PreparedNodeState&) = default; @@ -568,6 +582,27 @@ struct PreparedNodeState { // Пишет в сжатый двоичный формат std::u8string dump() const; + + bool hasVariability() const { + return HasVariability; + } + +private: + bool HasVariability = false; + + static bool read_uint16(std::basic_istream& stream, uint16_t& value) noexcept; + bool load(const std::u8string& data) noexcept; + uint16_t parseCondition(const std::string_view condition); + std::pair> parseModel(const std::string_view modid, const js::object& obj); + std::vector parseTransormations(const js::array& arr); +}; + +/* + Хранит распаршенную и по необходимости упрощённую модель + +*/ +struct PreparedModel { + }; struct TexturePipeline { @@ -577,6 +612,18 @@ struct TexturePipeline { using Hash_t = std::array; +inline std::pair parseDomainKey(const std::string& value, const std::string_view defaultDomain = "core") { + auto regResult = TOS::Str::match(value, "(?:([\\w\\d_]+):)?([\\w\\d_]+)"); + if(!regResult) + MAKE_ERROR("Недействительный домен:ключ"); + + if(regResult->at(1)) { + return std::pair{*regResult->at(1), *regResult->at(2)}; + } else { + return std::pair{defaultDomain, *regResult->at(2)}; + } +} + } diff --git a/Src/Server/Abstract.cpp b/Src/Server/Abstract.cpp index 5cbf82b..f2b7caf 100644 --- a/Src/Server/Abstract.cpp +++ b/Src/Server/Abstract.cpp @@ -4,391 +4,6 @@ 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: std::unreachable(); - } - - - 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: std::unreachable(); - } - - 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: - std::unreachable(); - } - } 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: - std::unreachable(); - } - } else { - std::unreachable(); - } - }; - - 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; -} } namespace std { diff --git a/Src/Server/Abstract.hpp b/Src/Server/Abstract.hpp index d7c1298..b556e62 100644 --- a/Src/Server/Abstract.hpp +++ b/Src/Server/Abstract.hpp @@ -153,30 +153,6 @@ struct CollisionAABB : public AABB { bool Skip = false; }; -/* - Модель, считанная с файла и предкомпилированна - Для компиляции нужно собрать зависимости модели, - и в случае их изменения нужно перекомпилировать модель (может просто перекомпилировать всё разом?) -*/ -struct PreCompiledModel_t { - -}; - -struct NodestateEntry { - std::string Name; - int Variability = 0; - std::vector ValueNames; -}; - -using NodeStateCondition = std::bitset<256>; -NodeStateCondition nodestateExpression(const std::vector& entries, const std::string& expression); - -struct ModelTransform { - std::vector Ids; - uint16_t Weight = 1; - bool UVLock = false; -}; - /* Указать модель, текстуры и поворот по конкретным осям. Может быть вариативность моделей относительно одного условия (случайность в зависимости от координат?) @@ -197,13 +173,11 @@ struct ModelTransform { uvlock ? https://minecraft.wiki/w/Blockstates_definition/format */ -using DefNodestates_t = std::unordered_map>; - // Скомпилированный профиль ноды struct DefNode_t { // Зарегистрированные состояния (мета) // Подгружается с файла assets//nodestate/node/nodeId.json - std::variant> StatesRouter; + // std::variant> StatesRouter; // Параметры рендера struct { diff --git a/Src/Server/ContentManager.cpp b/Src/Server/ContentManager.cpp index d1996cd..53d44bc 100644 --- a/Src/Server/ContentManager.cpp +++ b/Src/Server/ContentManager.cpp @@ -29,7 +29,7 @@ void ContentManager::registerBase_Node(ResourceId id, const sol::table& profile) std::string realKey; - if(regResult->at(1)) { + if(!regResult->at(1)) { realKey = *key; } else { realKey = "core:" + *regResult->at(2);