diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 62f20563..f799077e 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -9,7 +9,12 @@ on: jobs: build-appimage: - runs-on: ubuntu-20.04 + strategy: + matrix: + include: + - os: ubuntu-latest + + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ec5691b..b772fb72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ if(MSVC) endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /source-charset:UTF-8") else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -lstdc++fs + target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra # additional warnings -Wformat-nonliteral -Wcast-align -Wpointer-arith -Wundef @@ -114,7 +114,7 @@ if(UNIX) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs") endif() include_directories(${LUA_INCLUDE_DIR}) diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 9ef4696d..00ec8e0a 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -50,20 +50,20 @@ void AssetsLoader::createDefaults(AssetsLoader& loader) { loader.addLoader(ASSET_ATLAS, assetload::atlas); } -void AssetsLoader::addDefaults(AssetsLoader& loader, bool allAssets) { - if (allAssets) { - loader.add(ASSET_SHADER, SHADERS_FOLDER"/main", "main"); - loader.add(ASSET_SHADER, SHADERS_FOLDER"/lines", "lines"); - loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui"); +void AssetsLoader::addDefaults(AssetsLoader& loader, bool world) { + loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal"); + loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui"); + loader.add(ASSET_SHADER, SHADERS_FOLDER"/main", "main"); + loader.add(ASSET_SHADER, SHADERS_FOLDER"/lines", "lines"); + loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/menubg.png", "gui/menubg"); + loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/delete_icon.png", "gui/delete_icon"); + loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/no_icon.png", "gui/no_icon"); + if (world) { loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui3d", "ui3d"); loader.add(ASSET_SHADER, SHADERS_FOLDER"/background", "background"); loader.add(ASSET_SHADER, SHADERS_FOLDER"/skybox_gen", "skybox_gen"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/menubg.png", "gui/menubg"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/delete_icon.png", "gui/delete_icon"); - loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/no_icon.png", "gui/no_icon"); loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/misc/moon.png", "misc/moon"); loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/misc/sun.png", "misc/sun"); - loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal"); } loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/blocks", "blocks"); loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/items", "items"); diff --git a/src/assets/AssetsLoader.h b/src/assets/AssetsLoader.h index 13ea12fa..61a88b30 100644 --- a/src/assets/AssetsLoader.h +++ b/src/assets/AssetsLoader.h @@ -36,7 +36,7 @@ public: bool loadNext(); static void createDefaults(AssetsLoader& loader); - static void addDefaults(AssetsLoader& loader, bool allAssets); + static void addDefaults(AssetsLoader& loader, bool world); const ResPaths* getPaths() const; }; diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 6683d7f6..8336f5a8 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -20,12 +20,14 @@ bool assetload::texture(Assets* assets, const ResPaths* paths, const std::string filename, const std::string name) { - Texture* texture = png::load_texture(paths->find(filename).string()); + std::unique_ptr texture( + png::load_texture(paths->find(filename).string()) + ); if (texture == nullptr) { std::cerr << "failed to load texture '" << name << "'" << std::endl; return false; } - assets->store(texture, name); + assets->store(texture.release(), name); return true; } @@ -92,21 +94,21 @@ bool assetload::font(Assets* assets, const ResPaths* paths, const std::string filename, const std::string name) { - std::vector pages; + std::vector> pages; for (size_t i = 0; i <= 4; i++) { std::string name = filename + "_" + std::to_string(i) + ".png"; name = paths->find(name).string(); - Texture* texture = png::load_texture(name); + std::unique_ptr texture (png::load_texture(name)); if (texture == nullptr) { std::cerr << "failed to load bitmap font '" << name; std::cerr << "' (missing page " << std::to_string(i) << ")"; std::cerr << std::endl; return false; } - pages.push_back(texture); + pages.push_back(std::move(texture)); } - Font* font = new Font(pages, pages[0]->height / 16); - assets->store(font, name); + int res = pages[0]->height / 16; + assets->store(new Font(std::move(pages), res, 4), name); return true; } @@ -135,7 +137,6 @@ bool assetload::animation(Assets* assets, auto frameArr = root->list("frames"); - Frame temp; float frameDuration = DEFAULT_FRAME_DURATION; std::string frameName; @@ -164,7 +165,7 @@ bool assetload::animation(Assets* assets, if (!appendAtlas(builder, file)) continue; } - Atlas* srcAtlas = builder.build(2); + std::unique_ptr srcAtlas (builder.build(2)); Texture* srcTex = srcAtlas->getTexture(); Texture* dstTex = dstAtlas->getTexture(); @@ -196,7 +197,7 @@ bool assetload::animation(Assets* assets, } } - assets->store(srcAtlas, name + "_animation"); + assets->store(srcAtlas.release(), name + "_animation"); assets->store(animation); return true; diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index 82033cd5..ad4a8677 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -4,19 +4,6 @@ #include #include -inline int char2int(int c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } - if (c >= 'a' && c <= 'f') { - return 10 + c - 'a'; - } - if (c >= 'A' && c <= 'F') { - return 10 + c - 'A'; - } - return -1; -} - inline double power(double base, int64_t power) { double result = 1.0; for (int64_t i = 0; i < power; i++) { @@ -98,6 +85,18 @@ void BasicParser::skipWhitespace() { } } +void BasicParser::skip(size_t n) { + n = std::min(n, source.length()-pos); + + for (size_t i = 0; i < n; i++) { + char next = source[pos++]; + if (next == '\n') { + line++; + linestart = pos; + } + } +} + void BasicParser::skipLine() { while (hasNext()) { if (source[pos] == '\n') { @@ -110,10 +109,28 @@ void BasicParser::skipLine() { } } +bool BasicParser::skipTo(const std::string& substring) { + size_t idx = source.find(substring, pos); + if (idx == std::string::npos) { + skip(source.length()-pos); + return false; + } else { + skip(idx-pos); + return true; + } +} + bool BasicParser::hasNext() { return pos < source.length(); } +bool BasicParser::isNext(const std::string& substring) { + if (source.length() - pos < substring.length()) { + return false; + } + return source.substr(pos, substring.length()) == substring; +} + char BasicParser::nextChar() { if (!hasNext()) { throw error("unexpected end"); @@ -129,6 +146,17 @@ void BasicParser::expect(char expected) { pos++; } +void BasicParser::expect(const std::string& substring) { + if (substring.empty()) + return; + for (uint i = 0; i < substring.length(); i++) { + if (source.length() <= pos + i || source[pos+i] != substring[i]) { + throw error(escape_string(substring)+" expected"); + } + } + pos += substring.length(); +} + void BasicParser::expectNewLine() { while (hasNext()) { char next = source[pos]; @@ -145,6 +173,10 @@ void BasicParser::expectNewLine() { } } +void BasicParser::goBack() { + if (pos) pos--; +} + char BasicParser::peek() { skipWhitespace(); if (pos >= source.length()) { @@ -171,7 +203,7 @@ std::string BasicParser::parseName() { int64_t BasicParser::parseSimpleInt(int base) { char c = peek(); - int index = char2int(c); + int index = hexchar2int(c); if (index == -1 || index >= base) { throw error("invalid number literal"); } @@ -182,7 +214,7 @@ int64_t BasicParser::parseSimpleInt(int base) { while (c == '_') { c = source[++pos]; } - index = char2int(c); + index = hexchar2int(c); if (index == -1 || index >= base) { return value; } diff --git a/src/coders/commons.h b/src/coders/commons.h index 6c225309..509ff0d6 100644 --- a/src/coders/commons.h +++ b/src/coders/commons.h @@ -41,6 +41,19 @@ inline bool is_identifier_part(int c) { return is_identifier_start(c) || is_digit(c); } +inline int hexchar2int(int c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return 10 + c - 'a'; + } + if (c >= 'A' && c <= 'F') { + return 10 + c - 'A'; + } + return -1; +} + extern std::string escape_string(std::string s); class parsing_error : public std::runtime_error { @@ -70,12 +83,17 @@ protected: uint linestart = 0; virtual void skipWhitespace(); + void skip(size_t n); void skipLine(); + bool skipTo(const std::string& substring); void expect(char expected); + void expect(const std::string& substring); char peek(); char nextChar(); bool hasNext(); + bool isNext(const std::string& substring); void expectNewLine(); + void goBack(); std::string parseName(); int64_t parseSimpleInt(int base); diff --git a/src/coders/gzip.cpp b/src/coders/gzip.cpp index b30140c8..ee1afd62 100644 --- a/src/coders/gzip.cpp +++ b/src/coders/gzip.cpp @@ -34,7 +34,7 @@ std::vector gzip::compress(const ubyte* src, size_t size) { std::vector gzip::decompress(const ubyte* src, size_t size) { // getting uncompressed data length from gzip footer - size_t decompressed_size = *(uint32_t*)(src+size-4); + size_t decompressed_size = *reinterpret_cast(src+size-4); std::vector buffer; buffer.resize(decompressed_size); diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp new file mode 100644 index 00000000..aeb08dd1 --- /dev/null +++ b/src/coders/xml.cpp @@ -0,0 +1,354 @@ +#include "xml.h" + +#include +#include +#include +#include "../util/stringutil.h" + +using namespace xml; + +Attribute::Attribute(std::string name, std::string text) + : name(name), + text(text) { +} + +const std::string& Attribute::getName() const { + return name; +} + +const std::string& Attribute::getText() const { + return text; +} + +int64_t Attribute::asInt() const { + return std::stoll(text); +} + +double Attribute::asFloat() const { + return util::parse_double(text); +} + +bool Attribute::asBool() const { + return text == "true" || text == "1"; +} + +Node::Node(std::string tag) : tag(tag) { +} + +void Node::add(xmlelement element) { + elements.push_back(element); +} + +void Node::set(std::string name, std::string text) { + attrs[name] = Attribute(name, text); +} + +const std::string& Node::getTag() const { + return tag; +} + +const xmlattribute Node::attr(const std::string& name) const { + auto found = attrs.find(name); + if (found == attrs.end()) { + throw std::runtime_error("element <"+tag+" ...> missing attribute "+name); + } + return found->second; +} + +const xmlattribute Node::attr(const std::string& name, const std::string& def) const { + auto found = attrs.find(name); + if (found == attrs.end()) { + return Attribute(name, def); + } + return found->second; +} + +bool Node::has(const std::string& name) const { + auto found = attrs.find(name); + return found != attrs.end(); +} + +xmlelement Node::sub(size_t index) { + return elements.at(index); +} + +size_t Node::size() const { + return elements.size(); +} + +const std::vector& Node::getElements() const { + return elements; +} + +const xmlelements_map& Node::getAttributes() const { + return attrs; +} + +Document::Document(std::string version, std::string encoding) + : version(version), + encoding(encoding) { +} + +void Document::setRoot(xmlelement element) { + this->root = element; +} + +xmlelement Document::getRoot() const { + return root; +} + +const std::string& Document::getVersion() const { + return version; +} + +const std::string& Document::getEncoding() const { + return encoding; +} + +Parser::Parser(std::string filename, std::string source) + : BasicParser(filename, source) { +} + +xmlelement Parser::parseOpenTag() { + std::string tag = parseXMLName(); + auto node = std::make_shared(tag); + + char c; + while (true) { + skipWhitespace(); + c = peek(); + if (c == '/' || c == '>' || c == '?') + break; + std::string attrname = parseXMLName(); + std::string attrtext = ""; + skipWhitespace(); + if (peek() == '=') { + nextChar(); + skipWhitespace(); + expect('"'); + attrtext = parseString('"'); + } + node->set(attrname, attrtext); + } + return node; +} + +void Parser::parseDeclaration() { + std::string version = "1.0"; + std::string encoding = "UTF-8"; + expect('<'); + if (peek() == '?') { + nextChar(); + xmlelement node = parseOpenTag(); + expect("?>"); + if (node->getTag() != "xml") { + throw error("invalid declaration"); + } + version = node->attr("version", version).getText(); + encoding = node->attr("encoding", encoding).getText(); + if (encoding != "utf-8" && encoding != "UTF-8") { + throw error("UTF-8 encoding is only supported"); + } + } else { + goBack(); + } + document = std::make_shared(version, encoding); +} + +void Parser::parseComment() { + expect("!--"); + if (skipTo("-->")) { + skip(3); + } else { + throw error("comment close missing"); + } +} + +std::string Parser::parseText() { + size_t start = pos; + while (hasNext()) { + char c = peek(); + if (c == '<') { + break; + } + nextChar(); + } + return source.substr(start, pos-start); +} + +inline bool is_xml_identifier_start(char c) { + return is_identifier_start(c) || c == ':'; +} + +inline bool is_xml_identifier_part(char c) { + return is_identifier_part(c) || c == '-' || c == '.' || c == ':'; +} + +std::string Parser::parseXMLName() { + char c = peek(); + if (!is_xml_identifier_start(c)) { + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_xml_identifier_part(source[pos])) { + pos++; + } + return source.substr(start, pos-start); +} + +xmlelement Parser::parseElement() { + // text element + if (peek() != '<') { + auto element = std::make_shared("#"); + auto text = parseText(); + util::replaceAll(text, """, "\""); + util::replaceAll(text, "'", "'"); + util::replaceAll(text, "<", "<"); + util::replaceAll(text, ">", ">"); + util::replaceAll(text, "&", "&"); + element->set("#", text); + return element; + } + nextChar(); + + // + if (peek() == '!') { + if (isNext("!DOCTYPE ")) { + throw error("XML DTD is not supported yet"); + } + parseComment(); + return nullptr; + } + + auto element = parseOpenTag(); + char c = nextChar(); + + // + if (c == '/') { + expect('>'); + } + // ... + else if (c == '>') { + skipWhitespace(); + while (!isNext("add(sub); + } + skipWhitespace(); + } + skip(2); + expect(element->getTag()); + expect('>'); + } + // + else { + throw error("invalid syntax"); + } + return element; +} + +xmldocument Parser::parse() { + parseDeclaration(); + + xmlelement root = nullptr; + while (root == nullptr) { + root = parseElement(); + } + document->setRoot(root); + return document; +} + +xmldocument xml::parse(std::string filename, std::string source) { + Parser parser(filename, source); + return parser.parse(); +} + +inline void newline( + std::stringstream& ss, + bool nice, + const std::string& indentStr, + int indent +) { + if (!nice) + return; + ss << '\n'; + for (int i = 0; i < indent; i++) { + ss << indentStr; + } +} + +static void stringifyElement( + std::stringstream& ss, + const xmlelement element, + bool nice, + const std::string& indentStr, + int indent +) { + if (element->isText()) { + std::string text = element->attr("#").getText(); + util::replaceAll(text, "&", "&"); + util::replaceAll(text, "\"","""); + util::replaceAll(text, "'", "'"); + util::replaceAll(text, "<", "<"); + util::replaceAll(text, ">", ">"); + ss << text; + return; + } + const std::string& tag = element->getTag(); + + ss << '<' << tag; + auto& attrs = element->getAttributes(); + if (!attrs.empty()) { + ss << ' '; + int count = 0; + for (auto& entry : attrs) { + auto attr = entry.second; + ss << attr.getName(); + if (!attr.getText().empty()) { + ss << "=" << escape_string(attr.getText()); + } + if (count + 1 < int(attrs.size())) { + ss << " "; + } + count++; + } + } + auto& elements = element->getElements(); + if (elements.size() == 1 && elements[0]->isText()) { + ss << ">"; + stringifyElement(ss, elements[0], nice, indentStr, indent+1); + ss << ""; + return; + } + if (!elements.empty()) { + ss << '>'; + for (auto& sub : elements) { + newline(ss, nice, indentStr, indent+1); + stringifyElement(ss, sub, nice, indentStr, indent+1); + } + newline(ss, nice, indentStr, indent); + ss << ""; + + } else { + ss << "/>"; + } + +} + +std::string xml::stringify( + const xmldocument document, + bool nice, + const std::string& indentStr +) { + std::stringstream ss; + + // XML declaration + ss << "getVersion(); + ss << "\" encoding=\"UTF-8\" ?>"; + newline(ss, nice, indentStr, 0); + + stringifyElement(ss, document->getRoot(), nice, indentStr, 0); + + return ss.str(); +} diff --git a/src/coders/xml.h b/src/coders/xml.h new file mode 100644 index 00000000..275bd279 --- /dev/null +++ b/src/coders/xml.h @@ -0,0 +1,134 @@ +#ifndef CODERS_XML_H_ +#define CODERS_XML_H_ + +#include +#include +#include +#include + +#include "commons.h" + +namespace xml { + class Node; + class Attribute; + class Document; + + typedef Attribute xmlattribute; + typedef std::shared_ptr xmlelement; + typedef std::shared_ptr xmldocument; + typedef std::unordered_map xmlelements_map; + + class Attribute { + std::string name; + std::string text; + public: + Attribute() {}; + Attribute(std::string name, std::string text); + + const std::string& getName() const; + const std::string& getText() const; + int64_t asInt() const; + double asFloat() const; + bool asBool() const; + }; + + /* XML element class. Text element has tag 'text' and attribute 'text' */ + class Node { + std::string tag; + std::unordered_map attrs; + std::vector elements; + public: + Node(std::string tag); + + /* Add sub-element */ + void add(xmlelement element); + + /* Set attribute value. Creates attribute if does not exists */ + void set(std::string name, std::string text); + + /* Get element tag */ + const std::string& getTag() const; + + inline bool isText() const { + return getTag() == "#"; + } + + inline const std::string& text() const { + return attr("#").getText(); + } + + /* Get attribute by name + @param name attribute name + @throws std::runtime_error if element has no attribute + @return xmlattribute - {name, value} */ + const xmlattribute attr(const std::string& name) const; + /* Get attribute by name + @param name name + @param def default value will be returned wrapped in xmlattribute + if element has no attribute + @return xmlattribute - {name, value} or {name, def} if not found*/ + const xmlattribute attr(const std::string& name, const std::string& def) const; + + /* Check if element has attribute + @param name attribute name */ + bool has(const std::string& name) const; + + /* Get sub-element by index + @throws std::out_of_range if an invalid index given */ + xmlelement sub(size_t index); + + /* Get number of sub-elements */ + size_t size() const; + + const std::vector& getElements() const; + const xmlelements_map& getAttributes() const; + }; + + class Document { + xmlelement root = nullptr; + std::string version; + std::string encoding; + public: + Document(std::string version, std::string encoding); + + void setRoot(xmlelement element); + xmlelement getRoot() const; + + const std::string& getVersion() const; + const std::string& getEncoding() const; + }; + + class Parser : public BasicParser { + xmldocument document; + + xmlelement parseOpenTag(); + xmlelement parseElement(); + void parseDeclaration(); + void parseComment(); + std::string parseText(); + std::string parseXMLName(); + public: + Parser(std::string filename, std::string source); + + xmldocument parse(); + }; + + /* Serialize XML Document to string + @param document serializing document + @param nice use human readable format + (with indents and line-separators) + @param indentStr indentation characters sequence + (default - 4 spaces)*/ + extern std::string stringify( + const xmldocument document, + bool nice=true, + const std::string& indentStr=" " + ); + + /* Read XML Document from string + @param filename file name will be shown in error messages + @param source xml source code string */ + extern xmldocument parse(std::string filename, std::string source); +} + +#endif // CODERS_XML_H_ diff --git a/src/content/Content.cpp b/src/content/Content.cpp index aa98de9a..cdf03842 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -71,7 +71,7 @@ Content* ContentBuilder::build() { // Generating runtime info def->rt.id = blockDefsIndices.size(); - def->rt.emissive = *((uint32_t*)def->emission); + def->rt.emissive = *reinterpret_cast(def->emission); def->rt.solid = def->model == BlockModel::block; if (def->rotatable) { @@ -95,7 +95,7 @@ Content* ContentBuilder::build() { // Generating runtime info def->rt.id = itemDefsIndices.size(); - def->rt.emissive = *((uint32_t*)def->emission); + def->rt.emissive = *reinterpret_cast(def->emission); itemDefsIndices.push_back(def); } diff --git a/src/content/Content.h b/src/content/Content.h index dc865ff2..9df2a04d 100644 --- a/src/content/Content.h +++ b/src/content/Content.h @@ -9,7 +9,7 @@ #include #include "../typedefs.h" -typedef std::set DrawGroups; +using DrawGroups = std::set; class Block; class ItemDef; diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 6cec4a36..54f585f9 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -74,7 +74,7 @@ void ContentLoader::fixPackIndices() { std::unique_ptr root; if (fs::is_regular_file(indexFile)) { - root = std::move(files::read_json(indexFile)); + root = files::read_json(indexFile); } else { root.reset(new dynamic::Map()); } diff --git a/src/content/ContentLoader.h b/src/content/ContentLoader.h index e8517d53..df14910a 100644 --- a/src/content/ContentLoader.h +++ b/src/content/ContentLoader.h @@ -8,7 +8,7 @@ namespace fs = std::filesystem; class Block; class ItemDef; -class ContentPack; +struct ContentPack; class ContentBuilder; namespace dynamic { diff --git a/src/definitions.cpp b/src/core_defs.cpp similarity index 87% rename from src/definitions.cpp rename to src/core_defs.cpp index a9243ee4..da3136ba 100644 --- a/src/definitions.cpp +++ b/src/core_defs.cpp @@ -1,48 +1,43 @@ -#include "definitions.h" - -#include - -#include "items/ItemDef.h" -#include "content/Content.h" -#include "window/Window.h" -#include "window/Events.h" -#include "window/input.h" -#include "voxels/Block.h" - -using glm::vec3; - -// All in-game definitions (blocks, items, etc..) -void setup_definitions(ContentBuilder* builder) { // Strange function, need to REDO ? - Block* block = new Block("core:air", "air"); - block->replaceable = true; - block->drawGroup = 1; - block->lightPassing = true; - block->skyLightPassing = true; - block->obstacle = false; - block->selectable = false; - block->model = BlockModel::none; - block->pickingItem = "core:empty"; - builder->add(block); - - ItemDef* item = builder->createItem("core:empty"); - item->iconType = item_icon_type::none; -} - -void setup_bindings() { - Events::bind(BIND_MOVE_FORWARD, inputtype::keyboard, keycode::W); - Events::bind(BIND_MOVE_BACK, inputtype::keyboard, keycode::S); - Events::bind(BIND_MOVE_RIGHT, inputtype::keyboard, keycode::D); - Events::bind(BIND_MOVE_LEFT, inputtype::keyboard, keycode::A); - Events::bind(BIND_MOVE_JUMP, inputtype::keyboard, keycode::SPACE); - Events::bind(BIND_MOVE_SPRINT, inputtype::keyboard, keycode::LEFT_CONTROL); - Events::bind(BIND_MOVE_CROUCH, inputtype::keyboard, keycode::LEFT_SHIFT); - Events::bind(BIND_MOVE_CHEAT, inputtype::keyboard, keycode::R); - Events::bind(BIND_CAM_ZOOM, inputtype::keyboard, keycode::C); - Events::bind(BIND_CAM_MODE, inputtype::keyboard, keycode::F4); - Events::bind(BIND_PLAYER_NOCLIP, inputtype::keyboard, keycode::N); - Events::bind(BIND_PLAYER_FLIGHT, inputtype::keyboard, keycode::F); - Events::bind(BIND_PLAYER_ATTACK, inputtype::mouse, mousecode::BUTTON_1); - Events::bind(BIND_PLAYER_BUILD, inputtype::mouse, mousecode::BUTTON_2); - Events::bind(BIND_PLAYER_PICK, inputtype::mouse, mousecode::BUTTON_3); - Events::bind(BIND_HUD_INVENTORY, inputtype::keyboard, keycode::TAB); +#include "core_defs.h" + +#include "items/ItemDef.h" +#include "content/Content.h" +#include "window/Window.h" +#include "window/Events.h" +#include "window/input.h" +#include "voxels/Block.h" + +// All in-game definitions (blocks, items, etc..) +void corecontent::setup(ContentBuilder* builder) { + Block* block = builder->createBlock("core:air"); + block->replaceable = true; + block->drawGroup = 1; + block->lightPassing = true; + block->skyLightPassing = true; + block->obstacle = false; + block->selectable = false; + block->model = BlockModel::none; + block->pickingItem = "core:empty"; + + ItemDef* item = builder->createItem("core:empty"); + item->iconType = item_icon_type::none; +} + +void corecontent::setup_bindings() { + Events::bind(BIND_MOVE_FORWARD, inputtype::keyboard, keycode::W); + Events::bind(BIND_MOVE_BACK, inputtype::keyboard, keycode::S); + Events::bind(BIND_MOVE_RIGHT, inputtype::keyboard, keycode::D); + Events::bind(BIND_MOVE_LEFT, inputtype::keyboard, keycode::A); + Events::bind(BIND_MOVE_JUMP, inputtype::keyboard, keycode::SPACE); + Events::bind(BIND_MOVE_SPRINT, inputtype::keyboard, keycode::LEFT_CONTROL); + Events::bind(BIND_MOVE_CROUCH, inputtype::keyboard, keycode::LEFT_SHIFT); + Events::bind(BIND_MOVE_CHEAT, inputtype::keyboard, keycode::R); + Events::bind(BIND_CAM_ZOOM, inputtype::keyboard, keycode::C); + Events::bind(BIND_CAM_MODE, inputtype::keyboard, keycode::F4); + Events::bind(BIND_PLAYER_NOCLIP, inputtype::keyboard, keycode::N); + Events::bind(BIND_PLAYER_FLIGHT, inputtype::keyboard, keycode::F); + Events::bind(BIND_PLAYER_ATTACK, inputtype::mouse, mousecode::BUTTON_1); + Events::bind(BIND_PLAYER_BUILD, inputtype::mouse, mousecode::BUTTON_2); + Events::bind(BIND_PLAYER_PICK, inputtype::mouse, mousecode::BUTTON_3); + Events::bind(BIND_HUD_INVENTORY, inputtype::keyboard, keycode::TAB); } \ No newline at end of file diff --git a/src/core_defs.h b/src/core_defs.h index 5cdfbf2b..a813c133 100644 --- a/src/core_defs.h +++ b/src/core_defs.h @@ -3,7 +3,6 @@ #include - const std::string TEXTURE_NOTFOUND = "notfound"; /* bindings used in engine code */ @@ -24,4 +23,11 @@ const std::string BIND_PLAYER_BUILD = "player.build"; const std::string BIND_PLAYER_PICK = "player.pick"; const std::string BIND_HUD_INVENTORY = "hud.inventory"; +class ContentBuilder; + +namespace corecontent { + extern void setup_bindings(); + extern void setup(ContentBuilder* builder); +} + #endif // SRC_CORE_DEFS_H_ \ No newline at end of file diff --git a/src/definitions.h b/src/definitions.h deleted file mode 100644 index 31dc982c..00000000 --- a/src/definitions.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef DECLARATIONS_H -#define DECLARATIONS_H - -#include -#include "core_defs.h" - -class ContentBuilder; - -extern void setup_bindings(); -extern void setup_definitions(ContentBuilder* builder); - -#endif // DECLARATIONS_H - diff --git a/src/delegates.h b/src/delegates.h new file mode 100644 index 00000000..06f0d3a5 --- /dev/null +++ b/src/delegates.h @@ -0,0 +1,10 @@ +#ifndef DELEGATES_H_ +#define DELEGATES_H_ + +#include +#include + +typedef std::function runnable; +typedef std::function stringconsumer; + +#endif // DELEGATES_H_ diff --git a/src/engine.cpp b/src/engine.cpp index 0b6e979b..a2a8fd27 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -37,7 +37,7 @@ #include "frontend/locale/langs.h" #include "logic/scripting/scripting.h" -#include "definitions.h" +#include "core_defs.h" namespace fs = std::filesystem; @@ -56,7 +56,7 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths) assets.reset(new Assets()); AssetsLoader loader(assets.get(), resPaths.get()); AssetsLoader::createDefaults(loader); - AssetsLoader::addDefaults(loader, true); + AssetsLoader::addDefaults(loader, false); Shader::preprocessor->setPaths(resPaths.get()); while (loader.hasNext()) { @@ -148,7 +148,7 @@ inline const std::string checkPacks(const std::unordered_set& packs void Engine::loadContent() { auto resdir = paths->getResources(); ContentBuilder contentBuilder; - setup_definitions(&contentBuilder); + corecontent::setup(&contentBuilder); paths->setContentPacks(&contentPacks); std::vector resRoots; @@ -183,7 +183,7 @@ void Engine::loadContent() { std::cout << "-- loading assets" << std::endl; AssetsLoader loader(new_assets.get(), resPaths.get()); AssetsLoader::createDefaults(loader); - AssetsLoader::addDefaults(loader, false); + AssetsLoader::addDefaults(loader, true); while (loader.hasNext()) { if (!loader.loadNext()) { new_assets.reset(); @@ -214,7 +214,7 @@ void Engine::setScreen(std::shared_ptr screen) { void Engine::setLanguage(std::string locale) { settings.ui.language = locale; langs::setup(paths->getResources(), locale, contentPacks); - menus::create_menus(this, gui->getMenu()); + menus::create_menus(this); } gui::GUI* Engine::getGUI() { diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index a657a3ca..faea6438 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -30,10 +30,10 @@ fs::path EnginePaths::getScreenshotFile(std::string ext) { ss << std::put_time(&tm, format); std::string datetimestr = ss.str(); - fs::path filename = folder/fs::path("screenshot-"+datetimestr+"."+ext); + fs::path filename = folder/fs::u8path("screenshot-"+datetimestr+"."+ext); uint index = 0; while (fs::exists(filename)) { - filename = folder/fs::path("screenshot-"+datetimestr+"-"+std::to_string(index)+"."+ext); + filename = folder/fs::u8path("screenshot-"+datetimestr+"-"+std::to_string(index)+"."+ext); index++; } return filename; @@ -59,7 +59,7 @@ std::vector EnginePaths::scanForWorlds() { continue; } fs::path worldFolder = entry.path(); - fs::path worldFile = worldFolder/fs::path(WorldFiles::WORLD_FILE); + fs::path worldFile = worldFolder/fs::u8path(WorldFiles::WORLD_FILE); if (!fs::is_regular_file(worldFile)) { continue; } @@ -91,31 +91,31 @@ void EnginePaths::setContentPacks(std::vector* contentPacks) { fs::path EnginePaths::resolve(std::string path) { size_t separator = path.find(':'); if (separator == std::string::npos) { - return fs::path(path); + return fs::u8path(path); } std::string prefix = path.substr(0, separator); std::string filename = path.substr(separator+1); if (prefix == "res" || prefix == "core") { - return resources/fs::path(filename); + return resources/fs::u8path(filename); } if (prefix == "user") { - return userfiles/fs::path(filename); + return userfiles/fs::u8path(filename); } if (prefix == "world") { - return worldFolder/fs::path(filename); + return worldFolder/fs::u8path(filename); } if (contentPacks) { for (auto& pack : *contentPacks) { if (pack.id == prefix) { - return pack.folder/fs::path(filename); + return pack.folder/fs::u8path(filename); } } } - return fs::path("./"+filename); + return fs::u8path("./"+filename); } ResPaths::ResPaths(fs::path mainRoot, std::vector roots) @@ -125,19 +125,19 @@ ResPaths::ResPaths(fs::path mainRoot, std::vector roots) fs::path ResPaths::find(const std::string& filename) const { for (int i = roots.size()-1; i >= 0; i--) { auto& root = roots[i]; - fs::path file = root / fs::path(filename); + fs::path file = root / fs::u8path(filename); if (fs::exists(file)) { return file; } } - return mainRoot / fs::path(filename); + return mainRoot / fs::u8path(filename); } std::vector ResPaths::listdir(const std::string& folderName) const { std::vector entries; for (int i = roots.size()-1; i >= 0; i--) { auto& root = roots[i]; - fs::path folder = root / fs::path(folderName); + fs::path folder = root / fs::u8path(folderName); if (!fs::is_directory(folder)) continue; for (const auto& entry : fs::directory_iterator(folder)) { @@ -145,7 +145,7 @@ std::vector ResPaths::listdir(const std::string& folderName) const { } } { - fs::path folder = mainRoot / fs::path(folderName); + fs::path folder = mainRoot / fs::u8path(folderName); if (!fs::is_directory(folder)) return entries; for (const auto& entry : fs::directory_iterator(folder)) { diff --git a/src/frontend/InventoryView.cpp b/src/frontend/InventoryView.cpp index acae9e55..00db92b0 100644 --- a/src/frontend/InventoryView.cpp +++ b/src/frontend/InventoryView.cpp @@ -289,10 +289,11 @@ void SlotView::clicked(gui::GUI* gui, int button) { if (stack.isEmpty()) { stack.set(grabbed); stack.setCount(1); - } else { + grabbed.setCount(grabbed.getCount()-1); + } else if (stack.accepts(grabbed)){ stack.setCount(stack.getCount()+1); + grabbed.setCount(grabbed.getCount()-1); } - grabbed.setCount(grabbed.getCount()-1); } } } diff --git a/src/frontend/InventoryView.h b/src/frontend/InventoryView.h index 7c6b26db..9b9e01b4 100644 --- a/src/frontend/InventoryView.h +++ b/src/frontend/InventoryView.h @@ -120,8 +120,6 @@ class InventoryView : public gui::Container { InventoryInteraction* interaction; std::vector slots; - - int scroll = 0; public: InventoryView( const Content* content, diff --git a/src/frontend/WorldRenderer.cpp b/src/frontend/WorldRenderer.cpp index 0ee73c32..cf20fe9d 100644 --- a/src/frontend/WorldRenderer.cpp +++ b/src/frontend/WorldRenderer.cpp @@ -106,7 +106,7 @@ void WorldRenderer::drawChunks(Chunks* chunks, } float px = camera->position.x / (float)CHUNK_W; float pz = camera->position.z / (float)CHUNK_D; - std::sort(indices.begin(), indices.end(), [this, chunks, px, pz](size_t i, size_t j) { + std::sort(indices.begin(), indices.end(), [chunks, px, pz](size_t i, size_t j) { auto a = chunks->chunks[i]; auto b = chunks->chunks[j]; return ((a->x + 0.5f - px)*(a->x + 0.5f - px) + diff --git a/src/frontend/graphics/Skybox.cpp b/src/frontend/graphics/Skybox.cpp index bd41d1c4..cb481990 100644 --- a/src/frontend/graphics/Skybox.cpp +++ b/src/frontend/graphics/Skybox.cpp @@ -39,7 +39,7 @@ Skybox::Skybox(uint size, Shader* shader) -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f }; - vattr attrs[] {2, 0}; + vattr attrs[] {{2}, {0}}; mesh = std::make_unique(vertices, 6, attrs); sprites.push_back(skysprite { diff --git a/src/frontend/gui/GUI.cpp b/src/frontend/gui/GUI.cpp index 114693d3..8f7118a0 100644 --- a/src/frontend/gui/GUI.cpp +++ b/src/frontend/gui/GUI.cpp @@ -13,30 +13,23 @@ #include "../../window/input.h" #include "../../window/Camera.h" -using glm::vec2; -using glm::vec3; -using std::string; -using std::shared_ptr; using namespace gui; GUI::GUI() { - container = new Container(vec2(0, 0), vec2(1000)); - - uicamera = new Camera(vec3(), Window::height); + container = std::make_shared(glm::vec2(0, 0), glm::vec2(1000)); + uicamera = std::make_unique(glm::vec3(), Window::height); uicamera->perspective = false; uicamera->flipped = true; - menu = new PagesControl(); + menu = std::make_shared(); container->add(menu); container->scrollable(false); } GUI::~GUI() { - delete uicamera; - delete container; } -PagesControl* GUI::getMenu() { +std::shared_ptr GUI::getMenu() { return menu; } @@ -85,7 +78,7 @@ void GUI::actMouse(float delta) { } void GUI::act(float delta) { - container->setSize(vec2(Window::width, Window::height)); + container->setSize(glm::vec2(Window::width, Window::height)); container->act(delta); auto prevfocus = focus; @@ -132,7 +125,7 @@ void GUI::draw(const GfxContext* pctx, Assets* assets) { container->draw(pctx, assets); } -shared_ptr GUI::getFocused() const { +std::shared_ptr GUI::getFocused() const { return focus; } @@ -144,19 +137,19 @@ void GUI::addBack(std::shared_ptr panel) { container->addBack(panel); } -void GUI::add(shared_ptr panel) { +void GUI::add(std::shared_ptr panel) { container->add(panel); } -void GUI::remove(shared_ptr panel) { +void GUI::remove(std::shared_ptr panel) { container->remove(panel); } -void GUI::store(string name, shared_ptr node) { +void GUI::store(std::string name, std::shared_ptr node) { storage[name] = node; } -shared_ptr GUI::get(string name) { +std::shared_ptr GUI::get(std::string name) { auto found = storage.find(name); if (found == storage.end()) { return nullptr; @@ -164,11 +157,11 @@ shared_ptr GUI::get(string name) { return found->second; } -void GUI::remove(string name) { +void GUI::remove(std::string name) { storage.erase(name); } -void GUI::setFocus(shared_ptr node) { +void GUI::setFocus(std::shared_ptr node) { if (focus) { focus->defocus(); } diff --git a/src/frontend/gui/GUI.h b/src/frontend/gui/GUI.h index 6b8876f9..17ddd493 100644 --- a/src/frontend/gui/GUI.h +++ b/src/frontend/gui/GUI.h @@ -44,28 +44,25 @@ class Camera; */ namespace gui { - typedef std::function runnable; - typedef std::function stringconsumer; - class UINode; class Container; class PagesControl; class GUI { - Container* container; + std::shared_ptr container; std::shared_ptr hover = nullptr; std::shared_ptr pressed = nullptr; std::shared_ptr focus = nullptr; std::unordered_map> storage; - Camera* uicamera; - PagesControl* menu; + std::unique_ptr uicamera; + std::shared_ptr menu; void actMouse(float delta); public: GUI(); ~GUI(); - PagesControl* getMenu(); + std::shared_ptr getMenu(); std::shared_ptr getFocused() const; bool isFocusCaught() const; diff --git a/src/frontend/gui/UINode.cpp b/src/frontend/gui/UINode.cpp index d87e1c9e..653e0f97 100644 --- a/src/frontend/gui/UINode.cpp +++ b/src/frontend/gui/UINode.cpp @@ -88,6 +88,14 @@ void UINode::setInteractive(bool flag) { interactive = flag; } +void UINode::setResizing(bool flag) { + resizing = flag; +} + +bool UINode::isResizing() const { + return resizing; +} + vec2 UINode::calcCoord() const { if (parent) { return coord + parent->calcCoord() + parent->contentOffset(); @@ -115,6 +123,15 @@ void UINode::setSize(vec2 size) { void UINode::setColor(vec4 color) { this->color = color; + this->hoverColor = color; +} + +void UINode::setHoverColor(glm::vec4 newColor) { + this->hoverColor = newColor; +} + +glm::vec4 UINode::getHoverColor() const { + return hoverColor; } vec4 UINode::getColor() const { diff --git a/src/frontend/gui/UINode.h b/src/frontend/gui/UINode.h index 56859cd7..630bb54b 100644 --- a/src/frontend/gui/UINode.h +++ b/src/frontend/gui/UINode.h @@ -13,23 +13,26 @@ namespace gui { class UINode; class GUI; - typedef std::function onaction; - typedef std::function onnumberchange; + using onaction = std::function; + using onnumberchange = std::function; enum class Align { left, center, right }; + class UINode { protected: glm::vec2 coord; glm::vec2 size; glm::vec4 color {1.0f}; + glm::vec4 hoverColor {1.0f}; glm::vec4 margin {1.0f}; bool visible = true; bool hover = false; bool pressed = false; bool focused = false; bool interactive = true; + bool resizing = true; Align align = Align::left; UINode* parent = nullptr; UINode(glm::vec2 coord, glm::vec2 size); @@ -50,9 +53,16 @@ namespace gui { virtual void setParent(UINode* node); UINode* getParent() const; + /* Set element color (doesn't affect inner elements). + Also replaces hover color to avoid adding extra properties. */ virtual void setColor(glm::vec4 newColor); + + /* Get element color */ glm::vec4 getColor() const; + virtual void setHoverColor(glm::vec4 newColor); + glm::vec4 getHoverColor() const; + virtual void setMargin(glm::vec4 margin); glm::vec4 getMargin() const; @@ -66,18 +76,34 @@ namespace gui { bool isPressed() const; void defocus(); bool isFocused() const; + + /* Check if elements catches all user input when focused */ virtual bool isFocuskeeper() const {return false;} virtual void typed(unsigned int codepoint) {}; virtual void keyPressed(int key) {}; + /* Check if screen position is inside of the element + @param pos screen position */ virtual bool isInside(glm::vec2 pos); + + /* Get element under the cursor. + @param pos cursor screen position + @param self shared pointer to element + @return self, sub-element or nullptr if element is not interractive */ virtual std::shared_ptr getAt(glm::vec2 pos, std::shared_ptr self); + /* Check if element is opaque for cursor */ virtual bool isInteractive() const; + /* Make the element opaque (true) or transparent (false) for cursor */ virtual void setInteractive(bool flag); + virtual void setResizing(bool flag); + virtual bool isResizing() const; + + /* Get inner content offset. Used for scroll */ virtual glm::vec2 contentOffset() {return glm::vec2(0.0f);}; + /* Calculate screen position of the element */ virtual glm::vec2 calcCoord() const; virtual void setCoord(glm::vec2 coord); virtual glm::vec2 getSize() const; diff --git a/src/frontend/gui/controls.cpp b/src/frontend/gui/controls.cpp index b5fc5b65..6b6c2441 100644 --- a/src/frontend/gui/controls.cpp +++ b/src/frontend/gui/controls.cpp @@ -20,6 +20,7 @@ Label::Label(std::string text, std::string fontName) : UINode(vec2(), vec2(text.length() * 8, 15)), text(util::str2wstr_utf8(text)), fontName_(fontName) { + setInteractive(false); } @@ -27,6 +28,7 @@ Label::Label(std::wstring text, std::string fontName) : UINode(vec2(), vec2(text.length() * 8, 15)), text(text), fontName_(fontName) { + setInteractive(false); } void Label::setText(std::wstring text) { @@ -46,12 +48,23 @@ void Label::draw(const GfxContext* pctx, Assets* assets) { batch->color = getColor(); Font* font = assets->getFont(fontName_); vec2 size = getSize(); - vec2 newsize = vec2(font->calcWidth(text), font->lineHeight()); - if (newsize.x > size.x) { - setSize(newsize); - size = newsize; - } + vec2 newsize = vec2( + font->calcWidth(text), + font->getLineHeight()+font->getYOffset() + ); + vec2 coord = calcCoord(); + switch (align) { + case Align::left: + break; + case Align::center: + coord.x += (size.x-newsize.x)*0.5f; + break; + case Align::right: + coord.x += size.x-newsize.x; + break; + } + coord.y += (size.y-newsize.y)*0.5f; font->draw(batch, text, coord.x, coord.y); } @@ -60,10 +73,6 @@ Label* Label::textSupplier(wstringsupplier supplier) { return this; } -void Label::setSize(vec2 sizenew) { - UINode::setSize(vec2(UINode::getSize().x, sizenew.y)); -} - // ================================= Image ==================================== Image::Image(std::string texture, vec2 size) : UINode(vec2(), size), texture(texture) { setInteractive(false); @@ -87,17 +96,34 @@ Button::Button(std::shared_ptr content, glm::vec4 padding) padding[1]+padding[3]+margin[1]+margin[3])); add(content); scrollable(false); + setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f)); } -Button::Button(std::wstring text, glm::vec4 padding, glm::vec4 margin) - : Panel(vec2(32,32), padding, 0) +Button::Button( + std::wstring text, + vec4 padding, + onaction action, + vec2 size +) : Panel(size, padding, 0) { - setMargin(margin); + if (size.y < 0.0f) { + size = vec2( + glm::max(padding.x + padding.z + text.length()*8, size.x), + glm::max(padding.y + padding.w + 16, size.y) + ); + } + setSize(size); + + if (action) { + listenAction(action); + } scrollable(false); label = std::make_shared