diff --git a/doc/en/scripting/builtins/libplayer.md b/doc/en/scripting/builtins/libplayer.md index 7e27d817c..379ea89b6 100644 --- a/doc/en/scripting/builtins/libplayer.md +++ b/doc/en/scripting/builtins/libplayer.md @@ -48,6 +48,12 @@ player.set_rot(playerid: int, x: number, y: number, z: number) Set camera rotation (degrees) +```lua +player.get_dir(playerid: int) -> vec3 +``` + +Returns the player look direction vector + ```lua player.get_inventory(playerid: int) -> int, int ``` diff --git a/doc/en/scripting/builtins/libutf8.md b/doc/en/scripting/builtins/libutf8.md index bd8dd22bf..c8837c657 100644 --- a/doc/en/scripting/builtins/libutf8.md +++ b/doc/en/scripting/builtins/libutf8.md @@ -30,4 +30,7 @@ utf8.lower(text: str) -> str -- Escapes a string utf8.escape(text: str) -> str + +-- Escapes special XML characters +utf8.escape_xml(text: str) -> str ``` diff --git a/doc/ru/scripting/builtins/libplayer.md b/doc/ru/scripting/builtins/libplayer.md index c789df8d2..b156d6d27 100644 --- a/doc/ru/scripting/builtins/libplayer.md +++ b/doc/ru/scripting/builtins/libplayer.md @@ -48,6 +48,12 @@ player.set_rot(playerid: int, x: number, y: number, z: number) Устанавливает x, y вращения камеры (в градусах) +```lua +player.get_dir(playerid: int) -> vec3 +``` + +Возвращает вектор направления взгляда игрока + ```lua player.get_inventory(playerid: int) -> int, int ``` diff --git a/doc/ru/scripting/builtins/libutf8.md b/doc/ru/scripting/builtins/libutf8.md index ce933417e..e4d79f4ff 100644 --- a/doc/ru/scripting/builtins/libutf8.md +++ b/doc/ru/scripting/builtins/libutf8.md @@ -30,4 +30,7 @@ utf8.lower(text: str) -> str -- Экранирует строку utf8.escape(text: str) -> str + +-- Экранирует спец-символы XML +utf8.escape_xml(text: str) -> str ``` diff --git a/doc/ru/scripting/extensions.md b/doc/ru/scripting/extensions.md index 2d7f0a1d5..9a89b2b53 100644 --- a/doc/ru/scripting/extensions.md +++ b/doc/ru/scripting/extensions.md @@ -187,6 +187,12 @@ string.escape(str: string) -> string Экранирует строку. Является псевдонимом `utf8.escape`. +```lua +string.escape_xml(text: str) -> str +``` + +Экранирует спец-символы XML. Является псевдонимом `utf8.escape_xml`. + ```lua string.pad(str: string, size: number, [опционально] char: string) -> string ``` diff --git a/res/devtools/default_syntax_scheme.toml b/res/devtools/default_syntax_scheme.toml new file mode 100644 index 000000000..885defd61 --- /dev/null +++ b/res/devtools/default_syntax_scheme.toml @@ -0,0 +1,19 @@ +[default] +bold = false +italic = false +strikethrough = false +underline = false +color = [0.8, 0.8, 0.8, 1] + +[keyword] +bold = true +color = [0.9, 0.6, 0.4, 1] + +[literal] +color = [0.4, 0.8, 0.5, 1] + +[comment] +color = [0.5, 0.7, 1.0, 1] + +[error] +color = [1.0, 0.2, 0.1, 1] diff --git a/res/modules/internal/extensions/string.lua b/res/modules/internal/extensions/string.lua index f76470df6..313ae57bd 100644 --- a/res/modules/internal/extensions/string.lua +++ b/res/modules/internal/extensions/string.lua @@ -105,6 +105,7 @@ end string.lower = utf8.lower string.upper = utf8.upper string.escape = utf8.escape +string.escape_xml = utf8.escape_xml local meta = getmetatable("") diff --git a/res/modules/internal/gui_util.lua b/res/modules/internal/gui_util.lua index 153605211..4766cbfd9 100644 --- a/res/modules/internal/gui_util.lua +++ b/res/modules/internal/gui_util.lua @@ -3,8 +3,8 @@ local gui_util = { } --- Parse `pagename?arg1=value1&arg2=value2` queries ---- @param query page query string ---- @return page_name, args_table +--- @param query string page query string +--- @return string, string -> page_name, args_table function gui_util.parse_query(query) local args = {} local name @@ -23,8 +23,7 @@ function gui_util.parse_query(query) return name, args end ---- @param query page query string ---- @return document_id +--- @param query string page query string function gui_util.load_page(query) local name, args = gui_util.parse_query(query) for i = #gui_util.local_dispatchers, 1, -1 do @@ -116,4 +115,52 @@ gui_util.Document = Document gui_util.Element = Element gui_util.RadioGroup = RadioGroup +function gui.show_message(text, actual_callback) + local id = "dialog_"..random.uuid() + + local callback = function() + gui.root[id]:destruct() + if actual_callback then + actual_callback() + end + end + gui.root.root:add(string.format([[ + + + + + + + ]], id, string.escape_xml(text)), {callback=callback}) + input.add_callback("key:escape", callback, gui.root[id]) +end + +function gui.ask(text, on_yes, on_no) + on_yes = on_yes or function() end + on_no = on_no or function() end + + local id = "dialog_"..random.uuid() + + local yes_callback = function() + gui.root[id]:destruct() + on_yes() + end + local no_callback = function() + gui.root[id]:destruct() + on_no() + end + gui.root.root:add(string.format([[ + + + + + + + + ]], id, string.escape_xml(text)), {on_yes=yes_callback, on_no=no_callback}) + input.add_callback("key:escape", no_callback, gui.root[id]) +end + return gui_util diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 571bf32f7..dcca0f281 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -150,6 +150,18 @@ _MENU = _GUI_ROOT.menu menu = _MENU gui.root = _GUI_ROOT +do + local status, err = pcall(function() + local default_styles = toml.parse(file.read( + "res:devtools/default_syntax_scheme.toml" + )) + gui.set_syntax_styles(default_styles) + end) + if not status then + debug.error("could not to load default syntax scheme: "..err) + end +end + --- Console library extension --- console.cheats = {} diff --git a/res/texts/be_BY.txt b/res/texts/be_BY.txt index 1fb2a9931..6d7bca2e2 100644 --- a/res/texts/be_BY.txt +++ b/res/texts/be_BY.txt @@ -26,6 +26,7 @@ Error at line %{0}=Памылка ў радку %{0} Run=Запусціць Filter=Фільтр Are you sure you want to open the link: =Ці вы ўпэўненыя, што хочаце адкрыць спасылку: +Grant '%{0}' pack audio recording permission?=Выдаць паку «%{0}» дазвол на запіс гуку? editor.info.tooltip=CTRL+S - Захаваць\nCTRL+R - Запусціць\nCTRL+Z - Скасаваць\nCTRL+Y - Паўтарыць devtools.traceback=Стэк выклікаў (ад апошняга) @@ -33,12 +34,14 @@ devtools.output=Вывод error.pack-not-found=Не ўдалося знайсці пакет error.dependency-not-found=Выкарыстоўваная залежнасць не знойдзена +error.dependency-version-not-met=Версія залежнасці не адпавядае неабходнай pack.remove-confirm=Выдаліць увесь кантэнт, які пастаўляецца пакетам/пакетамі, са свету (беззваротна)? # Падказкі graphics.gamma.tooltip=Крывая яркасці асвятлення graphics.backlight.tooltip=Падсветка, якая прадухіляе поўную цемру graphics.dense-render.tooltip=Уключае празрыстасць блокаў, такіх як лісце. +graphics.soft-lighting.tooltip=Уключае мяккае асвятленне ў блоках # Меню menu.Apply=Ужыць @@ -60,7 +63,6 @@ menu.Quit=Выхад menu.Save and Quit to Menu=Захаваць і Выйсці ў Меню menu.Settings=Налады menu.Reset settings=Скінуць налады -menu.Contents Menu=Меню кантэнт-пакаў menu.Open data folder=Адкрыць тэчку з дадзенымі menu.Open content folder=Адкрыць тэчку [content] @@ -80,6 +82,7 @@ world.delete-confirm=Выдаліць свет беззваротна? settings.Ambient=Фон settings.Backlight=Падсветка settings.Dense blocks render=Шчыльны рэндэр блокаў +settings.Soft lighting=Мяккае асвятленне settings.Camera Shaking=Труска камеры settings.Camera Inertia=Інэрцыя камеры settings.Camera FOV Effects=Эфекты поля зроку @@ -100,6 +103,13 @@ settings.V-Sync=Вертыкальная сінхранізацыя settings.Key=Кнопка settings.Controls Search Mode=Пошук па прывязанай кнопцы кіравання settings.Limit Background FPS=Абмежаваць фонавую частату кадраў +settings.Advanced render=Прасунуты рэндэр +settings.Shadows quality=Якасць ценяў +settings.Conflict=Знойдзены магчымыя канфлікты +settings.Windowed=Ваконны +settings.Borderless=Безрамкавы +settings.Microphone=Мікрафон +settings.microphone.None=Няма # Кіраванне chunks.reload=Перазагрузіць чанкі diff --git a/src/devtools/SyntaxProcessor.cpp b/src/devtools/SyntaxProcessor.cpp index a4a1c7cfd..c1c3f179e 100644 --- a/src/devtools/SyntaxProcessor.cpp +++ b/src/devtools/SyntaxProcessor.cpp @@ -7,19 +7,15 @@ using namespace devtools; static std::unique_ptr build_styles( + const FontStylesScheme& colorScheme, const std::vector& tokens ) { using devtools::TokenTag; - FontStylesScheme styles { - { - {false, false, false, false, glm::vec4(0.8f, 0.8f, 0.8f, 1)}, // default - {true, false, false, false, glm::vec4(0.9, 0.6f, 0.4f, 1)}, // keyword - {false, false, false, false, glm::vec4(0.4, 0.8f, 0.5f, 1)}, // string - {false, false, false, false, glm::vec4(0.3, 0.3f, 0.3f, 1)}, // comment - {true, false, false, false, glm::vec4(1.0f, 0.2f, 0.1f, 1)}, // unexpected - }, - {} - }; + FontStylesScheme styles {colorScheme.palette, {}}; + if (styles.palette.empty()) { + styles.palette.push_back(FontStyle { + false, false, false, false, glm::vec4(0.8f, 0.8f, 0.8f, 1)}); + } size_t offset = 0; for (int i = 0; i < tokens.size(); i++) { const auto& token = tokens.at(i); @@ -47,6 +43,9 @@ static std::unique_ptr build_styles( styleIndex = 0; break; } + if (styleIndex >= styles.palette.size()) { + styleIndex = 0; + } styles.map.insert( styles.map.end(), token.end.pos - token.start.pos, styleIndex ); @@ -67,7 +66,9 @@ void SyntaxProcessor::addSyntax( } std::unique_ptr SyntaxProcessor::highlight( - const std::string& ext, std::wstring_view source + const FontStylesScheme& colorScheme, + const std::string& ext, + std::wstring_view source ) const { const auto& found = langsExtensions.find(ext); if (found == langsExtensions.end()) { @@ -76,7 +77,7 @@ std::unique_ptr SyntaxProcessor::highlight( const auto& syntax = *found->second; try { auto tokens = tokenize(syntax, "", source); - return build_styles(tokens); + return build_styles(colorScheme, tokens); } catch (const parsing_error& err) { return nullptr; } diff --git a/src/devtools/SyntaxProcessor.hpp b/src/devtools/SyntaxProcessor.hpp index afb975a83..d0ae41e7b 100644 --- a/src/devtools/SyntaxProcessor.hpp +++ b/src/devtools/SyntaxProcessor.hpp @@ -18,7 +18,9 @@ namespace devtools { class SyntaxProcessor { public: std::unique_ptr highlight( - const std::string& ext, std::wstring_view source + const FontStylesScheme& colorScheme, + const std::string& ext, + std::wstring_view source ) const; void addSyntax(std::unique_ptr syntax); diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index 3e9a3a274..da4fec517 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -23,11 +23,12 @@ void UiDocument::rebuildIndices() { map["root"] = root; } -const UINodesMap& UiDocument::getMap() const { - return map; +void UiDocument::pushIndices(const std::shared_ptr& node) { + gui::UINode::getIndices(node, map); + map["root"] = root; } -UINodesMap& UiDocument::getMapWriteable() { +const UINodesMap& UiDocument::getMap() const { return map; } diff --git a/src/frontend/UiDocument.hpp b/src/frontend/UiDocument.hpp index 9d6e6ecaa..2ad00da80 100644 --- a/src/frontend/UiDocument.hpp +++ b/src/frontend/UiDocument.hpp @@ -36,10 +36,10 @@ public: ); void rebuildIndices(); + void pushIndices(const std::shared_ptr& node); const std::string& getId() const; const UINodesMap& getMap() const; - UINodesMap& getMapWriteable(); std::shared_ptr getRoot() const; std::shared_ptr get(const std::string& id) const; const uidocscript& getScript() const; diff --git a/src/graphics/commons/FontStyle.cpp b/src/graphics/commons/FontStyle.cpp new file mode 100644 index 000000000..53cc06574 --- /dev/null +++ b/src/graphics/commons/FontStyle.cpp @@ -0,0 +1,37 @@ +#include "FontStyle.hpp" + +#include "data/dv.hpp" +#include "data/dv_util.hpp" +#include "devtools/SyntaxProcessor.hpp" + +FontStyle FontStyle::parse(const dv::value& src) { + FontStyle style {}; + src.at("bold").get(style.bold); + src.at("italic").get(style.italic); + src.at("strikethrough").get(style.strikethrough); + src.at("underline").get(style.underline); + dv::get_vec(src, "color", style.color); + return style; +} + +static void parse_style( + const dv::value& src, + FontStylesScheme& scheme, + const std::string& name, + devtools::SyntaxStyles tag +) { + if (src.has(name)) { + scheme.palette[static_cast(tag)] = FontStyle::parse(src[name]); + } +} + +FontStylesScheme FontStylesScheme::parse(const dv::value& src) { + FontStylesScheme scheme {}; + scheme.palette.resize(8); + parse_style(src, scheme, "default", devtools::SyntaxStyles::DEFAULT); + parse_style(src, scheme, "keyword", devtools::SyntaxStyles::KEYWORD); + parse_style(src, scheme, "literal", devtools::SyntaxStyles::LITERAL); + parse_style(src, scheme, "comment", devtools::SyntaxStyles::COMMENT); + parse_style(src, scheme, "error", devtools::SyntaxStyles::ERROR); + return scheme; +} diff --git a/src/graphics/commons/FontStyle.hpp b/src/graphics/commons/FontStyle.hpp new file mode 100644 index 000000000..5f0129f91 --- /dev/null +++ b/src/graphics/commons/FontStyle.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "data/dv_fwd.hpp" + +#include +#include + +struct FontStyle { + bool bold = false; + bool italic = false; + bool strikethrough = false; + bool underline = false; + glm::vec4 color {1, 1, 1, 1}; + + FontStyle() = default; + + FontStyle( + bool bold, + bool italic, + bool strikethrough, + bool underline, + glm::vec4 color + ) + : bold(bold), + italic(italic), + strikethrough(strikethrough), + underline(underline), + color(std::move(color)) { + } + + static FontStyle parse(const dv::value& src); +}; + +struct FontStylesScheme { + std::vector palette; + std::vector map; + + static FontStylesScheme parse(const dv::value& src); +}; diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index 12371595c..f30335be3 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -7,41 +7,14 @@ #include "typedefs.hpp" #include "FontMetics.hpp" +#include "data/dv_fwd.hpp" +#include "../commons/FontStyle.hpp" class Texture; class Batch2D; class Batch3D; class Camera; -struct FontStyle { - bool bold = false; - bool italic = false; - bool strikethrough = false; - bool underline = false; - glm::vec4 color {1, 1, 1, 1}; - - FontStyle() = default; - - FontStyle( - bool bold, - bool italic, - bool strikethrough, - bool underline, - glm::vec4 color - ) - : bold(bold), - italic(italic), - strikethrough(strikethrough), - underline(underline), - color(std::move(color)) { - } -}; - -struct FontStylesScheme { - std::vector palette; - std::vector map; -}; - class Font { int lineHeight; int yoffset; diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 3ab438b1f..22ae5b908 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -80,6 +80,14 @@ std::shared_ptr GUI::getMenu() { return menu; } +void GUI::setSyntaxColorScheme(std::unique_ptr scheme) { + syntaxColorScheme = std::move(scheme); +} + +FontStylesScheme* GUI::getSyntaxColorScheme() const { + return syntaxColorScheme.get(); +} + void GUI::onAssetsLoad(Assets* assets) { rootDocument->rebuildIndices(); assets->store(rootDocument, "core:root"); @@ -302,7 +310,7 @@ bool GUI::isFocusCaught() const { } void GUI::add(std::shared_ptr node) { - UINode::getIndices(node, rootDocument->getMapWriteable()); + rootDocument->pushIndices(node); container->add(std::move(node)); } diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index f2255fb5f..16d8e358c 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -17,6 +17,7 @@ struct CursorState; class Engine; class Input; class Window; +struct FontStylesScheme; namespace devtools { class Editor; @@ -73,6 +74,7 @@ namespace gui { std::shared_ptr focus; std::shared_ptr tooltip; std::shared_ptr rootDocument; + std::unique_ptr syntaxColorScheme; std::unordered_map> storage; std::unique_ptr uicamera; @@ -157,6 +159,9 @@ namespace gui { /// @deprecated std::shared_ptr getContainer() const; + void setSyntaxColorScheme(std::unique_ptr scheme); + FontStylesScheme* getSyntaxColorScheme() const; + void onAssetsLoad(Assets* assets); void postRunnable(const runnable& callback); diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 83c15544a..26160e126 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -918,7 +918,9 @@ void TextBox::onTab(bool shiftPressed) { void TextBox::refreshSyntax() { if (!syntax.empty()) { const auto& processor = gui.getEditor().getSyntaxProcessor(); - if (auto styles = processor.highlight(syntax, input)) { + auto scheme = gui.getSyntaxColorScheme(); + if (auto styles = + processor.highlight(scheme ? *scheme : FontStylesScheme {}, syntax, input)) { label->setStyles(std::move(styles)); } } diff --git a/src/lighting/LightSolver.cpp b/src/lighting/LightSolver.cpp index be608f1db..cda3ccc88 100644 --- a/src/lighting/LightSolver.cpp +++ b/src/lighting/LightSolver.cpp @@ -16,18 +16,23 @@ LightSolver::LightSolver(const ContentIndices& contentIds, Chunks& chunks, int c } void LightSolver::add(int x, int y, int z, int emission) { - if (emission <= 1) + if (emission <= 1) { return; + } Chunk* chunk = chunks.getChunkByVoxel(x, y, z); - if (chunk == nullptr) + if (chunk == nullptr) { return; - ubyte light = chunk->lightmap.get(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel); + } + assert(chunk->lightmap != nullptr); + auto& lightmap = *chunk->lightmap; + + ubyte light = lightmap.get(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel); if (emission < light) return; addqueue.push(lightentry {x, y, z, ubyte(emission)}); chunk->flags.modified = true; - chunk->lightmap.set(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel, emission); + lightmap.set(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel, emission); } void LightSolver::add(int x, int y, int z) { @@ -36,15 +41,18 @@ void LightSolver::add(int x, int y, int z) { void LightSolver::remove(int x, int y, int z) { Chunk* chunk = chunks.getChunkByVoxel(x, y, z); - if (chunk == nullptr) + if (chunk == nullptr) { return; + } + assert(chunk->lightmap != nullptr); + auto& lightmap = *chunk->lightmap; - ubyte light = chunk->lightmap.get(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel); - if (light == 0){ + ubyte light = lightmap.get(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel); + if (light == 0) { return; } remqueue.push(lightentry {x, y, z, light}); - chunk->lightmap.set(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel, 0); + lightmap.set(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel, 0); } void LightSolver::solve(){ @@ -73,18 +81,21 @@ void LightSolver::solve(){ int lz = z - chunk->z * CHUNK_D; chunk->flags.modified = true; - ubyte light = chunk->lightmap.get(lx,y,lz, channel); + assert(chunk->lightmap != nullptr); + auto& lightmap = *chunk->lightmap; + + ubyte light = lightmap.get(lx,y,lz, channel); if (light != 0 && light == entry.light-1){ voxel* vox = chunks.get(x, y, z); if (vox && vox->id != 0) { const Block* block = blockDefs[vox->id]; if (uint8_t emission = block->emission[channel]) { addqueue.push(lightentry {x, y, z, emission}); - chunk->lightmap.set(lx, y, lz, channel, emission); + lightmap.set(lx, y, lz, channel, emission); } - else chunk->lightmap.set(lx, y, lz, channel, 0); + else lightmap.set(lx, y, lz, channel, 0); } - else chunk->lightmap.set(lx, y, lz, channel, 0); + else lightmap.set(lx, y, lz, channel, 0); remqueue.push(lightentry {x, y, z, light}); } else if (light >= entry.light){ @@ -105,21 +116,24 @@ void LightSolver::solve(){ int z = entry.z+coords[imul3+2]; Chunk* chunk = chunks.getChunkByVoxel(x,y,z); - if (chunk) { - int lx = x - chunk->x * CHUNK_W; - int lz = z - chunk->z * CHUNK_D; - chunk->flags.modified = true; + if (chunk == nullptr) { + continue; + } + assert(chunk->lightmap != nullptr); + auto& lightmap = *chunk->lightmap; + int lx = x - chunk->x * CHUNK_W; + int lz = z - chunk->z * CHUNK_D; + chunk->flags.modified = true; - ubyte light = chunk->lightmap.get(lx, y, lz, channel); - voxel& v = chunk->voxels[vox_index(lx, y, lz)]; - const Block* block = blockDefs[v.id]; - if (block->lightPassing && light+2 <= entry.light){ - chunk->lightmap.set( - x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, - channel, - entry.light-1); - addqueue.push(lightentry {x, y, z, ubyte(entry.light-1)}); - } + ubyte light = lightmap.get(lx, y, lz, channel); + voxel& v = chunk->voxels[vox_index(lx, y, lz)]; + const Block* block = blockDefs[v.id]; + if (block->lightPassing && light+2 <= entry.light){ + lightmap.set( + x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, + channel, + entry.light-1); + addqueue.push(lightentry {x, y, z, ubyte(entry.light-1)}); } } } diff --git a/src/lighting/Lighting.cpp b/src/lighting/Lighting.cpp index eb721db29..9101c72ac 100644 --- a/src/lighting/Lighting.cpp +++ b/src/lighting/Lighting.cpp @@ -29,16 +29,21 @@ void Lighting::clear(){ const auto& chunks = this->chunks.getChunks(); for (size_t index = 0; index < chunks.size(); index++){ auto chunk = chunks[index]; - if (chunk == nullptr) + if (chunk == nullptr) { continue; - Lightmap& lightmap = chunk->lightmap; - for (int i = 0; i < CHUNK_VOL; i++){ - lightmap.map[i] = 0; } + auto& lightmap = chunk->lightmap; + if (lightmap == nullptr) { + continue; + } + std::memset(lightmap->map, 0, sizeof(Lightmap::map)); } } -void Lighting::prebuildSkyLight(Chunk& chunk, const ContentIndices& indices){ +void Lighting::prebuildSkyLight(Chunk& chunk, const ContentIndices& indices) { + assert(chunk.lightmap != nullptr); + auto& lightmap = *chunk.lightmap; + const auto* blockDefs = indices.blocks.getDefs(); int highestPoint = 0; @@ -49,17 +54,19 @@ void Lighting::prebuildSkyLight(Chunk& chunk, const ContentIndices& indices){ voxel& vox = chunk.voxels[index]; const Block* block = blockDefs[vox.id]; if (!block->skyLightPassing) { - if (highestPoint < y) + if (highestPoint < y) { highestPoint = y; + } break; } - chunk.lightmap.setS(x,y,z, 15); + lightmap.setS(x, y, z, 15); } } } - if (highestPoint < CHUNK_H-1) + if (highestPoint < CHUNK_H-1) { highestPoint++; - chunk.lightmap.highestPoint = highestPoint; + } + lightmap.highestPoint = highestPoint; } void Lighting::buildSkyLight(int cx, int cz){ @@ -70,15 +77,18 @@ void Lighting::buildSkyLight(int cx, int cz){ logger.error() << "attempted to build sky lights to chunk missing in local matrix"; return; } + assert(chunk->lightmap != nullptr); + auto& lightmap = *chunk->lightmap; + for (int z = 0; z < CHUNK_D; z++){ for (int x = 0; x < CHUNK_W; x++){ int gx = x + cx * CHUNK_W; int gz = z + cz * CHUNK_D; - for (int y = chunk->lightmap.highestPoint; y >= 0; y--){ + for (int y = lightmap.highestPoint; y >= 0; y--){ while (y > 0 && !blockDefs[chunk->voxels[vox_index(x, y, z)].id]->lightPassing) { y--; } - if (chunk->lightmap.getS(x, y, z) != 15) { + if (lightmap.getS(x, y, z) != 15) { solverS->add(gx,y+1,gz); for (; y >= 0; y--){ solverS->add(gx+1,y,gz); @@ -106,6 +116,9 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand) { logger.error() << "attempted to build lights to chunk missing in local matrix"; return; } + assert(chunk->lightmap != nullptr); + auto& lightmap = *chunk->lightmap; + for (uint y = 0; y < CHUNK_H; y++){ for (uint z = 0; z < CHUNK_D; z++){ for (uint x = 0; x < CHUNK_W; x++){ @@ -128,7 +141,7 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand) { for (int z = 0; z < CHUNK_D; z++) { int gx = x + cx * CHUNK_W; int gz = z + cz * CHUNK_D; - int rgbs = chunk->lightmap.get(x, y, z); + int rgbs = lightmap.get(x, y, z); if (rgbs){ solverR.add(gx,y,gz, Lightmap::extract(rgbs, 0)); solverG.add(gx,y,gz, Lightmap::extract(rgbs, 1)); @@ -143,7 +156,7 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand) { for (int x = 0; x < CHUNK_W; x++) { int gx = x + cx * CHUNK_W; int gz = z + cz * CHUNK_D; - int rgbs = chunk->lightmap.get(x, y, z); + int rgbs = lightmap.get(x, y, z); if (rgbs){ solverR.add(gx,y,gz, Lightmap::extract(rgbs, 0)); solverG.add(gx,y,gz, Lightmap::extract(rgbs, 1)); diff --git a/src/lighting/Lightmap.hpp b/src/lighting/Lightmap.hpp index d31ace74f..b376c0db3 100644 --- a/src/lighting/Lightmap.hpp +++ b/src/lighting/Lightmap.hpp @@ -89,4 +89,6 @@ public: std::unique_ptr encode() const; static std::unique_ptr decode(const ubyte* buffer); + + static inline light_t SUN_LIGHT_ONLY = combine(0U, 0U, 0U, 15U); }; diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index b521b0d81..0c65a8df7 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -150,7 +150,7 @@ bool ChunksController::buildLights( } } if (surrounding == MIN_SURROUNDING) { - if (lighting) { + if (lighting && chunk->lightmap) { bool lightsCache = chunk->flags.loadedLights; if (!lightsCache) { lighting->buildSkyLight(chunk->x, chunk->z); @@ -170,7 +170,7 @@ void ChunksController::createChunk(const Player& player, int x, int z) const { } return; } - auto chunk = level.chunks->create(x, z); + auto chunk = level.chunks->create(x, z, lighting != nullptr); player.chunks->putChunk(chunk); auto& chunkFlags = chunk->flags; if (!chunkFlags.loaded) { @@ -179,7 +179,7 @@ void ChunksController::createChunk(const Player& player, int x, int z) const { } chunk->updateHeights(); level.events->trigger(LevelEventType::CHUNK_PRESENT, chunk.get()); - if (!chunkFlags.loadedLights) { + if (!chunkFlags.loadedLights && chunk->lightmap) { Lighting::prebuildSkyLight(*chunk, *level.content.getIndices()); } chunkFlags.loaded = true; diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 01bfd0d62..b08d54455 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -105,7 +105,7 @@ static int l_container_add(lua::State* L) { auto subnode = guiutil::create( engine->getGUI(), xmlsrc, std::move(env) ); - UINode::getIndices(subnode, docnode.document->getMapWriteable()); + docnode.document->pushIndices(subnode); node->add(std::move(subnode)); } catch (const std::exception& err) { throw std::runtime_error("container:add(...): " + std::string(err.what())); @@ -410,9 +410,7 @@ static const std::string& request_node_id(const DocumentNode& docnode) { reinterpret_cast(docnode.node.get())); } docnode.node->setId(std::move(id)); - UINode::getIndices( - docnode.node, docnode.document->getMapWriteable() - ); + docnode.document->pushIndices(docnode.node); return docnode.node->getId(); } @@ -1089,6 +1087,16 @@ static int l_gui_load_document(lua::State* L) { return 0; } +static int l_set_syntax_styles(lua::State* L) { + if (engine->isHeadless()) { + return 0; + } + engine->getGUI().setSyntaxColorScheme(std::make_unique( + FontStylesScheme::parse(lua::tovalue(L, 1)) + )); + return 0; +} + const luaL_Reg guilib[] = { {"get_viewport", lua::wrap}, {"getattr", lua::wrap}, @@ -1101,6 +1109,7 @@ const luaL_Reg guilib[] = { {"confirm", lua::wrap}, {"alert", lua::wrap}, {"load_document", lua::wrap}, + {"set_syntax_styles", lua::wrap}, {"__reindex", lua::wrap}, {nullptr, nullptr} }; diff --git a/src/logic/scripting/lua/libs/libutf8.cpp b/src/logic/scripting/lua/libs/libutf8.cpp index e6538c2ec..15fc54e92 100644 --- a/src/logic/scripting/lua/libs/libutf8.cpp +++ b/src/logic/scripting/lua/libs/libutf8.cpp @@ -88,6 +88,11 @@ static int l_escape(lua::State* L) { return lua::pushstring(L, util::escape(string)); } +static int l_escape_xml(lua::State* L) { + auto string = lua::require_wstring(L, 1); + return lua::pushwstring(L, util::escape_xml(string)); +} + const luaL_Reg utf8lib[] = { {"tobytes", lua::wrap}, {"tostring", lua::wrap}, @@ -98,5 +103,6 @@ const luaL_Reg utf8lib[] = { {"lower", lua::wrap}, {"encode", lua::wrap}, {"escape", lua::wrap}, + {"escape_xml", lua::wrap}, {nullptr, nullptr} }; diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index eb966f505..7a53c4cb5 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -153,8 +153,10 @@ static void integrate_chunk_client(Chunk& chunk) { chunk.flags.loadedLights = false; chunk.flags.lighted = false; - chunk.lightmap.clear(); - Lighting::prebuildSkyLight(chunk, *indices); + if (chunk.lightmap) { + chunk.lightmap->clear(); + Lighting::prebuildSkyLight(chunk, *indices); + } for (int lz = -1; lz <= 1; lz++) { for (int lx = -1; lx <= 1; lx++) { diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index 63271a13d..e1a99f9e0 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -60,6 +60,33 @@ std::string util::escape(std::string_view s, bool escapeUnicode) { return ss.str(); } +std::wstring util::escape_xml(std::wstring_view s) { + std::wstringstream ss; + for (wchar_t c : s) { + switch (c) { + case L'&': + ss << L"&"; + break; + case L'<': + ss << L"<"; + break; + case L'>': + ss << L">"; + break; + case '"': + ss << L"""; + break; + case '\'': + ss << L"'"; + break; + default: + ss << c; + break; + } + } + return ss.str(); +} + std::string util::quote(const std::string& s) { return escape(s, false); } diff --git a/src/util/stringutil.hpp b/src/util/stringutil.hpp index 3d8bf16e3..c26f02741 100644 --- a/src/util/stringutil.hpp +++ b/src/util/stringutil.hpp @@ -10,6 +10,9 @@ namespace util { /// @brief Function used for string serialization in text formats std::string escape(std::string_view s, bool escapeUnicode=true); + /// @brief Escape all special XML characters + std::wstring escape_xml(std::wstring_view s); + /// @brief Function used for error messages std::string quote(const std::string& s); diff --git a/src/voxels/Chunk.cpp b/src/voxels/Chunk.cpp index e6da850ec..732e67a02 100644 --- a/src/voxels/Chunk.cpp +++ b/src/voxels/Chunk.cpp @@ -8,7 +8,8 @@ #include "util/data_io.hpp" #include "voxel.hpp" -Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos) { +Chunk::Chunk(int xpos, int zpos, std::shared_ptr lightmap) + : x(xpos), z(zpos), lightmap(std::move(lightmap)) { bottom = 0; top = CHUNK_H; } @@ -56,15 +57,6 @@ std::shared_ptr Chunk::getBlockInventory(uint x, uint y, uint z) return found->second; } -std::unique_ptr Chunk::clone() const { - auto other = std::make_unique(x, z); - for (uint i = 0; i < CHUNK_VOL; i++) { - other->voxels[i] = voxels[i]; - } - other->lightmap.set(&lightmap); - return other; -} - /** Current chunk format: - byte-order: little-endian diff --git a/src/voxels/Chunk.hpp b/src/voxels/Chunk.hpp index a15907f06..393f05f8a 100644 --- a/src/voxels/Chunk.hpp +++ b/src/voxels/Chunk.hpp @@ -27,7 +27,7 @@ public: int x, z; int bottom, top; voxel voxels[CHUNK_VOL] {}; - Lightmap lightmap; + std::shared_ptr lightmap; struct { bool modified : 1; bool ready : 1; @@ -45,14 +45,11 @@ public: /// @brief Blocks metadata heap BlocksMetadata blocksMetadata; - Chunk(int x, int z); + Chunk(int x, int z, std::shared_ptr lightmap=nullptr); /// @brief Refresh `bottom` and `top` values void updateHeights(); - // unused - std::unique_ptr clone() const; - /// @brief Creates new block inventory given size /// @return inventory id or 0 if block does not exists void addBlockInventory( diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index dcdee71e9..eded34e89 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -115,9 +115,10 @@ ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel) const { if (chunk == nullptr) { return 0; } + assert(chunk->lightmap != nullptr); int lx = x - cx * CHUNK_W; int lz = z - cz * CHUNK_D; - return chunk->lightmap.get(lx, y, lz, channel); + return chunk->lightmap->get(lx, y, lz, channel); } light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) const { @@ -135,9 +136,10 @@ light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) const { if (chunk == nullptr) { return 0; } + assert(chunk->lightmap != nullptr); int lx = x - cx * CHUNK_W; int lz = z - cz * CHUNK_D; - return chunk->lightmap.get(lx, y, lz); + return chunk->lightmap->get(lx, y, lz); } Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) const { @@ -373,7 +375,8 @@ void Chunks::getVoxels(VoxelsVolume& volume, bool backlight) const { } } else { const voxel* cvoxels = chunk->voxels; - const light_t* clights = chunk->lightmap.getLights(); + const light_t* clights = + chunk->lightmap ? chunk->lightmap->getLights() : nullptr; for (int ly = y; ly < y + h; ly++) { for (int lz = std::max(z, cz * CHUNK_D); lz < std::min(z + d, (cz + 1) * CHUNK_D); @@ -390,7 +393,8 @@ void Chunks::getVoxels(VoxelsVolume& volume, bool backlight) const { CHUNK_D ); voxels[vidx] = cvoxels[cidx]; - light_t light = clights[cidx]; + light_t light = clights ? clights[cidx] + : Lightmap::SUN_LIGHT_ONLY; if (backlight) { const auto block = indices.blocks.get(voxels[vidx].id); diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 438ae6d83..e284fa07c 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -91,14 +91,16 @@ static inline auto load_inventories( } static util::ObjectsPool chunks_pool(1'024); +static util::ObjectsPool lightmaps_pool; -std::shared_ptr GlobalChunks::create(int x, int z) { +std::shared_ptr GlobalChunks::create(int x, int z, bool lighting) { const auto& found = chunksMap.find(keyfrom(x, z)); if (found != chunksMap.end()) { return found->second; } - auto chunk = chunks_pool.create(x, z); + auto chunk = + chunks_pool.create(x, z, lighting ? lightmaps_pool.create() : nullptr); chunksMap[keyfrom(x, z)] = chunk; World& world = *level.getWorld(); @@ -125,9 +127,11 @@ std::shared_ptr GlobalChunks::create(int x, int z) { level.inventories->store(entry.second); } } - if (auto lights = regions.getLights(chunk->x, chunk->z)) { - chunk->lightmap.set(lights.get()); - chunk->flags.loadedLights = true; + if (chunk->lightmap) { + if (auto lights = regions.getLights(chunk->x, chunk->z)) { + chunk->lightmap->set(lights.get()); + chunk->flags.loadedLights = true; + } } chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z); return chunk; diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index 1a554dfd5..7120658c2 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -40,7 +40,7 @@ public: void setOnUnload(consumer onUnload); std::shared_ptr fetch(int x, int z); - std::shared_ptr create(int x, int z); + std::shared_ptr create(int x, int z, bool lighting); void pinChunk(std::shared_ptr chunk); void unpinChunk(int x, int z); diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index 990ff3909..ea668f8e6 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -434,7 +434,8 @@ inline void get_voxels_impl( } } else { const voxel* cvoxels = chunk->voxels; - const light_t* clights = chunk->lightmap.getLights(); + const light_t* clights = + chunk->lightmap ? chunk->lightmap->getLights() : nullptr; for (int ly = y; ly < y + h; ly++) { for (int lz = std::max(z, cz * CHUNK_D); lz < std::min(z + d, (cz + 1) * CHUNK_D); @@ -451,7 +452,8 @@ inline void get_voxels_impl( CHUNK_D ); voxels[vidx] = cvoxels[cidx]; - light_t light = clights[cidx]; + light_t light = clights ? clights[cidx] + : Lightmap::SUN_LIGHT_ONLY; if (backlight) { const auto block = blocks.get(voxels[vidx].id); if (block && block->lightPassing) { diff --git a/src/world/files/WorldRegions.cpp b/src/world/files/WorldRegions.cpp index 62817ee63..6cfe55d68 100644 --- a/src/world/files/WorldRegions.cpp +++ b/src/world/files/WorldRegions.cpp @@ -175,11 +175,11 @@ void WorldRegions::put(Chunk* chunk, std::vector entitiesData) { CHUNK_DATA_LEN); // Writing lights cache - if (doWriteLights && chunk->flags.lighted) { + if (doWriteLights && chunk->flags.lighted && chunk->lightmap) { put(chunk->x, chunk->z, REGION_LAYER_LIGHTS, - chunk->lightmap.encode(), + chunk->lightmap->encode(), LIGHTMAP_DATA_LEN); } // Writing block inventories