From 2aea19febdc6588c6e5a47564bdf1ea7f93085e7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 23 Mar 2025 08:49:09 +0300 Subject: [PATCH 01/34] add YAML parser --- src/coders/BasicParser.hpp | 6 +- src/coders/BasicParser.inl | 45 +++++- src/coders/json.cpp | 9 +- src/coders/toml.cpp | 11 +- src/coders/yaml.cpp | 318 +++++++++++++++++++++++++++++++++++++ src/coders/yaml.hpp | 10 ++ src/frontend/locale.cpp | 10 +- test/coders/yaml.cpp | 22 +++ 8 files changed, 406 insertions(+), 25 deletions(-) create mode 100644 src/coders/yaml.cpp create mode 100644 src/coders/yaml.hpp create mode 100644 test/coders/yaml.cpp diff --git a/src/coders/BasicParser.hpp b/src/coders/BasicParser.hpp index e3a3dda8..ba1cf5a8 100644 --- a/src/coders/BasicParser.hpp +++ b/src/coders/BasicParser.hpp @@ -7,16 +7,20 @@ template class BasicParser { using StringT = std::basic_string; using StringViewT = std::basic_string_view; + + void skipWhitespaceHashComment(bool newline = true); protected: std::string_view filename; StringViewT source; uint pos = 0; uint line = 1; uint linestart = 0; + bool hashComment = false; - virtual void skipWhitespace(); + void skipWhitespace(bool newline = true); void skip(size_t n); void skipLine(); + void skipEmptyLines(); bool skipTo(const StringT& substring); void expect(CharT expected); void expect(const StringT& substring); diff --git a/src/coders/BasicParser.inl b/src/coders/BasicParser.inl index 2d15d8e9..72f644ba 100644 --- a/src/coders/BasicParser.inl +++ b/src/coders/BasicParser.inl @@ -31,10 +31,17 @@ namespace { } template -void BasicParser::skipWhitespace() { +void BasicParser::skipWhitespace(bool newline) { + if (hashComment) { + skipWhitespaceHashComment(newline); + return; + } while (hasNext()) { char next = source[pos]; if (next == '\n') { + if (!newline) { + break; + } line++; linestart = ++pos; continue; @@ -47,6 +54,36 @@ void BasicParser::skipWhitespace() { } } +template +void BasicParser::skipWhitespaceHashComment(bool newline) { + while (hasNext()) { + char next = source[pos]; + if (next == '\n') { + if (!newline) { + break; + } + line++; + linestart = ++pos; + continue; + } + if (is_whitespace(next)) { + pos++; + } else { + break; + } + } + if (hasNext() && source[pos] == '#') { + if (!newline) { + readUntilEOL(); + return; + } + skipLine(); + if (hasNext() && (is_whitespace(source[pos]) || source[pos] == '#')) { + skipWhitespaceHashComment(newline); + } + } +} + template void BasicParser::skip(size_t n) { n = std::min(n, source.length() - pos); @@ -73,6 +110,12 @@ void BasicParser::skipLine() { } } +template +void BasicParser::skipEmptyLines() { + skipWhitespace(); + pos = linestart; +} + template bool BasicParser::skipTo(const std::basic_string& substring) { size_t idx = source.find(substring, pos); diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 19110f90..bbc56360 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -13,13 +13,14 @@ using namespace json; namespace { class Parser : BasicParser { - dv::value parseList(); - dv::value parseObject(); - dv::value parseValue(); - public: + public: Parser(std::string_view filename, std::string_view source); dv::value parse(); + private: + dv::value parseList(); + dv::value parseObject(); + dv::value parseValue(); }; } diff --git a/src/coders/toml.cpp b/src/coders/toml.cpp index 8fe05391..1652745a 100644 --- a/src/coders/toml.cpp +++ b/src/coders/toml.cpp @@ -16,16 +16,6 @@ using namespace toml; class TomlReader : BasicParser { dv::value root; - void skipWhitespace() override { - BasicParser::skipWhitespace(); - if (hasNext() && source[pos] == '#') { - skipLine(); - if (hasNext() && is_whitespace(peek())) { - skipWhitespace(); - } - } - } - // modified version of BaseParser.parseString // todo: extract common part std::string parseMultilineString() { @@ -214,6 +204,7 @@ class TomlReader : BasicParser { public: TomlReader(std::string_view file, std::string_view source) : BasicParser(file, source), root(dv::object()) { + hashComment = true; } dv::value read() { diff --git a/src/coders/yaml.cpp b/src/coders/yaml.cpp new file mode 100644 index 00000000..a7a1101f --- /dev/null +++ b/src/coders/yaml.cpp @@ -0,0 +1,318 @@ +#include "yaml.hpp" +#include "BasicParser.hpp" + +using namespace yaml; + +namespace { + enum Chomping { + CLIP, STRIP, KEEP + }; + + class Parser : BasicParser { + public: + Parser(std::string_view filename, std::string_view source); + + dv::value parseValue(); + dv::value parseFullValue(int indent); + dv::value parseArray(int indent = 0); + dv::value parseObject(dv::value&& object, int indent = 0); + dv::value parseInlineArray(); + dv::value parseInlineObject(); + private: + int countIndent(); + bool expectIndent(int indent); + std::string_view readYamlIdentifier(); + std::string readMultilineString(int indent, bool eols, Chomping chomp); + }; +} + +inline bool is_yaml_identifier_char(int c) { + return c > 20 && c != ':' && c != ' ' && c != '\n' && c != '\r' && + c != '\t' && c != '\f' && c != '\v'; +} + +static dv::value perform_literal(std::string_view literal) { + if (literal == "true" || literal == "True" || + literal == "false" || literal == "False") { + return literal[0] == 't'; + } + if (literal == "null" || literal == "Null") { + return nullptr; + } + return std::string(literal); +} + +Parser::Parser(std::string_view filename, std::string_view source) + : BasicParser(filename, source) { + hashComment = true; +} + +bool Parser::expectIndent(int required) { + int indent = 0; + while (hasNext() && source[pos] == ' ' && indent < required) { + indent++; + pos++; + } + return indent >= required; +} + +std::string Parser::readMultilineString(int indent, bool eols, Chomping chomp) { + int next_indent = countIndent(); + if (next_indent <= indent) { + throw error("indentation error"); + } + std::stringstream ss; + ss << readUntilEOL(); + if (hasNext()) { + skip(1); + } + int trailingEmpties = 0; + while (true) { + while (expectIndent(next_indent)) { + trailingEmpties = 0; + ss << (eols ? '\n' : ' '); + ss << readUntilEOL(); + if (hasNext()) { + skip(1); + } + } + while (true) { + skipWhitespace(false); + if (!hasNext() || source[pos] != '\n') { + break; + } + skip(1); + trailingEmpties++; + } + if (!expectIndent(next_indent)) { + break; + } + pos = linestart; + } + if (chomp == KEEP) { + for (int i = 0; i < trailingEmpties - 1; i++) { + ss << (eols ? '\n' : ' '); + } + } + ss << '\n'; + + pos = linestart; + + auto string = ss.str(); + if (chomp == STRIP) { + util::trim(string); + } + return string; +} + +std::string_view Parser::readYamlIdentifier() { + char c = peek(); + if (!is_yaml_identifier_char(c)) { + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_yaml_identifier_char(source[pos])) { + pos++; + } + return source.substr(start, pos - start); +} + +int Parser::countIndent() { + int indent = 0; + while (hasNext() && source[pos] == ' ') { + indent++; + pos++; + } + return indent; +} + +dv::value Parser::parseValue() { + char c = peek(); + if (is_digit(c)) { + return parseNumber(1); + } else if (c == '-' || c == '+') { + skip(1); + return parseNumber(c == '-' ? -1 : 1); + } else if (c == '"' || c == '\'') { + skip(1); + return parseString(c, true); + } else if (c == '[') { + return parseInlineArray(); + } else if (c == '{') { + return parseInlineObject(); + } else { + return perform_literal(readUntilEOL()); + } + throw error("unexpected character"); +} + +dv::value Parser::parseInlineArray() { + expect('['); + auto list = dv::list(); + while (peek() != ']') { + if (peek() == '#') { + skipLine(); + continue; + } + list.add(parseValue()); + + char next = peek(); + if (next == ',') { + pos++; + } else if (next == ']') { + break; + } else { + throw error("',' expected"); + } + } + pos++; + return list; +} + +dv::value Parser::parseInlineObject() { + expect('{'); + dv::value object = dv::object(); + while (peek() != '}') { + if (peek() == '#') { + skipLine(); + continue; + } + auto name = readYamlIdentifier(); + expect(':'); + object[std::string(name)] = parseValue(); + + char next = peek(); + if (next == ',') { + pos++; + } else if (next == '}') { + break; + } else { + throw error("',' expected"); + } + } + pos++; + return object; +} + +dv::value Parser::parseFullValue(int indent) { + dv::value value; + char c = source[pos]; + if (c == '\n') { + skip(1); + skipEmptyLines(); + int init_pos = pos; + int next_indent = countIndent(); + if (next_indent < indent) { + throw error("indentation error"); + } + if (source[pos] == '-') { + pos = init_pos; + return parseArray(next_indent); + } else { + pos = init_pos; + return parseObject(dv::object(), next_indent); + } + } else if (is_digit(c)) { + return parseNumber(1); + } else if (c == '-' || c == '+') { + skip(1); + return parseNumber(c == '-' ? -1 : 1); + } else if (c == '"' || c == '\'') { + skip(1); + return parseString(c, true); + } else if (c == '[') { + return parseInlineArray(); + } else if (c == '{') { + return parseInlineObject(); + } else if (c == '|' || c == '>') { + skip(1); + Chomping chomp = CLIP; + if (source[pos] == '-' || source[pos] == '+') { + chomp = source[pos] == '-' ? STRIP : KEEP; + skip(1); + } + skipWhitespace(false); + expectNewLine(); + return readMultilineString(indent, c == '|', chomp); + } else { + return perform_literal(readUntilEOL()); + } +} + +dv::value Parser::parseArray(int indent) { + dv::value list = dv::list(); + + while (hasNext()) { + skipEmptyLines(); + int next_indent = countIndent(); + if (next_indent < indent) { + pos = linestart; + break; + } + expect('-'); + skipWhitespace(); + size_t nlpos = source.find('\n', pos); + size_t colonpos = source.find(':', pos); + if (nlpos == std::string::npos && colonpos == std::string::npos) { + list.add(perform_literal(readUntilEOL())); + break; + } + if (nlpos < colonpos) { + list.add(parseFullValue(next_indent)); + skipLine(); + } else { + auto name = readYamlIdentifier(); + expect(':'); + skipWhitespace(false); + dv::value object = dv::object(); + object[std::string(name)] = parseFullValue(next_indent); + skipEmptyLines(); + next_indent = countIndent(); + if (next_indent > indent) { + pos = linestart; + object = parseObject(std::move(object), next_indent); + } else { + pos = linestart; + } + list.add(std::move(object)); + } + } + return list; +} + +dv::value Parser::parseObject(dv::value&& object, int indent) { + skipEmptyLines(); + while (hasNext()) { + size_t prev_pos = pos; + int next_indent = countIndent(); + if (source[pos] == '\n') { + skip(1); + continue; + } + if (next_indent < indent) { + pos = prev_pos; + break; + } + char c = peek(); + if (!is_yaml_identifier_char(c)) { + if (!is_whitespace(c)) { + throw error("invalid character"); + } + continue; + } + auto name = readYamlIdentifier(); + expect(':'); + skipWhitespace(false); + object[std::string(name)] = parseFullValue(indent); + skipEmptyLines(); + } + return object; +} + +dv::value yaml::parse(std::string_view filename, std::string_view source) { + return Parser(filename, source).parseObject(dv::object()); +} + +dv::value yaml::parse(std::string_view source) { + return parse("[string]", source); +} diff --git a/src/coders/yaml.hpp b/src/coders/yaml.hpp new file mode 100644 index 00000000..2802e523 --- /dev/null +++ b/src/coders/yaml.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "data/dv.hpp" + +namespace yaml { + dv::value parse(std::string_view filename, std::string_view source); + dv::value parse(std::string_view source); +} diff --git a/src/frontend/locale.cpp b/src/frontend/locale.cpp index 669d0b4c..4e1812dd 100644 --- a/src/frontend/locale.cpp +++ b/src/frontend/locale.cpp @@ -40,18 +40,10 @@ const std::string& langs::Lang::getId() const { /// @brief Language key-value txt files parser namespace { class Reader : BasicParser { - void skipWhitespace() override { - BasicParser::skipWhitespace(); - if (hasNext() && source[pos] == '#') { - skipLine(); - if (hasNext() && is_whitespace(peek())) { - skipWhitespace(); - } - } - } public: Reader(std::string_view file, std::string_view source) : BasicParser(file, source) { + hashComment = true; } void read(langs::Lang& lang, const std::string &prefix) { diff --git a/test/coders/yaml.cpp b/test/coders/yaml.cpp new file mode 100644 index 00000000..272b3b0e --- /dev/null +++ b/test/coders/yaml.cpp @@ -0,0 +1,22 @@ +#include + +#include "coders/yaml.hpp" +#include "coders/json.hpp" +#include "coders/commons.hpp" + +#include "io/io.hpp" +#include "io/devices/StdfsDevice.hpp" + +namespace fs = std::filesystem; + +TEST(YAML, EncodeDecode) { + io::set_device("root", std::make_shared(fs::u8path("../../"))); + auto filename = "root:.github/workflows/windows-clang.yml"; + try { + auto value = yaml::parse(io::read_string(filename)); + std::cout << json::stringify(value, true) << std::endl; + } catch (const parsing_error& error) { + std::cerr << error.errorLog() << std::endl; + throw error; + } +} From 396fab02b32ac75eaabf77c0b9d38e09cd2a816d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 05:30:56 +0300 Subject: [PATCH 02/34] add yaml::stringify --- src/coders/yaml.cpp | 141 ++++++++++++++++++++++++++++++++++++++++ src/coders/yaml.hpp | 2 + src/util/stringutil.hpp | 25 +++++++ test/coders/yaml.cpp | 2 +- 4 files changed, 169 insertions(+), 1 deletion(-) diff --git a/src/coders/yaml.cpp b/src/coders/yaml.cpp index a7a1101f..a9944ea3 100644 --- a/src/coders/yaml.cpp +++ b/src/coders/yaml.cpp @@ -1,6 +1,8 @@ #include "yaml.hpp" #include "BasicParser.hpp" +#include + using namespace yaml; namespace { @@ -316,3 +318,142 @@ dv::value yaml::parse(std::string_view filename, std::string_view source) { dv::value yaml::parse(std::string_view source) { return parse("[string]", source); } + +static void add_indent(std::stringstream& ss, int indent) { + for (int i = 0; i < indent; i++) { + ss << " "; + } +} + +static void insert_string( + std::stringstream& ss, const std::string& string, int indent +) { + bool has_spec_chars = false; + bool multiline = false; + for (char c : string) { + if (c < ' ' || c == '"' || c == '\'') { + has_spec_chars = true; + if (multiline) { + break; + } + } + if (c == '\n') { + multiline = true; + break; + } + } + if (multiline) { + ss << "|-\n"; + size_t offset = 0; + size_t newoffset = 0; + + do { + offset = newoffset; + if (offset == string.length() - 1 && string[offset] == '\n') { + break; + } + add_indent(ss, indent); + newoffset = string.find('\n', offset + 1); + if (newoffset == std::string::npos) { + ss << string.substr(offset); + break; + } else { + ss << string.substr(offset + 1, newoffset - offset - 1); + } + ss << '\n'; + } while (true); + } else { + if (has_spec_chars || string.empty()) { + ss << util::escape(string, false); + } else { + ss << string; + } + } +} + +static void to_string( + std::stringstream& ss, + const dv::value& value, + int indent, + bool eliminateIndent = false +) { + using dv::value_type; + + switch (value.getType()) { + case value_type::string: + insert_string(ss, value.asString(), indent); + break; + case value_type::number: + ss << std::setprecision(15) << value.asNumber(); + break; + case value_type::integer: + ss << value.asInteger(); + break; + case value_type::boolean: + ss << (value.asBoolean() ? "true" : "false"); + break; + case value_type::none: + ss << "null"; + break; + case value_type::object: { + if (value.empty()) { + ss << "{}"; + break; + } + bool first = true; + for (const auto& [key, elem] : value.asObject()) { + if (!first) { + ss << '\n'; + } + if (!eliminateIndent) { + add_indent(ss, indent); + } else { + eliminateIndent = false; + } + ss << key << ": "; + if ((elem.isObject() || elem.isList()) && !elem.empty()) { + ss << "\n"; + to_string(ss, elem, indent + 1); + } else { + to_string(ss, elem, indent + 1); + } + first = false; + } + break; + } + case value_type::list: { + if (value.empty()) { + ss << "[]"; + break; + } + bool first = true; + for (const auto& elem : value) { + if (!first) { + ss << '\n'; + } + if (!eliminateIndent) { + add_indent(ss, indent); + } else { + eliminateIndent = false; + } + ss << "- "; + to_string(ss, elem, indent + 1, true); + first = false; + } + break; + } + case value_type::bytes: { + const auto& bytes = value.asBytes(); + auto b64 = util::base64_encode(bytes.data(), bytes.size()); + b64 = util::join(util::split_by_n(b64, 64), '\n'); + insert_string(ss, b64, indent); + break; + } + } +} + +std::string yaml::stringify(const dv::value& value) { + std::stringstream ss; + to_string(ss, value, 0); + return ss.str(); +} diff --git a/src/coders/yaml.hpp b/src/coders/yaml.hpp index 2802e523..3d871855 100644 --- a/src/coders/yaml.hpp +++ b/src/coders/yaml.hpp @@ -7,4 +7,6 @@ namespace yaml { dv::value parse(std::string_view filename, std::string_view source); dv::value parse(std::string_view source); + + std::string stringify(const dv::value& value); } diff --git a/src/util/stringutil.hpp b/src/util/stringutil.hpp index d85f93cb..3d8bf16e 100644 --- a/src/util/stringutil.hpp +++ b/src/util/stringutil.hpp @@ -115,4 +115,29 @@ namespace util { std::string format_data_size(size_t size); std::pair split_at(std::string_view view, char c); + + template + std::vector> split_by_n( + const std::basic_string& str, size_t n + ) { + std::vector> result; + for (size_t i = 0; i < str.length(); i += n) { + result.push_back(str.substr(i, n)); + } + return result; + } + + template + std::basic_string join( + const std::vector>& strings, CharT delimiter + ) { + std::basic_stringstream ss; + for (size_t i = 0; i < strings.size(); ++i) { + ss << strings[i]; + if (i != strings.size() - 1) { + ss << delimiter; + } + } + return ss.str(); + } } diff --git a/test/coders/yaml.cpp b/test/coders/yaml.cpp index 272b3b0e..0401bb19 100644 --- a/test/coders/yaml.cpp +++ b/test/coders/yaml.cpp @@ -14,7 +14,7 @@ TEST(YAML, EncodeDecode) { auto filename = "root:.github/workflows/windows-clang.yml"; try { auto value = yaml::parse(io::read_string(filename)); - std::cout << json::stringify(value, true) << std::endl; + std::cout << yaml::stringify(value) << std::endl; } catch (const parsing_error& error) { std::cerr << error.errorLog() << std::endl; throw error; From c28bfae679d267c68b0de06d6cfe35afddc53a31 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 07:43:41 +0300 Subject: [PATCH 03/34] add yaml serialization library --- doc/en/scripting.md | 2 +- doc/en/scripting/filesystem.md | 16 ++++++++++++++++ doc/ru/scripting.md | 2 +- doc/ru/scripting/filesystem.md | 16 ++++++++++++++++ src/logic/scripting/lua/libs/api_lua.hpp | 3 ++- src/logic/scripting/lua/libs/libjson.cpp | 3 ++- src/logic/scripting/lua/libs/libyaml.cpp | 20 ++++++++++++++++++++ src/logic/scripting/lua/lua_engine.cpp | 1 + 8 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/logic/scripting/lua/libs/libyaml.cpp diff --git a/doc/en/scripting.md b/doc/en/scripting.md index 471bdb15..f205a237 100644 --- a/doc/en/scripting.md +++ b/doc/en/scripting.md @@ -11,7 +11,7 @@ Subsections: - [Libraries](#) - [app](scripting/builtins/libapp.md) - [base64](scripting/builtins/libbase64.md) - - [bjson, json, toml](scripting/filesystem.md) + - [bjson, json, toml, yaml](scripting/filesystem.md) - [block](scripting/builtins/libblock.md) - [byteutil](scripting/builtins/libbyteutil.md) - [cameras](scripting/builtins/libcameras.md) diff --git a/doc/en/scripting/filesystem.md b/doc/en/scripting/filesystem.md index b37c799f..8341e966 100644 --- a/doc/en/scripting/filesystem.md +++ b/doc/en/scripting/filesystem.md @@ -36,6 +36,22 @@ toml.parse(code: str) -> table Parses a TOML string into a table. +## *yaml* library + +The library contains functions for serializing and deserializing tables: + +```python +yaml.tostring(object: table) -> str +``` + +Serializes an object into a YAML string. + +```python +yaml.parse(code: str) -> table +``` + +Parses a YAML string into a table. + ## *bjson* library The library contains functions for working with the binary data exchange format [vcbjson](../../specs/binary_json_spec.md). diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index 942241b6..79e53450 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -11,7 +11,7 @@ - [Библиотеки](#) - [app](scripting/builtins/libapp.md) - [base64](scripting/builtins/libbase64.md) - - [bjson, json, toml](scripting/filesystem.md) + - [bjson, json, toml, yaml](scripting/filesystem.md) - [block](scripting/builtins/libblock.md) - [byteutil](scripting/builtins/libbyteutil.md) - [cameras](scripting/builtins/libcameras.md) diff --git a/doc/ru/scripting/filesystem.md b/doc/ru/scripting/filesystem.md index faaaf9b7..10d82c60 100644 --- a/doc/ru/scripting/filesystem.md +++ b/doc/ru/scripting/filesystem.md @@ -36,6 +36,22 @@ toml.parse(code: str) -> table Парсит TOML строку в таблицу. +## Библиотека yaml + +Библиотека содержит функции для сериализации и десериализации таблиц: + +```python +yaml.tostring(object: table) -> str +``` + +Сериализует объект в YAML строку. + +```python +yaml.parse(code: str) -> table +``` + +Парсит YAML строку в таблицу. + ## Библиотека bjson Библиотека содержит функции для работы с двоичным форматом обмена данными [vcbjson](../../specs/binary_json_spec.md). diff --git a/src/logic/scripting/lua/libs/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp index 818b9c30..39d09241 100644 --- a/src/logic/scripting/lua/libs/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -14,6 +14,7 @@ /// int l_function_name(lua::State* L); // Libraries +extern const luaL_Reg applib[]; extern const luaL_Reg audiolib[]; extern const luaL_Reg base64lib[]; extern const luaL_Reg bjsonlib[]; @@ -38,7 +39,6 @@ extern const luaL_Reg packlib[]; extern const luaL_Reg particleslib[]; // gfx.particles extern const luaL_Reg playerlib[]; extern const luaL_Reg quatlib[]; -extern const luaL_Reg applib[]; extern const luaL_Reg text3dlib[]; // gfx.text3d extern const luaL_Reg timelib[]; extern const luaL_Reg tomllib[]; @@ -48,6 +48,7 @@ extern const luaL_Reg vec3lib[]; // vecn.cpp extern const luaL_Reg vec4lib[]; // vecn.cpp extern const luaL_Reg weatherlib[]; extern const luaL_Reg worldlib[]; +extern const luaL_Reg yamllib[]; // Components extern const luaL_Reg skeletonlib[]; diff --git a/src/logic/scripting/lua/libs/libjson.cpp b/src/logic/scripting/lua/libs/libjson.cpp index 503173bd..ed929d3d 100644 --- a/src/logic/scripting/lua/libs/libjson.cpp +++ b/src/logic/scripting/lua/libs/libjson.cpp @@ -19,4 +19,5 @@ static int l_json_parse(lua::State* L) { const luaL_Reg jsonlib[] = { {"tostring", lua::wrap}, {"parse", lua::wrap}, - {NULL, NULL}}; + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/libs/libyaml.cpp b/src/logic/scripting/lua/libs/libyaml.cpp new file mode 100644 index 00000000..3b625ddb --- /dev/null +++ b/src/logic/scripting/lua/libs/libyaml.cpp @@ -0,0 +1,20 @@ +#include "coders/yaml.hpp" +#include "api_lua.hpp" + +static int l_stringify(lua::State* L) { + auto value = lua::tovalue(L, 1); + auto string = yaml::stringify(value); + return lua::pushstring(L, string); +} + +static int l_parse(lua::State* L) { + auto string = lua::require_string(L, 1); + auto element = yaml::parse("[string]", string); + return lua::pushvalue(L, element); +} + +const luaL_Reg yamllib[] = { + {"tostring", lua::wrap}, + {"parse", lua::wrap}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index ffeaf360..1ae35d39 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -56,6 +56,7 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "vec2", vec2lib); openlib(L, "vec3", vec3lib); openlib(L, "vec4", vec4lib); + openlib(L, "yaml", yamllib); if (stateType == StateType::SCRIPT) { openlib(L, "app", applib); From 2074ba8dd113164530670ebb625fd51fafe47f33 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 07:56:01 +0300 Subject: [PATCH 04/34] update workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows-clang.yml | 2 +- .github/workflows/windows.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index ed214cd0..0c2f4c0f 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "devel" ] jobs: build-appimage: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 3af2ee8b..5c11b77f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "devel" ] jobs: build-dmg: diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index 1356149f..ba7b26b0 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "devel" ] jobs: build-windows: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3f7efc9c..06f3ec51 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "devel" ] jobs: build-windows: From ccccf2254104b3ccd1555de705aa06cf50cdf32d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 07:57:19 +0300 Subject: [PATCH 05/34] trigger checks From aaf8053b87bb9238b258ee64e17664e3db2589ed Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 08:28:06 +0300 Subject: [PATCH 06/34] fix BasicParser::readUntilEOL --- src/coders/BasicParser.inl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coders/BasicParser.inl b/src/coders/BasicParser.inl index 72f644ba..3616b4a1 100644 --- a/src/coders/BasicParser.inl +++ b/src/coders/BasicParser.inl @@ -283,9 +283,12 @@ std::basic_string_view BasicParser::readUntilWhitespace() { template std::basic_string_view BasicParser::readUntilEOL() { int start = pos; - while (hasNext() && source[pos] != '\r' && source[pos] != '\n') { + while (hasNext() && source[pos] != '\n') { pos++; } + if (pos > start && source[pos - 1] == '\r') { + return source.substr(start, pos - start - 1); + } return source.substr(start, pos - start); } From 14310b05fbd59f13f379ca3f517cd2d069b416e5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 26 Mar 2025 09:23:33 +0300 Subject: [PATCH 07/34] update version to 0.28 --- doc/en/main-page.md | 2 +- doc/ru/main-page.md | 2 +- res/content/base/package.json | 2 +- src/constants.hpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/en/main-page.md b/doc/en/main-page.md index e1f13230..94aaaebe 100644 --- a/doc/en/main-page.md +++ b/doc/en/main-page.md @@ -1,6 +1,6 @@ # Documentation -Documentation for release 0.27. +Documentation for release 0.28. ## Sections diff --git a/doc/ru/main-page.md b/doc/ru/main-page.md index 72503052..8d034a88 100644 --- a/doc/ru/main-page.md +++ b/doc/ru/main-page.md @@ -1,6 +1,6 @@ # Документация -Документация версии 0.27. +Документация версии 0.28. ## Разделы diff --git a/res/content/base/package.json b/res/content/base/package.json index 4f992e4c..73b8dcd0 100644 --- a/res/content/base/package.json +++ b/res/content/base/package.json @@ -1,6 +1,6 @@ { "id": "base", "title": "Base", - "version": "0.27", + "version": "0.28", "description": "basic content package" } diff --git a/src/constants.hpp b/src/constants.hpp index 1accd619..32acc83e 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -6,7 +6,7 @@ #include inline constexpr int ENGINE_VERSION_MAJOR = 0; -inline constexpr int ENGINE_VERSION_MINOR = 27; +inline constexpr int ENGINE_VERSION_MINOR = 28; #ifdef NDEBUG inline constexpr bool ENGINE_DEBUG_BUILD = false; @@ -14,7 +14,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = false; inline constexpr bool ENGINE_DEBUG_BUILD = true; #endif // NDEBUG -inline const std::string ENGINE_VERSION_STRING = "0.27"; +inline const std::string ENGINE_VERSION_STRING = "0.28"; /// @brief world regions format version inline constexpr uint REGION_FORMAT_VERSION = 3; From 47c2dd8c7e39fbc6fd4211571d785a750f03c931 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 29 Mar 2025 18:17:19 +0300 Subject: [PATCH 08/34] fix doc/*/scripting/builtins/libinventory.md --- doc/en/scripting/builtins/libinventory.md | 2 +- doc/ru/scripting/builtins/libinventory.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/scripting/builtins/libinventory.md b/doc/en/scripting/builtins/libinventory.md index 3816fb45..0be4a103 100644 --- a/doc/en/scripting/builtins/libinventory.md +++ b/doc/en/scripting/builtins/libinventory.md @@ -170,7 +170,7 @@ inventory.decrement( -- creating a local `uses` property if none. -- Removes one item from the slot when the counter reaches zero. -- Does nothing if the `uses` property is not specified in the item's JSON. --- See [property `uses`](../../item-properties.md#number-of-uses-durability---uses) +-- See an item property `uses` inventory.use( -- inventory id invid: int, diff --git a/doc/ru/scripting/builtins/libinventory.md b/doc/ru/scripting/builtins/libinventory.md index 72a06b6b..701ed50c 100644 --- a/doc/ru/scripting/builtins/libinventory.md +++ b/doc/ru/scripting/builtins/libinventory.md @@ -181,7 +181,7 @@ inventory.decrement( -- создавая локальное свойство `uses` при отсутствии. -- Удаляет один предмет из слота при достижении нулевого значения счётчика. -- При отсутствии в JSON предмета свойства `uses` ничего не делает. --- См. [свойство `uses`](../../item-properties.md#число-использований-прочность---uses) +-- См. свойство предметов `uses` inventory.use( -- id инвентаря invid: int, From 74a94f869c06f6d59a84a9c2ad6072f2586c9ec5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 6 Mar 2025 15:32:58 +0300 Subject: [PATCH 09/34] refactor: reduce Window references --- src/core_defs.cpp | 1 - src/frontend/hud.cpp | 2 +- src/frontend/menu.cpp | 1 - src/frontend/screens/MenuScreen.cpp | 7 +- src/graphics/core/Viewport.hpp | 4 ++ src/graphics/render/BlockWrapsRenderer.cpp | 1 - src/graphics/render/ModelBatch.cpp | 1 - src/graphics/render/TextsRenderer.cpp | 13 ++-- src/graphics/render/WorldRenderer.cpp | 4 +- src/graphics/ui/GUI.cpp | 7 +- src/graphics/ui/gui_util.cpp | 8 +-- src/logic/scripting/lua/libs/libcore.cpp | 1 - src/logic/scripting/lua/libs/libinput.cpp | 2 +- src/window/Camera.cpp | 20 +++--- src/window/Camera.hpp | 3 +- src/window/Events.cpp | 47 +++++++------ src/window/Events.hpp | 76 ++++++++++----------- src/window/Window.cpp | 31 +++++---- src/window/Window.hpp | 77 +++++++++------------- 19 files changed, 151 insertions(+), 155 deletions(-) diff --git a/src/core_defs.cpp b/src/core_defs.cpp index def96d69..dc6bb37f 100644 --- a/src/core_defs.cpp +++ b/src/core_defs.cpp @@ -5,7 +5,6 @@ #include "content/ContentBuilder.hpp" #include "io/io.hpp" #include "io/engine_paths.hpp" -#include "window/Window.hpp" #include "window/Events.hpp" #include "window/input.hpp" #include "voxels/Block.hpp" diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index ecf030dd..072e85ce 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -253,7 +253,7 @@ void Hud::updateHotbarControl() { i <= static_cast(keycode::NUM_9); i++ ) { - if (Events::jpressed(i)) { + if (Events::jpressed(static_cast(i))) { player.setChosenSlot(i - static_cast(keycode::NUM_1)); } } diff --git a/src/frontend/menu.cpp b/src/frontend/menu.cpp index 3b8e70fb..717b2c72 100644 --- a/src/frontend/menu.cpp +++ b/src/frontend/menu.cpp @@ -16,7 +16,6 @@ #include "settings.hpp" #include "coders/commons.hpp" #include "util/stringutil.hpp" -#include "window/Window.hpp" #include #include diff --git a/src/frontend/screens/MenuScreen.cpp b/src/frontend/screens/MenuScreen.cpp index f16f73ee..e8ce3f4e 100644 --- a/src/frontend/screens/MenuScreen.cpp +++ b/src/frontend/screens/MenuScreen.cpp @@ -34,14 +34,15 @@ void MenuScreen::draw(float delta) { Window::clear(); Window::setBgColor(glm::vec3(0.2f)); + uint width = Window::width; + uint height = Window::height; + uicamera->setFov(Window::height); + uicamera->setAspectRatio(width / static_cast(height)); auto uishader = assets->get("ui"); uishader->use(); uishader->uniformMatrix("u_projview", uicamera->getProjView()); - uint width = Window::width; - uint height = Window::height; - auto bg = assets->get("gui/menubg"); batch->begin(); batch->texture(bg); diff --git a/src/graphics/core/Viewport.hpp b/src/graphics/core/Viewport.hpp index 7ec826a1..d7579227 100644 --- a/src/graphics/core/Viewport.hpp +++ b/src/graphics/core/Viewport.hpp @@ -16,4 +16,8 @@ public: glm::ivec2 size() const { return glm::ivec2(width, height); } + + float getRatio() const { + return width / static_cast(height); + } }; diff --git a/src/graphics/render/BlockWrapsRenderer.cpp b/src/graphics/render/BlockWrapsRenderer.cpp index da363352..ca10a2b9 100644 --- a/src/graphics/render/BlockWrapsRenderer.cpp +++ b/src/graphics/render/BlockWrapsRenderer.cpp @@ -11,7 +11,6 @@ #include "objects/Player.hpp" #include "voxels/Block.hpp" #include "voxels/Chunks.hpp" -#include "window/Window.hpp" #include "world/Level.hpp" BlockWrapsRenderer::BlockWrapsRenderer( diff --git a/src/graphics/render/ModelBatch.cpp b/src/graphics/render/ModelBatch.cpp index 558393c6..08deb7bd 100644 --- a/src/graphics/render/ModelBatch.cpp +++ b/src/graphics/render/ModelBatch.cpp @@ -6,7 +6,6 @@ #include "graphics/core/Atlas.hpp" #include "graphics/core/Texture.hpp" #include "assets/Assets.hpp" -#include "window/Window.hpp" #include "voxels/Chunks.hpp" #include "lighting/Lightmap.hpp" #include "settings.hpp" diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp index 283dacaa..205b12fa 100644 --- a/src/graphics/render/TextsRenderer.cpp +++ b/src/graphics/render/TextsRenderer.cpp @@ -4,11 +4,11 @@ #include "maths/util.hpp" #include "assets/Assets.hpp" #include "window/Camera.hpp" -#include "window/Window.hpp" #include "maths/FrustumCulling.hpp" #include "graphics/core/Font.hpp" #include "graphics/core/Batch3D.hpp" #include "graphics/core/Shader.hpp" +#include "graphics/core/DrawContext.hpp" #include "presets/NotePreset.hpp" #include "constants.hpp" @@ -66,6 +66,7 @@ void TextsRenderer::renderNote( xvec *= 1.0f + scale; yvec *= 1.0f + scale; } + const auto& viewport = context.getViewport(); if (preset.displayMode == NoteDisplayMode::PROJECTED) { float scale = 1.0f; if (glm::abs(preset.perspective) > 0.0001f) { @@ -84,14 +85,14 @@ void TextsRenderer::renderNote( } pos /= projpos.w; pos.z = 0; - xvec = {2.0f/Window::width*scale, 0, 0}; - yvec = {0, 2.0f/Window::height*scale, 0}; + xvec = {2.0f / viewport.getWidth() * scale, 0, 0}; + yvec = {0, 2.0f / viewport.getHeight() * scale, 0}; } else { auto matrix = camera.getProjView(); auto screenPos = matrix * glm::vec4(pos, 1.0f); - - xvec = glm::vec3(2.0f/Window::width*scale, 0, 0); - yvec = glm::vec3(0, 2.0f/Window::height*scale, 0); + + xvec = glm::vec3(2.0f / viewport.getWidth() * scale, 0, 0); + yvec = glm::vec3(0, 2.0f / viewport.getHeight() * scale, 0); pos = screenPos / screenPos.w; } diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index ab140c77..70464a26 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -338,8 +338,8 @@ void WorldRenderer::draw( auto world = level.getWorld(); - const Viewport& vp = pctx.getViewport(); - camera.aspect = vp.getWidth() / static_cast(vp.getHeight()); + const auto& vp = pctx.getViewport(); + camera.setAspectRatio(vp.getRatio()); const auto& settings = engine.getSettings(); const auto& worldInfo = world->getInfo(); diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 95425da4..b4886830 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -115,8 +115,10 @@ void GUI::actMouse(float delta) { } if (hover) { hover->setHover(true); - if (Events::scroll) { - hover->scrolled(Events::scroll); + + int scroll = Events::getScroll(); + if (scroll) { + hover->scrolled(scroll); } } this->hover = hover; @@ -229,6 +231,7 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) { } menu->setPos((wsize - menu->getSize()) / 2.0f); uicamera->setFov(wsize.y); + uicamera->setAspectRatio(viewport.getRatio()); auto uishader = assets.get("ui"); uishader->use(); diff --git a/src/graphics/ui/gui_util.cpp b/src/graphics/ui/gui_util.cpp index 7be277c4..4e9bb70b 100644 --- a/src/graphics/ui/gui_util.cpp +++ b/src/graphics/ui/gui_util.cpp @@ -68,11 +68,11 @@ void guiutil::alert( )); panel->refresh(); - panel->keepAlive(Events::keyCallbacks[keycode::ENTER].add([on_hidden_final](){ + panel->keepAlive(Events::addKeyCallback(keycode::ENTER, [on_hidden_final](){ on_hidden_final(); return true; })); - panel->keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([on_hidden_final](){ + panel->keepAlive(Events::addKeyCallback(keycode::ESCAPE, [on_hidden_final](){ on_hidden_final(); return true; })); @@ -130,11 +130,11 @@ void guiutil::confirm( })); panel->add(subpanel); - panel->keepAlive(Events::keyCallbacks[keycode::ENTER].add([=](){ + panel->keepAlive(Events::addKeyCallback(keycode::ENTER, [=](){ on_confirm_final(); return true; })); - panel->keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([=](){ + panel->keepAlive(Events::addKeyCallback(keycode::ESCAPE, [=](){ on_deny_final(); return true; })); diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 2a56644d..a874a4ba 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -18,7 +18,6 @@ #include "util/listutil.hpp" #include "util/platform.hpp" #include "window/Events.hpp" -#include "window/Window.hpp" #include "world/Level.hpp" #include "world/generator/WorldGenerator.hpp" diff --git a/src/logic/scripting/lua/libs/libinput.cpp b/src/logic/scripting/lua/libs/libinput.cpp index 09f762ef..f1a748fb 100644 --- a/src/logic/scripting/lua/libs/libinput.cpp +++ b/src/logic/scripting/lua/libs/libinput.cpp @@ -42,7 +42,7 @@ static int l_add_callback(lua::State* L) { std::string prefix = bindname.substr(0, pos); if (prefix == "key") { auto key = input_util::keycode_from(bindname.substr(pos + 1)); - handler = Events::keyCallbacks[key].add(actual_callback); + handler = Events::addKeyCallback(key, actual_callback); } } auto callback = [=]() -> bool { diff --git a/src/window/Camera.cpp b/src/window/Camera.cpp index 315acf56..042fa584 100644 --- a/src/window/Camera.cpp +++ b/src/window/Camera.cpp @@ -3,8 +3,6 @@ #include #include -#include "Window.hpp" - Camera::Camera(glm::vec3 position, float fov) : fov(fov), position(position) { updateVectors(); } @@ -30,17 +28,13 @@ void Camera::rotate(float x, float y, float z) { } glm::mat4 Camera::getProjection() const { - constexpr float epsilon = 1e-6f; // 0.000001 - float aspect_ratio = this->aspect; - if (std::fabs(aspect_ratio) < epsilon) { - aspect_ratio = Window::width / static_cast(Window::height); - } + constexpr float epsilon = 1e-6f; if (perspective) { - return glm::perspective(fov * zoom, aspect_ratio, near, far); + return glm::perspective(fov * zoom, ar, near, far); } else if (flipped) { - return glm::ortho(-0.5f, fov * aspect_ratio-0.5f, fov, 0.0f); + return glm::ortho(0.0f, fov * ar, fov, 0.0f); } else { - return glm::ortho(-0.5f, fov * aspect_ratio-0.5f, 0.0f, fov); + return glm::ortho(0.0f, fov * ar, 0.0f, fov); } } @@ -69,5 +63,9 @@ float Camera::getFov() const { } float Camera::getAspectRatio() const { - return aspect; + return ar; +} + +void Camera::setAspectRatio(float ar) { + this->ar = ar; } diff --git a/src/window/Camera.hpp b/src/window/Camera.hpp index 7d80cf8f..ba4a5331 100644 --- a/src/window/Camera.hpp +++ b/src/window/Camera.hpp @@ -5,6 +5,7 @@ class Camera { float fov = 1.0f; + float ar = 0.0f; public: glm::vec3 front {}; glm::vec3 up {}; @@ -17,7 +18,6 @@ public: glm::mat4 rotation {1.0f}; bool perspective = true; bool flipped = false; - float aspect = 0.0f; float near = 0.05f; float far = 1500.0f; @@ -37,4 +37,5 @@ public: float getFov() const; float getAspectRatio() const; + void setAspectRatio(float ar); }; diff --git a/src/window/Events.cpp b/src/window/Events.cpp index 49cdf929..a76909ec 100644 --- a/src/window/Events.cpp +++ b/src/window/Events.cpp @@ -12,18 +12,27 @@ static debug::Logger logger("events"); inline constexpr short _MOUSE_KEYS_OFFSET = 1024; -bool Events::keys[KEYS_BUFFER_SIZE] = {}; -uint Events::frames[KEYS_BUFFER_SIZE] = {}; -uint Events::currentFrame = 0; +namespace { + bool keys[KEYS_BUFFER_SIZE] = {}; + uint frames[KEYS_BUFFER_SIZE] = {}; + uint current_frame = 0; + bool cursor_drag = false; + bool cursor_locked = false; + std::unordered_map> key_callbacks; +} + int Events::scroll = 0; + glm::vec2 Events::delta = {}; glm::vec2 Events::cursor = {}; -bool Events::cursorDrag = false; -bool Events::cursorLocked = false; + std::vector Events::codepoints; std::vector Events::pressedKeys; std::unordered_map Events::bindings; -std::unordered_map> Events::keyCallbacks; + +int Events::getScroll() { + return scroll; +} bool Events::pressed(keycode keycode) { return pressed(static_cast(keycode)); @@ -41,7 +50,7 @@ bool Events::jpressed(keycode keycode) { } bool Events::jpressed(int keycode) { - return Events::pressed(keycode) && frames[keycode] == currentFrame; + return Events::pressed(keycode) && frames[keycode] == current_frame; } bool Events::clicked(mousecode button) { @@ -61,15 +70,15 @@ bool Events::jclicked(int button) { } void Events::toggleCursor() { - cursorDrag = false; - cursorLocked = !cursorLocked; + cursor_drag = false; + cursor_locked = !cursor_locked; Window::setCursorMode( - cursorLocked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL + cursor_locked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL ); } void Events::pollEvents() { - currentFrame++; + current_frame++; delta.x = 0.f; delta.y = 0.f; scroll = 0; @@ -155,11 +164,11 @@ bool Events::jactive(const std::string& name) { } void Events::setKey(int key, bool b) { - Events::keys[key] = b; - Events::frames[key] = currentFrame; + ::keys[key] = b; + ::frames[key] = current_frame; if (b) { - const auto& callbacks = keyCallbacks.find(static_cast(key)); - if (callbacks != keyCallbacks.end()) { + const auto& callbacks = ::key_callbacks.find(static_cast(key)); + if (callbacks != ::key_callbacks.end()) { callbacks->second.notify(); } } @@ -170,18 +179,18 @@ void Events::setButton(int button, bool b) { } void Events::setPosition(float xpos, float ypos) { - if (Events::cursorDrag) { + if (::cursor_drag) { Events::delta.x += xpos - Events::cursor.x; Events::delta.y += ypos - Events::cursor.y; } else { - Events::cursorDrag = true; + ::cursor_drag = true; } Events::cursor.x = xpos; Events::cursor.y = ypos; } observer_handler Events::addKeyCallback(keycode key, KeyCallback callback) { - return keyCallbacks[key].add(std::move(callback)); + return ::key_callbacks[key].add(std::move(callback)); } #include "coders/json.hpp" @@ -251,5 +260,5 @@ void Events::enableBindings() { } bool Events::isCursorLocked() { - return cursorLocked; + return cursor_locked; } diff --git a/src/window/Events.hpp b/src/window/Events.hpp index 92a97ab0..a213cea0 100644 --- a/src/window/Events.hpp +++ b/src/window/Events.hpp @@ -15,56 +15,52 @@ enum class BindType { REBIND = 1 }; -class Events { - static bool keys[KEYS_BUFFER_SIZE]; - static uint frames[KEYS_BUFFER_SIZE]; - static uint currentFrame; - static bool cursorDrag; - static bool cursorLocked; -public: - static int scroll; - static glm::vec2 delta; - static glm::vec2 cursor; - static std::vector codepoints; - static std::vector pressedKeys; - static std::unordered_map bindings; - static std::unordered_map> keyCallbacks; +namespace Events { + extern int scroll; + extern glm::vec2 delta; + extern glm::vec2 cursor; + extern std::vector codepoints; + extern std::vector pressedKeys; + extern std::unordered_map bindings; - static void pollEvents(); + void pollEvents(); - static bool pressed(keycode keycode); - static bool pressed(int keycode); - static bool jpressed(keycode keycode); - static bool jpressed(int keycode); + int getScroll(); - static bool clicked(mousecode button); - static bool clicked(int button); - static bool jclicked(mousecode button); - static bool jclicked(int button); + bool pressed(keycode keycode); + bool pressed(int keycode); + bool jpressed(keycode keycode); + bool jpressed(int keycode); - static void toggleCursor(); + bool clicked(mousecode button); + bool clicked(int button); + bool jclicked(mousecode button); + bool jclicked(int button); - static Binding& getBinding(const std::string& name); - static void bind(const std::string& name, inputtype type, keycode code); - static void bind(const std::string& name, inputtype type, mousecode code); - static void bind(const std::string& name, inputtype type, int code); - static void rebind(const std::string& name, inputtype type, int code); - static bool active(const std::string& name); - static bool jactive(const std::string& name); + void toggleCursor(); - static observer_handler addKeyCallback(keycode key, KeyCallback callback); + Binding& getBinding(const std::string& name); + void bind(const std::string& name, inputtype type, keycode code); + void bind(const std::string& name, inputtype type, mousecode code); + void bind(const std::string& name, inputtype type, int code); + void rebind(const std::string& name, inputtype type, int code); + bool active(const std::string& name); + bool jactive(const std::string& name); - static void setKey(int key, bool b); - static void setButton(int button, bool b); + observer_handler addKeyCallback(keycode key, KeyCallback callback); - static void setPosition(float xpos, float ypos); + void setKey(int key, bool b); + void setButton(int button, bool b); - static std::string writeBindings(); - static void loadBindings( - const std::string& filename, const std::string& source, + void setPosition(float xpos, float ypos); + + std::string writeBindings(); + void loadBindings( + const std::string& filename, + const std::string& source, BindType bindType ); - static void enableBindings(); + void enableBindings(); - static bool isCursorLocked(); + bool isCursorLocked(); }; diff --git a/src/window/Window.cpp b/src/window/Window.cpp index 4e68877f..5a3d675f 100644 --- a/src/window/Window.cpp +++ b/src/window/Window.cpp @@ -19,18 +19,21 @@ static debug::Logger logger("window"); -GLFWwindow* Window::window = nullptr; -DisplaySettings* Window::settings = nullptr; -std::stack Window::scissorStack; -glm::vec4 Window::scissorArea; +namespace { + GLFWwindow* window = nullptr; + DisplaySettings* settings = nullptr; + std::stack scissorStack; + glm::vec4 scissorArea; + int framerate = -1; + double prevSwap = 0.0; + bool fullscreen = false; + CursorShape cursor = CursorShape::ARROW; + int posX = 0; + int posY = 0; +} + uint Window::width = 0; uint Window::height = 0; -int Window::posX = 0; -int Window::posY = 0; -int Window::framerate = -1; -double Window::prevSwap = 0.0; -bool Window::fullscreen = false; -CursorShape Window::cursor = CursorShape::ARROW; static util::ObjectsKeeper observers_keeper; static std::unordered_set extensionsCache; @@ -173,7 +176,7 @@ static void glfw_error_callback(int error, const char* description) { static GLFWcursor* standard_cursors[static_cast(CursorShape::LAST) + 1] = {}; int Window::initialize(DisplaySettings* settings) { - Window::settings = settings; + ::settings = settings; Window::width = settings->width.get(); Window::height = settings->height.get(); @@ -381,10 +384,10 @@ void Window::setShouldClose(bool flag) { } void Window::setFramerate(int framerate) { - if ((framerate != -1) != (Window::framerate != -1)) { + if ((framerate != -1) != (::framerate != -1)) { glfwSwapInterval(framerate == -1); } - Window::framerate = framerate; + ::framerate = framerate; } void Window::toggleFullscreen() { @@ -472,7 +475,7 @@ void Window::setClipboardText(const char* text) { glfwSetClipboardString(window, text); } -bool Window::tryToMaximize(GLFWwindow* window, GLFWmonitor* monitor) { +static bool try_to_maximize(GLFWwindow* window, GLFWmonitor* monitor) { glm::ivec4 windowFrame(0); glm::ivec4 workArea(0); glfwGetWindowFrameSize( diff --git a/src/window/Window.hpp b/src/window/Window.hpp index b81da395..289648f0 100644 --- a/src/window/Window.hpp +++ b/src/window/Window.hpp @@ -10,60 +10,45 @@ class ImageData; struct DisplaySettings; -struct GLFWwindow; -struct GLFWmonitor; -class Window { - static GLFWwindow* window; - static DisplaySettings* settings; - static std::stack scissorStack; - static glm::vec4 scissorArea; - static bool fullscreen; - static int framerate; - static double prevSwap; - static CursorShape cursor; +namespace Window { + extern uint width; + extern uint height; - static bool tryToMaximize(GLFWwindow* window, GLFWmonitor* monitor); - static bool isGlExtensionSupported(const char *extension); -public: - static int posX; - static int posY; - static uint width; - static uint height; - static int initialize(DisplaySettings* settings); - static void terminate(); + int initialize(DisplaySettings* settings); + void terminate(); - static void viewport(int x, int y, int width, int height); - static void setCursorMode(int mode); - static bool isShouldClose(); - static void setShouldClose(bool flag); - static void swapBuffers(); - static void setFramerate(int interval); - static void toggleFullscreen(); - static bool isFullscreen(); - static bool isMaximized(); - static bool isFocused(); - static bool isIconified(); + void viewport(int x, int y, int width, int height); + void setCursorMode(int mode); + bool isShouldClose(); + void setShouldClose(bool flag); + void swapBuffers(); + void setFramerate(int interval); + void toggleFullscreen(); + bool isFullscreen(); + bool isMaximized(); + bool isFocused(); + bool isIconified(); - static void pushScissor(glm::vec4 area); - static void popScissor(); - static void resetScissor(); + void pushScissor(glm::vec4 area); + void popScissor(); + void resetScissor(); - static void setCursor(CursorShape shape); + void setCursor(CursorShape shape); - static void clear(); - static void clearDepth(); - static void setBgColor(glm::vec3 color); - static void setBgColor(glm::vec4 color); - static double time(); - static const char* getClipboardText(); - static void setClipboardText(const char* text); - static DisplaySettings* getSettings(); - static void setIcon(const ImageData* image); + void clear(); + void clearDepth(); + void setBgColor(glm::vec3 color); + void setBgColor(glm::vec4 color); + double time(); + const char* getClipboardText(); + void setClipboardText(const char* text); + DisplaySettings* getSettings(); + void setIcon(const ImageData* image); - static glm::vec2 size() { + inline glm::vec2 size() { return glm::vec2(width, height); } - static std::unique_ptr takeScreenshot(); + std::unique_ptr takeScreenshot(); }; From 4c48afbb9023e17b508c8a333a1f5a13e8158d17 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 7 Mar 2025 18:19:56 +0300 Subject: [PATCH 10/34] refactor: add GUI instance reference to UI nodes --- src/assets/AssetsLoader.cpp | 15 +- src/assets/AssetsLoader.hpp | 15 +- src/assets/assetload_funcs.cpp | 1 + src/engine/Engine.cpp | 43 ++-- src/engine/Engine.hpp | 27 ++- src/frontend/UiDocument.cpp | 8 +- src/frontend/UiDocument.hpp | 4 +- src/frontend/debug_panel.cpp | 59 +++--- src/frontend/hud.cpp | 60 +++--- src/frontend/hud.hpp | 6 +- src/frontend/menu.cpp | 97 +++++---- src/frontend/menu.hpp | 2 +- src/frontend/screens/LevelScreen.cpp | 40 ++-- src/frontend/screens/LevelScreen.hpp | 7 + src/frontend/screens/MenuScreen.cpp | 5 +- src/graphics/ui/GUI.cpp | 104 ++++++---- src/graphics/ui/GUI.hpp | 11 +- src/graphics/ui/elements/BasePanel.hpp | 3 +- src/graphics/ui/elements/Button.cpp | 37 ++-- src/graphics/ui/elements/Button.hpp | 20 +- src/graphics/ui/elements/Canvas.cpp | 3 +- src/graphics/ui/elements/Canvas.hpp | 2 +- src/graphics/ui/elements/CheckBox.cpp | 21 +- src/graphics/ui/elements/CheckBox.hpp | 19 +- src/graphics/ui/elements/Container.cpp | 10 +- src/graphics/ui/elements/Container.hpp | 8 +- src/graphics/ui/elements/Image.cpp | 34 ++-- src/graphics/ui/elements/Image.hpp | 2 +- src/graphics/ui/elements/InputBindBox.cpp | 8 +- src/graphics/ui/elements/InputBindBox.hpp | 14 +- src/graphics/ui/elements/InventoryView.cpp | 23 +-- src/graphics/ui/elements/InventoryView.hpp | 11 +- src/graphics/ui/elements/Label.cpp | 8 +- src/graphics/ui/elements/Label.hpp | 4 +- src/graphics/ui/elements/Menu.cpp | 2 +- src/graphics/ui/elements/Menu.hpp | 2 +- src/graphics/ui/elements/Panel.cpp | 24 +-- src/graphics/ui/elements/Panel.hpp | 9 +- src/graphics/ui/elements/Plotter.hpp | 26 ++- src/graphics/ui/elements/SplitBox.cpp | 8 +- src/graphics/ui/elements/SplitBox.hpp | 6 +- src/graphics/ui/elements/TextBox.cpp | 156 ++++++++------- src/graphics/ui/elements/TextBox.hpp | 12 +- src/graphics/ui/elements/TrackBar.cpp | 9 +- src/graphics/ui/elements/TrackBar.hpp | 17 +- src/graphics/ui/elements/UINode.cpp | 8 +- src/graphics/ui/elements/UINode.hpp | 23 ++- src/graphics/ui/gui_util.cpp | 117 ++++++----- src/graphics/ui/gui_util.hpp | 38 ++-- src/graphics/ui/gui_xml.cpp | 184 ++++++++++-------- src/graphics/ui/gui_xml.hpp | 4 +- src/logic/EngineController.cpp | 4 +- src/logic/PlayerController.cpp | 118 ++++++----- src/logic/PlayerController.hpp | 13 +- src/logic/scripting/lua/libs/libconsole.cpp | 14 +- src/logic/scripting/lua/libs/libcore.cpp | 1 - src/logic/scripting/lua/libs/libgui.cpp | 12 +- src/logic/scripting/lua/libs/libinput.cpp | 65 +++---- src/logic/scripting/lua/libs/libnetwork.cpp | 116 ++++++----- src/logic/scripting/lua/libs/libpack.cpp | 2 +- src/logic/scripting/scripting.cpp | 10 +- src/logic/scripting/scripting_hud.cpp | 8 +- .../scripting/scripting_world_generation.cpp | 2 +- src/objects/Player.hpp | 23 ++- src/window/Camera.cpp | 1 - src/window/Events.cpp | 46 ++--- src/window/Events.hpp | 5 +- src/window/Window.cpp | 68 ++++++- src/window/Window.hpp | 4 +- src/window/input.hpp | 77 +++++++- 70 files changed, 1133 insertions(+), 832 deletions(-) diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index e068295b..42fe4a0b 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -19,13 +19,14 @@ #include "items/ItemDef.hpp" #include "Assets.hpp" #include "assetload_funcs.hpp" +#include "engine/Engine.hpp" namespace fs = std::filesystem; static debug::Logger logger("assets-loader"); -AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths) - : assets(assets), paths(paths) { +AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths* paths) + : engine(engine), assets(assets), paths(paths) { addLoader(AssetType::SHADER, assetload::shader); addLoader(AssetType::TEXTURE, assetload::texture); addLoader(AssetType::FONT, assetload::font); @@ -73,7 +74,7 @@ void AssetsLoader::loadNext() { aloader_func loader = getLoader(entry.tag); auto postfunc = loader(this, paths, entry.filename, entry.alias, entry.config); - postfunc(assets); + postfunc(&assets); entries.pop(); } catch (std::runtime_error& err) { logger.error() << err.what(); @@ -101,7 +102,7 @@ static void add_layouts( AssetType::LAYOUT, file.string(), name, - std::make_shared(env) + std::make_shared(&loader.getEngine().getGUI(), env) ); } } @@ -296,6 +297,10 @@ bool AssetsLoader::loadExternalTexture( return false; } +Engine& AssetsLoader::getEngine() { + return engine; +} + const ResPaths* AssetsLoader::getPaths() const { return paths; } @@ -324,7 +329,7 @@ std::shared_ptr AssetsLoader::startTask(runnable onDone) { std::make_shared>( "assets-loader-pool", [=]() { return std::make_shared(this); }, - [=](const assetload::postfunc& func) { func(assets); } + [this](const assetload::postfunc& func) { func(&assets); } ); pool->setOnComplete(std::move(onDone)); while (!entries.empty()) { diff --git a/src/assets/AssetsLoader.hpp b/src/assets/AssetsLoader.hpp index bf117a3d..20dc50a3 100644 --- a/src/assets/AssetsLoader.hpp +++ b/src/assets/AssetsLoader.hpp @@ -18,6 +18,11 @@ class ResPaths; class AssetsLoader; class Content; +class Engine; + +namespace gui { + class GUI; +} struct AssetCfg { virtual ~AssetCfg() { @@ -25,9 +30,10 @@ struct AssetCfg { }; struct LayoutCfg : AssetCfg { + gui::GUI* gui; scriptenv env; - LayoutCfg(scriptenv env) : env(std::move(env)) { + LayoutCfg(gui::GUI* gui, scriptenv env) : gui(gui), env(std::move(env)) { } }; @@ -61,7 +67,8 @@ struct aloader_entry { }; class AssetsLoader { - Assets* assets; + Engine& engine; + Assets& assets; std::map loaders; std::queue entries; std::set> enqueued; @@ -76,7 +83,7 @@ class AssetsLoader { void processPreloadConfig(const io::path& file); void processPreloadConfigs(const Content* content); public: - AssetsLoader(Assets* assets, const ResPaths* paths); + AssetsLoader(Engine& engine, Assets& assets, const ResPaths* paths); void addLoader(AssetType tag, aloader_func func); /// @brief Enqueue asset load @@ -111,4 +118,6 @@ public: const std::string& name, const std::vector& alternatives ); + + Engine& getEngine(); }; diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index bad2bbb5..f6582333 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -189,6 +189,7 @@ assetload::postfunc assetload::layout( auto prefix = name.substr(0, pos); assets->store( UiDocument::read( + *cfg->gui, cfg->env, name, file, diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 5778ed98..b653794c 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -50,8 +50,6 @@ static debug::Logger logger("engine"); -namespace fs = std::filesystem; - static std::unique_ptr load_icon() { try { auto file = "res:textures/misc/icon.png"; @@ -65,20 +63,21 @@ static std::unique_ptr load_icon() { } Engine::Engine() = default; +Engine::~Engine() = default; -static std::unique_ptr engine; +static std::unique_ptr instance = nullptr; Engine& Engine::getInstance() { - if (!engine) { - engine = std::make_unique(); + if (!instance) { + instance = std::make_unique(); } - return *engine; + return *instance; } void Engine::initialize(CoreParameters coreParameters) { params = std::move(coreParameters); settingsHandler = std::make_unique(settings); - interpreter = std::make_unique(); + cmd = std::make_unique(); network = network::Network::create(settings.network); logger.info() << "engine version: " << ENGINE_VERSION_STRING; @@ -97,7 +96,7 @@ void Engine::initialize(CoreParameters coreParameters) { controller = std::make_unique(*this); if (!params.headless) { - if (Window::initialize(&settings.display)){ + if (!(input = Window::initialize(&settings.display))){ throw initialize_error("could not initialize window"); } time.set(Window::time()); @@ -107,9 +106,9 @@ void Engine::initialize(CoreParameters coreParameters) { } loadControls(); - gui = std::make_unique(); + gui = std::make_unique(*this); if (ENGINE_DEBUG_BUILD) { - menus::create_version_label(*this); + menus::create_version_label(*gui); } } audio::initialize(!params.headless, settings.audio); @@ -209,7 +208,7 @@ void Engine::nextFrame() { : settings.display.framerate.get() ); Window::swapBuffers(); - Events::pollEvents(); + input->pollEvents(); } void Engine::renderFrame() { @@ -229,7 +228,7 @@ void Engine::saveSettings() { } } -Engine::~Engine() { +void Engine::close() { saveSettings(); logger.info() << "shutting down"; if (screen) { @@ -238,7 +237,7 @@ Engine::~Engine() { } content.reset(); assets.reset(); - interpreter.reset(); + cmd.reset(); if (gui) { gui.reset(); logger.info() << "gui finished"; @@ -256,17 +255,14 @@ Engine::~Engine() { } void Engine::terminate() { - engine.reset(); + instance->close(); + instance.reset(); } EngineController* Engine::getController() { return controller.get(); } -cmd::CommandsInterpreter* Engine::getCommandsInterpreter() { - return interpreter.get(); -} - PacksManager Engine::createPacksManager(const io::path& worldFolder) { PacksManager manager; manager.setSources({ @@ -286,7 +282,7 @@ void Engine::loadAssets() { Shader::preprocessor->setPaths(resPaths.get()); auto new_assets = std::make_unique(); - AssetsLoader loader(new_assets.get(), resPaths.get()); + AssetsLoader loader(*this, *new_assets, resPaths.get()); AssetsLoader::addDefaults(loader, content.get()); // no need @@ -376,7 +372,6 @@ void Engine::loadContent() { load_configs(pack.folder); } content = contentBuilder.build(); - interpreter->reset(); scripting::on_content_load(content.get()); ContentLoader::loadScripts(*content); @@ -468,10 +463,6 @@ bool Engine::isQuitSignal() const { return quitSignal; } -gui::GUI* Engine::getGUI() { - return gui.get(); -} - EngineSettings& Engine::getSettings() { return settings; } @@ -518,10 +509,6 @@ SettingsHandler& Engine::getSettingsHandler() { return *settingsHandler; } -network::Network& Engine::getNetwork() { - return *network; -} - Time& Engine::getTime() { return time; } diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index ccfabeed..ca5bcb21 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -26,6 +26,7 @@ class ResPaths; class EngineController; class SettingsHandler; struct EngineSettings; +class Input; namespace gui { class GUI; @@ -66,8 +67,9 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr content; std::unique_ptr resPaths; std::unique_ptr controller; - std::unique_ptr interpreter; + std::unique_ptr cmd; std::unique_ptr network; + std::unique_ptr input; std::vector basePacks; std::unique_ptr gui; PostRunnables postRunnables; @@ -87,6 +89,7 @@ public: static Engine& getInstance(); void initialize(CoreParameters coreParameters); + void close(); static void terminate(); @@ -128,9 +131,6 @@ public: /// @brief Get active assets storage instance Assets* getAssets(); - - /// @brief Get main UI controller - gui::GUI* getGUI(); /// @brief Get writeable engine settings structure instance EngineSettings& getSettings(); @@ -171,7 +171,6 @@ public: void saveScreenshot(); EngineController* getController(); - cmd::CommandsInterpreter* getCommandsInterpreter(); PacksManager createPacksManager(const io::path& worldFolder); @@ -179,11 +178,25 @@ public: SettingsHandler& getSettingsHandler(); - network::Network& getNetwork(); - Time& getTime(); const CoreParameters& getCoreParameters() const; bool isHeadless() const; + + gui::GUI& getGUI() { + return *gui; + } + + Input& getInput() { + return *input; + } + + network::Network& getNetwork() { + return *network; + } + + cmd::CommandsInterpreter& getCmd() { + return *cmd; + } }; diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index 1dfa18aa..0ca64542 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -54,6 +54,7 @@ scriptenv UiDocument::getEnvironment() const { } std::unique_ptr UiDocument::read( + gui::GUI& gui, const scriptenv& penv, const std::string& name, const io::path& file, @@ -66,7 +67,7 @@ std::unique_ptr UiDocument::read( ? scripting::create_doc_environment(scripting::get_root_environment(), name) : scripting::create_doc_environment(penv, name); - gui::UiXmlReader reader(env); + gui::UiXmlReader reader(gui, env); auto view = reader.readXML(file.string(), *xmldoc->getRoot()); view->setId("root"); uidocscript script {}; @@ -80,8 +81,7 @@ std::unique_ptr UiDocument::read( } std::shared_ptr UiDocument::readElement( - const io::path& file, const std::string& fileName + gui::GUI& gui, const io::path& file, const std::string& fileName ) { - auto document = read(nullptr, file.name(), file, fileName); - return document->getRoot(); + return read(gui, nullptr, file.name(), file, fileName)->getRoot(); } diff --git a/src/frontend/UiDocument.hpp b/src/frontend/UiDocument.hpp index 298e63bc..b280249a 100644 --- a/src/frontend/UiDocument.hpp +++ b/src/frontend/UiDocument.hpp @@ -9,6 +9,7 @@ #include "io/fwd.hpp" namespace gui { + class GUI; class UINode; } @@ -45,12 +46,13 @@ public: scriptenv getEnvironment() const; static std::unique_ptr read( + gui::GUI&, const scriptenv& parent_env, const std::string& name, const io::path& file, const std::string& fileName ); static std::shared_ptr readElement( - const io::path& file, const std::string& fileName + gui::GUI&, const io::path& file, const std::string& fileName ); }; diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 779597e1..67b43f5a 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -35,8 +35,8 @@ using namespace gui; -static std::shared_ptr