From e7be135b78444d3241d3bc7938d1faceb6084319 Mon Sep 17 00:00:00 2001 From: mazes-80 <1608580+mazes-80@users.noreply.github.com> Date: Fri, 15 Dec 2023 10:22:58 +0100 Subject: [PATCH 01/85] Warning: inform about entity name when bug detected about attachement (#13354) --- src/server/luaentity_sao.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index df5282937..26ae43588 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -145,7 +145,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) // If attached, check that our parent is still there. If it isn't, detach. if (m_attachment_parent_id && !isAttached()) { // This is handled when objects are removed from the map - warningstream << "LuaEntitySAO::step() id=" << m_id << + warningstream << "LuaEntitySAO::step() " << m_init_name << " at: " << PP(m_last_sent_position) << ", id=" << m_id << " is attached to nonexistent parent. This is a bug." << std::endl; clearParentAttachment(); sendPosition(false, true); -- 2.46.0 From d4123a387c91b8659cbe41ce0c64d734cac74095 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 13:20:59 +0100 Subject: [PATCH 02/85] Clean up porting.h a bit --- src/porting.cpp | 2 ++ src/porting.h | 30 ++++++------------------------ 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/porting.cpp b/src/porting.cpp index 16e14631d..54c9ee2c9 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -52,6 +52,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting_android.h" #endif #if defined(__APPLE__) + #include + #include // For _NSGetEnviron() // Related: https://gitlab.haskell.org/ghc/ghc/issues/2458 #include diff --git a/src/porting.h b/src/porting.h index da9f83eab..6657d7bba 100644 --- a/src/porting.h +++ b/src/porting.h @@ -38,10 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define SWPRINTF_CHARSTRING L"%s" #endif -//currently not needed -//template struct alignment_trick { char c; T member; }; -//#define ALIGNOF(type) offsetof (alignment_trick, member) - #ifdef _WIN32 #include @@ -49,12 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define sleep_us(x) Sleep((x)/1000) #else #include - #include //for uintptr_t - // Use standard Posix macro for Linux - #if (defined(linux) || defined(__linux)) && !defined(__linux__) - #define __linux__ - #endif #if (defined(__linux__) || defined(__GNU__)) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -64,18 +55,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif #ifdef _MSC_VER - #define ALIGNOF(x) __alignof(x) #define strtok_r(x, y, z) strtok_s(x, y, z) #define strtof(x, y) (float)strtod(x, y) #define strtoll(x, y, z) _strtoi64(x, y, z) #define strtoull(x, y, z) _strtoui64(x, y, z) #define strcasecmp(x, y) stricmp(x, y) #define strncasecmp(x, y, n) strnicmp(x, y, n) -#else - #define ALIGNOF(x) __alignof__(x) #endif #ifdef __MINGW32__ + // was broken in 2013, unclear if still needed #define strtok_r(x, y, z) mystrtok_r(x, y, z) #endif @@ -97,18 +86,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #define strlcpy(d, s, n) mystrlcpy(d, s, n) #endif -#define PADDING(x, y) ((ALIGNOF(y) - ((uintptr_t)(x) & (ALIGNOF(y) - 1))) & (ALIGNOF(y) - 1)) - -#if defined(__APPLE__) - #include - #include -#endif - -#ifndef _WIN32 // Posix +#ifndef _WIN32 // POSIX #include #include -#if defined(__MACH__) && defined(__APPLE__) + #if defined(__MACH__) && defined(__APPLE__) #include #include #endif @@ -178,7 +160,7 @@ void initializePaths(); std::string get_sysinfo(); -// Monotonic counter getters. +// Monotonic timer #ifdef _WIN32 // Windows @@ -197,7 +179,7 @@ inline u64 getTimeMs() { return os_get_time(1000); } inline u64 getTimeUs() { return os_get_time(1000*1000); } inline u64 getTimeNs() { return os_get_time(1000*1000*1000); } -#else // Posix +#else // POSIX inline void os_get_clock(struct timespec *ts) { @@ -314,7 +296,7 @@ inline const char *getPlatformName() "Cygwin" #elif defined(__unix__) || defined(__unix) #if defined(_POSIX_VERSION) - "Posix" + "POSIX" #else "Unix" #endif -- 2.46.0 From bd06466d3af4f6a645ff264720eda5137090efbe Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 13:28:07 +0100 Subject: [PATCH 03/85] Improve clock_gettime usage - correctly use value of _POSIX_MONOTONIC_CLOCK - drop special path for macOS: it supports clock_gettime since macOS 10.12 --- src/porting.h | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/porting.h b/src/porting.h index 6657d7bba..bf01c3e4d 100644 --- a/src/porting.h +++ b/src/porting.h @@ -89,11 +89,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef _WIN32 // POSIX #include #include - - #if defined(__MACH__) && defined(__APPLE__) - #include - #include - #endif #endif namespace porting @@ -183,21 +178,16 @@ inline u64 getTimeNs() { return os_get_time(1000*1000*1000); } inline void os_get_clock(struct timespec *ts) { -#if defined(__MACH__) && defined(__APPLE__) -// From http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x -// OS X does not have clock_gettime, use clock_get_time - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - ts->tv_sec = mts.tv_sec; - ts->tv_nsec = mts.tv_nsec; -#elif defined(CLOCK_MONOTONIC_RAW) +#if defined(CLOCK_MONOTONIC_RAW) clock_gettime(CLOCK_MONOTONIC_RAW, ts); -#elif defined(_POSIX_MONOTONIC_CLOCK) +#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0 clock_gettime(CLOCK_MONOTONIC, ts); #else +# if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK == 0 + // zero means it might be supported at runtime + if (clock_gettime(CLOCK_MONOTONIC, ts) == 0) + return; +# endif struct timeval tv; gettimeofday(&tv, NULL); TIMEVAL_TO_TIMESPEC(&tv, ts); -- 2.46.0 From 64b59184d1d620040844df73f350abdddc5873ec Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Fri, 15 Dec 2023 12:23:32 +0300 Subject: [PATCH 04/85] Reduce test framework macrosity --- src/unittest/test.cpp | 22 +++++++++++- src/unittest/test.h | 79 +++++++++++++++++++------------------------ 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index b17aca0bf..761c90813 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -330,7 +330,7 @@ std::string TestBase::getTestTempDirectory() m_test_dir = fs::TempPath() + DIR_DELIM "mttest_" + buf; if (!fs::CreateDir(m_test_dir)) - throw TestFailedException(); + UASSERT(false); return m_test_dir; } @@ -343,6 +343,26 @@ std::string TestBase::getTestTempFile() return getTestTempDirectory() + DIR_DELIM + buf + ".tmp"; } +void TestBase::runTest(const char *name, std::function &&test) +{ + u64 t1 = porting::getTimeMs(); + try { + test(); + rawstream << "[PASS] "; + } catch (TestFailedException &e) { + rawstream << "Test assertion failed: " << e.message << std::endl; + rawstream << " at " << e.file << ":" << e.line << std::endl; + rawstream << "[FAIL] "; + num_tests_failed++; + } catch (std::exception &e) { + rawstream << "Caught unhandled exception: " << e.what() << std::endl; + rawstream << "[FAIL] "; + num_tests_failed++; + } + num_tests_run++; + u64 tdiff = porting::getTimeMs() - t1; + rawstream << name << " - " << tdiff << "ms" << std::endl; +} /* NOTE: These tests became non-working then NodeContainer was removed. diff --git a/src/unittest/test.h b/src/unittest/test.h index 8782bc92b..881189adf 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -19,7 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include #include +#include #include #include "irrlichttypes_extrabloated.h" @@ -27,63 +29,48 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "mapnode.h" -class TestFailedException : public std::exception { +class TestFailedException { // don’t derive from std::exception to avoid accidental catch +public: + TestFailedException(std::string in_message, const char *in_file, int in_line) + : message(std::move(in_message)) + , file(fs::GetFilenameFromPath(in_file)) + , line(in_line) + {} + + const std::string message; + const std::string file; + const int line; }; // Runs a unit test and reports results -#define TEST(fxn, ...) { \ - u64 t1 = porting::getTimeMs(); \ - try { \ - fxn(__VA_ARGS__); \ - rawstream << "[PASS] "; \ - } catch (TestFailedException &e) { \ - rawstream << "[FAIL] "; \ - num_tests_failed++; \ - } catch (std::exception &e) { \ - rawstream << "Caught unhandled exception: " << e.what() << std::endl; \ - rawstream << "[FAIL] "; \ - num_tests_failed++; \ - } \ - num_tests_run++; \ - u64 tdiff = porting::getTimeMs() - t1; \ - rawstream << #fxn << " - " << tdiff << "ms" << std::endl; \ -} +#define TEST(fxn, ...) runTest(#fxn, [&] () { fxn(__VA_ARGS__); }); // Asserts the specified condition is true, or fails the current unit test -#define UASSERT(x) \ - if (!(x)) { \ - rawstream << "Test assertion failed: " #x << std::endl \ - << " at " << fs::GetFilenameFromPath(__FILE__) \ - << ":" << __LINE__ << std::endl; \ - throw TestFailedException(); \ +#define UASSERT(x) \ + if (!(x)) { \ + throw TestFailedException(#x, __FILE__, __LINE__); \ } // Asserts the specified condition is true, or fails the current unit test // and prints the format specifier fmt -#define UTEST(x, fmt, ...) \ - if (!(x)) { \ - char utest_buf[1024]; \ - snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \ - rawstream << "Test assertion failed: " << utest_buf << std::endl \ - << " at " << fs::GetFilenameFromPath(__FILE__) \ - << ":" << __LINE__ << std::endl; \ - throw TestFailedException(); \ +#define UTEST(x, fmt, ...) \ + if (!(x)) { \ + char utest_buf[1024]; \ + snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \ + throw TestFailedException(utest_buf, __FILE__, __LINE__); \ } // Asserts the comparison specified by CMP is true, or fails the current unit test -#define UASSERTCMP(T, CMP, actual, expected) { \ - T a = (actual); \ - T e = (expected); \ - if (!(a CMP e)) { \ - rawstream \ - << "Test assertion failed: " << #actual << " " << #CMP << " " \ - << #expected << std::endl \ - << " at " << fs::GetFilenameFromPath(__FILE__) << ":" \ - << __LINE__ << std::endl \ - << " actual : " << a << std::endl << " expected: " \ - << e << std::endl; \ - throw TestFailedException(); \ - } \ +#define UASSERTCMP(T, CMP, actual, expected) { \ + T a = (actual); \ + T e = (expected); \ + if (!(a CMP e)) { \ + std::ostringstream message; \ + message << #actual " " #CMP " " #expected; \ + message << std::endl << " actual : " << a; \ + message << std::endl << " expected: " << e; \ + throw TestFailedException(message.str(), __FILE__, __LINE__); \ + } \ } #define UASSERTEQ(T, actual, expected) UASSERTCMP(T, ==, actual, expected) @@ -113,6 +100,8 @@ public: u32 num_tests_failed; u32 num_tests_run; + void runTest(const char *name, std::function &&test); + private: std::string m_test_dir; }; -- 2.46.0 From da832a295e0cf5620ad89415a8ed3ddd91be1547 Mon Sep 17 00:00:00 2001 From: Gary Miguel Date: Fri, 15 Dec 2023 01:23:44 -0800 Subject: [PATCH 05/85] Delete clang-format files and comments (#14079) --- .clang-format | 33 -- .github/CONTRIBUTING.md | 14 - .github/workflows/cpp_lint.yml | 17 - src/client/activeobjectmgr.cpp | 2 - src/client/meshgen/collector.h | 4 - src/content/mods.cpp | 4 - src/gui/guiEditBox.cpp | 4 - src/gui/guiPasswordChange.cpp | 2 - src/gui/guiScrollBar.cpp | 2 - src/gui/modalMenu.cpp | 5 - src/irr_ptr.h | 5 - src/script/lua_api/l_camera.cpp | 2 - src/script/lua_api/l_modchannels.cpp | 2 - src/script/lua_api/l_playermeta.cpp | 2 - src/server/activeobjectmgr.cpp | 2 - src/server/mods.cpp | 2 - src/server/unit_sao.cpp | 2 - src/texture_override.h | 2 - src/util/ieee_float.cpp | 4 - src/util/srp.cpp | 16 - src/util/srp.h | 7 - util/ci/clang-format-whitelist.txt | 500 --------------------------- util/ci/clang-format.sh | 64 ---- util/fix_format.sh | 5 - 24 files changed, 702 deletions(-) delete mode 100644 .clang-format delete mode 100644 util/ci/clang-format-whitelist.txt delete mode 100755 util/ci/clang-format.sh delete mode 100755 util/fix_format.sh diff --git a/.clang-format b/.clang-format deleted file mode 100644 index fcfff2c4d..000000000 --- a/.clang-format +++ /dev/null @@ -1,33 +0,0 @@ -BasedOnStyle: LLVM -IndentWidth: 4 -UseTab: Always -TabWidth: 4 -BreakBeforeBraces: Custom -Standard: c++17 -BraceWrapping: - AfterClass: true - AfterControlStatement: Never - AfterEnum: true - AfterFunction: true - AfterNamespace: true - AfterStruct: true - AfterUnion: true - BeforeCatch: false - BeforeElse: false -FixNamespaceComments: false -AllowShortIfStatementsOnASingleLine: false -IndentCaseLabels: false -AccessModifierOffset: -4 -ColumnLimit: 90 -AllowShortFunctionsOnASingleLine: InlineOnly -SortIncludes: Never -IncludeCategories: - - Regex: '^".*' - Priority: 2 - - Regex: '^<.*' - Priority: 1 -AlignAfterOpenBracket: DontAlign -ContinuationIndentWidth: 8 -ConstructorInitializerIndentWidth: 8 -BreakConstructorInitializers: AfterColon -AlwaysBreakTemplateDeclarations: Yes diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e7db7e8a8..6b7b95369 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -67,20 +67,6 @@ Contributions are welcome! Here's how you can help: might need more work in the future. 5. It uses protocols and formats which include the required compatibility. -### Important note about automated GitHub checks - -When you submit a pull request, GitHub automatically runs checks on the Minetest -Engine combined with your changes. One of these checks is called 'cpp lint / -clang format', which checks code formatting. Because formatting for readability -requires human judgement this check often fails and often makes unsuitable -formatting requests which make code readability worse. - -If this check fails, look at the details to check for any clear mistakes and -correct those. However, you should not apply everything ClangFormat requests. -Ignore requests that make code readability worse and any other clearly -unsuitable requests. Discuss in the pull request with a core developer about how -to progress. - ## Issues If you experience an issue, we would like to know the details - especially when diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index 581ee06d6..a5e49277b 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -24,23 +24,6 @@ on: - '.github/workflows/**.yml' jobs: - -# clang_format: -# runs-on: ubuntu-20.04 -# steps: -# - uses: actions/checkout@v3 -# - name: Install clang-format -# run: | -# sudo apt-get update -# sudo apt-get install -y clang-format-9 -# -# - name: Run clang-format -# run: | -# source ./util/ci/clang-format.sh -# check_format -# env: -# CLANG_FORMAT: clang-format-9 - clang_tidy: runs-on: ubuntu-20.04 steps: diff --git a/src/client/activeobjectmgr.cpp b/src/client/activeobjectmgr.cpp index ab7b06f7d..cafd22f20 100644 --- a/src/client/activeobjectmgr.cpp +++ b/src/client/activeobjectmgr.cpp @@ -50,7 +50,6 @@ void ActiveObjectMgr::step( } } -// clang-format off bool ActiveObjectMgr::registerObject(std::unique_ptr obj) { assert(obj); // Pre-condition @@ -93,7 +92,6 @@ void ActiveObjectMgr::removeObject(u16 id) obj->removeFromScene(true); } -// clang-format on void ActiveObjectMgr::getActiveObjects(const v3f &origin, f32 max_d, std::vector &dest) { diff --git a/src/client/meshgen/collector.h b/src/client/meshgen/collector.h index 9b49ce72e..093c20eb8 100644 --- a/src/client/meshgen/collector.h +++ b/src/client/meshgen/collector.h @@ -47,7 +47,6 @@ struct MeshCollector // offset: offset added to vertices MeshCollector(const v3f center_pos, v3f offset = v3f()) : m_center_pos(center_pos), offset(offset) {} - // clang-format off void append(const TileSpec &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices); @@ -55,10 +54,8 @@ struct MeshCollector const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, v3f pos, video::SColor c, u8 light_source); - // clang-format on private: - // clang-format off void append(const TileLayer &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, @@ -68,7 +65,6 @@ private: const u16 *indices, u32 numIndices, v3f pos, video::SColor c, u8 light_source, u8 layernum, bool use_scale = false); - // clang-format on PreMeshBuffer &findBuffer(const TileLayer &layer, u8 layernum, u32 numVertices); }; diff --git a/src/content/mods.cpp b/src/content/mods.cpp index 21c7bcfe2..13d104320 100644 --- a/src/content/mods.cpp +++ b/src/content/mods.cpp @@ -108,10 +108,8 @@ bool parseModContents(ModSpec &spec) if (info.exists("depends")) { mod_conf_has_depends = true; std::string dep = info.get("depends"); - // clang-format off dep.erase(std::remove_if(dep.begin(), dep.end(), static_cast(&std::isspace)), dep.end()); - // clang-format on for (const auto &dependency : str_split(dep, ',')) { spec.depends.insert(dependency); } @@ -120,10 +118,8 @@ bool parseModContents(ModSpec &spec) if (info.exists("optional_depends")) { mod_conf_has_depends = true; std::string dep = info.get("optional_depends"); - // clang-format off dep.erase(std::remove_if(dep.begin(), dep.end(), static_cast(&std::isspace)), dep.end()); - // clang-format on for (const auto &dependency : str_split(dep, ',')) { spec.optdepends.insert(dependency); } diff --git a/src/gui/guiEditBox.cpp b/src/gui/guiEditBox.cpp index be6731653..65bf3bffd 100644 --- a/src/gui/guiEditBox.cpp +++ b/src/gui/guiEditBox.cpp @@ -455,7 +455,6 @@ bool GUIEditBox::processKey(const SEvent &event) bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end) { - // clang-format off if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) { s32 lineNo = getLineFromPos(m_cursor_pos); s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : @@ -481,13 +480,11 @@ bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end) return true; } - // clang-format on return false; } bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end) { - // clang-format off if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) { s32 lineNo = getLineFromPos(m_cursor_pos); s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : @@ -513,7 +510,6 @@ bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end) return true; } - // clang-format on return false; } diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp index 8edb377de..e100643a4 100644 --- a/src/gui/guiPasswordChange.cpp +++ b/src/gui/guiPasswordChange.cpp @@ -199,14 +199,12 @@ bool GUIPasswordChange::processInput() bool GUIPasswordChange::OnEvent(const SEvent &event) { if (event.EventType == EET_KEY_INPUT_EVENT) { - // clang-format off if ((event.KeyInput.Key == KEY_ESCAPE || event.KeyInput.Key == KEY_CANCEL) && event.KeyInput.PressedDown) { quitMenu(); return true; } - // clang-format on if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) { acceptInput(); if (processInput()) diff --git a/src/gui/guiScrollBar.cpp b/src/gui/guiScrollBar.cpp index 634f60f2d..60e9b05f0 100644 --- a/src/gui/guiScrollBar.cpp +++ b/src/gui/guiScrollBar.cpp @@ -155,7 +155,6 @@ bool GUIScrollBar::OnEvent(const SEvent &event) if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) is_dragging = false; - // clang-format off if (!dragged_by_slider) { if (is_inside) { dragged_by_slider = slider_rect.isPointInside(p); @@ -167,7 +166,6 @@ bool GUIScrollBar::OnEvent(const SEvent &event) return is_inside; } } - // clang-format on const s32 new_pos = getPosFromMousePos(p); const s32 old_pos = scroll_pos; diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 0f5ec787a..2ccdf43af 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -29,7 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "touchscreengui.h" #endif -// clang-format off GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, bool remap_dbl_click) : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, @@ -57,7 +56,6 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, m_doubleclickdetect[0].pos = v2s32(0, 0); m_doubleclickdetect[1].pos = v2s32(0, 0); } -// clang-format on GUIModalMenu::~GUIModalMenu() { @@ -110,7 +108,6 @@ void GUIModalMenu::quitMenu() #endif } -// clang-format off bool GUIModalMenu::DoubleClickDetection(const SEvent &event) { /* The following code is for capturing double-clicks of the mouse button @@ -160,7 +157,6 @@ bool GUIModalMenu::DoubleClickDetection(const SEvent &event) return false; } -// clang-format on static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) { @@ -236,7 +232,6 @@ void GUIModalMenu::leave() bool GUIModalMenu::preprocessEvent(const SEvent &event) { #ifdef __ANDROID__ - // clang-format off // display software keyboard when clicking edit boxes if (event.EventType == EET_MOUSE_INPUT_EVENT && event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { diff --git a/src/irr_ptr.h b/src/irr_ptr.h index f1caf9c7d..fc4a0f558 100644 --- a/src/irr_ptr.h +++ b/src/irr_ptr.h @@ -144,9 +144,6 @@ public: } }; -// clang-format off -// ^ dislikes long lines - /** Constructs a shared pointer as a *secondary* reference to an object * * This function is intended to make a temporary reference to an object which @@ -205,5 +202,3 @@ irr_ptr make_irr(Args&&... args) { return irr_ptr(new T(std::forward(args)...)); } - -// clang-format on diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp index aef90562b..a00a937d1 100644 --- a/src/script/lua_api/l_camera.cpp +++ b/src/script/lua_api/l_camera.cpp @@ -193,7 +193,6 @@ void LuaCamera::Register(lua_State *L) registerClass(L, className, methods, metamethods); } -// clang-format off const char LuaCamera::className[] = "Camera"; const luaL_Reg LuaCamera::methods[] = { luamethod(LuaCamera, set_camera_mode), @@ -208,4 +207,3 @@ const luaL_Reg LuaCamera::methods[] = { {0, 0} }; -// clang-format on diff --git a/src/script/lua_api/l_modchannels.cpp b/src/script/lua_api/l_modchannels.cpp index 019bb2238..911f05f69 100644 --- a/src/script/lua_api/l_modchannels.cpp +++ b/src/script/lua_api/l_modchannels.cpp @@ -115,7 +115,6 @@ ModChannel *ModChannelRef::getobject(lua_State *L, ModChannelRef *ref) return getGameDef(L)->getModChannel(ref->m_modchannel_name); } -// clang-format off const char ModChannelRef::className[] = "ModChannelRef"; const luaL_Reg ModChannelRef::methods[] = { luamethod(ModChannelRef, leave), @@ -123,4 +122,3 @@ const luaL_Reg ModChannelRef::methods[] = { luamethod(ModChannelRef, send_all), {0, 0}, }; -// clang-format on diff --git a/src/script/lua_api/l_playermeta.cpp b/src/script/lua_api/l_playermeta.cpp index def65a1cc..e2e6ed8da 100644 --- a/src/script/lua_api/l_playermeta.cpp +++ b/src/script/lua_api/l_playermeta.cpp @@ -59,7 +59,6 @@ void PlayerMetaRef::Register(lua_State *L) // lua_register(L, className, create_object); } -// clang-format off const char PlayerMetaRef::className[] = "PlayerMetaRef"; const luaL_Reg PlayerMetaRef::methods[] = { luamethod(MetaDataRef, contains), @@ -76,4 +75,3 @@ const luaL_Reg PlayerMetaRef::methods[] = { luamethod(MetaDataRef, equals), {0,0} }; -// clang-format on diff --git a/src/server/activeobjectmgr.cpp b/src/server/activeobjectmgr.cpp index 543003e42..1b3376ae6 100644 --- a/src/server/activeobjectmgr.cpp +++ b/src/server/activeobjectmgr.cpp @@ -69,7 +69,6 @@ void ActiveObjectMgr::step( } } -// clang-format off bool ActiveObjectMgr::registerObject(std::unique_ptr obj) { assert(obj); // Pre-condition @@ -126,7 +125,6 @@ void ActiveObjectMgr::removeObject(u16 id) m_active_objects.erase(id); // `it` can be invalid now } -// clang-format on void ActiveObjectMgr::getObjectsInsideRadius(const v3f &pos, float radius, std::vector &result, std::function include_obj_cb) diff --git a/src/server/mods.cpp b/src/server/mods.cpp index f302d4240..98a4165de 100644 --- a/src/server/mods.cpp +++ b/src/server/mods.cpp @@ -49,7 +49,6 @@ ServerModManager::ServerModManager(const std::string &worldpath): configuration.checkConflictsAndDeps(); } -// clang-format off // This function cannot be currenctly easily tested but it should be ASAP void ServerModManager::loadMods(ServerScripting *script) { @@ -75,7 +74,6 @@ void ServerModManager::loadMods(ServerScripting *script) script->on_mods_loaded(); } -// clang-format on const ModSpec *ServerModManager::getModSpec(const std::string &modname) const { for (const auto &mod : configuration.getMods()) { diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index 54d4a029d..c928b830f 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -92,7 +92,6 @@ void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotat } } -// clang-format off void UnitSAO::sendOutdatedData() { if (!m_armor_groups_sent) { @@ -123,7 +122,6 @@ void UnitSAO::sendOutdatedData() m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand()); } } -// clang-format on void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation, bool force_visible) diff --git a/src/texture_override.h b/src/texture_override.h index 4c09fa540..237efcba1 100644 --- a/src/texture_override.h +++ b/src/texture_override.h @@ -44,13 +44,11 @@ enum class OverrideTarget : override_t SPECIAL_5 = 1 << 12, SPECIAL_6 = 1 << 13, - // clang-format off SIDES = LEFT | RIGHT | FRONT | BACK, ALL_FACES = TOP | BOTTOM | SIDES, ALL_SPECIAL = SPECIAL_1 | SPECIAL_2 | SPECIAL_3 | SPECIAL_4 | SPECIAL_5 | SPECIAL_6, NODE_TARGETS = ALL_FACES | ALL_SPECIAL, ITEM_TARGETS = INVENTORY | WIELD, - // clang-format on }; struct TextureOverride diff --git a/src/util/ieee_float.cpp b/src/util/ieee_float.cpp index b73763c55..8b13f043c 100644 --- a/src/util/ieee_float.cpp +++ b/src/util/ieee_float.cpp @@ -32,7 +32,6 @@ // float, return the float. f32 u32Tof32Slow(u32 i) { - // clang-format off int exp = (i >> 23) & 0xFF; u32 sign = i & 0x80000000UL; u32 imant = i & 0x7FFFFFUL; @@ -56,7 +55,6 @@ f32 u32Tof32Slow(u32 i) return sign ? -ldexpf((f32)(imant | 0x800000UL), exp - 150) : ldexpf((f32)(imant | 0x800000UL), exp - 150); - // clang-format on } // Given a float, return an unsigned 32-bit integer representing the f32 @@ -94,7 +92,6 @@ u32 f32Tou32Slow(f32 f) // - The endianness of f32s and integers must match. FloatType getFloatSerializationType() { - // clang-format off const f32 cf = -22220490.f; const u32 cu = 0xCBA98765UL; if (std::numeric_limits::is_iec559 && sizeof(cf) == 4 && @@ -132,5 +129,4 @@ FloatType getFloatSerializationType() } return FLOATTYPE_SLOW; - // clang-format on } diff --git a/src/util/srp.cpp b/src/util/srp.cpp index daa7f332b..ee26740c4 100644 --- a/src/util/srp.cpp +++ b/src/util/srp.cpp @@ -26,7 +26,6 @@ * */ -// clang-format off #include @@ -37,7 +36,6 @@ #include #endif -// clang-format on #include #include @@ -80,7 +78,6 @@ void *(*srp_alloc)(size_t) = &malloc; void *(*srp_realloc)(void *, size_t) = &realloc; void (*srp_free)(void *) = &free; -// clang-format off void srp_set_memory_functions( void *(*new_srp_alloc)(size_t), void *(*new_srp_realloc)(void *, size_t), @@ -90,7 +87,6 @@ void srp_set_memory_functions( srp_realloc = new_srp_realloc; srp_free = new_srp_free; } -// clang-format on typedef struct { mpz_t N; @@ -261,7 +257,6 @@ struct SRPUser { unsigned char session_key[SHA512_DIGEST_LENGTH]; }; -// clang-format off static int hash_init(SRP_HashAlgorithm alg, HashCTX *c) { switch (alg) { @@ -357,7 +352,6 @@ static size_t hash_length(SRP_HashAlgorithm alg) default: return 0; }; } -// clang-format on inline static int mpz_num_bytes(const mpz_t op) { @@ -588,7 +582,6 @@ static SRP_Result init_random() * ***********************************************************************************************************/ -// clang-format off SRP_Result srp_create_salted_verification_key( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char *username_for_verifier, const unsigned char *password, size_t len_password, @@ -600,7 +593,6 @@ SRP_Result srp_create_salted_verification_key( SRP_HashAlgorithm alg, mpz_t v; mpz_init(v); mpz_t x; mpz_init(x); - // clang-format on NGConstant *ng = new_ng(ng_type, n_hex, g_hex); @@ -646,7 +638,6 @@ error_and_exit: goto cleanup_and_exit; } -// clang-format off /* Out: bytes_B, len_B. * @@ -671,7 +662,6 @@ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg, mpz_t tmp1; mpz_init(tmp1); mpz_t tmp2; mpz_init(tmp2); mpz_t tmp3; mpz_init(tmp3); - // clang-format on size_t ulen = strlen(username) + 1; NGConstant *ng = new_ng(ng_type, n_hex, g_hex); struct SRPVerifier *ver = 0; @@ -922,13 +912,11 @@ size_t srp_user_get_session_key_length(struct SRPUser *usr) return hash_length(usr->hash_alg); } -// clang-format off /* Output: username, bytes_A, len_A */ SRP_Result srp_user_start_authentication(struct SRPUser *usr, char **username, const unsigned char *bytes_a, size_t len_a, unsigned char **bytes_A, size_t *len_A) { - // clang-format on if (bytes_a) { mpz_from_bin(bytes_a, len_a, usr->a); } else { @@ -956,7 +944,6 @@ error_and_exit: return SRP_ERR; } -// clang-format off /* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */ void srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, size_t len_s, @@ -972,7 +959,6 @@ void srp_user_process_challenge(struct SRPUser *usr, mpz_t tmp2; mpz_init(tmp2); mpz_t tmp3; mpz_init(tmp3); mpz_t tmp4; mpz_init(tmp4); - // clang-format on *len_M = 0; *bytes_M = 0; @@ -996,7 +982,6 @@ void srp_user_process_challenge(struct SRPUser *usr, srp_dbg_num(v, "Client calculated v: "); - // clang-format off /* S = (B - k*(g^x)) ^ (a + ux) */ mpz_mul(tmp1, u, x); mpz_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */ @@ -1004,7 +989,6 @@ void srp_user_process_challenge(struct SRPUser *usr, mpz_mulm(tmp3, k, tmp1, usr->ng->N, tmp4); /* tmp3 = k*(g^x) */ mpz_subm(tmp1, B, tmp3, usr->ng->N, tmp4); /* tmp1 = (B - K*(g^x)) */ mpz_powm(usr->S, tmp1, tmp2, usr->ng->N); - // clang-format on if (!hash_num(usr->hash_alg, usr->S, usr->session_key)) goto cleanup_and_exit; diff --git a/src/util/srp.h b/src/util/srp.h index cf2bdec50..ac66dc936 100644 --- a/src/util/srp.h +++ b/src/util/srp.h @@ -79,8 +79,6 @@ typedef enum { SRP_OK, } SRP_Result; -// clang-format off - /* Sets the memory functions used by srp. * Note: this doesn't set the memory functions used by gmp, * but it is supported to have different functions for srp and gmp. @@ -130,8 +128,6 @@ struct SRPVerifier* srp_verifier_new(SRP_HashAlgorithm alg, SRP_NGType ng_type, unsigned char** bytes_B, size_t *len_B, const char* n_hex, const char* g_hex); -// clang-format on - void srp_verifier_delete(struct SRPVerifier *ver); // srp_verifier_verify_session must have been called before @@ -170,8 +166,6 @@ const unsigned char *srp_user_get_session_key(struct SRPUser *usr, size_t *key_l size_t srp_user_get_session_key_length(struct SRPUser *usr); -// clang-format off - /* Output: username, bytes_A, len_A. * If you don't want it get written, set username to NULL. * If bytes_a == NULL, random data is used for a. */ @@ -185,7 +179,6 @@ void srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, size_t len_s, const unsigned char *bytes_B, size_t len_B, unsigned char **bytes_M, size_t *len_M); -// clang-format on /* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */ void srp_user_verify_session(struct SRPUser *usr, const unsigned char *bytes_HAMK); diff --git a/util/ci/clang-format-whitelist.txt b/util/ci/clang-format-whitelist.txt deleted file mode 100644 index 5cbc262ef..000000000 --- a/util/ci/clang-format-whitelist.txt +++ /dev/null @@ -1,500 +0,0 @@ -src/activeobject.h -src/ban.cpp -src/camera.cpp -src/camera.h -src/chat.cpp -src/chat.h -src/chat_interface.h -src/client/clientlauncher.cpp -src/client/clientlauncher.h -src/client/sound_openal.cpp -src/client.cpp -src/clientenvironment.cpp -src/clientenvironment.h -src/client/gameui.cpp -src/client.h -src/client/hud.cpp -src/client/hud.h -src/clientiface.cpp -src/clientiface.h -src/client/joystick_controller.cpp -src/client/joystick_controller.h -src/clientmap.cpp -src/clientmap.h -src/clientmedia.cpp -src/clientmedia.h -src/clientobject.cpp -src/clientobject.h -src/client/render/core.cpp -src/client/renderingengine.cpp -src/client/render/interlaced.cpp -src/client/render/plain.cpp -src/client/render/sidebyside.cpp -src/client/render/stereo.cpp -src/client/tile.cpp -src/client/tile.h -src/client/fontengine.h -src/client/clientenvironment.cpp -src/client/mapblock_mesh.cpp -src/client/sound_openal.h -src/client/clouds.cpp -src/client/fontengine.cpp -src/client/camera.h -src/client/hud.cpp -src/client/clientmap.cpp -src/client/sound_openal.cpp -src/client/minimap.h -src/client/content_cao.cpp -src/client/localplayer.h -src/client/mapblock_mesh.h -src/client/mesh.cpp -src/client/sound.cpp -src/client/guiscalingfilter.cpp -src/client/content_cso.cpp -src/client/gameui.cpp -src/client/wieldmesh.cpp -src/client/clientmedia.h -src/client/game.cpp -src/client/keys.h -src/client/client.h -src/client/shader.cpp -src/client/clientmap.h -src/client/inputhandler.h -src/client/content_mapblock.h -src/client/game.h -src/client/mesh.h -src/client/camera.cpp -src/client/sky.h -src/client/mesh_generator_thread.cpp -src/client/guiscalingfilter.h -src/client/clientobject.cpp -src/client/tile.cpp -src/client/hud.h -src/client/inputhandler.cpp -src/client/clientevent.h -src/client/gameui.h -src/client/content_cso.h -src/client/sky.cpp -src/client/localplayer.cpp -src/client/content_mapblock.cpp -src/client/clientobject.h -src/client/filecache.cpp -src/client/particles.h -src/client/clientenvironment.h -src/client/imagefilters.h -src/client/renderingengine.cpp -src/client/tile.h -src/client/clientmedia.cpp -src/client/event_manager.h -src/client/joystick_controller.h -src/client/clouds.h -src/client/clientlauncher.h -src/client/content_cao.h -src/client/minimap.cpp -src/client/sound.h -src/client/keycode.cpp -src/client/particles.cpp -src/client/joystick_controller.cpp -src/client/keycode.h -src/client/wieldmesh.h -src/client/filecache.h -src/client/shader.h -src/client/mesh_generator_thread.h -src/client/renderingengine.h -src/client/client.cpp -src/client/imagefilters.cpp -src/client/clientlauncher.cpp -src/clouds.cpp -src/clouds.h -src/collision.cpp -src/collision.h -src/config.h -src/content_cao.cpp -src/content_cao.h -src/content_cso.cpp -src/content_cso.h -src/content_mapblock.cpp -src/content_mapblock.h -src/content_mapnode.cpp -src/content_nodemeta.cpp -src/content_nodemeta.h -src/convert_json.cpp -src/convert_json.h -src/craftdef.cpp -src/craftdef.h -src/database/database.cpp -src/database/database-dummy.cpp -src/database/database-files.cpp -src/database/database-leveldb.cpp -src/database/database-postgresql.cpp -src/database/database-postgresql.h -src/database/database-redis.cpp -src/database/database-sqlite3.cpp -src/database/database-sqlite3.h -src/daynightratio.h -src/debug.cpp -src/debug.h -src/defaultsettings.cpp -src/emerge.cpp -src/emerge.h -src/environment.cpp -src/exceptions.h -src/face_position_cache.cpp -src/face_position_cache.h -src/filecache.cpp -src/filesys.cpp -src/filesys.h -src/fontengine.cpp -src/fontengine.h -src/game.cpp -src/gamedef.h -src/game.h -src/gettext.cpp -src/gettext.h -src/gui/guiAnimatedImage.cpp -src/gui/guiAnimatedImage.h -src/gui/guiBackgroundImage.cpp -src/gui/guiBackgroundImage.h -src/gui/guiBox.cpp -src/gui/guiBox.h -src/gui/guiButton.cpp -src/gui/guiButton.h -src/gui/guiButtonImage.cpp -src/gui/guiButtonImage.h -src/gui/guiButtonItemImage.cpp -src/gui/guiButtonItemImage.h -src/gui/guiChatConsole.cpp -src/gui/guiChatConsole.h -src/gui/guiConfirmRegistration.cpp -src/gui/guiEditBoxWithScrollbar.cpp -src/gui/guiEditBoxWithScrollbar.h -src/gui/guiEngine.cpp -src/gui/guiEngine.h -src/gui/guiFormSpecMenu.cpp -src/gui/guiFormSpecMenu.h -src/gui/guiKeyChangeMenu.cpp -src/gui/guiHyperText.cpp -src/gui/guiHyperText.h -src/gui/guiInventoryList.cpp -src/gui/guiInventoryList.h -src/gui/guiItemImage.cpp -src/gui/guiItemImage.h -src/gui/guiMainMenu.h -src/gui/guiPasswordChange.cpp -src/gui/guiPathSelectMenu.cpp -src/gui/guiPathSelectMenu.h -src/gui/guiScene.cpp -src/gui/guiScene.h -src/gui/guiScrollBar.cpp -src/gui/guiSkin.cpp -src/gui/guiSkin.h -src/gui/guiTable.cpp -src/gui/guiTable.h -src/gui/guiVolumeChange.cpp -src/gui/guiVolumeChange.h -src/gui/mainmenumanager.h -src/gui/modalMenu.h -src/guiscalingfilter.cpp -src/guiscalingfilter.h -src/gui/StyleSpec.h -src/gui/touchscreengui.cpp -src/httpfetch.cpp -src/hud.cpp -src/hud.h -src/imagefilters.cpp -src/imagefilters.h -src/inventory.cpp -src/inventory.h -src/inventorymanager.cpp -src/inventorymanager.h -src/irrlicht_changes/CGUITTFont.cpp -src/irrlicht_changes/CGUITTFont.h -src/irrlicht_changes/irrUString.h -src/irrlicht_changes/static_text.cpp -src/irrlicht_changes/static_text.h -src/irrlichttypes.h -src/itemdef.cpp -src/itemdef.h -src/itemstackmetadata.cpp -src/keycode.cpp -src/light.cpp -src/localplayer.cpp -src/log.cpp -src/log.h -src/main.cpp -src/mapblock.cpp -src/mapblock.h -src/mapblock_mesh.cpp -src/mapblock_mesh.h -src/map.cpp -src/mapgen/cavegen.cpp -src/mapgen/cavegen.h -src/mapgen/dungeongen.cpp -src/mapgen/dungeongen.h -src/mapgen/mapgen.cpp -src/mapgen/mapgen.h -src/mapgen/mapgen_carpathian.cpp -src/mapgen/mapgen_carpathian.h -src/mapgen/mapgen_flat.cpp -src/mapgen/mapgen_flat.h -src/mapgen/mapgen_fractal.cpp -src/mapgen/mapgen_fractal.h -src/mapgen/mapgen_singlenode.cpp -src/mapgen/mapgen_singlenode.h -src/mapgen/mapgen_v5.cpp -src/mapgen/mapgen_v5.h -src/mapgen/mapgen_v6.cpp -src/mapgen/mapgen_v6.h -src/mapgen/mapgen_v7.cpp -src/mapgen/mapgen_v7.h -src/mapgen/mapgen_valleys.cpp -src/mapgen/mapgen_valleys.h -src/mapgen/mg_biome.cpp -src/mapgen/mg_biome.h -src/mapgen/mg_decoration.cpp -src/mapgen/mg_decoration.h -src/mapgen/mg_ore.cpp -src/mapgen/mg_ore.h -src/mapgen/mg_schematic.cpp -src/mapgen/mg_schematic.h -src/mapgen/treegen.cpp -src/mapgen/treegen.h -src/map.h -src/mapnode.cpp -src/mapnode.h -src/mapsector.cpp -src/mapsector.h -src/map_settings_manager.cpp -src/map_settings_manager.h -src/mesh.cpp -src/mesh_generator_thread.cpp -src/mesh.h -src/metadata.h -src/minimap.cpp -src/minimap.h -src/mods.cpp -src/mods.h -src/network/address.cpp -src/network/clientopcodes.cpp -src/network/clientopcodes.h -src/network/clientpackethandler.cpp -src/network/connection.cpp -src/network/connection.h -src/network/connectionthreads.cpp -src/network/networkpacket.cpp -src/network/networkprotocol.h -src/network/serveropcodes.cpp -src/network/serveropcodes.h -src/network/serverpackethandler.cpp -src/nodedef.cpp -src/nodedef.h -src/nodemetadata.cpp -src/nodemetadata.h -src/nodetimer.cpp -src/nodetimer.h -src/noise.cpp -src/noise.h -src/objdef.cpp -src/objdef.h -src/object_properties.cpp -src/object_properties.h -src/particles.cpp -src/particles.h -src/pathfinder.cpp -src/pathfinder.h -src/player.cpp -src/player.h -src/porting_android.cpp -src/porting_android.h -src/porting.cpp -src/porting.h -src/profiler.h -src/raycast.cpp -src/raycast.h -src/reflowscan.cpp -src/reflowscan.h -src/remoteplayer.cpp -src/rollback.cpp -src/rollback.h -src/rollback_interface.cpp -src/rollback_interface.h -src/script/common/c_content.cpp -src/script/common/c_content.h -src/script/common/c_converter.cpp -src/script/common/c_converter.h -src/script/common/c_internal.cpp -src/script/common/c_internal.h -src/script/common/c_types.cpp -src/script/common/c_types.h -src/script/cpp_api/s_async.cpp -src/script/cpp_api/s_async.h -src/script/cpp_api/s_base.cpp -src/script/cpp_api/s_base.h -src/script/cpp_api/s_client.cpp -src/script/cpp_api/s_entity.cpp -src/script/cpp_api/s_entity.h -src/script/cpp_api/s_env.cpp -src/script/cpp_api/s_env.h -src/script/cpp_api/s_internal.h -src/script/cpp_api/s_inventory.cpp -src/script/cpp_api/s_inventory.h -src/script/cpp_api/s_item.cpp -src/script/cpp_api/s_item.h -src/script/cpp_api/s_mainmenu.h -src/script/cpp_api/s_node.cpp -src/script/cpp_api/s_node.h -src/script/cpp_api/s_nodemeta.cpp -src/script/cpp_api/s_nodemeta.h -src/script/cpp_api/s_player.cpp -src/script/cpp_api/s_player.h -src/script/cpp_api/s_security.cpp -src/script/cpp_api/s_security.h -src/script/cpp_api/s_server.cpp -src/script/cpp_api/s_server.h -src/script/lua_api/l_areastore.cpp -src/script/lua_api/l_base.cpp -src/script/lua_api/l_base.h -src/script/lua_api/l_client.cpp -src/script/lua_api/l_craft.cpp -src/script/lua_api/l_craft.h -src/script/lua_api/l_env.cpp -src/script/lua_api/l_env.h -src/script/lua_api/l_http.cpp -src/script/lua_api/l_http.h -src/script/lua_api/l_internal.h -src/script/lua_api/l_inventory.cpp -src/script/lua_api/l_inventory.h -src/script/lua_api/l_item.cpp -src/script/lua_api/l_item.h -src/script/lua_api/l_itemstackmeta.cpp -src/script/lua_api/l_itemstackmeta.h -src/script/lua_api/l_localplayer.cpp -src/script/lua_api/l_mainmenu.cpp -src/script/lua_api/l_mainmenu.h -src/script/lua_api/l_mapgen.cpp -src/script/lua_api/l_mapgen.h -src/script/lua_api/l_metadata.cpp -src/script/lua_api/l_minimap.cpp -src/script/lua_api/l_nodemeta.cpp -src/script/lua_api/l_nodemeta.h -src/script/lua_api/l_nodetimer.cpp -src/script/lua_api/l_noise.cpp -src/script/lua_api/l_object.cpp -src/script/lua_api/l_object.h -src/script/lua_api/l_particles.cpp -src/script/lua_api/l_particles.h -src/script/lua_api/l_particles_local.cpp -src/script/lua_api/l_rollback.cpp -src/script/lua_api/l_rollback.h -src/script/lua_api/l_server.cpp -src/script/lua_api/l_settings.cpp -src/script/lua_api/l_sound.cpp -src/script/lua_api/l_storage.cpp -src/script/lua_api/l_util.cpp -src/script/lua_api/l_vmanip.cpp -src/script/scripting_client.cpp -src/script/scripting_client.h -src/script/scripting_mainmenu.cpp -src/script/scripting_mainmenu.h -src/script/scripting_server.cpp -src/script/scripting_server.h -src/serialization.cpp -src/serialization.h -src/server.cpp -src/serverenvironment.cpp -src/serverenvironment.h -src/server.h -src/serverlist.cpp -src/serverlist.h -src/server/luaentity_sao.cpp -src/server/player_sao.cpp -src/server/serveractiveobject.cpp -src/server/serveractiveobject.h -src/settings.cpp -src/settings.h -src/settings_translation_file.cpp -src/shader.cpp -src/shader.h -src/sky.cpp -src/sound.cpp -src/staticobject.cpp -src/staticobject.h -src/subgame.cpp -src/subgame.h -src/terminal_chat_console.cpp -src/terminal_chat_console.h -src/texture_override.cpp -src/threading/atomic.h -src/threading/event.cpp -src/threading/mutex_auto_lock.h -src/threading/mutex.cpp -src/threading/mutex.h -src/threading/semaphore.cpp -src/threading/thread.cpp -src/threading/thread.h -src/threads.h -src/tileanimation.cpp -src/tileanimation.h -src/tool.cpp -src/tool.h -src/translation.cpp -src/unittest/test_areastore.cpp -src/unittest/test_collision.cpp -src/unittest/test_compression.cpp -src/unittest/test_connection.cpp -src/unittest/test.cpp -src/unittest/test_filepath.cpp -src/unittest/test.h -src/unittest/test_inventory.cpp -src/unittest/test_keycode.cpp -src/unittest/test_map_settings_manager.cpp -src/unittest/test_noderesolver.cpp -src/unittest/test_noise.cpp -src/unittest/test_random.cpp -src/unittest/test_schematic.cpp -src/unittest/test_serialization.cpp -src/unittest/test_settings.cpp -src/unittest/test_socket.cpp -src/unittest/test_threading.cpp -src/unittest/test_utilities.cpp -src/unittest/test_voxelalgorithms.cpp -src/unittest/test_voxelmanipulator.cpp -src/util/areastore.cpp -src/util/areastore.h -src/util/auth.cpp -src/util/auth.h -src/util/base64.cpp -src/util/base64.h -src/util/basic_macros.h -src/util/container.h -src/util/directiontables.cpp -src/util/directiontables.h -src/util/enriched_string.cpp -src/util/enriched_string.h -src/util/md32_common.h -src/util/numeric.cpp -src/util/numeric.h -src/util/pointedthing.cpp -src/util/pointedthing.h -src/util/pointer.h -src/util/quicktune.h -src/util/quicktune_shortcutter.h -src/util/quicktune.cpp -src/util/serialize.cpp -src/util/serialize.h -src/util/sha1.cpp -src/util/srp.cpp -src/util/srp.h -src/util/strfnd.h -src/util/string.cpp -src/util/string.h -src/util/thread.h -src/util/timetaker.cpp -src/util/timetaker.h -src/version.cpp -src/version.h -src/voxelalgorithms.cpp -src/voxelalgorithms.h -src/voxel.cpp -src/voxel.h -src/wieldmesh.cpp diff --git a/util/ci/clang-format.sh b/util/ci/clang-format.sh deleted file mode 100755 index 89576c656..000000000 --- a/util/ci/clang-format.sh +++ /dev/null @@ -1,64 +0,0 @@ -#! /bin/bash - -function setup_for_format() { - if [ -z "${CLANG_FORMAT}" ]; then - CLANG_FORMAT=clang-format - fi - echo "LINT: Using binary $CLANG_FORMAT" - CLANG_FORMAT_WHITELIST="util/ci/clang-format-whitelist.txt" - - files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')" -} - -function check_format() { - echo "Checking format..." - - setup_for_format - - local errorcount=0 - local fail=0 - for f in ${files_to_lint}; do - d=$(diff -u "$f" <(${CLANG_FORMAT} "$f") || true) - - if ! [ -z "$d" ]; then - whitelisted=$(awk '$1 == "'$f'" { print 1 }' "$CLANG_FORMAT_WHITELIST") - - # If file is not whitelisted, mark a failure - if [ -z "${whitelisted}" ]; then - errorcount=$((errorcount+1)) - - printf "The file %s is not compliant with the coding style" "$f" - if [ ${errorcount} -gt 50 ]; then - printf "\nToo many errors encountered previously, this diff is hidden.\n" - else - printf ":\n%s\n" "$d" - fi - - fail=1 - fi - fi - done - - if [ "$fail" = 1 ]; then - echo "LINT reports failure." - exit 1 - fi - - echo "LINT OK" -} - - - -function fix_format() { - echo "Fixing format..." - - setup_for_format - - for f in ${files_to_lint}; do - whitelisted=$(awk '$1 == "'$f'" { print 1 }' "$CLANG_FORMAT_WHITELIST") - if [ -z "${whitelisted}" ]; then - echo "$f" - $CLANG_FORMAT -i "$f" - fi - done -} diff --git a/util/fix_format.sh b/util/fix_format.sh deleted file mode 100755 index 3cef6f58d..000000000 --- a/util/fix_format.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -e - -. ./util/ci/clang-format.sh - -fix_format -- 2.46.0 From a292cc42aa2d45b0949b5f675177f8850a30d1b5 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 23:53:16 +0100 Subject: [PATCH 06/85] Fix Windows architecture reporting in sysinfo --- src/porting.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/porting.cpp b/src/porting.cpp index 54c9ee2c9..7784a671f 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -204,7 +204,6 @@ bool detectMSVCBuildDir(const std::string &path) std::string get_sysinfo() { #ifdef _WIN32 - std::ostringstream oss; LPSTR filePath = new char[MAX_PATH]; UINT blockSize; @@ -224,15 +223,25 @@ std::string get_sysinfo() << LOWORD(fixedFileInfo->dwProductVersionMS) << '.' // Minor << HIWORD(fixedFileInfo->dwProductVersionLS) << ' '; // Build - #ifdef _WIN64 - oss << "x86_64"; - #else - BOOL is64 = FALSE; - if (IsWow64Process(GetCurrentProcess(), &is64) && is64) - oss << "x86_64"; // 32-bit app on 64-bit OS - else + SYSTEM_INFO info; + GetNativeSystemInfo(&info); + switch (info.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + oss << "x86_64"; + break; + case PROCESSOR_ARCHITECTURE_ARM: + oss << "arm"; + break; + case PROCESSOR_ARCHITECTURE_ARM64: + oss << "arm64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: oss << "x86"; - #endif + break; + default: + oss << "unknown"; + break; + } delete[] lpVersionInfo; delete[] filePath; -- 2.46.0 From 704b5d88b99404c25bc39df7b35627086397faba Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 13 Dec 2023 00:04:58 +0100 Subject: [PATCH 07/85] Upload artifacts in MinGW CI --- .github/workflows/build.yml | 20 ++++++++++++++------ util/buildbot/buildwin64.sh | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 907f997c1..52f9890d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -175,9 +175,13 @@ jobs: - name: Build run: | - EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh winbuild - env: - NO_PACKAGE: 1 + EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh B + + - uses: actions/upload-artifact@v3 + with: + name: mingw32 + path: B/build/*.zip + if-no-files-found: error win64: name: "MinGW cross-compiler (64-bit)" @@ -192,9 +196,13 @@ jobs: - name: Build run: | - EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh winbuild - env: - NO_PACKAGE: 1 + EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh B + + - uses: actions/upload-artifact@v3 + with: + name: mingw64 + path: B/build/*.zip + if-no-files-found: error msvc: name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }} diff --git a/util/buildbot/buildwin64.sh b/util/buildbot/buildwin64.sh index 92a522ece..dba08b071 100755 --- a/util/buildbot/buildwin64.sh +++ b/util/buildbot/buildwin64.sh @@ -53,7 +53,7 @@ get_sources git_hash=$(cd $sourcedir && git rev-parse --short HEAD) # Build the thing -cd $sourcedir +cd $builddir [ -d build ] && rm -rf build cmake_args=( -- 2.46.0 From 62c6667b0bd098be59406015fe0598a6d58da731 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 13 Dec 2023 00:05:51 +0100 Subject: [PATCH 08/85] Get rid of VERSION_EXTRA for buildbot This is probably a leftover of when CMake didn't automatically detect the revision from git. --- util/buildbot/buildwin32.sh | 3 --- util/buildbot/buildwin64.sh | 3 --- 2 files changed, 6 deletions(-) diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh index 642b82c41..a7012d2b8 100755 --- a/util/buildbot/buildwin32.sh +++ b/util/buildbot/buildwin32.sh @@ -53,8 +53,6 @@ download "http://minetest.kitsunemimi.pw/openal-soft-$openal_version-win32.zip" # Set source dir, downloading Minetest as needed get_sources -git_hash=$(cd $sourcedir && git rev-parse --short HEAD) - # Build the thing cd $builddir [ -d build ] && rm -rf build @@ -62,7 +60,6 @@ cd $builddir cmake_args=( -DCMAKE_TOOLCHAIN_FILE=$toolchain_file -DCMAKE_INSTALL_PREFIX=/tmp - -DVERSION_EXTRA=$git_hash -DBUILD_CLIENT=1 -DBUILD_SERVER=0 -DEXTRA_DLL="$runtime_dlls" diff --git a/util/buildbot/buildwin64.sh b/util/buildbot/buildwin64.sh index dba08b071..1a9953c36 100755 --- a/util/buildbot/buildwin64.sh +++ b/util/buildbot/buildwin64.sh @@ -50,8 +50,6 @@ download "http://minetest.kitsunemimi.pw/openal-soft-$openal_version-win64.zip" # Set source dir, downloading Minetest as needed get_sources -git_hash=$(cd $sourcedir && git rev-parse --short HEAD) - # Build the thing cd $builddir [ -d build ] && rm -rf build @@ -59,7 +57,6 @@ cd $builddir cmake_args=( -DCMAKE_TOOLCHAIN_FILE=$toolchain_file -DCMAKE_INSTALL_PREFIX=/tmp - -DVERSION_EXTRA=$git_hash -DBUILD_CLIENT=1 -DBUILD_SERVER=0 -DEXTRA_DLL="$runtime_dlls" -- 2.46.0 From c871b6dd4e78113d7c566a06f15241711b4ca58b Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 13 Dec 2023 00:19:11 +0100 Subject: [PATCH 09/85] Hash-check buildbot dependencies --- .github/workflows/build.yml | 6 ++---- util/buildbot/buildwin32.sh | 25 +++++++++++++------------ util/buildbot/buildwin64.sh | 25 +++++++++++++------------ util/buildbot/common.sh | 1 + util/buildbot/download_toolchain.sh | 15 +++++++++++++++ util/buildbot/sha256sums.txt | 26 ++++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 28 deletions(-) create mode 100755 util/buildbot/download_toolchain.sh create mode 100644 util/buildbot/sha256sums.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 52f9890d8..446aa1e1f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -170,8 +170,7 @@ jobs: - name: Install compiler run: | sudo apt-get update && sudo apt-get install -y gettext - wget http://minetest.kitsunemimi.pw/mingw-w64-i686_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz - sudo tar -xaf mingw.tar.xz -C /usr + sudo ./util/buildbot/download_toolchain.sh i686 /usr - name: Build run: | @@ -191,8 +190,7 @@ jobs: - name: Install compiler run: | sudo apt-get update && sudo apt-get install -y gettext - wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz - sudo tar -xaf mingw.tar.xz -C /usr + sudo ./util/buildbot/download_toolchain.sh x86_64 /usr - name: Build run: | diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh index a7012d2b8..62dc964a4 100755 --- a/util/buildbot/buildwin32.sh +++ b/util/buildbot/buildwin32.sh @@ -37,18 +37,19 @@ mkdir -p $libdir # this distinction should be gotten rid of next time cd $libdir -download "https://github.com/minetest/irrlicht/releases/download/$irrlicht_version/win32.zip" irrlicht-$irrlicht_version.zip -download "http://minetest.kitsunemimi.pw/zlib-$zlib_version-win32.zip" -download "http://minetest.kitsunemimi.pw/zstd-$zstd_version-win32.zip" -download "http://minetest.kitsunemimi.pw/libogg-$ogg_version-win32.zip" -download "http://minetest.kitsunemimi.pw/dw2/libvorbis-$vorbis_version-win32.zip" -download "http://minetest.kitsunemimi.pw/curl-$curl_version-win32.zip" -download "http://minetest.kitsunemimi.pw/gettext-$gettext_version-win32.zip" -download "http://minetest.kitsunemimi.pw/freetype-$freetype_version-win32.zip" -download "http://minetest.kitsunemimi.pw/sqlite3-$sqlite3_version-win32.zip" -download "http://minetest.kitsunemimi.pw/luajit-$luajit_version-win32.zip" -download "http://minetest.kitsunemimi.pw/dw2/libleveldb-$leveldb_version-win32.zip" leveldb-$leveldb_version.zip -download "http://minetest.kitsunemimi.pw/openal-soft-$openal_version-win32.zip" +libhost="http://minetest.kitsunemimi.pw" +download "https://github.com/minetest/irrlicht/releases/download/$irrlicht_version/win32.zip" irrlicht-$irrlicht_version-win32.zip +download "$libhost/zlib-$zlib_version-win32.zip" +download "$libhost/zstd-$zstd_version-win32.zip" +download "$libhost/libogg-$ogg_version-win32.zip" +download "$libhost/dw2/libvorbis-$vorbis_version-win32.zip" +download "$libhost/curl-$curl_version-win32.zip" +download "$libhost/gettext-$gettext_version-win32.zip" +download "$libhost/freetype-$freetype_version-win32.zip" +download "$libhost/sqlite3-$sqlite3_version-win32.zip" +download "$libhost/luajit-$luajit_version-win32.zip" +download "$libhost/dw2/libleveldb-$leveldb_version-win32.zip" leveldb-$leveldb_version-win32.zip +download "$libhost/openal-soft-$openal_version-win32.zip" # Set source dir, downloading Minetest as needed get_sources diff --git a/util/buildbot/buildwin64.sh b/util/buildbot/buildwin64.sh index 1a9953c36..afe73cb9b 100755 --- a/util/buildbot/buildwin64.sh +++ b/util/buildbot/buildwin64.sh @@ -34,18 +34,19 @@ irrlicht_version=$(cat $topdir/../../misc/irrlichtmt_tag.txt) mkdir -p $libdir cd $libdir -download "https://github.com/minetest/irrlicht/releases/download/$irrlicht_version/win64.zip" irrlicht-$irrlicht_version.zip -download "http://minetest.kitsunemimi.pw/zlib-$zlib_version-win64.zip" -download "http://minetest.kitsunemimi.pw/zstd-$zstd_version-win64.zip" -download "http://minetest.kitsunemimi.pw/libogg-$ogg_version-win64.zip" -download "http://minetest.kitsunemimi.pw/libvorbis-$vorbis_version-win64.zip" -download "http://minetest.kitsunemimi.pw/curl-$curl_version-win64.zip" -download "http://minetest.kitsunemimi.pw/gettext-$gettext_version-win64.zip" -download "http://minetest.kitsunemimi.pw/freetype-$freetype_version-win64.zip" -download "http://minetest.kitsunemimi.pw/sqlite3-$sqlite3_version-win64.zip" -download "http://minetest.kitsunemimi.pw/luajit-$luajit_version-win64.zip" -download "http://minetest.kitsunemimi.pw/libleveldb-$leveldb_version-win64.zip" leveldb-$leveldb_version.zip -download "http://minetest.kitsunemimi.pw/openal-soft-$openal_version-win64.zip" +libhost="http://minetest.kitsunemimi.pw" +download "https://github.com/minetest/irrlicht/releases/download/$irrlicht_version/win64.zip" irrlicht-$irrlicht_version-win64.zip +download "$libhost/zlib-$zlib_version-win64.zip" +download "$libhost/zstd-$zstd_version-win64.zip" +download "$libhost/libogg-$ogg_version-win64.zip" +download "$libhost/libvorbis-$vorbis_version-win64.zip" +download "$libhost/curl-$curl_version-win64.zip" +download "$libhost/gettext-$gettext_version-win64.zip" +download "$libhost/freetype-$freetype_version-win64.zip" +download "$libhost/sqlite3-$sqlite3_version-win64.zip" +download "$libhost/luajit-$luajit_version-win64.zip" +download "$libhost/libleveldb-$leveldb_version-win64.zip" leveldb-$leveldb_version-win64.zip +download "$libhost/openal-soft-$openal_version-win64.zip" # Set source dir, downloading Minetest as needed get_sources diff --git a/util/buildbot/common.sh b/util/buildbot/common.sh index 4d0cc08b0..cd365d35a 100644 --- a/util/buildbot/common.sh +++ b/util/buildbot/common.sh @@ -23,6 +23,7 @@ download () { [ -d "./$foldername" ] && return 0 wget "$url" -c -O "./$filename" + sha256sum -w -c <(grep -F "$filename" "$topdir/sha256sums.txt") if [ "$extract" = "unzip" ]; then unzip -o "$filename" -d "$foldername" elif [ "$extract" = "unzip_nofolder" ]; then diff --git a/util/buildbot/download_toolchain.sh b/util/buildbot/download_toolchain.sh new file mode 100755 index 000000000..f6283979e --- /dev/null +++ b/util/buildbot/download_toolchain.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e +topdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +if [[ -z "$1" || -z "$2" ]]; then + echo "Usage: $0 " + exit 1 +fi + +ver=11.2.0 +os=ubuntu20.04 +name="mingw-w64-${1}_${ver}_${os}.tar.xz" +wget "http://minetest.kitsunemimi.pw/$name" -O "$name" +sha256sum -w -c <(grep -F "$name" "$topdir/sha256sums.txt") +tar -xaf "$name" -C "$2" +rm -f "$name" diff --git a/util/buildbot/sha256sums.txt b/util/buildbot/sha256sums.txt new file mode 100644 index 000000000..30cc44716 --- /dev/null +++ b/util/buildbot/sha256sums.txt @@ -0,0 +1,26 @@ +e85ac83dd884a6716fdf048af1d8a4978ff4aa925471148f88c7c3430a9e2b05 curl-8.0.1-win32.zip +babed7c963bba7867412d9a905d5732a9cefefed401e05194903be08801c2182 curl-8.0.1-win64.zip +d6d7b0cf4d547f38ed54b0ff60a4b302b4184bd69fb580fc998410d87244939e freetype-2.12.1-win32.zip +dc6ecbb67b59a0662f21ca49a84fbf4bb0af9fedbc621983c4c0832fbddcc845 freetype-2.12.1-win64.zip +59236564f072baddac3e123927c50856d3147698cdd07fd08308a0eceb6f8679 gettext-0.20.2-win32.zip +4b11cc8211a6408b41d2342f927200f8f01b305cbcf9fb2398b5e7d204c42918 gettext-0.20.2-win64.zip +be02f4c0afc9e713d23182ec7782102a2d6c62fb4c2566832cdba0541437efc4 irrlicht-1.9.0mt13-win32.zip +6dca6dc23f6511da585874f3ec7bbda06e75d9de96c5a272e23a11a46b143262 irrlicht-1.9.0mt13-win64.zip +429e23c9a4a425c04db5b17e1361d4ce9ddd588e8fda062f5c485db1657e1b53 leveldb-1.23-win32.zip +82121b595d39b8e63c524e04c6ff247a904a76c278c0f42936e2bca63ff910a0 leveldb-1.23-win64.zip +23b467388aaa6e1a437b189dd54ecc7de7e47a734972d69abad874c558caa33c libogg-1.3.5-win32.zip +55f11b1fb26be5bc31e16fb282b0481938f306d0d4351048d82121cae1f0a254 libogg-1.3.5-win64.zip +d3f2de1d6f14c937bbcccf82f09ba583b3ad6925d30f6cddc096fcaa80ab3c03 libvorbis-1.3.7-win32.zip +b7722da388b17fdc216c00c97d9306b71c06dc49b173d43ef783fe0511739f2c libvorbis-1.3.7-win64.zip +79b9c3a9fc83a006721f1c78482729578d87f72b15c5440d28ba8f054655466b luajit-20230221-win32.zip +22f0f0ca95ff142fcd57237fa3e34797bb0f0d6789bf078b9c50941a9e4b739a luajit-20230221-win64.zip +704817351dc54a5a4bb3b35db9316f4ff1b073b231b5f8dbbc3b4ff2f3e30fbe mingw-w64-i686_11.2.0_ubuntu20.04.tar.xz +d85ec9a7debe470ebeaa002af0a2843b83d40405d2a45fcc586c19f179362aab mingw-w64-x86_64_11.2.0_ubuntu20.04.tar.xz +e4385c73c497ae90f28199002035fb369c7fa94ac93676949f92a1539a607941 openal-soft-1.23.0-win32.zip +643cdac87f0f1b7a06266df233e8d21119839b8fa3b9d6b4274b51e075a3ac11 openal-soft-1.23.0-win64.zip +d595396b60f31fe53d2b598c1677d633e407c175585a676bfc0d13d6cc239336 sqlite3-3.41.2-win32.zip +9aabc9545c1a7d39956c51e84239adbd05283218f451a3a846b080b713ad7661 sqlite3-3.41.2-win64.zip +e9bab0a6fe07bcf6c5a8ff171dd63983e67f3aefd9b8f38e88bf20a3dc44678f zlib-1.2.13-win32.zip +9f3d4fd89958081917d2fdcaab1bbc947e3fb070d8b39a48d9cf11269dd52c24 zlib-1.2.13-win64.zip +4ccbc8ac5830b06362d495ca65cd4687a76514bd103b5506b54a4b62ce0402f7 zstd-1.5.5-win32.zip +20dac9cedbaba40de5a5c0608a62d1885cbf63d055cca619289081e14d32290d zstd-1.5.5-win64.zip -- 2.46.0 From 94a54375e25b916a486bf8e2fe43aa8bbcb9ebdf Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Fri, 15 Dec 2023 10:24:04 +0100 Subject: [PATCH 10/85] Inventory: prevent item loss when stacking oversized ItemStacks (#14072) --- src/inventory.cpp | 17 +++++------ src/unittest/helpers/helper_moveaction.lua | 4 +++ src/unittest/test_moveaction.cpp | 35 ++++++++++++++++++++-- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/inventory.cpp b/src/inventory.cpp index 9d42cee97..c99147323 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -777,20 +777,18 @@ u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, return 0; // Try to add the item to destination list - u32 oldcount = item1.count; + count = item1.count; item1 = dest->addItem(dest_i, item1); // If something is returned, the item was not fully added if (!item1.empty()) { - // If olditem is returned, nothing was added. - bool nothing_added = (item1.count == oldcount); + bool nothing_added = (item1.count == count); - // If something else is returned, part of the item was left unadded. - // Add the other part back to the source item - addItem(i, item1); + // Add any leftover stack back to the source stack. + item1.add(getItem(i).count); // leftover + source count + changeItem(i, item1); // do NOT use addItem to allow oversized stacks! - // If olditem is returned, nothing was added. - // Swap the items + // Swap if no items could be moved if (nothing_added && swap_if_needed) { // Tell that we swapped if (did_swap != NULL) { @@ -802,9 +800,10 @@ u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, ItemStack item2 = dest->changeItem(dest_i, item1); // Put item from destination list to the source list changeItem(i, item2); + item1.clear(); // no leftover } } - return (oldcount - item1.count); + return (count - item1.count); } void InventoryList::checkResizeLock() diff --git a/src/unittest/helpers/helper_moveaction.lua b/src/unittest/helpers/helper_moveaction.lua index 8cffe01b8..edb950e81 100644 --- a/src/unittest/helpers/helper_moveaction.lua +++ b/src/unittest/helpers/helper_moveaction.lua @@ -1,4 +1,8 @@ minetest.register_allow_player_inventory_action(function(player, action, inventory, info) + if action == "move" then + return info.count + end + if info.stack:get_name() == "default:water" then return 0 end diff --git a/src/unittest/test_moveaction.cpp b/src/unittest/test_moveaction.cpp index 7c2ecbb83..fb11faff8 100644 --- a/src/unittest/test_moveaction.cpp +++ b/src/unittest/test_moveaction.cpp @@ -36,6 +36,7 @@ public: void runTests(IGameDef *gamedef); void testMove(ServerActiveObject *obj, IGameDef *gamedef); + void testMoveFillStack(ServerActiveObject *obj, IGameDef *gamedef); void testMoveSomewhere(ServerActiveObject *obj, IGameDef *gamedef); void testMoveUnallowed(ServerActiveObject *obj, IGameDef *gamedef); void testMovePartial(ServerActiveObject *obj, IGameDef *gamedef); @@ -51,14 +52,24 @@ void TestMoveAction::runTests(IGameDef *gamedef) MockServer server; ServerScripting server_scripting(&server); - server_scripting.loadMod(Server::getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); - server_scripting.loadMod(std::string(HELPERS_PATH) + DIR_DELIM "helper_moveaction.lua", BUILTIN_MOD_NAME); + try { + server_scripting.loadMod(Server::getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); + server_scripting.loadMod( + std::string(HELPERS_PATH) + DIR_DELIM "helper_moveaction.lua", BUILTIN_MOD_NAME + ); + } catch (ModError &e) { + // Print backtrace in case of syntax errors + rawstream << e.what() << std::endl; + num_tests_failed = 1; + return; + } MetricsBackend mb; ServerEnvironment server_env(nullptr, &server_scripting, &server, "", &mb); MockServerActiveObject obj(&server_env); TEST(testMove, &obj, gamedef); + TEST(testMoveFillStack, &obj, gamedef); TEST(testMoveSomewhere, &obj, gamedef); TEST(testMoveUnallowed, &obj, gamedef); TEST(testMovePartial, &obj, gamedef); @@ -95,6 +106,26 @@ void TestMoveAction::testMove(ServerActiveObject *obj, IGameDef *gamedef) UASSERT(inv.p2.getList("main")->getItem(0).getItemString() == "default:stone 20"); } +void TestMoveAction::testMoveFillStack(ServerActiveObject *obj, IGameDef *gamedef) +{ + MockInventoryManager inv(gamedef); + + auto list = inv.p1.addList("main", 10); + list->changeItem(0, parse_itemstack("default:stone 209")); + list->changeItem(1, parse_itemstack("default:stone 90")); // 9 free slots + + apply_action("Move 209 player:p1 main 0 player:p1 main 1", &inv, obj, gamedef); + + UASSERT(list->getItem(0).getItemString() == "default:stone 200"); + UASSERT(list->getItem(1).getItemString() == "default:stone 99"); + + // Trigger stack swap + apply_action("Move 200 player:p1 main 0 player:p1 main 1", &inv, obj, gamedef); + + UASSERT(list->getItem(0).getItemString() == "default:stone 99"); + UASSERT(list->getItem(1).getItemString() == "default:stone 200"); +} + void TestMoveAction::testMoveSomewhere(ServerActiveObject *obj, IGameDef *gamedef) { MockInventoryManager inv(gamedef); -- 2.46.0 From 3c60d359edf190116401eab79ba51f796631aaf1 Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Fri, 15 Dec 2023 10:28:07 +0100 Subject: [PATCH 11/85] Remove usage of removed "PP" macro This fixes a compilation error introduced by e7be135. --- src/server/luaentity_sao.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 26ae43588..f18318f4c 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -145,7 +145,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) // If attached, check that our parent is still there. If it isn't, detach. if (m_attachment_parent_id && !isAttached()) { // This is handled when objects are removed from the map - warningstream << "LuaEntitySAO::step() " << m_init_name << " at: " << PP(m_last_sent_position) << ", id=" << m_id << + warningstream << "LuaEntitySAO::step() " << m_init_name << " at " << m_last_sent_position << ", id=" << m_id << " is attached to nonexistent parent. This is a bug." << std::endl; clearParentAttachment(); sendPosition(false, true); -- 2.46.0 From e5a6048eeca3050246911f0ebb40d7546905ab2b Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 14 Dec 2023 11:41:22 +0100 Subject: [PATCH 12/85] Allow running individual benchmarks mirrors and reuses the option from 2f6a9d12f1db84322e0b69fd5ddc986f1f143606 --- src/benchmark/benchmark.cpp | 10 ++++++---- src/benchmark/benchmark.h | 2 +- src/main.cpp | 7 +++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/benchmark/benchmark.cpp b/src/benchmark/benchmark.cpp index 0bc2af368..2eb35d9e0 100644 --- a/src/benchmark/benchmark.cpp +++ b/src/benchmark/benchmark.cpp @@ -23,10 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CATCH_CONFIG_RUNNER #include "benchmark_setup.h" -int run_benchmarks() +bool run_benchmarks(const char *arg) { - int argc = 1; - const char *argv[] = { "MinetestBenchmark", NULL }; + const char *const argv[] = { + "MinetestBenchmark", arg, nullptr + }; + const int argc = arg ? 2 : 1; int errCount = Catch::Session().run(argc, argv); - return errCount ? 1 : 0; + return errCount == 0; } diff --git a/src/benchmark/benchmark.h b/src/benchmark/benchmark.h index 45dd9b6a4..3726c3925 100644 --- a/src/benchmark/benchmark.h +++ b/src/benchmark/benchmark.h @@ -22,5 +22,5 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" #if BUILD_BENCHMARKS -extern int run_benchmarks(); +extern bool run_benchmarks(const char *arg = nullptr); #endif diff --git a/src/main.cpp b/src/main.cpp index 377d6547b..dc98fb74d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -234,7 +234,10 @@ int main(int argc, char *argv[]) // Run benchmarks if (cmd_args.getFlag("run-benchmarks")) { #if BUILD_BENCHMARKS - return run_benchmarks(); + if (cmd_args.exists("test-module")) + return run_benchmarks(cmd_args.get("test-module").c_str()) ? 0 : 1; + else + return run_benchmarks() ? 0 : 1; #else errorstream << "Benchmark support is not enabled in this binary. " << "If you want to enable it, compile project with BUILD_BENCHMARKS=1 flag." @@ -340,7 +343,7 @@ static void set_allowed_options(OptionList *allowed_options) allowed_options->insert(std::make_pair("run-benchmarks", ValueSpec(VALUETYPE_FLAG, _("Run the benchmarks and exit")))); allowed_options->insert(std::make_pair("test-module", ValueSpec(VALUETYPE_STRING, - _("Only run the specified test module")))); + _("Only run the specified test module or benchmark")))); allowed_options->insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING, _("Same as --world (deprecated)")))); allowed_options->insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING, -- 2.46.0 From 2c2bc4a4273e91cd6c502e273be9192e42256c9c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 13 Dec 2023 21:43:11 +0100 Subject: [PATCH 13/85] Try to benchmark common MapBlock usage --- src/benchmark/CMakeLists.txt | 1 + src/benchmark/benchmark_mapblock.cpp | 185 +++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 src/benchmark/benchmark_mapblock.cpp diff --git a/src/benchmark/CMakeLists.txt b/src/benchmark/CMakeLists.txt index 787bbcee4..402c81cbf 100644 --- a/src/benchmark/CMakeLists.txt +++ b/src/benchmark/CMakeLists.txt @@ -2,6 +2,7 @@ set (BENCHMARK_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapblock.cpp PARENT_SCOPE) set (BENCHMARK_CLIENT_SRCS diff --git a/src/benchmark/benchmark_mapblock.cpp b/src/benchmark/benchmark_mapblock.cpp new file mode 100644 index 000000000..c273764dc --- /dev/null +++ b/src/benchmark/benchmark_mapblock.cpp @@ -0,0 +1,185 @@ +/* +Minetest +Copyright (C) 2023 Minetest Authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "benchmark_setup.h" +#include "mapblock.h" +#include + +typedef std::vector MBContainer; + +static void allocateSome(MBContainer &vec, u32 n) +{ + vec.reserve(vec.size() + n); + Map *map = reinterpret_cast(0x1234); + for (u32 i = 0; i < n; i++) { + auto *mb = new MapBlock(map, {i & 0xff, 0, i >> 8}, nullptr); + vec.push_back(mb); + } +} + +static void freeSome(MBContainer &vec, u32 n) +{ + // deallocate from end since that has no cost moving data inside the vector + u32 start_i = 0; + if (vec.size() > n) + start_i = vec.size() - n; + for (u32 i = start_i; i < vec.size(); i++) + delete vec[i]; + vec.resize(start_i); +} + +static inline void freeAll(MBContainer &vec) { freeSome(vec, vec.size()); } + +// usage patterns inspired by ClientMap::updateDrawList() +static void workOnMetadata(const MBContainer &vec) +{ + for (MapBlock *block : vec) { +#ifndef SERVER + bool foo = !!block->mesh; +#else + bool foo = true; +#endif + + if (block->refGet() > 2) + block->refDrop(); + + v3s16 pos = block->getPos() * MAP_BLOCKSIZE; + if (foo) + pos += v3s16(MAP_BLOCKSIZE / 2); + + if (pos.getDistanceFrom(v3s16(0)) > 30000) + continue; + + block->resetUsageTimer(); + block->refGrab(); + } +} + +// usage patterns inspired by LBMManager::applyLBMs() +static u32 workOnNodes(const MBContainer &vec) +{ + u32 foo = 0; + for (MapBlock *block : vec) { + block->resetUsageTimer(); + + if (block->isOrphan()) + continue; + + v3s16 pos_of_block = block->getPosRelative(); + v3s16 pos; + MapNode n; + for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) { + for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) { + for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) { + n = block->getNodeNoCheck(pos); + + if (n.getContent() == CONTENT_AIR) { + auto p = pos + pos_of_block; + foo ^= p.X + p.Y + p.Z; + } + } + } + } + } + return foo; +} + +// usage patterns inspired by ABMHandler::apply() +// touches both metadata and node data at the same time +static u32 workOnBoth(const MBContainer &vec) +{ + int foo = 0; + for (MapBlock *block : vec) { + block->contents.clear(); + block->contents_cached = false; + + v3s16 p0; + for(p0.X=0; p0.XgetNodeNoCheck(p0); + content_t c = n.getContent(); + + if (!block->contents_cached && !block->do_not_cache_contents) { + block->contents.insert(c); + if (block->contents.size() > 10) { + // Too many different nodes... don't try to cache + block->do_not_cache_contents = true; + block->contents.clear(); + } + } + } + block->contents_cached = !block->do_not_cache_contents; + foo += block->contents.size(); + } + return foo; +} + +#define BENCH1(_count) \ + BENCHMARK_ADVANCED("allocate_" #_count)(Catch::Benchmark::Chronometer meter) { \ + MBContainer vec; \ + const u32 pcount = _count / meter.runs(); \ + meter.measure([&] { \ + allocateSome(vec, pcount); \ + return vec.size(); \ + }); \ + freeAll(vec); \ + }; \ + BENCHMARK_ADVANCED("testCase1_" #_count)(Catch::Benchmark::Chronometer meter) { \ + MBContainer vec; \ + allocateSome(vec, _count); \ + meter.measure([&] { \ + workOnMetadata(vec); \ + }); \ + freeAll(vec); \ + }; \ + BENCHMARK_ADVANCED("testCase2_" #_count)(Catch::Benchmark::Chronometer meter) { \ + MBContainer vec; \ + allocateSome(vec, _count); \ + meter.measure([&] { \ + return workOnNodes(vec); \ + }); \ + freeAll(vec); \ + }; \ + BENCHMARK_ADVANCED("testCase3_" #_count)(Catch::Benchmark::Chronometer meter) { \ + MBContainer vec; \ + allocateSome(vec, _count); \ + meter.measure([&] { \ + return workOnBoth(vec); \ + }); \ + freeAll(vec); \ + }; \ + BENCHMARK_ADVANCED("free_" #_count)(Catch::Benchmark::Chronometer meter) { \ + MBContainer vec; \ + allocateSome(vec, _count); \ + /* catch2 does multiple runs so we have to be careful to not dealloc too many */ \ + const u32 pcount = _count / meter.runs(); \ + meter.measure([&] { \ + freeSome(vec, pcount); \ + return vec.size(); \ + }); \ + freeAll(vec); \ + }; + +TEST_CASE("benchmark_mapblock") { + BENCH1(900) + BENCH1(2200) + BENCH1(7500) // <- default client_mapblock_limit +} -- 2.46.0 From cb6e3ac6e17e04aac1e13b6256305d7dd7946102 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 15:03:34 +0100 Subject: [PATCH 14/85] Allocate data seperately from MapBlock class again This effectively reverts commit b3503e7853a52a8c16431f6b983e30c9d25951bc. --- src/mapblock.cpp | 3 +++ src/mapblock.h | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 3b902153e..bdcc2fe16 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -70,6 +70,7 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef): m_parent(parent), m_pos(pos), m_pos_relative(pos * MAP_BLOCKSIZE), + data(new MapNode[nodecount]), m_gamedef(gamedef) { reallocate(); @@ -83,6 +84,8 @@ MapBlock::~MapBlock() mesh = nullptr; } #endif + + delete[] data; } bool MapBlock::onObjectsActivation() diff --git a/src/mapblock.h b/src/mapblock.h index f0491e25c..0c6fec7a0 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -502,6 +502,13 @@ private: */ v3s16 m_pos_relative; + /* + * Note that this is not an inline array because that has implications on + * heap fragmentation (the array is exactly 16K), CPU caches and/or + * optimizability of algorithms working on this array. + */ + MapNode *const data; // of `nodecount` elements + IGameDef *m_gamedef; /* @@ -558,7 +565,6 @@ private: */ int m_refcount = 0; - MapNode data[nodecount]; NodeTimerList m_node_timers; }; -- 2.46.0 From c6cf90f67b51730cd833462135df197746f09b8c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 15:30:26 +0100 Subject: [PATCH 15/85] Change MapBlock content cache to a vector --- src/benchmark/benchmark_mapblock.cpp | 4 +++- src/mapblock.h | 6 ++++-- src/serverenvironment.cpp | 9 +++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/benchmark/benchmark_mapblock.cpp b/src/benchmark/benchmark_mapblock.cpp index c273764dc..7756233af 100644 --- a/src/benchmark/benchmark_mapblock.cpp +++ b/src/benchmark/benchmark_mapblock.cpp @@ -118,11 +118,13 @@ static u32 workOnBoth(const MBContainer &vec) content_t c = n.getContent(); if (!block->contents_cached && !block->do_not_cache_contents) { - block->contents.insert(c); + if (!CONTAINS(block->contents, c)) + block->contents.push_back(c); if (block->contents.size() > 10) { // Too many different nodes... don't try to cache block->do_not_cache_contents = true; block->contents.clear(); + block->contents.shrink_to_fit(); } } } diff --git a/src/mapblock.h b/src/mapblock.h index 0c6fec7a0..54b118e64 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include +#include #include "irr_v3d.h" #include "mapnode.h" #include "exceptions.h" @@ -476,7 +476,9 @@ public: //// ABM optimizations //// // Cache of content types - std::unordered_set contents; + // This is actually a set but for the small sizes we have a vector should be + // more efficient. + std::vector contents; // True if content types are cached bool contents_cached = false; // True if we never want to cache content types for this block diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 3cd849103..c4f8d1b13 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -782,6 +782,8 @@ struct ActiveABM s16 max_y; }; +#define CONTENT_TYPE_CACHE_MAX 64 + class ABMHandler { private: @@ -922,13 +924,16 @@ public: { MapNode n = block->getNodeNoCheck(p0); content_t c = n.getContent(); + // Cache content types as we go if (!block->contents_cached && !block->do_not_cache_contents) { - block->contents.insert(c); - if (block->contents.size() > 64) { + if (!CONTAINS(block->contents, c)) + block->contents.push_back(c); + if (block->contents.size() > CONTENT_TYPE_CACHE_MAX) { // Too many different nodes... don't try to cache block->do_not_cache_contents = true; block->contents.clear(); + block->contents.shrink_to_fit(); } } -- 2.46.0 From f5b35a074f1372fbfd9ef32721920235d616d5af Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 15:43:39 +0100 Subject: [PATCH 16/85] Get rid of parent pointer in MapBlock --- src/benchmark/benchmark_mapblock.cpp | 3 +-- src/main.cpp | 2 +- src/map.cpp | 10 +++++----- src/mapblock.cpp | 22 +--------------------- src/mapblock.h | 26 ++++++-------------------- src/mapsector.cpp | 2 +- 6 files changed, 15 insertions(+), 50 deletions(-) diff --git a/src/benchmark/benchmark_mapblock.cpp b/src/benchmark/benchmark_mapblock.cpp index 7756233af..440888d16 100644 --- a/src/benchmark/benchmark_mapblock.cpp +++ b/src/benchmark/benchmark_mapblock.cpp @@ -26,9 +26,8 @@ typedef std::vector MBContainer; static void allocateSome(MBContainer &vec, u32 n) { vec.reserve(vec.size() + n); - Map *map = reinterpret_cast(0x1234); for (u32 i = 0; i < n; i++) { - auto *mb = new MapBlock(map, {i & 0xff, 0, i >> 8}, nullptr); + auto *mb = new MapBlock({i & 0xff, 0, i >> 8}, nullptr); vec.push_back(mb); } } diff --git a/src/main.cpp b/src/main.cpp index dc98fb74d..d85afe97a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1272,7 +1272,7 @@ static bool recompress_map_database(const GameParams &game_params, const Setting iss.clear(); { - MapBlock mb(nullptr, v3s16(0,0,0), &server); + MapBlock mb(v3s16(0,0,0), &server); u8 ver = readU8(iss); mb.deSerialize(iss, ver, true); diff --git a/src/map.cpp b/src/map.cpp index ae8a24633..c44e31193 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -164,11 +164,11 @@ MapNode Map::getNode(v3s16 p, bool *is_valid_position) return node; } -static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n) +static void set_node_in_block(const NodeDefManager *nodedef, MapBlock *block, + v3s16 relpos, MapNode n) { // Never allow placing CONTENT_IGNORE, it causes problems if(n.getContent() == CONTENT_IGNORE){ - const NodeDefManager *nodedef = block->getParent()->getNodeDefManager(); v3s16 blockpos = block->getPos(); v3s16 p = blockpos * MAP_BLOCKSIZE + relpos; errorstream<<"Not allowing to place CONTENT_IGNORE" @@ -186,7 +186,7 @@ void Map::setNode(v3s16 p, MapNode n) v3s16 blockpos = getNodeBlockPos(p); MapBlock *block = getBlockNoCreate(blockpos); v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - set_node_in_block(block, relpos, n); + set_node_in_block(m_gamedef->ndef(), block, relpos, n); } void Map::addNodeAndUpdate(v3s16 p, MapNode n, @@ -215,14 +215,14 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, // No light update needed, just copy over the old light. n.setLight(LIGHTBANK_DAY, oldnode.getLightRaw(LIGHTBANK_DAY, oldf), f); n.setLight(LIGHTBANK_NIGHT, oldnode.getLightRaw(LIGHTBANK_NIGHT, oldf), f); - set_node_in_block(block, relpos, n); + set_node_in_block(m_gamedef->ndef(), block, relpos, n); modified_blocks[blockpos] = block; } else { // Ignore light (because calling voxalgo::update_lighting_nodes) n.setLight(LIGHTBANK_DAY, 0, f); n.setLight(LIGHTBANK_NIGHT, 0, f); - set_node_in_block(block, relpos, n); + set_node_in_block(m_gamedef->ndef(), block, relpos, n); // Update lighting std::vector > oldnodes; diff --git a/src/mapblock.cpp b/src/mapblock.cpp index bdcc2fe16..ef9b47b9e 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -66,8 +66,7 @@ static const char *modified_reason_strings[] = { MapBlock */ -MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef): - m_parent(parent), +MapBlock::MapBlock(v3s16 pos, IGameDef *gamedef): m_pos(pos), m_pos_relative(pos * MAP_BLOCKSIZE), data(new MapNode[nodecount]), @@ -146,25 +145,6 @@ void MapBlock::step(float dtime, const std::function } } -bool MapBlock::isValidPositionParent(v3s16 p) -{ - if (isValidPosition(p)) { - return true; - } - - return m_parent->isValidPosition(getPosRelative() + p); -} - -MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position) -{ - if (!isValidPosition(p)) - return m_parent->getNode(getPosRelative() + p, is_valid_position); - - if (is_valid_position) - *is_valid_position = true; - return data[p.Z * zstride + p.Y * ystride + p.X]; -} - std::string MapBlock::getModifiedReasonString() { std::string reason; diff --git a/src/mapblock.h b/src/mapblock.h index 54b118e64..1778aef8b 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -72,30 +72,20 @@ class VoxelManipulator; class MapBlock { public: - MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef); + MapBlock(v3s16 pos, IGameDef *gamedef); ~MapBlock(); - /*virtual u16 nodeContainerId() const - { - return NODECONTAINER_ID_MAPBLOCK; - }*/ - - Map *getParent() - { - return m_parent; - } - // Any server-modding code can "delete" arbitrary blocks (i.e. with // core.delete_area), which makes them orphan. Avoid using orphan blocks for // anything. bool isOrphan() const { - return !m_parent; + return m_orphan; } void makeOrphan() { - m_parent = nullptr; + m_orphan = true; } void reallocate() @@ -310,11 +300,6 @@ public: setNodeNoCheck(p.X, p.Y, p.Z, n); } - // These functions consult the parent container if the position - // is not valid on this MapBlock. - bool isValidPositionParent(v3s16 p); - MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL); - // Copies data to VoxelManipulator to getPosRelative() void copyTo(VoxelManipulator &dst); @@ -491,8 +476,6 @@ private: Private member variables */ - // NOTE: Lots of things rely on this being the Map - Map *m_parent; // Position in blocks on parent v3s16 m_pos; @@ -545,6 +528,9 @@ private: bool m_day_night_differs = false; bool m_day_night_differs_expired = true; + // see isOrphan() + bool m_orphan = false; + // Whether mapgen has generated the content of this block (persisted) bool m_generated = false; /* diff --git a/src/mapsector.cpp b/src/mapsector.cpp index e966f77d2..0eaf81ed4 100644 --- a/src/mapsector.cpp +++ b/src/mapsector.cpp @@ -73,7 +73,7 @@ std::unique_ptr MapSector::createBlankBlockNoInsert(s16 y) v3s16 blockpos_map(m_pos.X, y, m_pos.Y); - return std::make_unique(m_parent, blockpos_map, m_gamedef); + return std::make_unique(blockpos_map, m_gamedef); } MapBlock *MapSector::createBlankBlock(s16 y) -- 2.46.0 From 777dca7043ee525e44eb22ae711037fb43819417 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 16:08:41 +0100 Subject: [PATCH 17/85] Elide MapBlock::contents_cached --- src/benchmark/benchmark_mapblock.cpp | 15 ++++++++------- src/mapblock.h | 5 ++--- src/serverenvironment.cpp | 18 +++++++++--------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/benchmark/benchmark_mapblock.cpp b/src/benchmark/benchmark_mapblock.cpp index 440888d16..5522276f9 100644 --- a/src/benchmark/benchmark_mapblock.cpp +++ b/src/benchmark/benchmark_mapblock.cpp @@ -106,7 +106,8 @@ static u32 workOnBoth(const MBContainer &vec) int foo = 0; for (MapBlock *block : vec) { block->contents.clear(); - block->contents_cached = false; + + bool want_contents_cached = block->contents.empty() && !block->do_not_cache_contents; v3s16 p0; for(p0.X=0; p0.XgetNodeNoCheck(p0); content_t c = n.getContent(); - if (!block->contents_cached && !block->do_not_cache_contents) { - if (!CONTAINS(block->contents, c)) - block->contents.push_back(c); - if (block->contents.size() > 10) { - // Too many different nodes... don't try to cache + if (want_contents_cached && !CONTAINS(block->contents, c)) { + if (block->contents.size() >= 10) { + want_contents_cached = false; block->do_not_cache_contents = true; block->contents.clear(); block->contents.shrink_to_fit(); + } else { + block->contents.push_back(c); } } } - block->contents_cached = !block->do_not_cache_contents; + foo += block->contents.size(); } return foo; diff --git a/src/mapblock.h b/src/mapblock.h index 1778aef8b..16aaa1f28 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -114,7 +114,7 @@ public: m_modified_reason |= reason; } if (mod == MOD_STATE_WRITE_NEEDED) - contents_cached = false; + contents.clear(); } inline u32 getModified() @@ -463,9 +463,8 @@ public: // Cache of content types // This is actually a set but for the small sizes we have a vector should be // more efficient. + // Can be empty, in which case nothing was cached yet. std::vector contents; - // True if content types are cached - bool contents_cached = false; // True if we never want to cache content types for this block bool do_not_cache_contents = false; // marks the sides which are opaque: 00+Z-Z+Y-Y+X-X diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index c4f8d1b13..810456b0d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -894,7 +894,8 @@ public: // Check the content type cache first // to see whether there are any ABMs // to be run at all for this block. - if (block->contents_cached) { + if (!block->contents.empty()) { + assert(!block->do_not_cache_contents); // invariant blocks_cached++; bool run_abms = false; for (content_t c : block->contents) { @@ -905,9 +906,6 @@ public: } if (!run_abms) return; - } else { - // Clear any caching - block->contents.clear(); } blocks_scanned++; @@ -917,6 +915,8 @@ public: u32 active_object_count = this->countObjects(block, map, active_object_count_wider); m_env->m_added_objects = 0; + bool want_contents_cached = block->contents.empty() && !block->do_not_cache_contents; + v3s16 p0; for(p0.X=0; p0.Xcontents_cached && !block->do_not_cache_contents) { - if (!CONTAINS(block->contents, c)) - block->contents.push_back(c); - if (block->contents.size() > CONTENT_TYPE_CACHE_MAX) { + if (want_contents_cached && !CONTAINS(block->contents, c)) { + if (block->contents.size() >= CONTENT_TYPE_CACHE_MAX) { // Too many different nodes... don't try to cache + want_contents_cached = false; block->do_not_cache_contents = true; block->contents.clear(); block->contents.shrink_to_fit(); + } else { + block->contents.push_back(c); } } @@ -997,7 +998,6 @@ public: break; } } - block->contents_cached = !block->do_not_cache_contents; } }; -- 2.46.0 From 9408a1a0250b2523bf393eafd8287843c57af591 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 16:14:40 +0100 Subject: [PATCH 18/85] Reduce size of some MapBlock members Also adds assertions to catch refcounting errors (on a debug build). --- src/mapblock.h | 8 +++++--- src/modifiedstate.h | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mapblock.h b/src/mapblock.h index 16aaa1f28..5d87acaed 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -379,15 +379,17 @@ public: inline void refGrab() { + assert(m_refcount < SHRT_MAX); m_refcount++; } inline void refDrop() { + assert(m_refcount > 0); m_refcount--; } - inline int refGet() + inline short refGet() { return m_refcount; } @@ -500,7 +502,7 @@ private: block has been modified from the one on disk. - On the client, this is used for nothing. */ - u32 m_modified = MOD_STATE_WRITE_NEEDED; + u16 m_modified = MOD_STATE_WRITE_NEEDED; u32 m_modified_reason = MOD_REASON_INITIAL; /* @@ -550,7 +552,7 @@ private: Reference count; currently used for determining if this block is in the list of blocks to be drawn. */ - int m_refcount = 0; + short m_refcount = 0; NodeTimerList m_node_timers; }; diff --git a/src/modifiedstate.h b/src/modifiedstate.h index 3eeb55d02..5bf3b643c 100644 --- a/src/modifiedstate.h +++ b/src/modifiedstate.h @@ -19,7 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -enum ModifiedState +#include "irrlichttypes.h" + +enum ModifiedState : u16 { // Has not been modified. MOD_STATE_CLEAN = 0, -- 2.46.0 From 128ed87dd8ff5848d4ac1ce905b48bf166c06ff8 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 12 Dec 2023 16:28:21 +0100 Subject: [PATCH 19/85] Reorder members of MapBlock for performance Before and after as obtained via `pahole -C MapBlock bin/minetest`: /* size: 336, cachelines: 6, members: 23 */ /* sum members: 329, holes: 4, sum holes: 7 */ vs. /* size: 336, cachelines: 6, members: 23 */ /* sum members: 329, holes: 2, sum holes: 7 */ There is not much to be gained by packing but I made sure to move the most important data (mainly for the client) into the first cache line. --- src/mapblock.h | 126 ++++++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 60 deletions(-) diff --git a/src/mapblock.h b/src/mapblock.h index 5d87acaed..9a91a05cf 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -437,6 +437,11 @@ public: // clearObject and return removed objects count u32 clearObjects(); + static const u32 ystride = MAP_BLOCKSIZE; + static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE; + + static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE; + private: /* Private methods @@ -444,59 +449,73 @@ private: void deSerialize_pre22(std::istream &is, u8 version, bool disk); -public: /* - Public member variables - */ + * PLEASE NOTE: When adding something here be mindful of position and size + * of member variables! This is also the reason for the weird public-private + * interleaving. + * If in doubt consult `pahole` to see the effects. + */ +public: #ifndef SERVER // Only on client MapBlockMesh *mesh = nullptr; + + // marks the sides which are opaque: 00+Z-Z+Y-Y+X-X + u8 solid_sides = 0; #endif - NodeMetadataList m_node_metadata; - StaticObjectList m_static_objects; - - static const u32 ystride = MAP_BLOCKSIZE; - static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE; - - static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE; - - //// ABM optimizations //// - // Cache of content types - // This is actually a set but for the small sizes we have a vector should be - // more efficient. - // Can be empty, in which case nothing was cached yet. - std::vector contents; - // True if we never want to cache content types for this block - bool do_not_cache_contents = false; - // marks the sides which are opaque: 00+Z-Z+Y-Y+X-X - u8 solid_sides {0}; - private: - /* - Private member variables - */ + // see isOrphan() + bool m_orphan = false; // Position in blocks on parent v3s16 m_pos; - /* This is the precalculated m_pos_relative value - * This caches the value, improving performance by removing 3 s16 multiplications - * at runtime on each getPosRelative call - * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications - * The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins - */ + /* Precalculated m_pos_relative value + * This caches the value, improving performance by removing 3 s16 multiplications + * at runtime on each getPosRelative call. + * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications. + * The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins. + */ v3s16 m_pos_relative; /* - * Note that this is not an inline array because that has implications on + Reference count; currently used for determining if this block is in + the list of blocks to be drawn. + */ + short m_refcount = 0; + + /* + * Note that this is not an inline array because that has implications for * heap fragmentation (the array is exactly 16K), CPU caches and/or * optimizability of algorithms working on this array. */ MapNode *const data; // of `nodecount` elements + // provides the item and node definitions IGameDef *m_gamedef; + /* + When the block is accessed, this is set to 0. + Map will unload the block when this reaches a timeout. + */ + float m_usage_timer = 0; + +public: + //// ABM optimizations //// + // True if we never want to cache content types for this block + bool do_not_cache_contents = false; + // Cache of content types + // This is actually a set but for the small sizes we have a vector should be + // more efficient. + // Can be empty, in which case nothing was cached yet. + std::vector contents; + +private: + // Whether day and night lighting differs + bool m_day_night_differs = false; + bool m_day_night_differs_expired = true; + /* - On the server, this is used for telling whether the block has been modified from the one on disk. @@ -506,14 +525,12 @@ private: u32 m_modified_reason = MOD_REASON_INITIAL; /* - When propagating sunlight and the above block doesn't exist, - sunlight is assumed if this is false. - - In practice this is set to true if the block is completely - undeground with nothing visible above the ground except - caves. + When block is removed from active blocks, this is set to gametime. + Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp. */ - bool is_underground = false; + u32 m_timestamp = BLOCK_TIMESTAMP_UNDEFINED; + // The on-disk (or to-be on-disk) timestamp value + u32 m_disk_timestamp = BLOCK_TIMESTAMP_UNDEFINED; /*! * Each bit indicates if light spreading was finished @@ -525,35 +542,24 @@ private: */ u16 m_lighting_complete = 0xFFFF; - // Whether day and night lighting differs - bool m_day_night_differs = false; - bool m_day_night_differs_expired = true; - - // see isOrphan() - bool m_orphan = false; // Whether mapgen has generated the content of this block (persisted) bool m_generated = false; /* - When block is removed from active blocks, this is set to gametime. - Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp. - */ - u32 m_timestamp = BLOCK_TIMESTAMP_UNDEFINED; - // The on-disk (or to-be on-disk) timestamp value - u32 m_disk_timestamp = BLOCK_TIMESTAMP_UNDEFINED; + When propagating sunlight and the above block doesn't exist, + sunlight is assumed if this is false. - /* - When the block is accessed, this is set to 0. - Map will unload the block when this reaches a timeout. + In practice this is set to true if the block is completely + undeground with nothing visible above the ground except + caves. */ - float m_usage_timer = 0; + bool is_underground = false; - /* - Reference count; currently used for determining if this block is in - the list of blocks to be drawn. - */ - short m_refcount = 0; +public: + NodeMetadataList m_node_metadata; + StaticObjectList m_static_objects; +private: NodeTimerList m_node_timers; }; -- 2.46.0 From 16c22477c29478d2a956b08402fe6e11b5bc24d5 Mon Sep 17 00:00:00 2001 From: superfloh247 Date: Sat, 16 Dec 2023 12:52:07 +0100 Subject: [PATCH 20/85] Update porting.h to fix build errors on macOS 14 / Xcode 15 --- src/porting.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/porting.h b/src/porting.h index bf01c3e4d..0c7598805 100644 --- a/src/porting.h +++ b/src/porting.h @@ -89,6 +89,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef _WIN32 // POSIX #include #include + #if defined(__MACH__) && defined(__APPLE__) + #include + #endif #endif namespace porting -- 2.46.0 From ca1a7238909e12c01653e9fd40dda86d86a2e1f3 Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Sat, 16 Dec 2023 15:04:21 -0800 Subject: [PATCH 21/85] Allow cheaper culling checks at a distance (#14073) * Allow cheaper culling checks at a distance * Pick a random ray, so that far missing block will eventually be shown --- builtin/settingtypes.txt | 11 +++++++++-- src/clientiface.cpp | 5 ++++- src/clientiface.h | 1 + src/defaultsettings.cpp | 1 + src/map.cpp | 10 +++++++++- src/map.h | 2 +- 6 files changed, 25 insertions(+), 5 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index ec71f1998..93abd3575 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2061,8 +2061,8 @@ liquid_update (Liquid update tick) float 1.0 0.001 # as well as sometimes on land). # Setting this to a value greater than max_block_send_distance disables this # optimization. -# Stated in mapblocks (16 nodes). -block_send_optimize_distance (Block send optimize distance) int 4 2 32767 +# Stated in MapBlocks (16 nodes). +block_send_optimize_distance (Block send optimize distance) int 4 2 2047 # If enabled, the server will perform map block occlusion culling based on # on the eye position of the player. This can reduce the number of blocks @@ -2070,6 +2070,13 @@ block_send_optimize_distance (Block send optimize distance) int 4 2 32767 # invisible blocks, so that the utility of noclip mode is reduced. server_side_occlusion_culling (Server-side occlusion culling) bool true +# At this distance the server will perform a simpler and cheaper occlusion check. +# Smaller values potentially improve performance, at the expense of temporarily visible +# rendering glitches (missing blocks). +# This is especially useful for very large viewing range (upwards of 500). +# Stated in MapBlocks (16 nodes). +block_cull_optimize_distance (Block cull optimize distance) int 20 2 2047 + [**Mapgen] # Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes). diff --git a/src/clientiface.cpp b/src/clientiface.cpp index e609c98f6..33d07364a 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -59,6 +59,7 @@ RemoteClient::RemoteClient() : g_settings->getFloat("full_block_send_enable_min_time_from_building")), m_max_send_distance(g_settings->getS16("max_block_send_distance")), m_block_optimize_distance(g_settings->getS16("block_send_optimize_distance")), + m_block_cull_optimize_distance(g_settings->getS16("block_cull_optimize_distance")), m_max_gen_distance(g_settings->getS16("max_block_generate_distance")), m_occ_cull(g_settings->getBool("server_side_occlusion_culling")) { @@ -225,6 +226,8 @@ void RemoteClient::GetNextBlocks ( wanted_range); const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, prop_zoom_fov), wanted_range); + const s16 d_cull_opt = std::min(adjustDist(m_block_cull_optimize_distance, prop_zoom_fov), + wanted_range); const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE; s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov), @@ -355,7 +358,7 @@ void RemoteClient::GetNextBlocks ( continue; if (m_occ_cull && !block_not_found && - env->getMap().isBlockOccluded(block, cam_pos_nodes)) { + env->getMap().isBlockOccluded(block, cam_pos_nodes, d >= d_cull_opt)) { m_blocks_occ.insert(p); continue; } diff --git a/src/clientiface.h b/src/clientiface.h index 261c09fce..c531d743a 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -397,6 +397,7 @@ private: const float m_min_time_from_building; const s16 m_max_send_distance; const s16 m_block_optimize_distance; + const s16 m_block_cull_optimize_distance; const s16 m_max_gen_distance; const bool m_occ_cull; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index a815f89ed..6a48a9b40 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -393,6 +393,7 @@ void set_default_settings() // This causes frametime jitter on client side, or does it? settings->setDefault("max_block_send_distance", "12"); settings->setDefault("block_send_optimize_distance", "4"); + settings->setDefault("block_cull_optimize_distance", "20"); settings->setDefault("server_side_occlusion_culling", "true"); settings->setDefault("csm_restriction_flags", "62"); settings->setDefault("csm_restriction_noderange", "0"); diff --git a/src/map.cpp b/src/map.cpp index c44e31193..f63732cf4 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1155,7 +1155,7 @@ bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target, return false; } -bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) +bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes, bool simple_check) { // Check occlusion for center and all 8 corners of the mapblock // Overshoot a little for less flickering @@ -1193,6 +1193,14 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) // this is a HACK, we should think of a more precise algorithm u32 needed_count = 2; + v3s16 random_point(myrand_range(-bs2, bs2), myrand_range(-bs2, bs2), myrand_range(-bs2, bs2)); + if (!isOccluded(cam_pos_nodes, pos_blockcenter + random_point, step, stepfac, + start_offset, end_offset, needed_count)) + return false; + + if (simple_check) + return true; + // Additional occlusion check, see comments in that function v3s16 check; if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) { diff --git a/src/map.h b/src/map.h index 07f6c56e8..543e2e89b 100644 --- a/src/map.h +++ b/src/map.h @@ -305,7 +305,7 @@ public: } } - bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes); + bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes, bool simple_check = false); protected: IGameDef *m_gamedef; -- 2.46.0 From 7162b536eb43c70f41cac1403323e128e66cef19 Mon Sep 17 00:00:00 2001 From: JosiahWI <41302989+JosiahWI@users.noreply.github.com> Date: Sun, 17 Dec 2023 13:44:45 -0600 Subject: [PATCH 22/85] Extract Game::drawScene from Game::updateFrame --- src/client/game.cpp | 94 +++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/src/client/game.cpp b/src/client/game.cpp index 7bc2c8697..f7d31bc3b 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -821,6 +821,7 @@ protected: const CameraOrientation &cam); void updateClouds(float dtime); void updateShadows(); + void drawScene(ProfilerGraph *graph, RunStats *stats); // Misc void showOverlayMessage(const char *msg, float dtime, int percent, @@ -4133,52 +4134,17 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* ==================== Drawing begins ==================== */ - const video::SColor skycolor = sky->getSkyColor(); - - TimeTaker tt_draw("Draw scene", nullptr, PRECISION_MICRO); - driver->beginScene(true, true, skycolor); - - bool draw_wield_tool = (m_game_ui->m_flags.show_hud && - (player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) && - (camera->getCameraMode() == CAMERA_MODE_FIRST)); - bool draw_crosshair = ( - (player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) && - (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT)); -#ifdef HAVE_TOUCHSCREENGUI - if (isNoCrosshairAllowed()) - draw_crosshair = false; -#endif - m_rendering_engine->draw_scene(skycolor, m_game_ui->m_flags.show_hud, - m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair); - - /* - Profiler graph - */ - v2u32 screensize = driver->getScreenSize(); - - if (m_game_ui->m_flags.show_profiler_graph) - graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont()); - - /* - Damage flash - */ - if (runData.damage_flash > 0.0f) { - video::SColor color(runData.damage_flash, 180, 0, 0); - driver->draw2DRectangle(color, - core::rect(0, 0, screensize.X, screensize.Y), - NULL); - - runData.damage_flash -= 384.0f * dtime; - } - + drawScene(graph, stats); /* ==================== End scene ==================== */ - driver->endScene(); + // Damage flash is drawn in drawScene, but the timing update is done here to + // keep dtime out of the drawing code. + if (runData.damage_flash > 0.0f) { + runData.damage_flash -= 384.0f * dtime; + } - stats->drawtime = tt_draw.stop(true); - g_profiler->graphAdd("Draw scene [us]", stats->drawtime); g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true)); } @@ -4246,6 +4212,52 @@ void Game::updateShadows() shadow->getDirectionalLight().update_frustum(camera, client, m_camera_offset_changed); } +void Game::drawScene(ProfilerGraph *graph, RunStats *stats) +{ + const video::SColor skycolor = this->sky->getSkyColor(); + + TimeTaker tt_draw("Draw scene", nullptr, PRECISION_MICRO); + this->driver->beginScene(true, true, skycolor); + + const LocalPlayer *player = this->client->getEnv().getLocalPlayer(); + bool draw_wield_tool = (this->m_game_ui->m_flags.show_hud && + (player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) && + (this->camera->getCameraMode() == CAMERA_MODE_FIRST)); + bool draw_crosshair = ( + (player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) && + (this->camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT)); +#ifdef HAVE_TOUCHSCREENGUI + if (this->isNoCrosshairAllowed()) + draw_crosshair = false; +#endif + this->m_rendering_engine->draw_scene(skycolor, this->m_game_ui->m_flags.show_hud, + this->m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair); + + /* + Profiler graph + */ + v2u32 screensize = this->driver->getScreenSize(); + + if (this->m_game_ui->m_flags.show_profiler_graph) + graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont()); + + /* + Damage flash + */ + if (this->runData.damage_flash > 0.0f) { + video::SColor color(this->runData.damage_flash, 180, 0, 0); + this->driver->draw2DRectangle(color, + core::rect(0, 0, screensize.X, screensize.Y), + NULL); + } + + this->driver->endScene(); + + stats->drawtime = tt_draw.stop(true); + g_profiler->graphAdd("Draw scene [us]", stats->drawtime); + +} + /**************************************************************************** Misc ****************************************************************************/ -- 2.46.0 From 91ba02449bd6649f15730520c12d8d2d8031b6b0 Mon Sep 17 00:00:00 2001 From: grorp Date: Sun, 17 Dec 2023 20:47:07 +0100 Subject: [PATCH 23/85] Add `touch_controls` boolean to `get_player_window_information()` (#14092) --- doc/lua_api.md | 6 ++++++ doc/menu_lua_api.md | 4 ++++ games/devtest/mods/testfullscreenfs/init.lua | 5 ++++- src/client/client.cpp | 1 + src/clientdynamicinfo.h | 12 ++++++++++-- src/network/serverpackethandler.cpp | 6 ++++++ src/script/lua_api/l_mainmenu.cpp | 4 ++++ src/script/lua_api/l_server.cpp | 5 +++++ 8 files changed, 40 insertions(+), 3 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index c1cf1ea40..4b03db52c 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5350,6 +5350,12 @@ Utilities -- HUD Scaling multiplier -- Equal to the setting `hud_scaling` multiplied by `dpi / 96` real_hud_scaling = 1, + + -- Whether the touchscreen controls are enabled. + -- Usually (but not always) `true` on Android. + -- Requires at least Minetest 5.9.0 on the client. For older clients, it + -- is always set to `false`. + touch_controls = false, } ``` diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index 0fbca092a..6c3777f5f 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -249,6 +249,10 @@ GUI -- HUD Scaling multiplier -- Equal to the setting `hud_scaling` multiplied by `dpi / 96` real_hud_scaling = 1, + + -- Whether the touchscreen controls are enabled. + -- Usually (but not always) `true` on Android. + touch_controls = false, } ``` diff --git a/games/devtest/mods/testfullscreenfs/init.lua b/games/devtest/mods/testfullscreenfs/init.lua index b9d20610b..e1af3ae33 100644 --- a/games/devtest/mods/testfullscreenfs/init.lua +++ b/games/devtest/mods/testfullscreenfs/init.lua @@ -7,6 +7,8 @@ local function show_fullscreen_fs(name) print(dump(window)) local size = { x = window.max_formspec_size.x * 1.1, y = window.max_formspec_size.y * 1.1 } + local touch_text = window.touch_controls and "Touch controls enabled" or + "Touch controls disabled" local fs = { "formspec_version[4]", ("size[%f,%f]"):format(size.x, size.y), @@ -16,7 +18,8 @@ local function show_fullscreen_fs(name) ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, size.y - 1, "br", "BR"), ("button[%f,%f;1,1;%s;%s]"):format(0, size.y - 1, "bl", "BL"), - ("label[%f,%f;%s]"):format(size.x / 2, size.y / 2, "Fullscreen") + ("label[%f,%f;%s]"):format(size.x / 2, size.y / 2, "Fullscreen"), + ("label[%f,%f;%s]"):format(size.x / 2, size.y / 2 + 1, touch_text), } minetest.show_formspec(name, "testfullscreenfs:fs", table.concat(fs)) diff --git a/src/client/client.cpp b/src/client/client.cpp index a49578ae0..8d204931c 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -1439,6 +1439,7 @@ void Client::sendUpdateClientInfo(const ClientDynamicInfo& info) pkt << info.real_gui_scaling; pkt << info.real_hud_scaling; pkt << (f32)info.max_fs_size.X << (f32)info.max_fs_size.Y; + pkt << info.touch_controls; Send(&pkt); } diff --git a/src/clientdynamicinfo.h b/src/clientdynamicinfo.h index c377a64f0..547329b06 100644 --- a/src/clientdynamicinfo.h +++ b/src/clientdynamicinfo.h @@ -33,11 +33,13 @@ public: f32 real_gui_scaling; f32 real_hud_scaling; v2f32 max_fs_size; + bool touch_controls; bool equal(const ClientDynamicInfo &other) const { return render_target_size == other.render_target_size && abs(real_gui_scaling - other.real_gui_scaling) < 0.001f && - abs(real_hud_scaling - other.real_hud_scaling) < 0.001f; + abs(real_hud_scaling - other.real_hud_scaling) < 0.001f && + touch_controls == other.touch_controls; } #ifndef SERVER @@ -48,10 +50,16 @@ public: f32 hud_scaling = g_settings->getFloat("hud_scaling", 0.5f, 20.0f); f32 real_gui_scaling = gui_scaling * density; f32 real_hud_scaling = hud_scaling * density; +#ifdef HAVE_TOUCHSCREENGUI + bool touch_controls = true; +#else + bool touch_controls = false; +#endif return { screen_size, real_gui_scaling, real_hud_scaling, - ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling) + ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling), + touch_controls }; } #endif diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 9eaf30dc1..bce2b2261 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1873,6 +1873,12 @@ void Server::handleCommand_UpdateClientInfo(NetworkPacket *pkt) *pkt >> info.real_hud_scaling; *pkt >> info.max_fs_size.X; *pkt >> info.max_fs_size.Y; + try { + // added in 5.9.0 + *pkt >> info.touch_controls; + } catch (PacketError &e) { + info.touch_controls = false; + } session_t peer_id = pkt->getPeerId(); RemoteClient *client = getClient(peer_id, CS_Invalid); diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 9d64a76f6..f3aa22ebc 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -946,6 +946,10 @@ int ModApiMainMenu::l_get_window_info(lua_State *L) lua_pushnumber(L, info.real_hud_scaling); lua_settable(L, top); + lua_pushstring(L, "touch_controls"); + lua_pushboolean(L, info.touch_controls); + lua_settable(L, top); + return 1; } diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 12e5a1a5d..264e88801 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -317,6 +317,11 @@ int ModApiServer::l_get_player_window_information(lua_State *L) lua_pushstring(L, "real_hud_scaling"); lua_pushnumber(L, dynamic->real_hud_scaling); lua_settable(L, dyn_table); + + lua_pushstring(L, "touch_controls"); + lua_pushboolean(L, dynamic->touch_controls); + lua_settable(L, dyn_table); + return 1; } -- 2.46.0 From 5d3e83017679317c27fe02b7087effd9d67f79cc Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 13 Dec 2023 12:54:44 +0100 Subject: [PATCH 24/85] MinGW toolchain refresh --- .github/workflows/build.yml | 4 +-- util/buildbot/buildwin32.sh | 15 ++++----- util/buildbot/buildwin64.sh | 14 ++++---- util/buildbot/common.sh | 12 +++---- util/buildbot/download_toolchain.sh | 7 ++-- util/buildbot/sha256sums.txt | 52 ++++++++++++++--------------- 6 files changed, 54 insertions(+), 50 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 446aa1e1f..77f3898ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -164,7 +164,7 @@ jobs: win32: name: "MinGW cross-compiler (32-bit)" - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install compiler @@ -184,7 +184,7 @@ jobs: win64: name: "MinGW cross-compiler (64-bit)" - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install compiler diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh index 62dc964a4..fbc05ad8f 100755 --- a/util/buildbot/buildwin32.sh +++ b/util/buildbot/buildwin32.sh @@ -33,22 +33,21 @@ irrlicht_version=$(cat $topdir/../../misc/irrlichtmt_tag.txt) mkdir -p $libdir -# 'dw2' just points to rebuilt versions after a toolchain change -# this distinction should be gotten rid of next time +# 'ucrt' just points to rebuilt versions after a toolchain change cd $libdir libhost="http://minetest.kitsunemimi.pw" -download "https://github.com/minetest/irrlicht/releases/download/$irrlicht_version/win32.zip" irrlicht-$irrlicht_version-win32.zip +download "https://github.com/minetest/irrlicht/releases/download/$irrlicht_version/win32-ucrt.zip" irrlicht-$irrlicht_version-win32.zip download "$libhost/zlib-$zlib_version-win32.zip" -download "$libhost/zstd-$zstd_version-win32.zip" -download "$libhost/libogg-$ogg_version-win32.zip" -download "$libhost/dw2/libvorbis-$vorbis_version-win32.zip" +download "$libhost/ucrt/zstd-$zstd_version-win32.zip" +download "$libhost/ucrt/libogg-$ogg_version-win32.zip" +download "$libhost/ucrt/libvorbis-$vorbis_version-win32.zip" download "$libhost/curl-$curl_version-win32.zip" -download "$libhost/gettext-$gettext_version-win32.zip" +download "$libhost/ucrt/gettext-$gettext_version-win32.zip" download "$libhost/freetype-$freetype_version-win32.zip" download "$libhost/sqlite3-$sqlite3_version-win32.zip" download "$libhost/luajit-$luajit_version-win32.zip" -download "$libhost/dw2/libleveldb-$leveldb_version-win32.zip" leveldb-$leveldb_version-win32.zip +download "$libhost/ucrt/libleveldb-$leveldb_version-win32.zip" leveldb-$leveldb_version-win32.zip download "$libhost/openal-soft-$openal_version-win32.zip" # Set source dir, downloading Minetest as needed diff --git a/util/buildbot/buildwin64.sh b/util/buildbot/buildwin64.sh index afe73cb9b..7e7c73ea7 100755 --- a/util/buildbot/buildwin64.sh +++ b/util/buildbot/buildwin64.sh @@ -33,19 +33,21 @@ irrlicht_version=$(cat $topdir/../../misc/irrlichtmt_tag.txt) mkdir -p $libdir +# 'ucrt' just points to rebuilt versions after a toolchain change + cd $libdir libhost="http://minetest.kitsunemimi.pw" -download "https://github.com/minetest/irrlicht/releases/download/$irrlicht_version/win64.zip" irrlicht-$irrlicht_version-win64.zip +download "https://github.com/minetest/irrlicht/releases/download/$irrlicht_version/win64-ucrt.zip" irrlicht-$irrlicht_version-win64.zip download "$libhost/zlib-$zlib_version-win64.zip" -download "$libhost/zstd-$zstd_version-win64.zip" -download "$libhost/libogg-$ogg_version-win64.zip" -download "$libhost/libvorbis-$vorbis_version-win64.zip" +download "$libhost/ucrt/zstd-$zstd_version-win64.zip" +download "$libhost/ucrt/libogg-$ogg_version-win64.zip" +download "$libhost/ucrt/libvorbis-$vorbis_version-win64.zip" download "$libhost/curl-$curl_version-win64.zip" -download "$libhost/gettext-$gettext_version-win64.zip" +download "$libhost/ucrt/gettext-$gettext_version-win64.zip" download "$libhost/freetype-$freetype_version-win64.zip" download "$libhost/sqlite3-$sqlite3_version-win64.zip" download "$libhost/luajit-$luajit_version-win64.zip" -download "$libhost/libleveldb-$leveldb_version-win64.zip" leveldb-$leveldb_version-win64.zip +download "$libhost/ucrt/libleveldb-$leveldb_version-win64.zip" leveldb-$leveldb_version-win64.zip download "$libhost/openal-soft-$openal_version-win64.zip" # Set source dir, downloading Minetest as needed diff --git a/util/buildbot/common.sh b/util/buildbot/common.sh index cd365d35a..9fe04bf87 100644 --- a/util/buildbot/common.sh +++ b/util/buildbot/common.sh @@ -3,15 +3,15 @@ CORE_BRANCH=master CORE_NAME=minetest ogg_version=1.3.5 -openal_version=1.23.0 +openal_version=1.23.1 vorbis_version=1.3.7 -curl_version=8.0.1 +curl_version=8.5.0 gettext_version=0.20.2 -freetype_version=2.12.1 -sqlite3_version=3.41.2 -luajit_version=20230221 +freetype_version=2.13.2 +sqlite3_version=3.44.2 +luajit_version=20231211 leveldb_version=1.23 -zlib_version=1.2.13 +zlib_version=1.3 zstd_version=1.5.5 download () { diff --git a/util/buildbot/download_toolchain.sh b/util/buildbot/download_toolchain.sh index f6283979e..e28f8dc27 100755 --- a/util/buildbot/download_toolchain.sh +++ b/util/buildbot/download_toolchain.sh @@ -6,8 +6,11 @@ if [[ -z "$1" || -z "$2" ]]; then exit 1 fi -ver=11.2.0 -os=ubuntu20.04 +# our current toolchain: +# binutils 2.41 + GCC 13.2.0 + Mingw-w64 11.0.1 with UCRT enabled and winpthreads support +# built from source on Ubuntu 22.04, so should work on any similarily up-to-date distro +ver=13.2.0 +os=ubuntu22.04 name="mingw-w64-${1}_${ver}_${os}.tar.xz" wget "http://minetest.kitsunemimi.pw/$name" -O "$name" sha256sum -w -c <(grep -F "$name" "$topdir/sha256sums.txt") diff --git a/util/buildbot/sha256sums.txt b/util/buildbot/sha256sums.txt index 30cc44716..3811513be 100644 --- a/util/buildbot/sha256sums.txt +++ b/util/buildbot/sha256sums.txt @@ -1,26 +1,26 @@ -e85ac83dd884a6716fdf048af1d8a4978ff4aa925471148f88c7c3430a9e2b05 curl-8.0.1-win32.zip -babed7c963bba7867412d9a905d5732a9cefefed401e05194903be08801c2182 curl-8.0.1-win64.zip -d6d7b0cf4d547f38ed54b0ff60a4b302b4184bd69fb580fc998410d87244939e freetype-2.12.1-win32.zip -dc6ecbb67b59a0662f21ca49a84fbf4bb0af9fedbc621983c4c0832fbddcc845 freetype-2.12.1-win64.zip -59236564f072baddac3e123927c50856d3147698cdd07fd08308a0eceb6f8679 gettext-0.20.2-win32.zip -4b11cc8211a6408b41d2342f927200f8f01b305cbcf9fb2398b5e7d204c42918 gettext-0.20.2-win64.zip -be02f4c0afc9e713d23182ec7782102a2d6c62fb4c2566832cdba0541437efc4 irrlicht-1.9.0mt13-win32.zip -6dca6dc23f6511da585874f3ec7bbda06e75d9de96c5a272e23a11a46b143262 irrlicht-1.9.0mt13-win64.zip -429e23c9a4a425c04db5b17e1361d4ce9ddd588e8fda062f5c485db1657e1b53 leveldb-1.23-win32.zip -82121b595d39b8e63c524e04c6ff247a904a76c278c0f42936e2bca63ff910a0 leveldb-1.23-win64.zip -23b467388aaa6e1a437b189dd54ecc7de7e47a734972d69abad874c558caa33c libogg-1.3.5-win32.zip -55f11b1fb26be5bc31e16fb282b0481938f306d0d4351048d82121cae1f0a254 libogg-1.3.5-win64.zip -d3f2de1d6f14c937bbcccf82f09ba583b3ad6925d30f6cddc096fcaa80ab3c03 libvorbis-1.3.7-win32.zip -b7722da388b17fdc216c00c97d9306b71c06dc49b173d43ef783fe0511739f2c libvorbis-1.3.7-win64.zip -79b9c3a9fc83a006721f1c78482729578d87f72b15c5440d28ba8f054655466b luajit-20230221-win32.zip -22f0f0ca95ff142fcd57237fa3e34797bb0f0d6789bf078b9c50941a9e4b739a luajit-20230221-win64.zip -704817351dc54a5a4bb3b35db9316f4ff1b073b231b5f8dbbc3b4ff2f3e30fbe mingw-w64-i686_11.2.0_ubuntu20.04.tar.xz -d85ec9a7debe470ebeaa002af0a2843b83d40405d2a45fcc586c19f179362aab mingw-w64-x86_64_11.2.0_ubuntu20.04.tar.xz -e4385c73c497ae90f28199002035fb369c7fa94ac93676949f92a1539a607941 openal-soft-1.23.0-win32.zip -643cdac87f0f1b7a06266df233e8d21119839b8fa3b9d6b4274b51e075a3ac11 openal-soft-1.23.0-win64.zip -d595396b60f31fe53d2b598c1677d633e407c175585a676bfc0d13d6cc239336 sqlite3-3.41.2-win32.zip -9aabc9545c1a7d39956c51e84239adbd05283218f451a3a846b080b713ad7661 sqlite3-3.41.2-win64.zip -e9bab0a6fe07bcf6c5a8ff171dd63983e67f3aefd9b8f38e88bf20a3dc44678f zlib-1.2.13-win32.zip -9f3d4fd89958081917d2fdcaab1bbc947e3fb070d8b39a48d9cf11269dd52c24 zlib-1.2.13-win64.zip -4ccbc8ac5830b06362d495ca65cd4687a76514bd103b5506b54a4b62ce0402f7 zstd-1.5.5-win32.zip -20dac9cedbaba40de5a5c0608a62d1885cbf63d055cca619289081e14d32290d zstd-1.5.5-win64.zip +c6759580175dee6c3673bb0544f0aca855f76b415b441db2b949fe9e2af4e6ee curl-8.5.0-win32.zip +a99ebdccad524f3738fa3a6a9d1dcabc39cb668f97790638d77b4bb96ea3edca curl-8.5.0-win64.zip +d70c9886526513a2c8a7962815fb425f296ab934239470a03ea350944169a7ac freetype-2.13.2-win32.zip +06aa20c71724e832874baa296d047aa866db2c336e26aa49e4faa72e559414a6 freetype-2.13.2-win64.zip +41b10766de2773f0f0851fde16b363024685e0397f4bb2e5cd2a7be196960a01 gettext-0.20.2-win32.zip +1ceed167ff16fea944f76ab6ea2969160c71a67419259b17c9c523e7a01eb883 gettext-0.20.2-win64.zip +15d09d259b62ce0b5d7582a26360f60bb99784c5b223364b6f21dc9d99844d15 irrlicht-1.9.0mt13-win32.zip +ddfa8d61ac5404202588c0793842f49b0198d8e87b963fe196cca0781a3099ca irrlicht-1.9.0mt13-win64.zip +6d49348215916ff355187fec808d0847450f70e45fe2719f45af9eb61c047358 leveldb-1.23-win32.zip +30c680277320bdda130b238d0adc30c3c59e7522dc008d677893ebfaea22f28b leveldb-1.23-win64.zip +d58b67954f3f552fba5e315ed476c38b230d0cf53445fe07dc733e72f8ba7dc2 libogg-1.3.5-win32.zip +2083cceb79b648cd500afe8b71c56170481f309cb6abd950195cdd13570e03dd libogg-1.3.5-win64.zip +1ce1c71e1dfdd99f47c93614a521ec0797d8fb55fb3fc07b67937ea7c6f76cca libvorbis-1.3.7-win32.zip +1c6fe4aa1c38079f2917e17e6b5acd7505331236c426e3b86054efccec6cee1c libvorbis-1.3.7-win64.zip +1c9b9580d869ee57b8c30a083d0e9a737310c1bb5e376b05fba483bac99eb2e1 luajit-20231211-win32.zip +3b42a31887ad7901f83a9f5b5faa4745ce95c7e95a7d8fd569d603fc95573ea5 luajit-20231211-win64.zip +9f0cfab8ca089d48be7a59f85d5fd5648f18f54c91d7ac6c31b281ba5e90852a mingw-w64-i686_13.2.0_ubuntu22.04.tar.xz +93bc9f04d43a023358d1ae2b76dec42d3d79baecd452402ee9fb3ee21945fdfe mingw-w64-x86_64_13.2.0_ubuntu22.04.tar.xz +34c4e6826a8e0dc4f7a49f7e4e4d54676f89a20fe781bad876795b857f7c5395 openal-soft-1.23.1-win32.zip +4b9c9a7f42aa8f7e6d26347ad61c55a32a2b11e4f02b8562542bcd132b0c7115 openal-soft-1.23.1-win64.zip +082dfee313c7e29e48ff798503acb286a4542c315618d5d3b33fc2bbed4170a5 sqlite3-3.44.2-win32.zip +e8fda50178f1371c52f85ac19a0998d797ad6b2439f1da87c49a1f44ba33649c sqlite3-3.44.2-win64.zip +3c5abd40e9492c834651d995db6bbf0f57a7579d091d2d03110293b95e9b039a zlib-1.3-win32.zip +f63d9a38c2ee56fa1e95a486224c274412cb5b3275734c1da53b0a68a7e8c654 zlib-1.3-win64.zip +7508d714dbed4e1b1340cfb13ea77ef631746dad99ac97434171f2f4dd64d94b zstd-1.5.5-win32.zip +30353afddb459974c4e90c4eb3fbf975951247cf310fa5f40208806e275776fa zstd-1.5.5-win64.zip -- 2.46.0 From b1aec1b5c8367c222ef03c63e60f52f3882abcea Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Tue, 19 Dec 2023 20:18:11 +0100 Subject: [PATCH 25/85] Add dithering (#9014) --- builtin/settingtypes.txt | 11 ++++++++ .../shaders/second_stage/opengl_fragment.glsl | 26 +++++++++++++++++++ src/client/shader.cpp | 3 +++ src/defaultsettings.cpp | 2 ++ 4 files changed, 42 insertions(+) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 93abd3575..ec00b3b89 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -580,6 +580,17 @@ enable_auto_exposure (Enable Automatic Exposure) bool false # Requires: shaders, enable_auto_exposure exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0 +# Apply dithering to reduce color banding artifacts. +# Dithering significantly increases the size of losslessly-compressed +# screenshots and it works incorrectly if the display or operating system +# performs additional dithering or if the color channels are not quantized +# to 8 bits. +# With OpenGL ES, dithering only works if the shader supports high +# floating-point precision and it may have a higher performance impact. +# +# Requires: shaders +debanding (Enable Debanding) bool true + [**Bloom] # Set to true to enable bloom effect. diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl index 928e408e2..973cfc424 100644 --- a/client/shaders/second_stage/opengl_fragment.glsl +++ b/client/shaders/second_stage/opengl_fragment.glsl @@ -1,6 +1,13 @@ #define rendered texture0 #define bloom texture1 +#ifdef GL_ES +// Dithering requires sufficient floating-point precision +#ifndef GL_FRAGMENT_PRECISION_HIGH +#undef ENABLE_DITHERING +#endif +#endif + struct ExposureParams { float compensationFactor; }; @@ -79,6 +86,20 @@ vec3 applySaturation(vec3 color, float factor) } #endif +#ifdef ENABLE_DITHERING +// From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf +// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom) +// NOTE: `frag_coord` is in pixels (i.e. not normalized UV). +vec3 screen_space_dither(highp vec2 frag_coord) { + // Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR. + highp vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord)); + dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0)); + + // Subtract 0.5 to avoid slightly brightening the whole viewport. + return (dither.rgb - 0.5) / 255.0; +} +#endif + void main(void) { vec2 uv = varTexCoord.st; @@ -125,5 +146,10 @@ void main(void) // return to sRGB colorspace (approximate) color.rgb = pow(color.rgb, vec3(1.0 / 2.2)); +#ifdef ENABLE_DITHERING + // Apply dithering just before quantisation + color.rgb += screen_space_dither(gl_FragCoord.xy); +#endif + gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image. } diff --git a/src/client/shader.cpp b/src/client/shader.cpp index 0fcdebf60..3e6e67e45 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -767,6 +767,9 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, shaders_header << "#define SSAA_SCALE " << ssaa_scale << ".\n"; } + if (g_settings->getBool("debanding")) + shaders_header << "#define ENABLE_DITHERING 1\n"; + shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics std::string common_header = shaders_header.str(); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 6a48a9b40..d61a91d40 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -264,6 +264,7 @@ void set_default_settings() settings->setDefault("enable_waving_plants", "false"); settings->setDefault("exposure_compensation", "0.0"); settings->setDefault("enable_auto_exposure", "false"); + settings->setDefault("debanding", "true"); settings->setDefault("antialiasing", "none"); settings->setDefault("enable_bloom", "false"); settings->setDefault("enable_bloom_debug", "false"); @@ -499,6 +500,7 @@ void set_default_settings() settings->setDefault("active_block_range", "2"); settings->setDefault("viewing_range", "50"); settings->setDefault("leaves_style", "simple"); + settings->setDefault("debanding", "false"); settings->setDefault("curl_verify_cert", "false"); // Apply settings according to screen size -- 2.46.0 From 00d9d96e48ec3337c415dbc600b0b0800eaecfad Mon Sep 17 00:00:00 2001 From: grorp Date: Tue, 19 Dec 2023 20:18:28 +0100 Subject: [PATCH 26/85] Android: Pause rendering while the app is paused (#14058) --- src/client/game.cpp | 63 +++++++++++++++++++----------------- src/client/renderingengine.h | 11 +++++++ src/gui/guiEngine.cpp | 53 +++++++++++++++--------------- 3 files changed, 72 insertions(+), 55 deletions(-) diff --git a/src/client/game.cpp b/src/client/game.cpp index f7d31bc3b..8712f1aa8 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -4010,31 +4010,6 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, */ client->getParticleManager()->step(dtime); - /* - Fog - */ - if (m_cache_enable_fog) { - driver->setFog( - sky->getBgColor(), - video::EFT_FOG_LINEAR, - runData.fog_range * sky->getFogStart(), - runData.fog_range * 1.0, - 0.01, - false, // pixel fog - true // range fog - ); - } else { - driver->setFog( - sky->getBgColor(), - video::EFT_FOG_LINEAR, - 100000 * BS, - 110000 * BS, - 0.01f, - false, // pixel fog - false // range fog - ); - } - /* Damage camera tilt */ @@ -4134,7 +4109,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* ==================== Drawing begins ==================== */ - drawScene(graph, stats); + if (RenderingEngine::shouldRender()) + drawScene(graph, stats); /* ==================== End scene ==================== */ @@ -4214,10 +4190,39 @@ void Game::updateShadows() void Game::drawScene(ProfilerGraph *graph, RunStats *stats) { - const video::SColor skycolor = this->sky->getSkyColor(); + const video::SColor bg_color = this->sky->getBgColor(); + const video::SColor sky_color = this->sky->getSkyColor(); + /* + Fog + */ + if (this->m_cache_enable_fog) { + this->driver->setFog( + bg_color, + video::EFT_FOG_LINEAR, + this->runData.fog_range * this->sky->getFogStart(), + this->runData.fog_range * 1.0f, + 0.01f, + false, // pixel fog + true // range fog + ); + } else { + this->driver->setFog( + bg_color, + video::EFT_FOG_LINEAR, + 100000 * BS, + 110000 * BS, + 0.01f, + false, // pixel fog + false // range fog + ); + } + + /* + Drawing + */ TimeTaker tt_draw("Draw scene", nullptr, PRECISION_MICRO); - this->driver->beginScene(true, true, skycolor); + this->driver->beginScene(true, true, sky_color); const LocalPlayer *player = this->client->getEnv().getLocalPlayer(); bool draw_wield_tool = (this->m_game_ui->m_flags.show_hud && @@ -4230,7 +4235,7 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats) if (this->isNoCrosshairAllowed()) draw_crosshair = false; #endif - this->m_rendering_engine->draw_scene(skycolor, this->m_game_ui->m_flags.show_hud, + this->m_rendering_engine->draw_scene(sky_color, this->m_game_ui->m_flags.show_hud, this->m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair); /* diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 22b8a3946..801649ad4 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -138,6 +138,17 @@ public: const irr::core::dimension2d initial_screen_size, const bool initial_window_maximized); + static bool shouldRender() + { + // On Android, pause rendering while the app is in background (generally not visible). + // Don't do this on desktop because windows can be partially visible. +#ifdef __ANDROID__ + return get_raw_device()->isWindowActive(); +#else + return true; +#endif + }; + private: v2u32 _getWindowSize() const; diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 7bdc067ac..4bb7b041f 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -265,35 +265,36 @@ void GUIEngine::run() f32 dtime = 0.0f; while (m_rendering_engine->run() && (!m_startgame) && (!m_kill)) { + if (RenderingEngine::shouldRender()) { + // check if we need to update the "upper left corner"-text + if (text_height != g_fontengine->getTextHeight()) { + updateTopLeftTextSize(); + text_height = g_fontengine->getTextHeight(); + } - //check if we need to update the "upper left corner"-text - if (text_height != g_fontengine->getTextHeight()) { - updateTopLeftTextSize(); - text_height = g_fontengine->getTextHeight(); + driver->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR); + + if (m_clouds_enabled) + { + cloudPreProcess(); + drawOverlay(driver); + } + else + drawBackground(driver); + + drawFooter(driver); + + m_rendering_engine->get_gui_env()->drawAll(); + + // The header *must* be drawn after the menu because it uses + // GUIFormspecMenu::getAbsoluteRect(). + // The header *can* be drawn after the menu because it never intersects + // the menu. + drawHeader(driver); + + driver->endScene(); } - driver->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR); - - if (m_clouds_enabled) - { - cloudPreProcess(); - drawOverlay(driver); - } - else - drawBackground(driver); - - drawFooter(driver); - - m_rendering_engine->get_gui_env()->drawAll(); - - // The header *must* be drawn after the menu because it uses - // GUIFormspecMenu::getAbsoluteRect(). - // The header *can* be drawn after the menu because it never intersects - // the menu. - drawHeader(driver); - - driver->endScene(); - IrrlichtDevice *device = m_rendering_engine->get_raw_device(); u32 frametime_min = 1000 / (device->isWindowFocused() -- 2.46.0 From 61d0f613df643f7f3c552ff9eacaebdfbc68bc4c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 19 Dec 2023 20:18:43 +0100 Subject: [PATCH 27/85] Hand roll UTF-16 conversion in CGUITTFont (#14121) --- src/irrlicht_changes/CGUITTFont.cpp | 36 ++++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/irrlicht_changes/CGUITTFont.cpp b/src/irrlicht_changes/CGUITTFont.cpp index 298cfbf2c..7e2ea8186 100644 --- a/src/irrlicht_changes/CGUITTFont.cpp +++ b/src/irrlicht_changes/CGUITTFont.cpp @@ -32,8 +32,6 @@ #include #include -#include -#include #include "CGUITTFont.h" namespace irr @@ -1251,21 +1249,31 @@ std::u32string CGUITTFont::convertWCharToU32String(const wchar_t* const charArra { static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "unexpected wchar size"); - if (sizeof(wchar_t) == 4) // Systems where wchar_t is UTF-32 + if (sizeof(wchar_t) == 4) // wchar_t is UTF-32 return std::u32string(reinterpret_cast(charArray)); - // Systems where wchar_t is UTF-16: - // First, convert to UTF-8 - std::u16string utf16String(reinterpret_cast(charArray)); - std::wstring_convert, char16_t> converter1; - std::string utf8String = converter1.to_bytes(utf16String); + // wchar_t is UTF-16 and we need to convert. + // std::codecvt could do this for us but aside from being deprecated, + // it turns out that it's laughably slow on MSVC. Thanks Microsoft. - // Next, convert to UTF-32 - std::wstring_convert, char32_t> converter2; - return converter2.from_bytes(utf8String); - - // This is inefficient, but importantly it is _correct_, rather than a hand-rolled UTF-16 to - // UTF-32 converter which may or may not be correct. + std::u32string ret; + ret.reserve(wcslen(charArray)); + const wchar_t *p = charArray; + while (*p) { + char32_t c = *p; + if (c >= 0xD800 && c < 0xDC00) { + p++; + char32_t c2 = *p; + if (!c2) + break; + else if (c2 < 0xDC00 || c2 > 0xDFFF) + continue; // can't find low surrogate, skip + c = 0x10000 + ( ((c & 0x3ff) << 10) | (c2 & 0x3ff) ); + } + ret.push_back(c); + p++; + } + return ret; } -- 2.46.0 From 0d61598d8aef6466fe232e97bd0de1ed4e726a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Wed, 20 Dec 2023 21:21:53 +0100 Subject: [PATCH 28/85] Extend bone override capabilities (#12388) --- doc/lua_api.md | 37 +++++--- src/activeobject.h | 74 +++++++++++++++ src/client/content_cao.cpp | 75 ++++++++++++--- src/client/content_cao.h | 4 +- src/network/networkprotocol.h | 5 +- src/script/lua_api/l_object.cpp | 159 +++++++++++++++++++++++++++++--- src/script/lua_api/l_object.h | 9 ++ src/server/luaentity_sao.cpp | 10 +- src/server/player_sao.cpp | 12 +-- src/server/serveractiveobject.h | 11 ++- src/server/unit_sao.cpp | 45 +++++---- src/server/unit_sao.h | 14 +-- 12 files changed, 375 insertions(+), 80 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 4b03db52c..67d9cf604 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -7556,17 +7556,32 @@ child will follow movement and rotation of that bone. object. * `set_detach()`: Detaches object. No-op if object was not attached. * `set_bone_position([bone, position, rotation])` - * `bone`: string. Default is `""`, the root bone - * `position`: `{x=num, y=num, z=num}`, relative, `default {x=0, y=0, z=0}` - * `rotation`: `{x=num, y=num, z=num}`, default `{x=0, y=0, z=0}` -* `get_bone_position(bone)`: - * returns bone parameters previously set by `set_bone_position` - * returns `position, rotation` of the specified bone (as vectors) - * note: position is relative to the object -* `set_properties(object property table)`: - * set a number of object properties in the given table - * only properties listed in the table will be changed - * see the 'Object properties' section for details + * Shorthand for `set_bone_override(bone, {position = position, rotation = rotation:apply(math.rad)})` using absolute values. + * **Note:** Rotation is in degrees, not radians. + * **Deprecated:** Use `set_bone_override` instead. +* `get_bone_position(bone)`: returns the previously set position and rotation of the bone + * Shorthand for `get_bone_override(bone).position.vec, get_bone_override(bone).rotation.vec:apply(math.deg)`. + * **Note:** Returned rotation is in degrees, not radians. + * **Deprecated:** Use `get_bone_override` instead. +* `set_bone_override(bone, override)` + * `bone`: string + * `override`: `{ position = property, rotation = property, scale = property }` or `nil` + * `property`: `{ vec = vector, interpolation = 0, absolute = false}` or `nil`; + * `vec` is in the same coordinate system as the model, and in degrees for rotation + * `property = nil` is equivalent to no override on that property + * `absolute`: If set to `false`, the override will be relative to the animated property: + * Transposition in the case of `position`; + * Composition in the case of `rotation`; + * Multiplication in the case of `scale` + * `interpolation`: Old and new values are interpolated over this timeframe (in seconds) + * `override = nil` (including omission) is shorthand for `override = {}` which clears the override + * **Note:** Unlike `set_bone_position`, the rotation is in radians, not degrees. + * Compatibility note: Clients prior to 5.9.0 only support absolute position and rotation. + All values are treated as absolute and are set immediately (no interpolation). +* `get_bone_override(bone)`: returns `override` in the above format + * **Note:** Unlike `get_bone_position`, the returned rotation is in radians, not degrees. +* `get_bone_overrides()`: returns all bone overrides as table `{[bonename] = override, ...}` +* `set_properties(object property table)` * `get_properties()`: returns a table of all object properties * `is_player()`: returns true for players, false otherwise * `get_nametag_attributes()` diff --git a/src/activeobject.h b/src/activeobject.h index 1d8a3712b..02c16b557 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -21,7 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_aabb3d.h" #include "irr_v3d.h" +#include #include +#include enum ActiveObjectType { @@ -72,6 +74,78 @@ enum ActiveObjectCommand { AO_CMD_SET_ANIMATION_SPEED }; +struct BoneOverride +{ + struct PositionProperty + { + v3f previous; + v3f vector; + bool absolute = false; + f32 interp_timer = 0; + } position; + + v3f getPosition(v3f anim_pos) const { + f32 progress = dtime_passed / position.interp_timer; + if (progress > 1.0f || position.interp_timer == 0.0f) + progress = 1.0f; + return position.vector.getInterpolated(position.previous, progress) + + (position.absolute ? v3f() : anim_pos); + } + + struct RotationProperty + { + core::quaternion previous; + core::quaternion next; + bool absolute = false; + f32 interp_timer = 0; + } rotation; + + v3f getRotationEulerDeg(v3f anim_rot_euler) const { + core::quaternion rot; + + f32 progress = dtime_passed / rotation.interp_timer; + if (progress > 1.0f || rotation.interp_timer == 0.0f) + progress = 1.0f; + rot.slerp(rotation.previous, rotation.next, progress); + if (!rotation.absolute) { + core::quaternion anim_rot(anim_rot_euler * core::DEGTORAD); + rot = rot * anim_rot; // first rotate by anim. bone rot., then rot. + } + + v3f rot_euler; + rot.toEuler(rot_euler); + return rot_euler * core::RADTODEG; + } + + struct ScaleProperty + { + v3f previous; + v3f vector{1, 1, 1}; + bool absolute = false; + f32 interp_timer = 0; + } scale; + + v3f getScale(v3f anim_scale) const { + f32 progress = dtime_passed / scale.interp_timer; + if (progress > 1.0f || scale.interp_timer == 0.0f) + progress = 1.0f; + return scale.vector.getInterpolated(scale.previous, progress) + * (scale.absolute ? v3f(1) : anim_scale); + } + + f32 dtime_passed = 0; + + bool isIdentity() const + { + return !position.absolute && position.vector == v3f() + && !rotation.absolute && rotation.next == core::quaternion() + && !scale.absolute && scale.vector == v3f(1); + } +}; + +typedef std::unordered_map BoneOverrideMap; + + /* Parent class for ServerActiveObject and ClientActiveObject */ diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 1c66062e0..d3d2285dd 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "client/shader.h" #include "client/minimap.h" +#include class Settings; struct ToolCapabilities; @@ -828,7 +829,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) updateMarker(); updateNodePos(); updateAnimation(); - updateBonePosition(); + updateBones(.0f); updateAttachments(); setNodeLight(m_last_light); updateMeshCulling(); @@ -1246,7 +1247,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) updatePositionRecursive(m_matrixnode); m_animated_meshnode->updateAbsolutePosition(); m_animated_meshnode->animateJoints(); - updateBonePosition(); + updateBones(dtime); } } @@ -1527,19 +1528,28 @@ void GenericCAO::updateAnimationSpeed() m_animated_meshnode->setAnimationSpeed(m_animation_speed); } -void GenericCAO::updateBonePosition() +void GenericCAO::updateBones(f32 dtime) { - if (m_bone_position.empty() || !m_animated_meshnode) + if (!m_animated_meshnode) return; + if (m_bone_override.empty()) { + m_animated_meshnode->setJointMode(scene::EJUOR_NONE); + return; + } m_animated_meshnode->setJointMode(scene::EJUOR_CONTROL); // To write positions to the mesh on render - for (auto &it : m_bone_position) { + for (auto &it : m_bone_override) { std::string bone_name = it.first; scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str()); - if (bone) { - bone->setPosition(it.second.X); - bone->setRotation(it.second.Y); - } + if (!bone) + continue; + + BoneOverride &props = it.second; + props.dtime_passed += dtime; + + bone->setPosition(props.getPosition(bone->getPosition())); + bone->setRotation(props.getRotationEulerDeg(bone->getRotation())); + bone->setScale(props.getScale(bone->getScale())); } // search through bones to find mistakenly rotated bones due to bug in Irrlicht @@ -1550,7 +1560,7 @@ void GenericCAO::updateBonePosition() //If bone is manually positioned there is no need to perform the bug check bool skip = false; - for (auto &it : m_bone_position) { + for (auto &it : m_bone_override) { if (it.first == bone->getName()) { skip = true; break; @@ -1852,11 +1862,46 @@ void GenericCAO::processMessage(const std::string &data) updateAnimationSpeed(); } else if (cmd == AO_CMD_SET_BONE_POSITION) { std::string bone = deSerializeString16(is); - v3f position = readV3F32(is); - v3f rotation = readV3F32(is); - m_bone_position[bone] = core::vector2d(position, rotation); - - // updateBonePosition(); now called every step + auto it = m_bone_override.find(bone); + BoneOverride props; + if (it != m_bone_override.end()) { + props = it->second; + // Reset timer + props.dtime_passed = 0; + // Save previous values for interpolation + props.position.previous = props.position.vector; + props.rotation.previous = props.rotation.next; + props.scale.previous = props.scale.vector; + } else { + // Disable interpolation + props.position.interp_timer = 0.0f; + props.rotation.interp_timer = 0.0f; + props.scale.interp_timer = 0.0f; + } + // Read new values + props.position.vector = readV3F32(is); + props.rotation.next = core::quaternion(readV3F32(is) * core::DEGTORAD); + props.scale.vector = readV3F32(is); // reads past end of string on older cmds + if (is.eof()) { + // Backwards compatibility + props.scale.vector = v3f(1, 1, 1); // restore the scale which was not sent + props.position.absolute = true; + props.rotation.absolute = true; + } else { + props.position.interp_timer = readF32(is); + props.rotation.interp_timer = readF32(is); + props.scale.interp_timer = readF32(is); + u8 absoluteFlag = readU8(is); + props.position.absolute = (absoluteFlag & 1) > 0; + props.rotation.absolute = (absoluteFlag & 2) > 0; + props.scale.absolute = (absoluteFlag & 4) > 0; + } + if (props.isIdentity()) { + m_bone_override.erase(bone); + } else { + m_bone_override[bone] = props; + } + // updateBones(); now called every step } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); std::string bone = deSerializeString16(is); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index b090cdc08..05cce5bcf 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -104,7 +104,7 @@ private: float m_animation_blend = 0.0f; bool m_animation_loop = true; // stores position and rotation for each bone name - std::unordered_map> m_bone_position; + BoneOverrideMap m_bone_override; int m_attachment_parent_id = 0; std::unordered_set m_attachment_child_ids; @@ -267,7 +267,7 @@ public: void updateAnimationSpeed(); - void updateBonePosition(); + void updateBones(f32 dtime); void processMessage(const std::string &data) override; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 5b9ead992..0ededba38 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -219,9 +219,12 @@ with this program; if not, write to the Free Software Foundation, Inc., "start_time" added to TOCLIENT_PLAY_SOUND place_param2 type change u8 -> optional [scheduled bump for 5.8.0] + PROTOCOL VERSION 44: + AO_CMD_SET_BONE_POSITION extended + [scheduled bump for 5.9.0] */ -#define LATEST_PROTOCOL_VERSION 43 +#define LATEST_PROTOCOL_VERSION 44 #define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION) // Server's supported network protocol range diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 3815f815f..a0e8f69a5 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server/luaentity_sao.h" #include "server/player_sao.h" #include "server/serverinventorymgr.h" +#include "server/unit_sao.h" /* ObjectRef @@ -518,16 +519,25 @@ int ObjectRef::l_set_animation_frame_speed(lua_State *L) int ObjectRef::l_set_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; + + log_deprecated(L,"Deprecated call to set_bone_position, use set_bone_override instead"); + ObjectRef *ref = checkObject(L, 1); ServerActiveObject *sao = getobject(ref); if (sao == nullptr) return 0; - std::string bone = readParam(L, 2, ""); - v3f position = readParam(L, 3, v3f(0, 0, 0)); - v3f rotation = readParam(L, 4, v3f(0, 0, 0)); - - sao->setBonePosition(bone, position, rotation); + std::string bone; + if (!lua_isnil(L, 2)) + bone = readParam(L, 2); + BoneOverride props; + if (!lua_isnil(L, 3)) + props.position.vector = check_v3f(L, 3); + if (!lua_isnil(L, 4)) + props.rotation.next = core::quaternion(check_v3f(L, 4) * core::DEGTORAD); + props.position.absolute = true; + props.rotation.absolute = true; + sao->setBoneOverride(bone, props); return 0; } @@ -535,22 +545,144 @@ int ObjectRef::l_set_bone_position(lua_State *L) int ObjectRef::l_get_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; + + log_deprecated(L,"Deprecated call to get_bone_position, use get_bone_override instead"); + ObjectRef *ref = checkObject(L, 1); ServerActiveObject *sao = getobject(ref); if (sao == nullptr) return 0; std::string bone = readParam(L, 2, ""); - - v3f position = v3f(0, 0, 0); - v3f rotation = v3f(0, 0, 0); - sao->getBonePosition(bone, &position, &rotation); - - push_v3f(L, position); - push_v3f(L, rotation); + BoneOverride props = sao->getBoneOverride(bone); + push_v3f(L, props.position.vector); + v3f euler_rot; + props.rotation.next.toEuler(euler_rot); + push_v3f(L, euler_rot * core::RADTODEG); return 2; } +// set_bone_override(self, bone, override) +int ObjectRef::l_set_bone_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == NULL) + return 0; + + std::string bone = readParam(L, 2); + + BoneOverride props; + if (lua_isnoneornil(L, 3)) { + sao->setBoneOverride(bone, props); + return 0; + } + + auto read_prop_attrs = [L](auto &prop) { + lua_getfield(L, -1, "absolute"); + prop.absolute = lua_toboolean(L, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "interpolate"); + if (lua_isnumber(L, -1)) + prop.interp_timer = lua_tonumber(L, -1); + lua_pop(L, 1); + }; + + lua_getfield(L, 3, "position"); + if (!lua_isnil(L, -1)) { + lua_getfield(L, -1, "vec"); + if (!lua_isnil(L, -1)) + props.position.vector = check_v3f(L, -1); + lua_pop(L, 1); + + read_prop_attrs(props.position); + } + lua_pop(L, 1); + + lua_getfield(L, 3, "rotation"); + if (!lua_isnil(L, -1)) { + lua_getfield(L, -1, "vec"); + if (!lua_isnil(L, -1)) + props.rotation.next = core::quaternion(check_v3f(L, -1)); + lua_pop(L, 1); + + read_prop_attrs(props.rotation); + } + lua_pop(L, 1); + + lua_getfield(L, 3, "scale"); + if (!lua_isnil(L, -1)) { + lua_getfield(L, -1, "vec"); + props.scale.vector = lua_isnil(L, -1) ? v3f(1) : check_v3f(L, -1); + lua_pop(L, 1); + + read_prop_attrs(props.scale); + } + lua_pop(L, 1); + + sao->setBoneOverride(bone, props); + return 0; +} + +static void push_bone_override(lua_State *L, const BoneOverride &props) +{ + lua_newtable(L); + + auto push_prop = [L](const char *name, const auto &prop, v3f vec) { + lua_newtable(L); + push_v3f(L, vec); + lua_setfield(L, -2, "vec"); + lua_pushnumber(L, prop.interp_timer); + lua_setfield(L, -2, "interpolate"); + lua_pushboolean(L, prop.absolute); + lua_setfield(L, -2, "absolute"); + lua_setfield(L, -2, name); + }; + + push_prop("position", props.position, props.position.vector); + + v3f euler_rot; + props.rotation.next.toEuler(euler_rot); + push_prop("rotation", props.rotation, euler_rot); + + push_prop("scale", props.scale, props.scale.vector); + + // leave only override table on top of the stack +} + +// get_bone_override(self, bone) +int ObjectRef::l_get_bone_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == NULL) + return 0; + + std::string bone = readParam(L, 2); + + push_bone_override(L, sao->getBoneOverride(bone)); + return 1; +} + +// get_bone_overrides(self) +int ObjectRef::l_get_bone_overrides(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + ServerActiveObject *co = getobject(ref); + if (co == NULL) + return 0; + lua_newtable(L); + for (const auto &bone_pos : co->getBoneOverrides()) { + push_bone_override(L, bone_pos.second); + lua_setfield(L, -2, bone_pos.first.c_str()); + } + return 1; +} + // set_attach(self, parent, bone, position, rotation, force_visible) int ObjectRef::l_set_attach(lua_State *L) { @@ -2471,6 +2603,9 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_animation_frame_speed), luamethod(ObjectRef, set_bone_position), luamethod(ObjectRef, get_bone_position), + luamethod(ObjectRef, set_bone_override), + luamethod(ObjectRef, get_bone_override), + luamethod(ObjectRef, get_bone_overrides), luamethod(ObjectRef, set_attach), luamethod(ObjectRef, get_attach), luamethod(ObjectRef, get_children), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index cbbe4f921..4465a823f 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -130,6 +130,15 @@ private: // get_bone_position(self, bone) static int l_get_bone_position(lua_State *L); + // set_bone_override(self, bone) + static int l_set_bone_override(lua_State *L); + + // get_bone_override(self, bone) + static int l_get_bone_override(lua_State *L); + + // get_bone_override(self) + static int l_get_bone_overrides(lua_State *L); + // set_attach(self, parent, bone, position, rotation) static int l_set_attach(lua_State *L); diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index f18318f4c..e4c392eb2 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -256,13 +256,13 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) msg_os << serializeString32(getPropertyPacket()); // message 1 msg_os << serializeString32(generateUpdateArmorGroupsCommand()); // 2 msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 - for (const auto &bone_pos : m_bone_position) { - msg_os << serializeString32(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N + for (const auto &bone_override : m_bone_override) { + msg_os << serializeString32(generateUpdateBoneOverrideCommand( + bone_override.first, bone_override.second)); // 3 + N } - msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size + msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_override.size - int message_count = 4 + m_bone_position.size(); + int message_count = 4 + m_bone_override.size(); for (const auto &id : getAttachmentChildIds()) { if (ServerActiveObject *obj = m_env->getActiveObject(id)) { diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 158bb8c64..681841f23 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -121,14 +121,14 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) msg_os << serializeString32(getPropertyPacket()); // message 1 msg_os << serializeString32(generateUpdateArmorGroupsCommand()); // 2 msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 - for (const auto &bone_pos : m_bone_position) { - msg_os << serializeString32(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N + for (const auto &it : m_bone_override) { + msg_os << serializeString32(generateUpdateBoneOverrideCommand( + it.first, it.second)); // 3 + N } - msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size - msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + m_bone_position.size + msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_override.size + msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + m_bone_override.size - int message_count = 5 + m_bone_position.size(); + int message_count = 5 + m_bone_override.size(); for (const auto &id : getAttachmentChildIds()) { if (ServerActiveObject *obj = m_env->getActiveObject(id)) { diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index e9cf3ee37..a42096f7b 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" #include "util/container.h" + /* Some planning @@ -167,14 +168,16 @@ public: {} virtual void setAnimationSpeed(float frame_speed) {} - virtual void setBonePosition(const std::string &bone, v3f position, v3f rotation) - {} - virtual void getBonePosition(const std::string &bone, v3f *position, v3f *lotation) + virtual void setBoneOverride(const std::string &bone, const BoneOverride &props) {} + virtual BoneOverride getBoneOverride(const std::string &bone) + { BoneOverride props; return props; } + virtual const BoneOverrideMap &getBoneOverrides() const + { static BoneOverrideMap rv; return rv; } virtual const std::unordered_set &getAttachmentChildIds() const { static std::unordered_set rv; return rv; } virtual ServerActiveObject *getParent() const { return nullptr; } - virtual ObjectProperties* accessObjectProperties() + virtual ObjectProperties *accessObjectProperties() { return NULL; } virtual void notifyObjectPropertiesModified() {} diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index c928b830f..fd44ae323 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -76,20 +76,20 @@ void UnitSAO::setAnimationSpeed(float frame_speed) m_animation_speed_sent = false; } -void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation) +void UnitSAO::setBoneOverride(const std::string &bone, const BoneOverride &props) { // store these so they can be updated to clients - m_bone_position[bone] = core::vector2d(position, rotation); - m_bone_position_sent = false; + m_bone_override[bone] = props; + m_bone_override_sent = false; } -void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) +BoneOverride UnitSAO::getBoneOverride(const std::string &bone) { - auto it = m_bone_position.find(bone); - if (it != m_bone_position.end()) { - *position = it->second.X; - *rotation = it->second.Y; - } + auto it = m_bone_override.find(bone); + BoneOverride props; + if (it != m_bone_override.end()) + props = it->second; + return props; } void UnitSAO::sendOutdatedData() @@ -109,11 +109,11 @@ void UnitSAO::sendOutdatedData() m_messages_out.emplace(getId(), true, generateUpdateAnimationSpeedCommand()); } - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (const auto &bone_pos : m_bone_position) { - m_messages_out.emplace(getId(), true, generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); + if (!m_bone_override_sent) { + m_bone_override_sent = true; + for (const auto &bone_pos : m_bone_override) { + m_messages_out.emplace(getId(), true, generateUpdateBoneOverrideCommand( + bone_pos.first, bone_pos.second)); } } @@ -275,16 +275,25 @@ std::string UnitSAO::generateUpdateAttachmentCommand() const return os.str(); } -std::string UnitSAO::generateUpdateBonePositionCommand( - const std::string &bone, const v3f &position, const v3f &rotation) +std::string UnitSAO::generateUpdateBoneOverrideCommand( + const std::string &bone, const BoneOverride &props) { std::ostringstream os(std::ios::binary); // command writeU8(os, AO_CMD_SET_BONE_POSITION); // parameters os << serializeString16(bone); - writeV3F32(os, position); - writeV3F32(os, rotation); + writeV3F32(os, props.position.vector); + v3f euler_rot; + props.rotation.next.toEuler(euler_rot); + writeV3F32(os, euler_rot * core::RADTODEG); + writeV3F32(os, props.scale.vector); + writeF32(os, props.position.interp_timer); + writeF32(os, props.rotation.interp_timer); + writeF32(os, props.scale.interp_timer); + writeU8(os, (props.position.absolute & 1) << 0 + | (props.rotation.absolute & 1) << 1 + | (props.scale.absolute & 1) << 2); return os.str(); } diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index dedb1874e..cbd845708 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -70,8 +70,10 @@ public: void setAnimationSpeed(float frame_speed); // Bone position - void setBonePosition(const std::string &bone, v3f position, v3f rotation); - void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); + void setBoneOverride(const std::string &bone, const BoneOverride &props); + BoneOverride getBoneOverride(const std::string &bone); + const std::unordered_map + &getBoneOverrides() const { return m_bone_override; }; // Attachments ServerActiveObject *getParent() const; @@ -100,8 +102,8 @@ public: const v3f &velocity, const v3f &acceleration, const v3f &rotation, bool do_interpolate, bool is_movement_end, f32 update_interval); std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; - static std::string generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation); + static std::string generateUpdateBoneOverrideCommand( + const std::string &bone, const BoneOverride &props); void sendPunchCommand(); protected: @@ -117,7 +119,7 @@ protected: ObjectProperties m_prop; // Stores position and rotation for each bone name - std::unordered_map> m_bone_position; + std::unordered_map m_bone_override; int m_attachment_parent_id = 0; @@ -139,7 +141,7 @@ private: bool m_animation_speed_sent = false; // Bone positions - bool m_bone_position_sent = false; + bool m_bone_override_sent = false; // Attachments std::unordered_set m_attachment_child_ids; -- 2.46.0 From 3b346fd3c916851af1d8bbee8f4bd0d9b2750938 Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Sun, 17 Dec 2023 09:56:25 +0100 Subject: [PATCH 29/85] Fix touch input on Linux The code relied on touch IDs being consecutive. This is true on Android, but not on Linux. Therefore, touch input on Linux was broken since 53886dcdb52de80d862539e22950c84fbf88df88. --- src/gui/modalMenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 2ccdf43af..d4322c9f7 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -273,7 +273,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) irr_ptr holder; holder.grab(this); // keep this alive until return (it might be dropped downstream [?]) - if (event.TouchInput.ID == 0) { + if (event.TouchInput.touchedCount == 1) { if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED) m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y); gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d(m_pointer)); @@ -290,7 +290,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) if (event.TouchInput.Event == ETIE_LEFT_UP) leave(); return ret; - } else if (event.TouchInput.ID == 1) { + } else if (event.TouchInput.touchedCount == 2) { if (event.TouchInput.Event != ETIE_LEFT_UP) return true; // ignore auto focused = Environment->getFocus(); -- 2.46.0 From 47e557b96ad4b9f053cf35150b2f46fe623b4dc7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 19 Dec 2023 20:19:39 +0100 Subject: [PATCH 30/85] Enable segment heap on Windows --- .github/workflows/build.yml | 4 ++++ misc/minetest.exe.manifest | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 77f3898ae..e67bcb7bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,8 @@ on: - 'cmake/Modules/**' - 'util/buildbot/**' - 'util/ci/**' + - 'misc/irrlichtmt_tag.txt' + - 'misc/*.manifest' - '.github/workflows/**.yml' - 'Dockerfile' - '.dockerignore' @@ -25,6 +27,8 @@ on: - 'cmake/Modules/**' - 'util/buildbot/**' - 'util/ci/**' + - 'misc/irrlichtmt_tag.txt' + - 'misc/*.manifest' - '.github/workflows/**.yml' - 'Dockerfile' - '.dockerignore' diff --git a/misc/minetest.exe.manifest b/misc/minetest.exe.manifest index dcad3fcde..ff5469250 100644 --- a/misc/minetest.exe.manifest +++ b/misc/minetest.exe.manifest @@ -12,6 +12,7 @@ true UTF-8 + SegmentHeap -- 2.46.0 From 04dc4a10f03c2f7d28ae59c772bc726a9b6856cc Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Tue, 19 Dec 2023 20:35:50 +0100 Subject: [PATCH 31/85] Fix TouchScreenGUI ignoring server-sent pitch changes --- src/client/game.cpp | 2 +- src/gui/touchscreengui.cpp | 2 +- src/gui/touchscreengui.h | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/client/game.cpp b/src/client/game.cpp index 8712f1aa8..6b5163616 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2617,7 +2617,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) #ifdef HAVE_TOUCHSCREENGUI if (g_touchscreengui) { cam->camera_yaw += g_touchscreengui->getYawChange(); - cam->camera_pitch = g_touchscreengui->getPitch(); + cam->camera_pitch += g_touchscreengui->getPitchChange(); } else { #endif v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2); diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 4a6486573..bf8fc5afe 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -832,7 +832,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) const double d = g_settings->getFloat("touchscreen_sensitivity", 0.001f, 10.0f) * 3.0f; m_camera_yaw_change -= dir_free.X * d; - m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dir_free.Y * d), -180.0f), 180.0f); + m_camera_pitch_change += dir_free.Y * d; // update shootline // no need to update (X, Y) when using crosshair since the shootline is not used diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index ff5219f3a..b60edac79 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -171,7 +171,11 @@ public: return res; } - double getPitch() { return m_camera_pitch; } + double getPitchChange() { + double res = m_camera_pitch_change; + m_camera_pitch_change = 0; + return res; + } /** * Returns a line which describes what the player is pointing at. @@ -213,7 +217,7 @@ private: // value in degree double m_camera_yaw_change = 0.0; - double m_camera_pitch = 0.0; + double m_camera_pitch_change = 0.0; /** * A line starting at the camera and pointing towards the selected object. -- 2.46.0 From 7e143cb33d818b520c1b6f2cf185baeb18df4357 Mon Sep 17 00:00:00 2001 From: Warr1024 Date: Thu, 21 Dec 2023 12:53:30 -0500 Subject: [PATCH 32/85] Manually configurable minimum protocol version (#14054) Partially address #13483. Server operators can set a minimum protocol version to match the game requirements (or any other restriction they may want), and it's applied as an additional constraint on top of the baseline compatibility range, optional strict_protocol_version_checking, and any kick-on-join used by the game/mods. --- builtin/settingtypes.txt | 8 ++++++++ minetest.conf.example | 9 +++++++++ src/defaultsettings.cpp | 1 + src/network/serverpackethandler.cpp | 6 ++---- src/server.cpp | 17 +++++++++++++++++ src/server.h | 3 +++ src/serverlist.cpp | 6 +++--- 7 files changed, 43 insertions(+), 7 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index ec00b3b89..205d427fb 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -810,6 +810,14 @@ bind_address (Bind address) string # to new servers, but they may not support all new features that you are expecting. strict_protocol_version_checking (Strict protocol checking) bool false +# Define the oldest clients allowed to connect. +# Older clients are compatible in the sense that they will not crash when connecting +# to new servers, but they may not support all new features that you are expecting. +# This allows for more fine-grained control than strict_protocol_version_checking. +# Minetest still enforces its own internal minimum, and enabling +# strict_protocol_version_checking will effectively override this. +protocol_version_min (Protocol version minimum) int 1 1 65535 + # Specifies URL from which client fetches media instead of using UDP. # $filename should be accessible from $remote_media$filename via cURL # (obviously, remote_media should end with a slash). diff --git a/minetest.conf.example b/minetest.conf.example index 42ffecde3..d88abe92a 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -760,6 +760,15 @@ # type: bool # strict_protocol_version_checking = false +# Define the oldest clients allowed to connect. +# Older clients are compatible in the sense that they will not crash when connecting +# to new servers, but they may not support all new features that you are expecting. +# This allows more fine-grained control than strict_protocol_version_checking. +# Minetest may still enforce its own internal minimum, and enabling +# strict_protocol_version_checking will effectively override this. +# type: int min: 1 max: 65535 +# protocol_version_min = 1 + # Specifies URL from which client fetches media instead of using UDP. # $filename should be accessible from $remote_media$filename via cURL # (obviously, remote_media should end with a slash). diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index d61a91d40..90d7df05f 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -365,6 +365,7 @@ void set_default_settings() settings->setDefault("max_packets_per_iteration", "1024"); settings->setDefault("port", "30000"); settings->setDefault("strict_protocol_version_checking", "false"); + settings->setDefault("protocol_version_min", "1"); settings->setDefault("player_transfer_distance", "0"); settings->setDefault("max_simultaneous_block_sends_per_client", "40"); settings->setDefault("time_send_interval", "5"); diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index bce2b2261..92ff4d031 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -147,10 +147,8 @@ void Server::handleCommand_Init(NetworkPacket* pkt) client->net_proto_version = net_proto_version; - if ((g_settings->getBool("strict_protocol_version_checking") && - net_proto_version != LATEST_PROTOCOL_VERSION) || - net_proto_version < SERVER_PROTOCOL_VERSION_MIN || - net_proto_version > SERVER_PROTOCOL_VERSION_MAX) { + if (net_proto_version < Server::getProtocolVersionMin() || + net_proto_version > Server::getProtocolVersionMax()) { actionstream << "Server: A mismatched client tried to connect from " << addr_s << " proto_max=" << (int)max_net_proto_version << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION); diff --git a/src/server.cpp b/src/server.cpp index 67c3a8592..19b506ddd 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -4191,3 +4191,20 @@ bool Server::migrateModStorageDatabase(const GameParams &game_params, const Sett return succeeded; } + +u16 Server::getProtocolVersionMin() +{ + u16 min_proto = g_settings->getU16("protocol_version_min"); + if (g_settings->getBool("strict_protocol_version_checking")) + min_proto = LATEST_PROTOCOL_VERSION; + return rangelim(min_proto, + SERVER_PROTOCOL_VERSION_MIN, + SERVER_PROTOCOL_VERSION_MAX); +} + +u16 Server::getProtocolVersionMax() +{ + return g_settings->getBool("strict_protocol_version_checking") + ? LATEST_PROTOCOL_VERSION + : SERVER_PROTOCOL_VERSION_MAX; +} diff --git a/src/server.h b/src/server.h index 4ef374352..a4f975432 100644 --- a/src/server.h +++ b/src/server.h @@ -383,6 +383,9 @@ public: static bool migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args); + static u16 getProtocolVersionMin(); + static u16 getProtocolVersionMax(); + // Lua files registered for init of async env, pair of modname + path std::vector> m_async_init_files; diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 29e3ac9a6..e702ba73d 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "convert_json.h" #include "httpfetch.h" +#include "server.h" namespace ServerList { @@ -50,12 +51,11 @@ void sendAnnounce(AnnounceAction action, server["address"] = g_settings->get("server_address"); } if (action != AA_DELETE) { - bool strict_checking = g_settings->getBool("strict_protocol_version_checking"); server["name"] = g_settings->get("server_name"); server["description"] = g_settings->get("server_description"); server["version"] = g_version_string; - server["proto_min"] = strict_checking ? LATEST_PROTOCOL_VERSION : SERVER_PROTOCOL_VERSION_MIN; - server["proto_max"] = strict_checking ? LATEST_PROTOCOL_VERSION : SERVER_PROTOCOL_VERSION_MAX; + server["proto_min"] = Server::getProtocolVersionMin(); + server["proto_max"] = Server::getProtocolVersionMax(); server["url"] = g_settings->get("server_url"); server["creative"] = g_settings->getBool("creative_mode"); server["damage"] = g_settings->getBool("enable_damage"); -- 2.46.0 From d58cc7fb7a2c63ad41d9896d94cec7560a2a0487 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Wed, 20 Dec 2023 21:14:24 +0100 Subject: [PATCH 33/85] Fix on_(grant|revoke) not being run by mods --- builtin/game/auth.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index e7d502bb3..fa1860d5d 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -87,19 +87,20 @@ core.builtin_auth_handler = { core.settings:get("default_password"))) end + local prev_privs = auth_entry.privileges auth_entry.privileges = privileges core_auth.save(auth_entry) -- Run grant callbacks for priv, _ in pairs(privileges) do - if not auth_entry.privileges[priv] then + if not prev_privs[priv] then core.run_priv_callbacks(name, priv, nil, "grant") end end -- Run revoke callbacks - for priv, _ in pairs(auth_entry.privileges) do + for priv, _ in pairs(prev_privs) do if not privileges[priv] then core.run_priv_callbacks(name, priv, nil, "revoke") end -- 2.46.0 From cb38b841afd25641c0374ab1f45e4e7245ee4cf9 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 20 Dec 2023 21:35:19 +0100 Subject: [PATCH 34/85] Split windows from linux CI workflows --- .github/workflows/android.yml | 4 + .github/workflows/linux.yml | 163 +++++++++++++++++++ .github/workflows/{build.yml => windows.yml} | 149 +---------------- 3 files changed, 173 insertions(+), 143 deletions(-) create mode 100644 .github/workflows/linux.yml rename .github/workflows/{build.yml => windows.yml} (55%) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 8cbe5e09f..ede978268 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -8,6 +8,8 @@ on: - 'lib/**.cpp' - 'src/**.[ch]' - 'src/**.cpp' + - '**/CMakeLists.txt' + - 'cmake/Modules/**' - 'android/**' - '.github/workflows/android.yml' pull_request: @@ -16,6 +18,8 @@ on: - 'lib/**.cpp' - 'src/**.[ch]' - 'src/**.cpp' + - '**/CMakeLists.txt' + - 'cmake/Modules/**' - 'android/**' - '.github/workflows/android.yml' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 000000000..2a4a79b7b --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,163 @@ +name: linux + +# build on c/cpp changes or workflow changes +on: + push: + paths: + - 'lib/**.[ch]' + - 'lib/**.cpp' + - 'src/**.[ch]' + - 'src/**.cpp' + - '**/CMakeLists.txt' + - 'cmake/Modules/**' + - 'util/ci/**' + - 'misc/irrlichtmt_tag.txt' + - 'Dockerfile' + - '.dockerignore' + - '.github/workflows/linux.yml' + pull_request: + paths: + - 'lib/**.[ch]' + - 'lib/**.cpp' + - 'src/**.[ch]' + - 'src/**.cpp' + - '**/CMakeLists.txt' + - 'cmake/Modules/**' + - 'util/ci/**' + - 'misc/irrlichtmt_tag.txt' + - 'Dockerfile' + - '.dockerignore' + - '.github/workflows/linux.yml' + +env: + MINETEST_POSTGRESQL_CONNECT_STRING: 'host=localhost user=minetest password=minetest dbname=minetest' + +jobs: + # Older gcc version (should be close to our minimum supported version) + gcc_7: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: Install deps + run: | + source ./util/ci/common.sh + install_linux_deps g++-7 + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: gcc-7 + CXX: g++-7 + + - name: Test + run: | + ./bin/minetest --run-unittests + + # Current gcc version + gcc_12: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Install deps + run: | + source ./util/ci/common.sh + install_linux_deps g++-12 libluajit-5.1-dev + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: gcc-12 + CXX: g++-12 + + - name: Test + run: | + ./bin/minetest --run-unittests + + # Older clang version (should be close to our minimum supported version) + clang_7: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: Install deps + run: | + source ./util/ci/common.sh + install_linux_deps clang-7 valgrind + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: clang-7 + CXX: clang++-7 + + - name: Unittest + run: | + ./bin/minetest --run-unittests + + - name: Valgrind + run: | + valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/minetest --run-unittests + + # Current clang version + clang_14: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Install deps + run: | + source ./util/ci/common.sh + install_linux_deps clang-14 gdb + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: clang-14 + CXX: clang++-14 + + - name: Test + run: | + ./bin/minetest --run-unittests + + - name: Integration test + devtest + run: | + ./util/test_multiplayer.sh + + # Build with prometheus-cpp (server-only) + clang_9_prometheus: + name: "clang_9 (PROMETHEUS=1)" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: Install deps + run: | + source ./util/ci/common.sh + install_linux_deps clang-9 + + - name: Build prometheus-cpp + run: | + ./util/ci/build_prometheus_cpp.sh + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: clang-9 + CXX: clang++-9 + CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0" + + - name: Test + run: | + ./bin/minetestserver --run-unittests + + docker: + name: "Docker image" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: Build docker image + run: | + docker build . -t minetest:latest + docker run --rm minetest:latest /usr/local/bin/minetestserver --version diff --git a/.github/workflows/build.yml b/.github/workflows/windows.yml similarity index 55% rename from .github/workflows/build.yml rename to .github/workflows/windows.yml index e67bcb7bb..7f15e841d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/windows.yml @@ -1,4 +1,4 @@ -name: build +name: windows # build on c/cpp changes or workflow changes on: @@ -11,12 +11,9 @@ on: - '**/CMakeLists.txt' - 'cmake/Modules/**' - 'util/buildbot/**' - - 'util/ci/**' - 'misc/irrlichtmt_tag.txt' - 'misc/*.manifest' - - '.github/workflows/**.yml' - - 'Dockerfile' - - '.dockerignore' + - '.github/workflows/windows.yml' pull_request: paths: - 'lib/**.[ch]' @@ -26,147 +23,12 @@ on: - '**/CMakeLists.txt' - 'cmake/Modules/**' - 'util/buildbot/**' - - 'util/ci/**' - 'misc/irrlichtmt_tag.txt' - 'misc/*.manifest' - - '.github/workflows/**.yml' - - 'Dockerfile' - - '.dockerignore' - -env: - MINETEST_POSTGRESQL_CONNECT_STRING: 'host=localhost user=minetest password=minetest dbname=minetest' + - '.github/workflows/windows.yml' jobs: - # Older gcc version (should be close to our minimum supported version) - gcc_7: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: Install deps - run: | - source ./util/ci/common.sh - install_linux_deps g++-7 - - - name: Build - run: | - ./util/ci/build.sh - env: - CC: gcc-7 - CXX: g++-7 - - - name: Test - run: | - ./bin/minetest --run-unittests - - # Current gcc version - gcc_12: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - name: Install deps - run: | - source ./util/ci/common.sh - install_linux_deps g++-12 libluajit-5.1-dev - - - name: Build - run: | - ./util/ci/build.sh - env: - CC: gcc-12 - CXX: g++-12 - - - name: Test - run: | - ./bin/minetest --run-unittests - - # Older clang version (should be close to our minimum supported version) - clang_7: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: Install deps - run: | - source ./util/ci/common.sh - install_linux_deps clang-7 valgrind - - - name: Build - run: | - ./util/ci/build.sh - env: - CC: clang-7 - CXX: clang++-7 - - - name: Unittest - run: | - ./bin/minetest --run-unittests - - - name: Valgrind - run: | - valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/minetest --run-unittests - - # Current clang version - clang_14: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - name: Install deps - run: | - source ./util/ci/common.sh - install_linux_deps clang-14 gdb - - - name: Build - run: | - ./util/ci/build.sh - env: - CC: clang-14 - CXX: clang++-14 - - - name: Test - run: | - ./bin/minetest --run-unittests - - - name: Integration test + devtest - run: | - ./util/test_multiplayer.sh - - # Build with prometheus-cpp (server-only) - clang_9_prometheus: - name: "clang_9 (PROMETHEUS=1)" - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: Install deps - run: | - source ./util/ci/common.sh - install_linux_deps clang-9 - - - name: Build prometheus-cpp - run: | - ./util/ci/build_prometheus_cpp.sh - - - name: Build - run: | - ./util/ci/build.sh - env: - CC: clang-9 - CXX: clang++-9 - CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0" - - - name: Test - run: | - ./bin/minetestserver --run-unittests - - docker: - name: "Docker image" - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: Build docker image - run: | - docker build . -t minetest:latest - docker run --rm minetest:latest /usr/local/bin/minetestserver --version - - win32: + mingw32: name: "MinGW cross-compiler (32-bit)" runs-on: ubuntu-22.04 steps: @@ -186,7 +48,7 @@ jobs: path: B/build/*.zip if-no-files-found: error - win64: + mingw64: name: "MinGW cross-compiler (64-bit)" runs-on: ubuntu-22.04 steps: @@ -282,3 +144,4 @@ jobs: with: name: msvc-${{ matrix.config.arch }}-${{ matrix.type }} path: .\Package\ + if-no-files-found: error -- 2.46.0 From cad8e895f24c74d4cf329594e7069797f8480602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Thu, 21 Dec 2023 18:55:12 +0100 Subject: [PATCH 35/85] Fix set_bone_position regression (error on passing none) --- src/script/common/c_internal.cpp | 13 +++++++++---- src/script/common/c_internal.h | 3 ++- src/script/lua_api/l_object.cpp | 10 +++++----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index b02591f3a..1ca9f3767 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -124,8 +124,10 @@ void script_error(lua_State *L, int pcall_result, const char *mod, const char *f static void script_log_add_source(lua_State *L, std::string &message, int stack_depth) { - lua_Debug ar; + if (stack_depth <= 0) + return; + lua_Debug ar; if (lua_getstack(L, stack_depth, &ar)) { FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed"); message.append(" (at " + std::string(ar.short_src) + ":" @@ -172,15 +174,18 @@ DeprecatedHandlingMode get_deprecated_handling_mode() return ret; } -void log_deprecated(lua_State *L, std::string message, int stack_depth) +void log_deprecated(lua_State *L, std::string message, int stack_depth, bool once) { DeprecatedHandlingMode mode = get_deprecated_handling_mode(); if (mode == DeprecatedHandlingMode::Ignore) return; - if (stack_depth >= 0) + if (once) { + script_log_unique(L, message, warningstream, stack_depth); + } else { script_log_add_source(L, message, stack_depth); - warningstream << message << std::endl; + warningstream << message << std::endl; + } if (mode == DeprecatedHandlingMode::Error) script_error(L, LUA_ERRRUN, NULL, NULL); diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h index eac492d08..78ce48e7e 100644 --- a/src/script/common/c_internal.h +++ b/src/script/common/c_internal.h @@ -149,8 +149,9 @@ DeprecatedHandlingMode get_deprecated_handling_mode(); * @param message The deprecation method * @param stack_depth How far on the stack to the first user function * (ie: not builtin or core). -1 to disabled. + * @param once Log the deprecation warning only once per callsite. */ -void log_deprecated(lua_State *L, std::string message, int stack_depth = 1); +void log_deprecated(lua_State *L, std::string message, int stack_depth = 1, bool once = false); // Safely call string.dump on a function value // (does not pop, leaves one value on stack) diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index a0e8f69a5..0221b2576 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -520,7 +520,7 @@ int ObjectRef::l_set_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; - log_deprecated(L,"Deprecated call to set_bone_position, use set_bone_override instead"); + log_deprecated(L, "Deprecated call to set_bone_position, use set_bone_override instead", 1, true); ObjectRef *ref = checkObject(L, 1); ServerActiveObject *sao = getobject(ref); @@ -528,12 +528,12 @@ int ObjectRef::l_set_bone_position(lua_State *L) return 0; std::string bone; - if (!lua_isnil(L, 2)) + if (!lua_isnoneornil(L, 2)) bone = readParam(L, 2); BoneOverride props; - if (!lua_isnil(L, 3)) + if (!lua_isnoneornil(L, 3)) props.position.vector = check_v3f(L, 3); - if (!lua_isnil(L, 4)) + if (!lua_isnoneornil(L, 4)) props.rotation.next = core::quaternion(check_v3f(L, 4) * core::DEGTORAD); props.position.absolute = true; props.rotation.absolute = true; @@ -546,7 +546,7 @@ int ObjectRef::l_get_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; - log_deprecated(L,"Deprecated call to get_bone_position, use get_bone_override instead"); + log_deprecated(L, "Deprecated call to get_bone_position, use get_bone_override instead", 1, true); ObjectRef *ref = checkObject(L, 1); ServerActiveObject *sao = getobject(ref); -- 2.46.0 From 04f0d545da000dc86a5c9895110f77b3bfecbf9f Mon Sep 17 00:00:00 2001 From: x2048 Date: Mon, 9 Oct 2023 11:58:58 -0700 Subject: [PATCH 36/85] Initial implementation of 'Godrays' --- .../extract_bloom/opengl_fragment.glsl | 93 +++++++++++++++++++ src/client/game.cpp | 55 ++++++++++- src/client/render/secondstage.cpp | 2 +- 3 files changed, 148 insertions(+), 2 deletions(-) diff --git a/client/shaders/extract_bloom/opengl_fragment.glsl b/client/shaders/extract_bloom/opengl_fragment.glsl index 36671b06c..b79911b9a 100644 --- a/client/shaders/extract_bloom/opengl_fragment.glsl +++ b/client/shaders/extract_bloom/opengl_fragment.glsl @@ -1,13 +1,28 @@ #define rendered texture0 +#define depthmap texture2 struct ExposureParams { float compensationFactor; }; uniform sampler2D rendered; +uniform sampler2D depthmap; + uniform mediump float bloomStrength; uniform ExposureParams exposureParams; +uniform vec3 sunPositionScreen; +uniform float sunBrightness; +uniform vec3 moonPositionScreen; +uniform float moonBrightness; + +uniform vec3 dayLight; +#ifdef ENABLE_DYNAMIC_SHADOWS +uniform vec3 v_LightDirection; +#else +const vec3 v_LightDirection = vec3(0.0, -1.0, 0.0); +#endif + #ifdef GL_ES varying mediump vec2 varTexCoord; #else @@ -18,6 +33,80 @@ centroid varying vec2 varTexCoord; varying float exposure; // linear exposure factor, see vertex shader #endif +const float far = 1000.; +const float near = 1.; +float mapDepth(float depth) +{ + return min(1., 1. / (1.00001 - depth) / far); +} + +float noise(vec3 uvd) { + return fract(dot(sin(uvd * vec3(13041.19699, 27723.29171, 61029.77801)), vec3(73137.11101, 37312.92319, 10108.89991))); +} + +float sampleVolumetricLight(vec2 uv, vec3 lightVec, float rawDepth) +{ + lightVec = 0.5 * lightVec / lightVec.z + 0.5; + const float samples = 30.; + float result = texture2D(depthmap, uv).r < 1. ? 0.0 : 1.0; + float bias = noise(vec3(uv, rawDepth)); + vec2 samplepos; + for (float i = 1.; i < samples; i++) { + samplepos = mix(uv, lightVec.xy, (i + bias) / samples); + if (min(samplepos.x, samplepos.y) > 0. && max(samplepos.x, samplepos.y) < 1.) + result += texture2D(depthmap, samplepos).r < 1. ? 0.0 : 1.0; + } + return result / samples; +} + +vec3 getDirectLightScatteringAtGround(vec3 v_LightDirection) +{ + // Based on talk at 2002 Game Developers Conference by Naty Hoffman and Arcot J. Preetham + const float beta_r0 = 1e-5; // Rayleigh scattering beta + + // These factors are calculated based on expected value of scattering factor of 1e-5 + // for Nitrogen at 532nm (green), 2e25 molecules/m3 in atmosphere + const vec3 beta_r0_l = vec3(3.3362176e-01, 8.75378289198826e-01, 1.95342379700656) * beta_r0; // wavelength-dependent scattering + + const float atmosphere_height = 15000.; // height of the atmosphere in meters + // sun/moon light at the ground level, after going through the atmosphere + return exp(-beta_r0_l * atmosphere_height / (1e-5 - dot(v_LightDirection, vec3(0., 1., 0.)))); +} + +vec3 applyVolumetricLight(vec3 color, vec2 uv, float rawDepth) +{ + vec3 lookDirection = normalize(vec3(uv.x * 2. - 1., uv.y * 2. - 1., rawDepth)); + vec3 lightSourceTint = vec3(1.0, 0.98, 0.4); + + const float boost = 4.0; + float brightness = 0.; + vec3 sourcePosition = vec3(-1., -1., -1); + + if (sunPositionScreen.z > 0. && sunBrightness > 0.) { + brightness = sunBrightness; + sourcePosition = sunPositionScreen; + } + else if (moonPositionScreen.z > 0. && moonBrightness > 0.) { + lightSourceTint = vec3(0.4, 0.9, 1.); + brightness = moonBrightness * 0.05; + sourcePosition = moonPositionScreen; + } + + float cameraDirectionFactor = pow(clamp(dot(sourcePosition, vec3(0., 0., 1.)), 0.0, 0.7), 2.5); + float viewAngleFactor = pow(max(0., dot(sourcePosition, lookDirection)), 8.); + + float lightFactor = brightness * sampleVolumetricLight(uv, sourcePosition, rawDepth) * + (0.05 * cameraDirectionFactor + 0.95 * viewAngleFactor); + + color = mix(color, boost * getDirectLightScatteringAtGround(v_LightDirection) * dayLight, lightFactor); + + // if (sunPositionScreen.z < 0.) + // color.rg += 1. - clamp(abs((2. * uv.xy - 1.) - sunPositionScreen.xy / sunPositionScreen.z) * 1000., 0., 1.); + // if (moonPositionScreen.z < 0.) + // color.rg += 1. - clamp(abs((2. * uv.xy - 1.) - moonPositionScreen.xy / moonPositionScreen.z) * 1000., 0., 1.); + return color; +} + void main(void) { vec2 uv = varTexCoord.st; @@ -31,5 +120,9 @@ void main(void) color *= exposure; #endif + float rawDepth = texture2D(depthmap, uv).r; + + color = applyVolumetricLight(color, uv, rawDepth); + gl_FragColor = vec4(color, 1.0); // force full alpha to avoid holes in the image. } diff --git a/src/client/game.cpp b/src/client/game.cpp index 6b5163616..ab1927eab 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -404,6 +404,10 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_bloom_radius_pixel; float m_bloom_radius; CachedPixelShaderSetting m_saturation_pixel; + CachedPixelShaderSetting m_sun_position_pixel; + CachedPixelShaderSetting m_sun_brightness_pixel; + CachedPixelShaderSetting m_moon_position_pixel; + CachedPixelShaderSetting m_moon_brightness_pixel; public: void onSettingsChange(const std::string &name) @@ -461,7 +465,11 @@ public: m_bloom_intensity_pixel("bloomIntensity"), m_bloom_strength_pixel("bloomStrength"), m_bloom_radius_pixel("bloomRadius"), - m_saturation_pixel("saturation") + m_saturation_pixel("saturation"), + m_sun_position_pixel("sunPositionScreen"), + m_sun_brightness_pixel("sunBrightness"), + m_moon_position_pixel("moonPositionScreen"), + m_moon_brightness_pixel("moonBrightness") { g_settings->registerChangedCallback("enable_fog", settingsCallback, this); g_settings->registerChangedCallback("exposure_compensation", settingsCallback, this); @@ -579,6 +587,51 @@ public: } float saturation = m_client->getEnv().getLocalPlayer()->getLighting().saturation; m_saturation_pixel.set(&saturation, services); + + // Map directional light to screen space + auto camera_node = m_client->getCamera()->getCameraNode(); + core::matrix4 transform = camera_node->getProjectionMatrix(); + transform *= camera_node->getViewMatrix(); + + if (m_sky->getSunVisible()) { + v3f sun_position = camera_node->getAbsolutePosition() + + 10000. * m_sky->getSunDirection(); + transform.transformVect(sun_position); + sun_position.normalize(); + + float sun_position_array[3] = { sun_position.X, sun_position.Y, sun_position.Z}; + m_sun_position_pixel.set(sun_position_array, services); + + float sun_brightness = rangelim(107.143f * m_sky->getSunDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f); + m_sun_brightness_pixel.set(&sun_brightness, services); + } + else { + float sun_position_array[3] = { 0.f, 0.f, -1.f }; + m_sun_position_pixel.set(sun_position_array, services); + + float sun_brightness = 0.f; + m_sun_brightness_pixel.set(&sun_brightness, services); + } + + if (m_sky->getMoonVisible()) { + v3f moon_position = camera_node->getAbsolutePosition() + + 10000. * m_sky->getMoonDirection(); + transform.transformVect(moon_position); + moon_position.normalize(); + + float moon_position_array[3] = { moon_position.X, moon_position.Y, moon_position.Z}; + m_moon_position_pixel.set(moon_position_array, services); + + float moon_brightness = rangelim(107.143f * m_sky->getMoonDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f); + m_moon_brightness_pixel.set(&moon_brightness, services); + } + else { + float moon_position_array[3] = { 0.f, 0.f, -1.f }; + m_moon_position_pixel.set(moon_position_array, services); + + float moon_brightness = 0.f; + m_moon_brightness_pixel.set(&moon_brightness, services); + } } void onSetMaterial(const video::SMaterial &material) override diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp index f33f1975e..6a71f395e 100644 --- a/src/client/render/secondstage.cpp +++ b/src/client/render/secondstage.cpp @@ -171,7 +171,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // get bright spots u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH); - RenderStep *extract_bloom = pipeline->addStep(shader_id, std::vector { TEXTURE_COLOR, TEXTURE_EXPOSURE_1 }); + RenderStep *extract_bloom = pipeline->addStep(shader_id, std::vector { TEXTURE_COLOR, TEXTURE_EXPOSURE_1, TEXTURE_DEPTH }); extract_bloom->setRenderSource(buffer); extract_bloom->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_BLOOM)); source = TEXTURE_BLOOM; -- 2.46.0 From e0d4a9d5756e66102cfc05dc9a187c5ada8cf0be Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 23 Oct 2023 17:05:31 -0700 Subject: [PATCH 37/85] Make volumetric light effect strength server controllable - Make volumetric light effect strength server controllable - Separate volumetric and bloom shader pipeline - Require bloom to be enable, scale godrays with bloom --- builtin/settingtypes.txt | 4 + .../extract_bloom/opengl_fragment.glsl | 93 -------------- .../shaders/second_stage/opengl_fragment.glsl | 1 - .../volumetric_light/opengl_fragment.glsl | 115 ++++++++++++++++++ .../volumetric_light/opengl_vertex.glsl | 12 ++ doc/lua_api.md | 3 + src/client/game.cpp | 80 ++++++------ src/client/render/secondstage.cpp | 36 ++++-- src/client/shader.cpp | 4 + src/client/sky.h | 3 + src/defaultsettings.cpp | 1 + src/lighting.h | 1 + src/network/clientpackethandler.cpp | 2 + src/script/lua_api/l_object.cpp | 13 +- src/server.cpp | 2 + src/skyparams.h | 1 + 16 files changed, 227 insertions(+), 144 deletions(-) create mode 100644 client/shaders/volumetric_light/opengl_fragment.glsl create mode 100644 client/shaders/volumetric_light/opengl_vertex.glsl diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 205d427fb..0af96cc33 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -627,6 +627,10 @@ bloom_strength_factor (Bloom Strength Factor) float 1.0 0.1 10.0 # Requires: shaders, enable_bloom bloom_radius (Bloom Radius) float 1 0.1 8 +# Set to true to enable volumetric lighting effect (a.k.a. "Godrays"). +# +# Requires: shaders, enable_bloom +enable_volumetric_lighting (Volumetric lighting) bool false [*Audio] diff --git a/client/shaders/extract_bloom/opengl_fragment.glsl b/client/shaders/extract_bloom/opengl_fragment.glsl index b79911b9a..36671b06c 100644 --- a/client/shaders/extract_bloom/opengl_fragment.glsl +++ b/client/shaders/extract_bloom/opengl_fragment.glsl @@ -1,28 +1,13 @@ #define rendered texture0 -#define depthmap texture2 struct ExposureParams { float compensationFactor; }; uniform sampler2D rendered; -uniform sampler2D depthmap; - uniform mediump float bloomStrength; uniform ExposureParams exposureParams; -uniform vec3 sunPositionScreen; -uniform float sunBrightness; -uniform vec3 moonPositionScreen; -uniform float moonBrightness; - -uniform vec3 dayLight; -#ifdef ENABLE_DYNAMIC_SHADOWS -uniform vec3 v_LightDirection; -#else -const vec3 v_LightDirection = vec3(0.0, -1.0, 0.0); -#endif - #ifdef GL_ES varying mediump vec2 varTexCoord; #else @@ -33,80 +18,6 @@ centroid varying vec2 varTexCoord; varying float exposure; // linear exposure factor, see vertex shader #endif -const float far = 1000.; -const float near = 1.; -float mapDepth(float depth) -{ - return min(1., 1. / (1.00001 - depth) / far); -} - -float noise(vec3 uvd) { - return fract(dot(sin(uvd * vec3(13041.19699, 27723.29171, 61029.77801)), vec3(73137.11101, 37312.92319, 10108.89991))); -} - -float sampleVolumetricLight(vec2 uv, vec3 lightVec, float rawDepth) -{ - lightVec = 0.5 * lightVec / lightVec.z + 0.5; - const float samples = 30.; - float result = texture2D(depthmap, uv).r < 1. ? 0.0 : 1.0; - float bias = noise(vec3(uv, rawDepth)); - vec2 samplepos; - for (float i = 1.; i < samples; i++) { - samplepos = mix(uv, lightVec.xy, (i + bias) / samples); - if (min(samplepos.x, samplepos.y) > 0. && max(samplepos.x, samplepos.y) < 1.) - result += texture2D(depthmap, samplepos).r < 1. ? 0.0 : 1.0; - } - return result / samples; -} - -vec3 getDirectLightScatteringAtGround(vec3 v_LightDirection) -{ - // Based on talk at 2002 Game Developers Conference by Naty Hoffman and Arcot J. Preetham - const float beta_r0 = 1e-5; // Rayleigh scattering beta - - // These factors are calculated based on expected value of scattering factor of 1e-5 - // for Nitrogen at 532nm (green), 2e25 molecules/m3 in atmosphere - const vec3 beta_r0_l = vec3(3.3362176e-01, 8.75378289198826e-01, 1.95342379700656) * beta_r0; // wavelength-dependent scattering - - const float atmosphere_height = 15000.; // height of the atmosphere in meters - // sun/moon light at the ground level, after going through the atmosphere - return exp(-beta_r0_l * atmosphere_height / (1e-5 - dot(v_LightDirection, vec3(0., 1., 0.)))); -} - -vec3 applyVolumetricLight(vec3 color, vec2 uv, float rawDepth) -{ - vec3 lookDirection = normalize(vec3(uv.x * 2. - 1., uv.y * 2. - 1., rawDepth)); - vec3 lightSourceTint = vec3(1.0, 0.98, 0.4); - - const float boost = 4.0; - float brightness = 0.; - vec3 sourcePosition = vec3(-1., -1., -1); - - if (sunPositionScreen.z > 0. && sunBrightness > 0.) { - brightness = sunBrightness; - sourcePosition = sunPositionScreen; - } - else if (moonPositionScreen.z > 0. && moonBrightness > 0.) { - lightSourceTint = vec3(0.4, 0.9, 1.); - brightness = moonBrightness * 0.05; - sourcePosition = moonPositionScreen; - } - - float cameraDirectionFactor = pow(clamp(dot(sourcePosition, vec3(0., 0., 1.)), 0.0, 0.7), 2.5); - float viewAngleFactor = pow(max(0., dot(sourcePosition, lookDirection)), 8.); - - float lightFactor = brightness * sampleVolumetricLight(uv, sourcePosition, rawDepth) * - (0.05 * cameraDirectionFactor + 0.95 * viewAngleFactor); - - color = mix(color, boost * getDirectLightScatteringAtGround(v_LightDirection) * dayLight, lightFactor); - - // if (sunPositionScreen.z < 0.) - // color.rg += 1. - clamp(abs((2. * uv.xy - 1.) - sunPositionScreen.xy / sunPositionScreen.z) * 1000., 0., 1.); - // if (moonPositionScreen.z < 0.) - // color.rg += 1. - clamp(abs((2. * uv.xy - 1.) - moonPositionScreen.xy / moonPositionScreen.z) * 1000., 0., 1.); - return color; -} - void main(void) { vec2 uv = varTexCoord.st; @@ -120,9 +31,5 @@ void main(void) color *= exposure; #endif - float rawDepth = texture2D(depthmap, uv).r; - - color = applyVolumetricLight(color, uv, rawDepth); - gl_FragColor = vec4(color, 1.0); // force full alpha to avoid holes in the image. } diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl index 973cfc424..45e99928e 100644 --- a/client/shaders/second_stage/opengl_fragment.glsl +++ b/client/shaders/second_stage/opengl_fragment.glsl @@ -126,7 +126,6 @@ void main(void) #endif } - #ifdef ENABLE_BLOOM color = applyBloom(color, uv); #endif diff --git a/client/shaders/volumetric_light/opengl_fragment.glsl b/client/shaders/volumetric_light/opengl_fragment.glsl new file mode 100644 index 000000000..9ed5fa9ba --- /dev/null +++ b/client/shaders/volumetric_light/opengl_fragment.glsl @@ -0,0 +1,115 @@ +#define rendered texture0 +#define depthmap texture1 + +uniform sampler2D rendered; +uniform sampler2D depthmap; + +uniform vec3 sunPositionScreen; +uniform float sunBrightness; +uniform vec3 moonPositionScreen; +uniform float moonBrightness; + +uniform lowp float volumetricLightStrength; + +uniform vec3 dayLight; +#ifdef ENABLE_DYNAMIC_SHADOWS +uniform vec3 v_LightDirection; +#else +const vec3 v_LightDirection = vec3(0.0, -1.0, 0.0); +#endif + +#ifdef GL_ES +varying mediump vec2 varTexCoord; +#else +centroid varying vec2 varTexCoord; +#endif + +const float far = 1000.; +float mapDepth(float depth) +{ + return min(1., 1. / (1.00001 - depth) / far); +} + +float noise(vec3 uvd) { + return fract(dot(sin(uvd * vec3(13041.19699, 27723.29171, 61029.77801)), vec3(73137.11101, 37312.92319, 10108.89991))); +} + +float sampleVolumetricLight(vec2 uv, vec3 lightVec, float rawDepth) +{ + lightVec = 0.5 * lightVec / lightVec.z + 0.5; + const float samples = 30.; + float result = texture2D(depthmap, uv).r < 1. ? 0.0 : 1.0; + float bias = noise(vec3(uv, rawDepth)); + vec2 samplepos; + for (float i = 1.; i < samples; i++) { + samplepos = mix(uv, lightVec.xy, (i + bias) / samples); + if (min(samplepos.x, samplepos.y) > 0. && max(samplepos.x, samplepos.y) < 1.) + result += texture2D(depthmap, samplepos).r < 1. ? 0.0 : 1.0; + } + return result / samples; +} + +vec3 getDirectLightScatteringAtGround(vec3 v_LightDirection) +{ + // Based on talk at 2002 Game Developers Conference by Naty Hoffman and Arcot J. Preetham + const float beta_r0 = 1e-5; // Rayleigh scattering beta + + // These factors are calculated based on expected value of scattering factor of 1e-5 + // for Nitrogen at 532nm (green), 2e25 molecules/m3 in atmosphere + const vec3 beta_r0_l = vec3(3.3362176e-01, 8.75378289198826e-01, 1.95342379700656) * beta_r0; // wavelength-dependent scattering + + const float atmosphere_height = 15000.; // height of the atmosphere in meters + // sun/moon light at the ground level, after going through the atmosphere + return exp(-beta_r0_l * atmosphere_height / (1e-5 - dot(v_LightDirection, vec3(0., 1., 0.)))); +} + +vec3 applyVolumetricLight(vec3 color, vec2 uv, float rawDepth) +{ + vec3 lookDirection = normalize(vec3(uv.x * 2. - 1., uv.y * 2. - 1., rawDepth)); + + const float boost = 4.0; + float brightness = 0.; + vec3 sourcePosition = vec3(-1., -1., -1); + + if (sunPositionScreen.z > 0. && sunBrightness > 0.) { + brightness = sunBrightness; + sourcePosition = sunPositionScreen; + } + else if (moonPositionScreen.z > 0. && moonBrightness > 0.) { + brightness = moonBrightness * 0.05; + sourcePosition = moonPositionScreen; + } + + float cameraDirectionFactor = pow(clamp(dot(sourcePosition, vec3(0., 0., 1.)), 0.0, 0.7), 2.5); + float viewAngleFactor = pow(max(0., dot(sourcePosition, lookDirection)), 8.); + + float lightFactor = brightness * sampleVolumetricLight(uv, sourcePosition, rawDepth) * + (0.05 * cameraDirectionFactor + 0.95 * viewAngleFactor); + + color = mix(color, boost * getDirectLightScatteringAtGround(v_LightDirection) * dayLight, lightFactor); + + // a factor of 5 tested well + color *= volumetricLightStrength * 5.0; + + // if (sunPositionScreen.z < 0.) + // color.rg += 1. - clamp(abs((2. * uv.xy - 1.) - sunPositionScreen.xy / sunPositionScreen.z) * 1000., 0., 1.); + // if (moonPositionScreen.z < 0.) + // color.rg += 1. - clamp(abs((2. * uv.xy - 1.) - moonPositionScreen.xy / moonPositionScreen.z) * 1000., 0., 1.); + return color; +} + +void main(void) +{ + vec2 uv = varTexCoord.st; + vec3 color = texture2D(rendered, uv).rgb; + // translate to linear colorspace (approximate) + color = pow(color, vec3(2.2)); + + if (volumetricLightStrength > 0.0) { + float rawDepth = texture2D(depthmap, uv).r; + + color = applyVolumetricLight(color, uv, rawDepth); + } + + gl_FragColor = vec4(color, 1.0); // force full alpha to avoid holes in the image. +} diff --git a/client/shaders/volumetric_light/opengl_vertex.glsl b/client/shaders/volumetric_light/opengl_vertex.glsl new file mode 100644 index 000000000..d264ae071 --- /dev/null +++ b/client/shaders/volumetric_light/opengl_vertex.glsl @@ -0,0 +1,12 @@ +#ifdef GL_ES +varying mediump vec2 varTexCoord; +#else +centroid varying vec2 varTexCoord; +#endif + + +void main(void) +{ + varTexCoord.st = inTexCoord0.st; + gl_Position = inVertexPosition; +} diff --git a/doc/lua_api.md b/doc/lua_api.md index 67d9cf604..ab4ff52cc 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -8036,6 +8036,9 @@ child will follow movement and rotation of that bone. * `speed_dark_bright` set the speed of adapting to bright light (default: `1000.0`) * `speed_bright_dark` set the speed of adapting to dark scene (default: `1000.0`) * `center_weight_power` set the power factor for center-weighted luminance measurement (default: `1.0`) + * `volumetric_light`: is a table that controls volumetric light (a.k.a. "godrays") + * `strength`: sets the strength of the volumetric light effect from 0 (off, default) to 1 (strongest) + * This value has no effect on clients who have the "Volumetric Lighting" or "Bloom" shaders disabled. * `get_lighting()`: returns the current state of lighting for the player. * Result is a table with the same fields as `light_definition` in `set_lighting`. diff --git a/src/client/game.cpp b/src/client/game.cpp index ab1927eab..af225924a 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -404,10 +404,12 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_bloom_radius_pixel; float m_bloom_radius; CachedPixelShaderSetting m_saturation_pixel; + bool m_volumetric_light_enabled; CachedPixelShaderSetting m_sun_position_pixel; CachedPixelShaderSetting m_sun_brightness_pixel; CachedPixelShaderSetting m_moon_position_pixel; CachedPixelShaderSetting m_moon_brightness_pixel; + CachedPixelShaderSetting m_volumetric_light_strength_pixel; public: void onSettingsChange(const std::string &name) @@ -469,7 +471,8 @@ public: m_sun_position_pixel("sunPositionScreen"), m_sun_brightness_pixel("sunBrightness"), m_moon_position_pixel("moonPositionScreen"), - m_moon_brightness_pixel("moonBrightness") + m_moon_brightness_pixel("moonBrightness"), + m_volumetric_light_strength_pixel("volumetricLightStrength") { g_settings->registerChangedCallback("enable_fog", settingsCallback, this); g_settings->registerChangedCallback("exposure_compensation", settingsCallback, this); @@ -483,6 +486,7 @@ public: m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f); m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f); m_bloom_radius = g_settings->getFloat("bloom_radius", 0.1f, 8.0f); + m_volumetric_light_enabled = g_settings->getBool("enable_volumetric_lighting") && m_bloom_enabled; } ~GameGlobalShaderConstantSetter() @@ -588,49 +592,52 @@ public: float saturation = m_client->getEnv().getLocalPlayer()->getLighting().saturation; m_saturation_pixel.set(&saturation, services); - // Map directional light to screen space - auto camera_node = m_client->getCamera()->getCameraNode(); - core::matrix4 transform = camera_node->getProjectionMatrix(); - transform *= camera_node->getViewMatrix(); + if (m_volumetric_light_enabled) { + // Map directional light to screen space + auto camera_node = m_client->getCamera()->getCameraNode(); + core::matrix4 transform = camera_node->getProjectionMatrix(); + transform *= camera_node->getViewMatrix(); - if (m_sky->getSunVisible()) { - v3f sun_position = camera_node->getAbsolutePosition() + - 10000. * m_sky->getSunDirection(); - transform.transformVect(sun_position); - sun_position.normalize(); + if (m_sky->getSunVisible()) { + v3f sun_position = camera_node->getAbsolutePosition() + + 10000. * m_sky->getSunDirection(); + transform.transformVect(sun_position); + sun_position.normalize(); - float sun_position_array[3] = { sun_position.X, sun_position.Y, sun_position.Z}; - m_sun_position_pixel.set(sun_position_array, services); + float sun_position_array[3] = { sun_position.X, sun_position.Y, sun_position.Z}; + m_sun_position_pixel.set(sun_position_array, services); - float sun_brightness = rangelim(107.143f * m_sky->getSunDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f); - m_sun_brightness_pixel.set(&sun_brightness, services); - } - else { - float sun_position_array[3] = { 0.f, 0.f, -1.f }; - m_sun_position_pixel.set(sun_position_array, services); + float sun_brightness = rangelim(107.143f * m_sky->getSunDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f); + m_sun_brightness_pixel.set(&sun_brightness, services); + } else { + float sun_position_array[3] = { 0.f, 0.f, -1.f }; + m_sun_position_pixel.set(sun_position_array, services); - float sun_brightness = 0.f; - m_sun_brightness_pixel.set(&sun_brightness, services); - } + float sun_brightness = 0.f; + m_sun_brightness_pixel.set(&sun_brightness, services); + } - if (m_sky->getMoonVisible()) { - v3f moon_position = camera_node->getAbsolutePosition() + - 10000. * m_sky->getMoonDirection(); - transform.transformVect(moon_position); - moon_position.normalize(); + if (m_sky->getMoonVisible()) { + v3f moon_position = camera_node->getAbsolutePosition() + + 10000. * m_sky->getMoonDirection(); + transform.transformVect(moon_position); + moon_position.normalize(); - float moon_position_array[3] = { moon_position.X, moon_position.Y, moon_position.Z}; - m_moon_position_pixel.set(moon_position_array, services); + float moon_position_array[3] = { moon_position.X, moon_position.Y, moon_position.Z}; + m_moon_position_pixel.set(moon_position_array, services); - float moon_brightness = rangelim(107.143f * m_sky->getMoonDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f); - m_moon_brightness_pixel.set(&moon_brightness, services); - } - else { - float moon_position_array[3] = { 0.f, 0.f, -1.f }; - m_moon_position_pixel.set(moon_position_array, services); + float moon_brightness = rangelim(107.143f * m_sky->getMoonDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f); + m_moon_brightness_pixel.set(&moon_brightness, services); + } + else { + float moon_position_array[3] = { 0.f, 0.f, -1.f }; + m_moon_position_pixel.set(moon_position_array, services); - float moon_brightness = 0.f; - m_moon_brightness_pixel.set(&moon_brightness, services); + float moon_brightness = 0.f; + m_moon_brightness_pixel.set(&moon_brightness, services); + } + float volumetric_light_strength = m_client->getEnv().getLocalPlayer()->getLighting().volumetric_light_strength; + m_volumetric_light_strength_pixel.set(&volumetric_light_strength, services); } } @@ -3089,7 +3096,6 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) else sky->setFogStart(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f)); - delete event->set_sky; } diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp index 6a71f395e..da6536b97 100644 --- a/src/client/render/secondstage.cpp +++ b/src/client/render/secondstage.cpp @@ -120,8 +120,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep static const u8 TEXTURE_EXPOSURE_1 = 3; static const u8 TEXTURE_EXPOSURE_2 = 4; static const u8 TEXTURE_FXAA = 5; - static const u8 TEXTURE_BLOOM_DOWN = 10; - static const u8 TEXTURE_BLOOM_UP = 20; + static const u8 TEXTURE_VOLUME = 6; + static const u8 TEXTURE_SCALE_DOWN = 10; + static const u8 TEXTURE_SCALE_UP = 20; // Super-sampling is simply rendering into a larger texture. // Downscaling is done by the final step when rendering to the screen. @@ -130,6 +131,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure"); const bool enable_ssaa = antialiasing == "ssaa"; const bool enable_fxaa = antialiasing == "fxaa"; + const bool enable_volumetric_light = g_settings->getBool("enable_volumetric_lighting") && enable_bloom; if (enable_ssaa) { u16 ssaa_scale = MYMAX(2, g_settings->getU16("fsaa")); @@ -160,9 +162,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep v2f downscale = scale * 0.5; for (u8 i = 0; i < MIPMAP_LEVELS; i++) { - buffer->setTexture(TEXTURE_BLOOM_DOWN + i, downscale, std::string("downsample") + std::to_string(i), color_format); + buffer->setTexture(TEXTURE_SCALE_DOWN + i, downscale, std::string("downsample") + std::to_string(i), color_format); if (enable_bloom) - buffer->setTexture(TEXTURE_BLOOM_UP + i, downscale, std::string("upsample") + std::to_string(i), color_format); + buffer->setTexture(TEXTURE_SCALE_UP + i, downscale, std::string("upsample") + std::to_string(i), color_format); downscale *= 0.5; } @@ -171,20 +173,30 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // get bright spots u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH); - RenderStep *extract_bloom = pipeline->addStep(shader_id, std::vector { TEXTURE_COLOR, TEXTURE_EXPOSURE_1, TEXTURE_DEPTH }); + RenderStep *extract_bloom = pipeline->addStep(shader_id, std::vector { source, TEXTURE_EXPOSURE_1 }); extract_bloom->setRenderSource(buffer); extract_bloom->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_BLOOM)); source = TEXTURE_BLOOM; } + if (enable_volumetric_light) { + buffer->setTexture(TEXTURE_VOLUME, scale, "volume", color_format); + + shader_id = client->getShaderSource()->getShader("volumetric_light", TILE_MATERIAL_PLAIN, NDT_MESH); + auto volume = pipeline->addStep(shader_id, std::vector { source, TEXTURE_DEPTH }); + volume->setRenderSource(buffer); + volume->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_VOLUME)); + source = TEXTURE_VOLUME; + } + // downsample shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH); for (u8 i = 0; i < MIPMAP_LEVELS; i++) { auto step = pipeline->addStep(shader_id, std::vector { source }); step->setRenderSource(buffer); step->setBilinearFilter(0, true); - step->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_BLOOM_DOWN + i)); - source = TEXTURE_BLOOM_DOWN + i; + step->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_SCALE_DOWN + i)); + source = TEXTURE_SCALE_DOWN + i; } } @@ -193,19 +205,19 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // upsample shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH); for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) { - auto step = pipeline->addStep(shader_id, std::vector { u8(TEXTURE_BLOOM_DOWN + i - 1), source }); + auto step = pipeline->addStep(shader_id, std::vector { u8(TEXTURE_SCALE_DOWN + i - 1), source }); step->setRenderSource(buffer); step->setBilinearFilter(0, true); step->setBilinearFilter(1, true); - step->setRenderTarget(pipeline->createOwned(buffer, u8(TEXTURE_BLOOM_UP + i - 1))); - source = TEXTURE_BLOOM_UP + i - 1; + step->setRenderTarget(pipeline->createOwned(buffer, u8(TEXTURE_SCALE_UP + i - 1))); + source = TEXTURE_SCALE_UP + i - 1; } } // Dynamic Exposure pt2 if (enable_auto_exposure) { shader_id = client->getShaderSource()->getShader("update_exposure", TILE_MATERIAL_PLAIN, NDT_MESH); - auto update_exposure = pipeline->addStep(shader_id, std::vector { TEXTURE_EXPOSURE_1, u8(TEXTURE_BLOOM_DOWN + MIPMAP_LEVELS - 1) }); + auto update_exposure = pipeline->addStep(shader_id, std::vector { TEXTURE_EXPOSURE_1, u8(TEXTURE_SCALE_DOWN + MIPMAP_LEVELS - 1) }); update_exposure->setBilinearFilter(1, true); update_exposure->setRenderSource(buffer); update_exposure->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_EXPOSURE_2)); @@ -228,7 +240,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // final merge shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH); - PostProcessingStep *effect = pipeline->createOwned(shader_id, std::vector { final_stage_source, TEXTURE_BLOOM_UP, TEXTURE_EXPOSURE_2 }); + PostProcessingStep *effect = pipeline->createOwned(shader_id, std::vector { final_stage_source, TEXTURE_SCALE_UP, TEXTURE_EXPOSURE_2 }); pipeline->addStep(effect); if (enable_ssaa) effect->setBilinearFilter(0, true); diff --git a/src/client/shader.cpp b/src/client/shader.cpp index 3e6e67e45..9d074e886 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -770,6 +770,10 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, if (g_settings->getBool("debanding")) shaders_header << "#define ENABLE_DITHERING 1\n"; + if (g_settings->getBool("enable_volumetric_lighting")) { + shaders_header << "#define VOLUMETRIC_LIGHT 1\n"; + } + shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics std::string common_header = shaders_header.str(); diff --git a/src/client/sky.h b/src/client/sky.h index a5b92ace2..b76a5311b 100644 --- a/src/client/sky.h +++ b/src/client/sky.h @@ -120,6 +120,9 @@ public: void setFogStart(float fog_start) { m_sky_params.fog_start = fog_start; } float getFogStart() const { return m_sky_params.fog_start; } + void setVolumetricLightStrength(float volumetric_light_strength) { m_sky_params.volumetric_light_strength = volumetric_light_strength; } + float getVolumetricLightStrength() const { return m_sky_params.volumetric_light_strength; } + private: aabb3f m_box; video::SMaterial m_materials[SKY_MATERIAL_COUNT]; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 90d7df05f..4505bdc2c 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -271,6 +271,7 @@ void set_default_settings() settings->setDefault("bloom_strength_factor", "1.0"); settings->setDefault("bloom_intensity", "0.05"); settings->setDefault("bloom_radius", "1"); + settings->setDefault("enable_volumetric_lighting", "false"); // Effects Shadows settings->setDefault("enable_dynamic_shadows", "false"); diff --git a/src/lighting.h b/src/lighting.h index 0535383a6..262a48b5d 100644 --- a/src/lighting.h +++ b/src/lighting.h @@ -53,4 +53,5 @@ struct Lighting AutoExposure exposure; float shadow_intensity {0.0f}; float saturation {1.0f}; + float volumetric_light_strength {0.0f}; }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 2cb3b20ed..3c6da09a7 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1804,4 +1804,6 @@ void Client::handleCommand_SetLighting(NetworkPacket *pkt) >> lighting.exposure.speed_bright_dark >> lighting.exposure.center_weight_power; } + if (pkt->getRemainingBytes() >= 4) + *pkt >> lighting.volumetric_light_strength; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 0221b2576..c9dae1885 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -2495,7 +2495,14 @@ int ObjectRef::l_set_lighting(lua_State *L) lighting.exposure.center_weight_power = getfloatfield_default(L, -1, "center_weight_power", lighting.exposure.center_weight_power); } lua_pop(L, 1); // exposure - } + + lua_getfield(L, 2, "volumetric_light"); + if (lua_istable(L, -1)) { + getfloatfield(L, -1, "strength", lighting.volumetric_light_strength); + lighting.volumetric_light_strength = rangelim(lighting.volumetric_light_strength, 0.0f, 1.0f); + } + lua_pop(L, 1); // volumetric_light +} getServer(L)->setLighting(player, lighting); return 0; @@ -2533,6 +2540,10 @@ int ObjectRef::l_get_lighting(lua_State *L) lua_pushnumber(L, lighting.exposure.center_weight_power); lua_setfield(L, -2, "center_weight_power"); lua_setfield(L, -2, "exposure"); + lua_newtable(L); // "volumetric_light" + lua_pushnumber(L, lighting.volumetric_light_strength); + lua_setfield(L, -2, "strength"); + lua_setfield(L, -2, "volumetric_light"); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 19b506ddd..af79632e5 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1919,6 +1919,8 @@ void Server::SendSetLighting(session_t peer_id, const Lighting &lighting) << lighting.exposure.speed_bright_dark << lighting.exposure.center_weight_power; + pkt << lighting.volumetric_light_strength; + Send(&pkt); } diff --git a/src/skyparams.h b/src/skyparams.h index 554904bc0..ff9a921ae 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -46,6 +46,7 @@ struct SkyboxParams float body_orbit_tilt { INVALID_SKYBOX_TILT }; s16 fog_distance { -1 }; float fog_start { -1.0f }; + float volumetric_light_strength { 0.0f }; }; struct SunParams -- 2.46.0 From 46c930cf701087d63eb0e23fb403e45f2eb3966a Mon Sep 17 00:00:00 2001 From: grorp Date: Sat, 23 Dec 2023 14:39:42 +0100 Subject: [PATCH 38/85] Touchscreen: Make server-sent overrides of button textures work (#14145) --- src/client/game.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/client/game.cpp b/src/client/game.cpp index af225924a..37e44edf0 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1489,12 +1489,6 @@ bool Game::createClient(const GameStartData &start_data) return false; bool could_connect, connect_aborted; -#ifdef HAVE_TOUCHSCREENGUI - if (g_touchscreengui) { - g_touchscreengui->init(texture_src); - g_touchscreengui->hide(); - } -#endif if (!connectToServer(start_data, &could_connect, &connect_aborted)) return false; @@ -1603,10 +1597,8 @@ bool Game::initGui() -1, chat_backend, client, &g_menumgr); #ifdef HAVE_TOUCHSCREENGUI - if (g_touchscreengui) - g_touchscreengui->show(); - + g_touchscreengui->init(texture_src); #endif return true; -- 2.46.0 From b6c7c5a7abc9a4df544bfc309c3800fe4942c768 Mon Sep 17 00:00:00 2001 From: Desour Date: Sat, 22 Apr 2023 18:42:08 +0200 Subject: [PATCH 39/85] Link with -latomic --- src/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2e0fdc7c1..082158c6e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -314,6 +314,13 @@ else() endif() endif() +# On clang and gcc, some functionalities of std::atomic require -latomic. +# See . +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PLATFORM_LIBS ${PLATFORM_LIBS} atomic) +endif() + check_include_files(endian.h HAVE_ENDIAN_H) configure_file( -- 2.46.0 From 322c4a5b2b8343c3942555c6a337e765ef84c3c4 Mon Sep 17 00:00:00 2001 From: Desour Date: Wed, 29 Mar 2023 11:42:50 +0200 Subject: [PATCH 40/85] Rework server stepping and dtime calculation --- src/client/game.cpp | 35 ++++++++------ src/network/connection.cpp | 8 ++-- src/network/connection.h | 5 +- src/server.cpp | 97 ++++++++++++++++++-------------------- src/server.h | 23 +++++---- 5 files changed, 87 insertions(+), 81 deletions(-) diff --git a/src/client/game.cpp b/src/client/game.cpp index 37e44edf0..0ba2dac44 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1304,8 +1304,8 @@ void Game::run() updatePauseState(); if (m_is_paused) dtime = 0.0f; - else - step(dtime); + + step(dtime); processClientEvents(&cam_view_target); updateDebugState(); @@ -1454,7 +1454,7 @@ bool Game::createSingleplayerServer(const std::string &map_dir, } else { bind_str = g_settings->get("bind_address"); } - + Address bind_addr(0, 0, 0, 0, port); if (g_settings->getBool("ipv6_server")) @@ -1682,10 +1682,7 @@ bool Game::connectToServer(const GameStartData &start_data, fps_control.limit(device, &dtime); // Update client and server - client->step(dtime); - - if (server != NULL) - server->step(dtime); + step(dtime); // End condition if (client->getState() == LC_Init) { @@ -1744,10 +1741,7 @@ bool Game::getServerContent(bool *aborted) fps_control.limit(device, &dtime); // Update client and server - client->step(dtime); - - if (server != NULL) - server->step(dtime); + step(dtime); // End condition if (client->mediaReceived() && client->itemdefReceived() && @@ -2765,10 +2759,23 @@ void Game::updatePauseState() inline void Game::step(f32 dtime) { - if (server) - server->step(dtime); + if (server) { + float fps_max = (!device->isWindowFocused() || g_menumgr.pausesGame()) ? + g_settings->getFloat("fps_max_unfocused") : + g_settings->getFloat("fps_max"); + fps_max = std::max(fps_max, 1.0f); + float steplen = 1.0f / fps_max; - client->step(dtime); + server->setStepSettings(Server::StepSettings{ + steplen, + m_is_paused + }); + + server->step(); + } + + if (!m_is_paused) + client->step(dtime); } static void pauseNodeAnimation(PausedNodesList &paused, scene::ISceneNode *node) { diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 54ef5390c..b324ca68f 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -1418,7 +1418,7 @@ void Connection::Disconnect() putCommand(ConnectionCommand::disconnect()); } -bool Connection::Receive(NetworkPacket *pkt, u32 timeout) +bool Connection::ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms) { /* Note that this function can potentially wait infinitely if non-data @@ -1426,7 +1426,7 @@ bool Connection::Receive(NetworkPacket *pkt, u32 timeout) This is not considered to be a problem (is it?) */ for(;;) { - ConnectionEventPtr e_ptr = waitEvent(timeout); + ConnectionEventPtr e_ptr = waitEvent(timeout_ms); const ConnectionEvent &e = *e_ptr; if (e.type != CONNEVENT_NONE) { @@ -1467,14 +1467,14 @@ bool Connection::Receive(NetworkPacket *pkt, u32 timeout) void Connection::Receive(NetworkPacket *pkt) { - bool any = Receive(pkt, m_bc_receive_timeout); + bool any = ReceiveTimeoutMs(pkt, m_bc_receive_timeout); if (!any) throw NoIncomingDataException("No incoming data"); } bool Connection::TryReceive(NetworkPacket *pkt) { - return Receive(pkt, 0); + return ReceiveTimeoutMs(pkt, 0); } void Connection::Send(session_t peer_id, u8 channelnum, diff --git a/src/network/connection.h b/src/network/connection.h index dec0ffb66..3bd944c00 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -714,7 +714,8 @@ public: void Connect(Address address); bool Connected(); void Disconnect(); - void Receive(NetworkPacket* pkt); + bool ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms); + void Receive(NetworkPacket *pkt); bool TryReceive(NetworkPacket *pkt); void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable); session_t GetPeerID() const { return m_peer_id; } @@ -747,8 +748,6 @@ protected: // Command queue: user -> SendThread MutexedQueue m_command_queue; - bool Receive(NetworkPacket *pkt, u32 timeout); - void putEvent(ConnectionEventPtr e); void TriggerSend(); diff --git a/src/server.cpp b/src/server.cpp index af79632e5..1949de288 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -106,26 +106,33 @@ void *ServerThread::run() /* * The real business of the server happens on the ServerThread. * How this works: - * AsyncRunStep() runs an actual server step as soon as enough time has - * passed (dedicated_server_loop keeps track of that). - * Receive() blocks at least(!) 30ms waiting for a packet (so this loop - * doesn't busy wait) and will process any remaining packets. + * AsyncRunStep() (which runs the actual server step) is called at the + * server-step frequency. Receive() is used for waiting between the steps. */ try { - m_server->AsyncRunStep(true); + m_server->AsyncRunStep(0.0f, true); } catch (con::ConnectionBindFailed &e) { m_server->setAsyncFatalError(e.what()); } catch (LuaError &e) { m_server->setAsyncFatalError(e); } + float dtime = 0.0f; + while (!stopRequested()) { ScopeProfiler spm(g_profiler, "Server::RunStep() (max)", SPT_MAX); - try { - m_server->AsyncRunStep(); - m_server->Receive(); + u64 t0 = porting::getTimeUs(); + + const Server::StepSettings step_settings = m_server->getStepSettings(); + + try { + m_server->AsyncRunStep(step_settings.pause ? 0.0f : dtime); + + const float remaining_time = step_settings.steplen + - 1e-6f * (porting::getTimeUs() - t0); + m_server->Receive(remaining_time); } catch (con::PeerNotFoundException &e) { infostream<<"Server: PeerNotFoundException"<setAsyncFatalError(e); } + + dtime = 1e-6f * (porting::getTimeUs() - t0); } END_DEBUG_EXCEPTION_HANDLER @@ -574,15 +583,8 @@ void Server::stop() infostream<<"Server: Threads stopped"< DTIME_LIMIT) - dtime = DTIME_LIMIT; - { - MutexAutoLock lock(m_step_dtime_mutex); - m_step_dtime += dtime; - } // Throw if fatal error occurred in thread std::string async_err = m_async_fatal_error.get(); if (!async_err.empty()) { @@ -595,30 +597,18 @@ void Server::step(float dtime) } } -void Server::AsyncRunStep(bool initial_step) +void Server::AsyncRunStep(float dtime, bool initial_step) { - - float dtime; - { - MutexAutoLock lock1(m_step_dtime_mutex); - dtime = m_step_dtime; - } - { // Send blocks to clients SendBlocks(dtime); } - if((dtime < 0.001) && !initial_step) + if ((dtime < 0.001f) && !initial_step) return; ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG); - { - MutexAutoLock lock1(m_step_dtime_mutex); - m_step_dtime -= dtime; - } - /* Update uptime */ @@ -1048,25 +1038,27 @@ void Server::AsyncRunStep(bool initial_step) m_shutdown_state.tick(dtime, this); } -void Server::Receive() +void Server::Receive(float timeout) { + const u64 t0 = porting::getTimeUs(); + const float timeout_us = timeout * 1e6f; + auto remaining_time_us = [&]() -> float { + return std::max(0.0f, timeout_us - (porting::getTimeUs() - t0)); + }; + NetworkPacket pkt; session_t peer_id; - bool first = true; for (;;) { pkt.clear(); peer_id = 0; try { - /* - In the first iteration *wait* for a packet, afterwards process - all packets that are immediately available (no waiting). - */ - if (first) { - m_con->Receive(&pkt); - first = false; - } else { - if (!m_con->TryReceive(&pkt)) - return; + if (!m_con->ReceiveTimeoutMs(&pkt, + (u32)remaining_time_us() / 1000)) { + // No incoming data. + // Already break if there's 1ms left, as ReceiveTimeoutMs is too coarse + // and a faster server-step is better than busy waiting. + if (remaining_time_us() < 1000.0f) + break; } peer_id = pkt.getPeerId(); @@ -1085,8 +1077,6 @@ void Server::Receive() DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); } catch (const con::PeerNotFoundException &e) { // Do nothing - } catch (const con::NoIncomingDataException &e) { - return; } } } @@ -3953,21 +3943,24 @@ void dedicated_server_loop(Server &server, bool &kill) IntervalLimiter m_profiler_interval; - static thread_local const float steplen = - g_settings->getFloat("dedicated_server_step"); - static thread_local const float profiler_print_interval = - g_settings->getFloat("profiler_print_interval"); + constexpr float steplen = 0.05f; // always 50 ms + const float profiler_print_interval = g_settings->getFloat("profiler_print_interval"); + + server.setStepSettings(Server::StepSettings{ + g_settings->getFloat("dedicated_server_step"), + false + }); /* - * The dedicated server loop only does time-keeping (in Server::step) and - * provides a way to main.cpp to kill the server externally (bool &kill). + * The dedicated server loop only provides a way to main.cpp to kill the + * server externally (bool &kill). */ for(;;) { // This is kind of a hack but can be done like this // because server.step() is very light - sleep_ms((int)(steplen*1000.0)); - server.step(steplen); + sleep_ms((int)(steplen*1000.0f)); + server.step(); if (server.isShutdownRequested() || kill) break; diff --git a/src/server.h b/src/server.h index a4f975432..802612934 100644 --- a/src/server.h +++ b/src/server.h @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chatmessage.h" #include "sound.h" #include "translation.h" +#include #include #include #include @@ -157,12 +158,12 @@ public: void start(); void stop(); - // This is mainly a way to pass the time to the server. // Actual processing is done in another thread. - void step(float dtime); + // This just checks if there was an error in that thread. + void step(); // This is run by ServerThread and does the actual processing - void AsyncRunStep(bool initial_step=false); - void Receive(); + void AsyncRunStep(float dtime, bool initial_step = false); + void Receive(float timeout); PlayerSAO* StageTwoClientInit(session_t peer_id); /* @@ -293,6 +294,14 @@ public: inline bool isSingleplayer() const { return m_simple_singleplayer_mode; } + struct StepSettings { + float steplen; + bool pause; + }; + + void setStepSettings(StepSettings spdata) { m_step_settings.store(spdata); } + StepSettings getStepSettings() { return m_step_settings.load(); } + inline void setAsyncFatalError(const std::string &error) { m_async_fatal_error.set(error); } inline void setAsyncFatalError(const LuaError &e) @@ -624,10 +633,8 @@ private: /* Threads */ - // A buffer for time steps - // step() increments and AsyncRunStep() run by m_thread reads it. - float m_step_dtime = 0.0f; - std::mutex m_step_dtime_mutex; + // Set by Game + std::atomic m_step_settings{{0.1f, false}}; // The server mainly operates in this thread ServerThread *m_thread = nullptr; -- 2.46.0 From 961652c2e9db075eca3a5bb3a9ecaade5f6c8442 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 23 Dec 2023 13:02:17 +0100 Subject: [PATCH 41/85] Address some clang-tidy warnings --- src/clientiface.cpp | 5 ++--- src/database/database-sqlite3.cpp | 4 ++-- src/inventory.cpp | 2 +- src/inventory.h | 2 +- src/mapgen/mg_schematic.cpp | 2 +- src/script/cpp_api/s_mainmenu.cpp | 4 ++-- src/script/cpp_api/s_mainmenu.h | 4 ++-- src/script/lua_api/l_item.cpp | 2 +- src/script/lua_api/l_mainmenu.cpp | 14 +++++++------- src/script/lua_api/l_mainmenu.h | 6 +++--- src/settings.cpp | 5 ++--- src/settings.h | 2 +- src/texture_override.cpp | 6 +++--- src/texture_override.h | 6 +++--- src/util/enriched_string.cpp | 3 ++- src/util/enriched_string.h | 2 +- src/util/string.cpp | 2 +- 17 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 33d07364a..c7166ce51 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -261,10 +261,9 @@ void RemoteClient::GetNextBlocks ( Get the border/face dot coordinates of a "d-radiused" box */ - std::vector list = FacePositionCache::getFacePositions(d); + const auto &list = FacePositionCache::getFacePositions(d); - std::vector::iterator li; - for (li = list.begin(); li != list.end(); ++li) { + for (auto li = list.begin(); li != list.end(); ++li) { v3s16 p = *li + center; /* diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index e73f9c77f..6f6ad341e 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -942,8 +942,8 @@ void ModStorageDatabaseSQLite3::listMods(std::vector *res) return 0; }, (void *) res, &errmsg); if (status != SQLITE_OK) { - DatabaseException e(std::string("Error trying to list mods with metadata: ") + errmsg); + auto msg = std::string("Error trying to list mods with metadata: ") + errmsg; sqlite3_free(errmsg); - throw e; + throw DatabaseException(msg); } } diff --git a/src/inventory.cpp b/src/inventory.cpp index c99147323..a0f0ea9a2 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -392,7 +392,7 @@ bool ItemStack::itemFits(ItemStack newitem, return newitem.empty(); } -bool ItemStack::stacksWith(ItemStack other) const +bool ItemStack::stacksWith(const ItemStack &other) const { return (this->name == other.name && this->wear == other.wear && diff --git a/src/inventory.h b/src/inventory.h index 3109246d4..64ca857fc 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -164,7 +164,7 @@ struct ItemStack // Checks if another itemstack would stack with this one. // Does not check if the item actually fits in the stack. - bool stacksWith(ItemStack other) const; + bool stacksWith(const ItemStack &other) const; // Takes some items. // If there are not enough, takes as many as it can. diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp index 1e44ae34e..28a1abf83 100644 --- a/src/mapgen/mg_schematic.cpp +++ b/src/mapgen/mg_schematic.cpp @@ -625,7 +625,7 @@ void Schematic::condenseContentIds() numids++; m_nodenames.push_back(m_ndef->get(c).name); - nodeidmap.emplace(std::make_pair(c, id)); + nodeidmap.emplace(c, id); } else { id = it->second; } diff --git a/src/script/cpp_api/s_mainmenu.cpp b/src/script/cpp_api/s_mainmenu.cpp index 1e9ba3a41..290758e4a 100644 --- a/src/script/cpp_api/s_mainmenu.cpp +++ b/src/script/cpp_api/s_mainmenu.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_internal.h" #include "common/c_converter.h" -void ScriptApiMainMenu::setMainMenuData(MainMenuDataForScript *data) +void ScriptApiMainMenu::setMainMenuData(const MainMenuDataForScript *data) { SCRIPTAPI_PRECHECKHEADER @@ -38,7 +38,7 @@ void ScriptApiMainMenu::setMainMenuData(MainMenuDataForScript *data) lua_pop(L, 1); } -void ScriptApiMainMenu::handleMainMenuEvent(std::string text) +void ScriptApiMainMenu::handleMainMenuEvent(const std::string &text) { SCRIPTAPI_PRECHECKHEADER diff --git a/src/script/cpp_api/s_mainmenu.h b/src/script/cpp_api/s_mainmenu.h index 470577a29..7085c649b 100644 --- a/src/script/cpp_api/s_mainmenu.h +++ b/src/script/cpp_api/s_mainmenu.h @@ -29,13 +29,13 @@ public: * Hand over MainMenuDataForScript to lua to inform lua of the content * @param data the data */ - void setMainMenuData(MainMenuDataForScript *data); + void setMainMenuData(const MainMenuDataForScript *data); /** * process events received from formspec * @param text events in textual form */ - void handleMainMenuEvent(std::string text); + void handleMainMenuEvent(const std::string &text); /** * process field data received from formspec diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index c245ba5ba..c68046b5d 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -678,7 +678,7 @@ int ModApiItem::l_get_content_id(lua_State *L) // If this is called at mod load time, NodeDefManager isn't aware of // aliases yet, so we need to handle them manually - std::string alias_name = idef->getAlias(name); + const auto &alias_name = idef->getAlias(name); content_t content_id; if (alias_name != name) { diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index f3aa22ebc..eef3cd635 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" /******************************************************************************/ -std::string ModApiMainMenu::getTextData(lua_State *L, std::string name) +std::string ModApiMainMenu::getTextData(lua_State *L, const std::string &name) { lua_getglobal(L, "gamedata"); @@ -56,7 +56,7 @@ std::string ModApiMainMenu::getTextData(lua_State *L, std::string name) } /******************************************************************************/ -int ModApiMainMenu::getIntegerData(lua_State *L, std::string name,bool& valid) +int ModApiMainMenu::getIntegerData(lua_State *L, const std::string &name, bool& valid) { lua_getglobal(L, "gamedata"); @@ -65,14 +65,14 @@ int ModApiMainMenu::getIntegerData(lua_State *L, std::string name,bool& valid) if(lua_isnil(L, -1)) { valid = false; return -1; - } + } valid = true; return luaL_checkinteger(L, -1); } /******************************************************************************/ -int ModApiMainMenu::getBoolData(lua_State *L, std::string name,bool& valid) +int ModApiMainMenu::getBoolData(lua_State *L, const std::string &name, bool& valid) { lua_getglobal(L, "gamedata"); @@ -81,7 +81,7 @@ int ModApiMainMenu::getBoolData(lua_State *L, std::string name,bool& valid) if(lua_isnil(L, -1)) { valid = false; return false; - } + } valid = true; return readParam(L, -1); @@ -553,7 +553,7 @@ int ModApiMainMenu::l_create_world(lua_State *L) // Set the settings for world creation // this is a bad hack but the best we have right now.. StringMap backup; - for (auto it : use_settings) { + for (auto &it : use_settings) { if (g_settings->existsLocal(it.first)) backup[it.first] = g_settings->get(it.first); g_settings->set(it.first, it.second); @@ -569,7 +569,7 @@ int ModApiMainMenu::l_create_world(lua_State *L) } // Restore previous settings - for (auto it : use_settings) { + for (auto &it : use_settings) { auto it2 = backup.find(it.first); if (it2 == backup.end()) g_settings->remove(it.first); // wasn't set before diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h index 6cb8b6d2c..a5fff3872 100644 --- a/src/script/lua_api/l_mainmenu.h +++ b/src/script/lua_api/l_mainmenu.h @@ -34,7 +34,7 @@ private: * @param name name of variable to read * @return string value of requested variable */ - static std::string getTextData(lua_State *L, std::string name); + static std::string getTextData(lua_State *L, const std::string &name); /** * read an integer variable from gamedata table within lua stack @@ -42,7 +42,7 @@ private: * @param name name of variable to read * @return integer value of requested variable */ - static int getIntegerData(lua_State *L, std::string name,bool& valid); + static int getIntegerData(lua_State *L, const std::string &name, bool& valid); /** * read a bool variable from gamedata table within lua stack @@ -50,7 +50,7 @@ private: * @param name name of variable to read * @return bool value of requested variable */ - static int getBoolData(lua_State *L, std::string name,bool& valid); + static int getBoolData(lua_State *L, const std::string &name ,bool& valid); /** * Checks if a path may be modified. Paths in the temp directory or the user diff --git a/src/settings.cpp b/src/settings.cpp index b537005aa..8b89bf489 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -403,7 +403,7 @@ bool Settings::updateConfigFile(const char *filename) bool Settings::parseCommandLine(int argc, char *argv[], - std::map &allowed_options) + const std::map &allowed_options) { int nonopt_index = 0; for (int i = 1; i < argc; i++) { @@ -424,8 +424,7 @@ bool Settings::parseCommandLine(int argc, char *argv[], std::string name = arg_name.substr(2); - std::map::iterator n; - n = allowed_options.find(name); + auto n = allowed_options.find(name); if (n == allowed_options.end()) { errorstream << "Unknown command-line parameter \"" << arg_name << "\"" << std::endl; diff --git a/src/settings.h b/src/settings.h index 4b0787343..1aeec0255 100644 --- a/src/settings.h +++ b/src/settings.h @@ -147,7 +147,7 @@ public: bool updateConfigFile(const char *filename); // NOTE: Types of allowed_options are ignored. Returns success. bool parseCommandLine(int argc, char *argv[], - std::map &allowed_options); + const std::map &allowed_options); bool parseConfigLines(std::istream &is); void writeLines(std::ostream &os, u32 tab_depth=0) const; diff --git a/src/texture_override.cpp b/src/texture_override.cpp index 88f754f88..7dc9dfa8c 100644 --- a/src/texture_override.cpp +++ b/src/texture_override.cpp @@ -46,7 +46,7 @@ static const std::map override_LUT = { { "*", OverrideTarget::ALL_FACES } }; -TextureOverrideSource::TextureOverrideSource(std::string filepath) +TextureOverrideSource::TextureOverrideSource(const std::string &filepath) { std::ifstream infile(filepath.c_str()); std::string line; @@ -115,7 +115,7 @@ TextureOverrideSource::TextureOverrideSource(std::string filepath) } //! Get all overrides that apply to item definitions -std::vector TextureOverrideSource::getItemTextureOverrides() +std::vector TextureOverrideSource::getItemTextureOverrides() const { std::vector found_overrides; @@ -128,7 +128,7 @@ std::vector TextureOverrideSource::getItemTextureOverrides() } //! Get all overrides that apply to node definitions -std::vector TextureOverrideSource::getNodeTileOverrides() +std::vector TextureOverrideSource::getNodeTileOverrides() const { std::vector found_overrides; diff --git a/src/texture_override.h b/src/texture_override.h index 237efcba1..19e55689c 100644 --- a/src/texture_override.h +++ b/src/texture_override.h @@ -70,13 +70,13 @@ struct TextureOverride class TextureOverrideSource { public: - TextureOverrideSource(std::string filepath); + TextureOverrideSource(const std::string &filepath); //! Get all overrides that apply to item definitions - std::vector getItemTextureOverrides(); + std::vector getItemTextureOverrides() const; //! Get all overrides that apply to node definitions - std::vector getNodeTileOverrides(); + std::vector getNodeTileOverrides() const; private: std::vector m_overrides; diff --git a/src/util/enriched_string.cpp b/src/util/enriched_string.cpp index 941f8b08d..7edbf8eb7 100644 --- a/src/util/enriched_string.cpp +++ b/src/util/enriched_string.cpp @@ -59,10 +59,11 @@ void EnrichedString::clear() m_background = irr::video::SColor(0, 0, 0, 0); } -void EnrichedString::operator=(const wchar_t *str) +EnrichedString &EnrichedString::operator=(const wchar_t *str) { clear(); addAtEnd(translate_string(std::wstring(str)), m_default_color); + return *this; } void EnrichedString::addAtEnd(const std::wstring &s, SColor initial_color) diff --git a/src/util/enriched_string.h b/src/util/enriched_string.h index 4e249eac5..b48408ac6 100644 --- a/src/util/enriched_string.h +++ b/src/util/enriched_string.h @@ -34,7 +34,7 @@ public: const video::SColor &color = video::SColor(255, 255, 255, 255)); EnrichedString(const std::wstring &string, const std::vector &colors); - void operator=(const wchar_t *str); + EnrichedString &operator=(const wchar_t *str); void clear(); diff --git a/src/util/string.cpp b/src/util/string.cpp index 52b7c71ff..1ac295bb4 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -846,7 +846,7 @@ std::string sanitizeDirName(const std::string &str, const std::string &optional_ { std::wstring safe_name = utf8_to_wide(str); - for (std::wstring disallowed_name : disallowed_dir_names) { + for (auto &disallowed_name : disallowed_dir_names) { if (str_equal(safe_name, disallowed_name, true)) { safe_name = utf8_to_wide(optional_prefix) + safe_name; break; -- 2.46.0 From 094c433e587946ae0c3943bc6481876ea783842d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 23 Dec 2023 14:20:24 +0100 Subject: [PATCH 42/85] Update clang-tidy workflow --- .github/workflows/cpp_lint.yml | 7 +++++-- util/ci/clang-tidy.sh | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index a5e49277b..f15ba705f 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -23,15 +23,18 @@ on: - 'util/ci/**' - '.github/workflows/**.yml' +env: + CLANG_TIDY: clang-tidy-15 + jobs: clang_tidy: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install deps run: | source ./util/ci/common.sh - install_linux_deps clang-tidy-9 + install_linux_deps $CLANG_TIDY - name: Run clang-tidy run: | diff --git a/util/ci/clang-tidy.sh b/util/ci/clang-tidy.sh index e678cf3b9..99f419395 100755 --- a/util/ci/clang-tidy.sh +++ b/util/ci/clang-tidy.sh @@ -3,11 +3,11 @@ cmake -B build -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DRUN_IN_PLACE=TRUE \ - -DENABLE_{GETTEXT,SOUND}=FALSE \ + -DENABLE_GETTEXT=FALSE \ -DBUILD_SERVER=TRUE cmake --build build --target GenerateVersion ./util/ci/run-clang-tidy.py \ - -clang-tidy-binary=clang-tidy-9 -p build \ + -clang-tidy-binary=$CLANG_TIDY -p build \ -quiet -config="$(cat .clang-tidy)" \ 'src/.*' -- 2.46.0 From 5405a558fd1bd76c4d04aa409c43ef31e3f39640 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 25 Dec 2023 19:47:34 +0100 Subject: [PATCH 43/85] Fix minor issue with log_deprecated() --- src/script/common/c_internal.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index 1ca9f3767..b80ee6e40 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -180,16 +180,17 @@ void log_deprecated(lua_State *L, std::string message, int stack_depth, bool onc if (mode == DeprecatedHandlingMode::Ignore) return; + bool log = true; if (once) { - script_log_unique(L, message, warningstream, stack_depth); + log = script_log_unique(L, message, warningstream, stack_depth); } else { script_log_add_source(L, message, stack_depth); warningstream << message << std::endl; } if (mode == DeprecatedHandlingMode::Error) - script_error(L, LUA_ERRRUN, NULL, NULL); - else + script_error(L, LUA_ERRRUN, nullptr, nullptr); + else if (log) infostream << script_get_backtrace(L) << std::endl; } -- 2.46.0 From 524721ee275875d4c76808bb0c9cb0ed3ccb3ac1 Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Tue, 26 Dec 2023 11:16:30 +0100 Subject: [PATCH 44/85] Remove non-existent textures from texture_packs.md These textures were removed 5 years ago by 326eeca306f7bfb53ae3685eef18978dd81e587e. --- doc/texture_packs.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/texture_packs.md b/doc/texture_packs.md index c1bd1dd86..bea1af93a 100644 --- a/doc/texture_packs.md +++ b/doc/texture_packs.md @@ -133,11 +133,6 @@ are placeholders intended to be overwritten by the game. ### Android textures -* `down_arrow.png` -* `left_arrow.png` -* `right_arrow.png` -* `up_arrow.png` - * `drop_btn.png` * `fast_btn.png` * `fly_btn.png` -- 2.46.0 From 335af393f09b3629587f14d41a90ded4a3cbddcd Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Tue, 26 Dec 2023 11:27:42 +0100 Subject: [PATCH 45/85] Make the loading screen progress bar respect "gui_scaling" --- src/client/renderingengine.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index e94de87bb..57f658bfb 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -249,8 +249,10 @@ void RenderingEngine::draw_load_screen(const std::wstring &text, #ifndef __ANDROID__ const core::dimension2d &img_size = progress_img_bg->getSize(); - u32 imgW = rangelim(img_size.Width, 200, 600) * getDisplayDensity(); - u32 imgH = rangelim(img_size.Height, 24, 72) * getDisplayDensity(); + float density = g_settings->getFloat("gui_scaling", 0.5f, 20.0f) * + getDisplayDensity(); + u32 imgW = rangelim(img_size.Width, 200, 600) * density; + u32 imgH = rangelim(img_size.Height, 24, 72) * density; #else const core::dimension2d img_size(256, 48); float imgRatio = (float)img_size.Height / img_size.Width; -- 2.46.0 From 5054918efc98afbedaee3032fe558636aa059fbe Mon Sep 17 00:00:00 2001 From: Simon Boehm Date: Wed, 27 Dec 2023 02:44:54 -0800 Subject: [PATCH 46/85] MacOS: Add codesigning instructions to docs (#14060) --- doc/compiling/macos.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/compiling/macos.md b/doc/compiling/macos.md index 460c8d99e..34d6aa675 100644 --- a/doc/compiling/macos.md +++ b/doc/compiling/macos.md @@ -33,13 +33,15 @@ mkdir build cd build cmake .. \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \ -DCMAKE_FIND_FRAMEWORK=LAST \ -DCMAKE_INSTALL_PREFIX=../build/macos/ \ -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE make -j$(sysctl -n hw.logicalcpu) make install + +# M1 Macs w/ MacOS >= BigSur +codesign --force --deep -s - macos/minetest.app ``` ## Run -- 2.46.0 From 93dfa8a6d8477a0d3555ca2bac19731d51e26017 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 27 Dec 2023 11:56:48 +0100 Subject: [PATCH 47/85] Optimize and improve built-in PNG writer (#14020) --- builtin/game/misc_s.lua | 7 ++ doc/lua_api.md | 5 +- games/devtest/mods/testnodes/textures.lua | 30 ++++++- src/util/png.cpp | 104 ++++++++++++++++++---- 4 files changed, 120 insertions(+), 26 deletions(-) diff --git a/builtin/game/misc_s.lua b/builtin/game/misc_s.lua index 93d2bafa8..af4002bf2 100644 --- a/builtin/game/misc_s.lua +++ b/builtin/game/misc_s.lua @@ -64,6 +64,13 @@ function core.encode_png(width, height, data, compression) error("Incorrect type for 'height', expected number, got " .. type(height)) end + if width < 1 then + error("Incorrect value for 'width', must be at least 1") + end + if height < 1 then + error("Incorrect value for 'height', must be at least 1") + end + local expected_byte_count = width * height * 4 if type(data) ~= "table" and type(data) ~= "string" then diff --git a/doc/lua_api.md b/doc/lua_api.md index ab4ff52cc..4ba2e32da 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5418,9 +5418,8 @@ Utilities * `compression`: Optional zlib compression level, number in range 0 to 9. The data is one-dimensional, starting in the upper left corner of the image and laid out in scanlines going from left to right, then top to bottom. - Please note that it's not safe to use string.char to generate raw data, - use `colorspec_to_bytes` to generate raw RGBA values in a predictable way. - The resulting PNG image is always 32-bit. Palettes are not supported at the moment. + You can use `colorspec_to_bytes` to generate raw RGBA values. + Palettes are not supported at the moment. You may use this to procedurally generate textures during server init. * `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a percent sign followed by two hex digits. See diff --git a/games/devtest/mods/testnodes/textures.lua b/games/devtest/mods/testnodes/textures.lua index 8e9fec515..9ecf0cc12 100644 --- a/games/devtest/mods/testnodes/textures.lua +++ b/games/devtest/mods/testnodes/textures.lua @@ -105,6 +105,19 @@ local function gen_checkers(w, h, tile) return r end +-- The engine should perform color reduction of the generated PNG in certain +-- cases, so we have this helper to check the result +local function encode_and_check(w, h, ctype, data) + local ret = core.encode_png(w, h, data) + assert(ret:sub(1, 8) == "\137PNG\r\n\026\n", "missing png signature") + assert(ret:sub(9, 16) == "\000\000\000\rIHDR", "didn't find ihdr chunk") + local ctype_actual = ret:byte(26) -- Color Type (1 byte) + ctype = ({rgba=6, rgb=2, gray=0})[ctype] + assert(ctype_actual == ctype, "png should have color type " .. ctype .. + " but actually has " .. ctype_actual) + return ret +end + local fractal = mandelbrot(512, 512, 128) local frac_emb = mandelbrot(64, 64, 64) local checker = gen_checkers(512, 512, 32) @@ -129,17 +142,21 @@ for i=1, #fractal do b = floor(abs(1 - fractal[i]) * 255), a = 255, } - data_ck[i] = checker[i] > 0 and "#F80" or "#000" + data_ck[i] = checker[i] > 0 and "#888" or "#000" end +fractal = nil +frac_emb = nil +checker = nil + local textures_path = minetest.get_modpath( minetest.get_current_modname() ) .. "/textures/" minetest.safe_file_write( textures_path .. "testnodes_generated_mb.png", - minetest.encode_png(512,512,data_mb) + encode_and_check(512, 512, "rgb", data_mb) ) minetest.safe_file_write( textures_path .. "testnodes_generated_ck.png", - minetest.encode_png(512,512,data_ck) + encode_and_check(512, 512, "gray", data_ck) ) minetest.register_node("testnodes:generated_png_mb", { @@ -155,7 +172,8 @@ minetest.register_node("testnodes:generated_png_ck", { groups = { dig_immediate = 2 }, }) -local png_emb = "[png:" .. minetest.encode_base64(minetest.encode_png(64,64,data_emb)) +local png_emb = "[png:" .. minetest.encode_base64( + encode_and_check(64, 64, "rgba", data_emb)) minetest.register_node("testnodes:generated_png_emb", { description = S("Generated In-Band Mandelbrot PNG Test Node"), @@ -182,6 +200,10 @@ minetest.register_node("testnodes:generated_png_dst_emb", { groups = { dig_immediate = 2 }, }) +data_emb = nil +data_mb = nil +data_ck = nil + --[[ The following nodes can be used to demonstrate the TGA format support. diff --git a/src/util/png.cpp b/src/util/png.cpp index 698cbc9a5..b4bc6b72f 100755 --- a/src/util/png.cpp +++ b/src/util/png.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "png.h" #include +#include #include #include #include @@ -26,43 +27,108 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" #include "irrlichttypes.h" -static void writeChunk(std::ostringstream &target, const std::string &chunk_str) +enum { + COLOR_GRAY = 0, + COLOR_RGB = 2, + COLOR_RGBA = 6, +}; + +static void writeChunk(std::string &target, const std::string &chunk_str) { assert(chunk_str.size() >= 4); assert(chunk_str.size() - 4 < U32_MAX); - writeU32(target, chunk_str.size() - 4); // Write length minus the identifier - target << chunk_str; - writeU32(target, crc32(0,(const u8*)chunk_str.data(), chunk_str.size())); + u8 tmp[4]; + target.reserve(target.size() + 4 + chunk_str.size() + 4); + + writeU32(tmp, chunk_str.size() - 4); // Length minus the identifier + target.append(reinterpret_cast(tmp), 4); + target.append(chunk_str); // Data + const u32 csum = crc32(0, reinterpret_cast(chunk_str.data()), + chunk_str.size()); + writeU32(tmp, csum); // CRC32 checksum + target.append(reinterpret_cast(tmp), 4); +} + +static std::optional reduceColor(const u8 *data, u32 width, u32 height, std::string &new_data) +{ + const u32 npixels = width * height; + // check if the alpha channel is all opaque + for (u32 i = 0; i < npixels; i++) { + if (data[4*i + 3] != 255) + return std::nullopt; + } + + // check if RGB components are identical + bool gray = true; + for (u32 i = 0; i < npixels; i++) { + const u8 *pixel = &data[4*i]; + if (pixel[0] != pixel[1] || pixel[1] != pixel[2]) { + gray = false; + break; + } + } + + if (gray) { + // convert to grayscale + new_data.resize(width * height); + u8 *dst = reinterpret_cast(new_data.data()); + for (u32 i = 0; i < npixels; i++) + dst[i] = data[4*i]; + return COLOR_GRAY; + } else { + // convert to RGB + new_data.resize(width * 3 * height); + u8 *dst = reinterpret_cast(new_data.data()); + for (u32 i = 0; i < npixels; i++) + memcpy(&dst[3*i], &data[4*i], 3); + return COLOR_RGB; + } } std::string encodePNG(const u8 *data, u32 width, u32 height, s32 compression) { - std::ostringstream file(std::ios::binary); - file << "\x89PNG\r\n\x1a\n"; + u8 color_type = COLOR_RGBA; + std::string new_data; + if (compression == Z_DEFAULT_COMPRESSION || compression >= 2) { + // try to reduce the image data to grayscale or RGB + if (auto ret = reduceColor(data, width, height, new_data); ret.has_value()) { + color_type = ret.value(); + assert(!new_data.empty()); + data = reinterpret_cast(new_data.data()); + } + } + + std::string file; + file.append("\x89PNG\r\n\x1a\n"); { - std::ostringstream IHDR(std::ios::binary); - IHDR << "IHDR"; - writeU32(IHDR, width); - writeU32(IHDR, height); - // 8 bpp, color type 6 (RGBA) - IHDR.write("\x08\x06\x00\x00\x00", 5); - writeChunk(file, IHDR.str()); + std::ostringstream header(std::ios::binary); + header << "IHDR"; + writeU32(header, width); + writeU32(header, height); + writeU8(header, 8); // bpp + writeU8(header, color_type); + header.write("\x00\x00\x00", 3); + writeChunk(file, header.str()); } { std::ostringstream IDAT(std::ios::binary); IDAT << "IDAT"; - std::ostringstream scanlines(std::ios::binary); + const u32 ps = color_type == COLOR_GRAY ? 1 : + (color_type == COLOR_RGB ? 3 : 4); + std::string scanlines; + scanlines.reserve(width * ps * height + height); for(u32 i = 0; i < height; i++) { - scanlines.write("\x00", 1); // Null predictor - scanlines.write((const char*) data + width * 4 * i, width * 4); + scanlines.append(1, 0); // Null predictor + scanlines.append(reinterpret_cast(data + width * ps * i), + width * ps); } - compressZlib(scanlines.str(), IDAT, compression); + compressZlib(scanlines, IDAT, compression); writeChunk(file, IDAT.str()); } - file.write("\x00\x00\x00\x00IEND\xae\x42\x60\x82", 12); + file.append("\x00\x00\x00\x00IEND\xae\x42\x60\x82", 12); - return file.str(); + return file; } -- 2.46.0 From 4f1dbb127a6a3439549ec7966cf799dd07aeec2e Mon Sep 17 00:00:00 2001 From: superfloh247 Date: Wed, 27 Dec 2023 22:19:56 +0100 Subject: [PATCH 48/85] Update CMakeLists.txt to fix MacOS build (#14160) Co-authored-by: sfan5 --- src/CMakeLists.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 082158c6e..3780e31b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -316,9 +316,14 @@ endif() # On clang and gcc, some functionalities of std::atomic require -latomic. # See . -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" - OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(PLATFORM_LIBS ${PLATFORM_LIBS} atomic) +# Note that find_library does not reliably find it so we have to resort to this. +# Also, passing -latomic is not always the same as adding atomic to the library list. +include(CheckCSourceCompiles) +set(CMAKE_REQUIRED_LIBRARIES "-latomic") +check_c_source_compiles("int main(){}" HAVE_LINK_ATOMIC) +set(CMAKE_REQUIRED_LIBRARIES "") +if(HAVE_LINK_ATOMIC) + set(PLATFORM_LIBS ${PLATFORM_LIBS} "-latomic") endif() check_include_files(endian.h HAVE_ENDIAN_H) -- 2.46.0 From 32e492837cbf286aeb91b4b63ecf3c890c71a1bc Mon Sep 17 00:00:00 2001 From: grorp Date: Wed, 27 Dec 2023 22:37:36 +0100 Subject: [PATCH 49/85] Support both mouse and touch input in GUIs in a single binary (#14146) --- src/gui/guiFormSpecMenu.cpp | 49 +++++++++-------------- src/gui/guiHyperText.cpp | 21 ++++------ src/gui/guiInventoryList.cpp | 6 +-- src/gui/modalMenu.cpp | 76 +++++++++++++++++++++--------------- src/gui/modalMenu.h | 26 +++++++++--- 5 files changed, 95 insertions(+), 83 deletions(-) diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 445c5b902..c53c65ded 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3656,22 +3656,18 @@ void GUIFormSpecMenu::drawMenu() NULL, m_client, IT_ROT_HOVERED); } - // On touchscreens, m_pointer is set by GUIModalMenu::preprocessEvent instead. -#ifndef HAVE_TOUCHSCREENGUI - m_pointer = RenderingEngine::get_raw_device()->getCursorControl()->getPosition(); -#endif - /* Draw fields/buttons tooltips and update the mouse cursor */ gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(m_pointer); -#ifndef HAVE_TOUCHSCREENGUI gui::ICursorControl *cursor_control = RenderingEngine::get_raw_device()-> getCursorControl(); - gui::ECURSOR_ICON current_cursor_icon = cursor_control->getActiveIcon(); -#endif + gui::ECURSOR_ICON current_cursor_icon = gui::ECI_NORMAL; + if (cursor_control) + current_cursor_icon = cursor_control->getActiveIcon(); + bool hovered_element_found = false; if (hovered) { @@ -3715,11 +3711,10 @@ void GUIFormSpecMenu::drawMenu() m_tooltips[field.fname].bgcolor); } -#ifndef HAVE_TOUCHSCREENGUI - if (field.ftype != f_HyperText && // Handled directly in guiHyperText + if (cursor_control && + field.ftype != f_HyperText && // Handled directly in guiHyperText current_cursor_icon != field.fcursor_icon) cursor_control->setActiveIcon(field.fcursor_icon); -#endif hovered_element_found = true; @@ -3730,10 +3725,8 @@ void GUIFormSpecMenu::drawMenu() if (!hovered_element_found) { // no element is hovered -#ifndef HAVE_TOUCHSCREENGUI - if (current_cursor_icon != ECI_NORMAL) + if (cursor_control && current_cursor_icon != ECI_NORMAL) cursor_control->setActiveIcon(ECI_NORMAL); -#endif } m_tooltip_element->draw(); @@ -3764,16 +3757,13 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text, v2u32 screenSize = Environment->getVideoDriver()->getScreenSize(); int tooltip_offset_x = m_btn_height; int tooltip_offset_y = m_btn_height; -#ifdef HAVE_TOUCHSCREENGUI - tooltip_offset_x *= 3; - tooltip_offset_y = 0; - if (m_pointer.X > (s32)screenSize.X / 2) - tooltip_offset_x = -(tooltip_offset_x + tooltip_width); - // Hide tooltip after ETIE_LEFT_UP - if (m_pointer.X == 0) - return; -#endif + if (m_pointer_type == PointerType::Touch) { + tooltip_offset_x *= 3; + tooltip_offset_y = 0; + if (m_pointer.X > (s32)screenSize.X / 2) + tooltip_offset_x = -(tooltip_offset_x + tooltip_width); + } // Calculate and set the tooltip position s32 tooltip_x = m_pointer.X + tooltip_offset_x; @@ -4070,6 +4060,11 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode) bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) { + // This must be done first so that GUIModalMenu can set m_pointer_type + // correctly. + if (GUIModalMenu::preprocessEvent(event)) + return true; + // The IGUITabControl renders visually using the skin's selected // font, which we override for the duration of form drawing, // but computes tab hotspots based on how it would have rendered @@ -4147,7 +4142,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) return handled; } - return GUIModalMenu::preprocessEvent(event); + return false; } void GUIFormSpecMenu::tryClose() @@ -4326,14 +4321,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } -#ifdef HAVE_TOUCHSCREENGUI // The second touch (see GUIModalMenu::preprocessEvent() function) ButtonEventType touch = BET_OTHER; if (event.EventType == EET_TOUCH_INPUT_EVENT) { if (event.TouchInput.Event == ETIE_LEFT_UP) touch = BET_RIGHT; } -#endif // Set this number to a positive value to generate a move action // from m_selected_item to s. @@ -4678,7 +4671,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) break; } -#ifdef HAVE_TOUCHSCREENGUI if (touch == BET_RIGHT && m_selected_item && !m_left_dragging) { if (!s.isValid()) { // Not a valid slot @@ -4698,7 +4690,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } } -#endif // Update left-dragged slots if (m_left_dragging && m_left_drag_stacks.size() > 1) { @@ -5067,10 +5058,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } -#ifdef HAVE_TOUCHSCREENGUI if (m_second_touch) return true; // Stop propagating the event -#endif return Parent ? Parent->OnEvent(event) : false; } diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 66771f2e2..8aba3083c 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -1052,14 +1052,10 @@ void GUIHyperText::checkHover(s32 X, s32 Y) } } -#ifndef HAVE_TOUCHSCREENGUI - if (m_drawer.m_hovertag) - RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon( - gui::ECI_HAND); - else - RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon( - gui::ECI_NORMAL); -#endif + ICursorControl *cursor_control = RenderingEngine::get_raw_device()->getCursorControl(); + + if (cursor_control) + cursor_control->setActiveIcon(m_drawer.m_hovertag ? gui::ECI_HAND : gui::ECI_NORMAL); } bool GUIHyperText::OnEvent(const SEvent &event) @@ -1075,12 +1071,11 @@ bool GUIHyperText::OnEvent(const SEvent &event) if (event.EventType == EET_GUI_EVENT && event.GUIEvent.EventType == EGET_ELEMENT_LEFT) { m_drawer.m_hovertag = nullptr; -#ifndef HAVE_TOUCHSCREENGUI - gui::ICursorControl *cursor_control = - RenderingEngine::get_raw_device()->getCursorControl(); - if (cursor_control->isVisible()) + + ICursorControl *cursor_control = RenderingEngine::get_raw_device()->getCursorControl(); + + if (cursor_control && cursor_control->isVisible()) cursor_control->setActiveIcon(gui::ECI_NORMAL); -#endif } if (event.EventType == EET_MOUSE_INPUT_EVENT) { diff --git a/src/gui/guiInventoryList.cpp b/src/gui/guiInventoryList.cpp index 6f1d28e0b..02505d436 100644 --- a/src/gui/guiInventoryList.cpp +++ b/src/gui/guiInventoryList.cpp @@ -152,10 +152,10 @@ void GUIInventoryList::draw() // Add hovering tooltip bool show_tooltip = !item.empty() && hovering && !selected_item; -#ifdef HAVE_TOUCHSCREENGUI // Make it possible to see item tooltips on touchscreens - show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0; -#endif + if (m_fs_menu->getPointerType() == PointerType::Touch) { + show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0; + } if (show_tooltip) { std::string tooltip = orig_item.getDescription(client->idef()); if (m_fs_menu->doTooltipAppendItemname()) diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index d4322c9f7..5d4e03baf 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include +#include #include "client/renderingengine.h" #include "modalMenu.h" #include "gettext.h" @@ -103,7 +104,7 @@ void GUIModalMenu::quitMenu() m_menumgr->deletingMenu(this); this->remove(); #ifdef HAVE_TOUCHSCREENGUI - if (g_touchscreengui && m_touchscreen_visible) + if (g_touchscreengui) g_touchscreengui->show(); #endif } @@ -169,8 +170,6 @@ static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) return false; } -#ifdef HAVE_TOUCHSCREENGUI - bool GUIModalMenu::simulateMouseEvent( gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event) { @@ -194,41 +193,51 @@ bool GUIModalMenu::simulateMouseEvent( default: return false; } - if (preprocessEvent(mouse_event)) - return true; - if (!target) - return false; - return target->OnEvent(mouse_event); + + bool retval; + m_simulated_mouse = true; + do { + if (preprocessEvent(mouse_event)) { + retval = true; + break; + } + if (!target) { + retval = false; + break; + } + retval = target->OnEvent(mouse_event); + } while (false); + m_simulated_mouse = false; + + return retval; } void GUIModalMenu::enter(gui::IGUIElement *hovered) { if (!hovered) return; - sanity_check(!m_hovered); - m_hovered.grab(hovered); + sanity_check(!m_touch_hovered); + m_touch_hovered.grab(hovered); SEvent gui_event{}; gui_event.EventType = EET_GUI_EVENT; - gui_event.GUIEvent.Caller = m_hovered.get(); - gui_event.GUIEvent.EventType = EGET_ELEMENT_HOVERED; + gui_event.GUIEvent.Caller = m_touch_hovered.get(); + gui_event.GUIEvent.EventType = gui::EGET_ELEMENT_HOVERED; gui_event.GUIEvent.Element = gui_event.GUIEvent.Caller; - m_hovered->OnEvent(gui_event); + m_touch_hovered->OnEvent(gui_event); } void GUIModalMenu::leave() { - if (!m_hovered) + if (!m_touch_hovered) return; SEvent gui_event{}; gui_event.EventType = EET_GUI_EVENT; - gui_event.GUIEvent.Caller = m_hovered.get(); - gui_event.GUIEvent.EventType = EGET_ELEMENT_LEFT; - m_hovered->OnEvent(gui_event); - m_hovered.reset(); + gui_event.GUIEvent.Caller = m_touch_hovered.get(); + gui_event.GUIEvent.EventType = gui::EGET_ELEMENT_LEFT; + m_touch_hovered->OnEvent(gui_event); + m_touch_hovered.reset(); } -#endif - bool GUIModalMenu::preprocessEvent(const SEvent &event) { #ifdef __ANDROID__ @@ -268,25 +277,26 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) } #endif -#ifdef HAVE_TOUCHSCREENGUI + // Convert touch events into mouse events. if (event.EventType == EET_TOUCH_INPUT_EVENT) { irr_ptr holder; holder.grab(this); // keep this alive until return (it might be dropped downstream [?]) if (event.TouchInput.touchedCount == 1) { - if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED) - m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y); + m_pointer_type = PointerType::Touch; + m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y); + gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d(m_pointer)); if (event.TouchInput.Event == ETIE_PRESSED_DOWN) Environment->setFocus(hovered); - if (m_hovered != hovered) { + if (m_touch_hovered != hovered) { leave(); enter(hovered); } gui::IGUIElement *focused = Environment->getFocus(); bool ret = simulateMouseEvent(focused, event.TouchInput.Event); - if (!ret && m_hovered != focused) - ret = simulateMouseEvent(m_hovered.get(), event.TouchInput.Event); + if (!ret && m_touch_hovered != focused) + ret = simulateMouseEvent(m_touch_hovered.get(), event.TouchInput.Event); if (event.TouchInput.Event == ETIE_LEFT_UP) leave(); return ret; @@ -306,20 +316,24 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) return true; } } -#endif if (event.EventType == EET_MOUSE_INPUT_EVENT) { - s32 x = event.MouseInput.X; - s32 y = event.MouseInput.Y; + if (!m_simulated_mouse) { + // Only set the pointer type to mouse if this is a real mouse event. + m_pointer_type = PointerType::Mouse; + m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y); + m_touch_hovered.reset(); + } + gui::IGUIElement *hovered = - Environment->getRootGUIElement()->getElementFromPoint( - core::position2d(x, y)); + Environment->getRootGUIElement()->getElementFromPoint(m_pointer); if (!isChild(hovered, this)) { if (DoubleClickDetection(event)) { return true; } } } + return false; } diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h index f811bafc9..30eda8bc9 100644 --- a/src/gui/modalMenu.h +++ b/src/gui/modalMenu.h @@ -23,6 +23,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_ptr.h" #include "util/string.h" +enum class PointerType { + Mouse, + Touch, +}; + class GUIModalMenu; class IMenuManager @@ -58,6 +63,8 @@ public: bool hasAndroidUIInput(); #endif + PointerType getPointerType() { return m_pointer_type; }; + protected: virtual std::wstring getLabelByID(s32 id) = 0; virtual std::string getNameByID(s32 id) = 0; @@ -69,18 +76,25 @@ protected: */ bool DoubleClickDetection(const SEvent &event); + // Stores the last known pointer type. + PointerType m_pointer_type = PointerType::Mouse; + // Stores the last known pointer position. + // If the last input event was a mouse event, it's the cursor position. + // If the last input event was a touch event, it's the finger position. v2s32 m_pointer; v2s32 m_old_pointer; // Mouse position after previous mouse event + v2u32 m_screensize_old; float m_gui_scale; #ifdef __ANDROID__ std::string m_jni_field_name; #endif -#ifdef HAVE_TOUCHSCREENGUI + // This is set to true if the menu is currently processing a second-touch event. bool m_second_touch = false; - bool m_touchscreen_visible = true; -#endif + // This is set to true if the menu is currently processing a mouse event + // that was synthesized by the menu itself from a touch event. + bool m_simulated_mouse = false; private: struct clickpos @@ -102,11 +116,11 @@ private: // wants to launch other menus bool m_allow_focus_removal = false; -#ifdef HAVE_TOUCHSCREENGUI - irr_ptr m_hovered; + // Stuff related to touchscreen input + + irr_ptr m_touch_hovered; bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event); void enter(gui::IGUIElement *element); void leave(); -#endif }; -- 2.46.0 From bc336480e6317fec4eea03b055bcf9602ff633d7 Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Thu, 28 Dec 2023 09:10:11 -0800 Subject: [PATCH 50/85] Avoid short overflow with large viewing ranges (#14175) --- src/clientiface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clientiface.cpp b/src/clientiface.cpp index c7166ce51..6171e2116 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -228,7 +228,8 @@ void RemoteClient::GetNextBlocks ( wanted_range); const s16 d_cull_opt = std::min(adjustDist(m_block_cull_optimize_distance, prop_zoom_fov), wanted_range); - const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE; + // f32 to prevent overflow, it is also what isBlockInSight(...) expects + const f32 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE; s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov), wanted_range); -- 2.46.0 From 467d3a8c62aa39a9eda7095935de1a56c96fb708 Mon Sep 17 00:00:00 2001 From: cx384 Date: Fri, 29 Dec 2023 21:51:02 +0100 Subject: [PATCH 51/85] Rename `hud_elem_type` to `type` (#14065) --- builtin/game/features.lua | 1 + builtin/game/statbars.lua | 6 ++--- clientmods/preview/init.lua | 2 +- doc/client_lua_api.md | 4 +++- doc/lua_api.md | 11 +++++++-- .../mods/testentities/selectionbox.lua | 2 +- games/devtest/mods/testhud/init.lua | 10 ++++---- src/script/common/c_content.cpp | 24 +++++++++++++++++-- 8 files changed, 45 insertions(+), 15 deletions(-) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index c76566ed3..f438743b7 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -29,6 +29,7 @@ core.features = { compress_zstd = true, sound_params_start_time = true, physics_overrides_v2 = true, + hud_def_type_field = true, } function core.has_feature(arg) diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 78d1d2728..72b711bdc 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -3,7 +3,7 @@ local enable_damage = core.settings:get_bool("enable_damage") local bar_definitions = { hp = { - hud_elem_type = "statbar", + type = "statbar", position = {x = 0.5, y = 1}, text = "heart.png", text2 = "heart_gone.png", @@ -14,7 +14,7 @@ local bar_definitions = { offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)}, }, breath = { - hud_elem_type = "statbar", + type = "statbar", position = {x = 0.5, y = 1}, text = "bubble.png", text2 = "bubble_gone.png", @@ -139,7 +139,7 @@ end function core.hud_replace_builtin(hud_name, definition) if type(definition) ~= "table" or - definition.hud_elem_type ~= "statbar" then + (definition.type or definition.hud_elem_type) ~= "statbar" then return false end diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index 46d59ec9a..f8d3a006b 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -33,7 +33,7 @@ end) core.after(1, function() print("armor: " .. dump(core.localplayer:get_armor_groups())) id = core.localplayer:hud_add({ - hud_elem_type = "text", + type = "text", name = "example", number = 0xff0000, position = {x=0, y=1}, diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index 30c6d1f82..ef0d9e1d3 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -1332,8 +1332,10 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or ```lua { - hud_elem_type = "image", -- see HUD element types, default "text" + type = "image", -- see HUD element types, default "text" -- ^ type of HUD element, can be either of "image", "text", "statbar", or "inventory" + hud_elem_type = "image", +-- ^ Deprecated, same as `type`. In case both are specified `type` will be used. position = {x=0.5, y=0.5}, -- ^ Left corner position of element, default `{x=0,y=0}`. name = "", -- default "" diff --git a/doc/lua_api.md b/doc/lua_api.md index 4ba2e32da..7461127c2 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5278,6 +5278,8 @@ Utilities -- liquid_fluidity, liquid_fluidity_smooth, liquid_sink, -- acceleration_default, acceleration_air (5.8.0) physics_overrides_v2 = true, + -- In HUD definitions the field `type` is used and `hud_elem_type` is deprecated (5.9.0) + hud_def_type_field = true, } ``` @@ -7790,7 +7792,7 @@ child will follow movement and rotation of that bone. * `hud_change(id, stat, value)`: change a value of a previously added HUD element. * `stat` supports the same keys as in the hud definition table except for - `"hud_elem_type"`. + `"type"` (or the deprecated `"hud_elem_type"`). * `hud_get(id)`: gets the HUD element definition structure of the specified ID * `hud_set_flags(flags)`: sets specified HUD flags of player. * `flags`: A table with the following fields set to boolean values @@ -10050,9 +10052,14 @@ Used by `ObjectRef:hud_add`. Returned by `ObjectRef:hud_get`. ```lua { - hud_elem_type = "image", + type = "image", -- Type of element, can be "image", "text", "statbar", "inventory", -- "waypoint", "image_waypoint", "compass" or "minimap" + -- If undefined "text" will be used. + + hud_elem_type = "image", + -- Deprecated, same as `type`. + -- In case both are specified `type` will be used. position = {x=0.5, y=0.5}, -- Top left corner position of element diff --git a/games/devtest/mods/testentities/selectionbox.lua b/games/devtest/mods/testentities/selectionbox.lua index 36a8daa41..a455fd459 100644 --- a/games/devtest/mods/testentities/selectionbox.lua +++ b/games/devtest/mods/testentities/selectionbox.lua @@ -66,7 +66,7 @@ minetest.register_globalstep(function() local ent = pointed_thing.ref:get_luaentity() if ent and ent.name == "testentities:selectionbox" then hud_ids[pname] = hud_id or player:hud_add({ - hud_elem_type = "text", -- See HUD element types + type = "text", -- See HUD element types position = {x=0.5, y=0.5}, text = "X", number = 0xFF0000, diff --git a/games/devtest/mods/testhud/init.lua b/games/devtest/mods/testhud/init.lua index 6b55b924b..da07f17e4 100644 --- a/games/devtest/mods/testhud/init.lua +++ b/games/devtest/mods/testhud/init.lua @@ -12,7 +12,7 @@ local font_states = { local font_default_def = { - hud_elem_type = "text", + type = "text", position = {x = 0.5, y = 0.5}, scale = {x = 2, y = 2}, alignment = { x = 0, y = 0 }, @@ -102,14 +102,14 @@ minetest.register_chatcommand("hudwaypoints", { return false end local regular = player:hud_add { - hud_elem_type = "waypoint", + type = "waypoint", name = "regular waypoint", text = "m", number = 0xFFFFFF, world_pos = vector.add(player:get_pos(), {x = 0, y = 1.5, z = 0}) } local reduced_precision = player:hud_add { - hud_elem_type = "waypoint", + type = "waypoint", name = "imprecise waypoint", text = "m (0.1 steps, precision = 10)", precision = 10, @@ -117,7 +117,7 @@ minetest.register_chatcommand("hudwaypoints", { world_pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0}) } local hidden_distance = player:hud_add { - hud_elem_type = "waypoint", + type = "waypoint", name = "waypoint with hidden distance", text = "this text is hidden as well (precision = 0)", precision = 0, @@ -149,7 +149,7 @@ minetest.register_chatcommand("hudwaypoints", { minetest.after(0.5, change, player) end local image_waypoint = player:hud_add { - hud_elem_type = "image_waypoint", + type = "image_waypoint", text = "testhud_waypoint.png", world_pos = player:get_pos(), scale = {x = 3, y = 3}, diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 1756c49c5..97b651ce8 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1959,8 +1959,28 @@ void push_objectRef(lua_State *L, const u16 id) void read_hud_element(lua_State *L, HudElement *elem) { - elem->type = (HudElementType)getenumfield(L, 2, "hud_elem_type", - es_HudElementType, HUD_ELEM_TEXT); + std::string type_string; + bool has_type = getstringfield(L, 2, "type", type_string); + + // Handle deprecated hud_elem_type + std::string deprecated_type_string; + if (getstringfield(L, 2, "hud_elem_type", deprecated_type_string)) { + if (has_type && deprecated_type_string != type_string) { + log_deprecated(L, "Ambiguous HUD element fields \"type\" and \"hud_elem_type\", " + "\"type\" will be used.", 1, true); + } else { + has_type = true; + type_string = deprecated_type_string; + log_deprecated(L, "Deprecated \"hud_elem_type\" field, use \"type\" instead.", + 1, true); + } + } + + int type_enum; + if (has_type && string_to_enum(es_HudElementType, type_enum, type_string)) + elem->type = static_cast(type_enum); + else + elem->type = HUD_ELEM_TEXT; lua_getfield(L, 2, "position"); elem->pos = lua_istable(L, -1) ? read_v2f(L, -1) : v2f(); -- 2.46.0 From ad5e9aa5e38bed7f3407321fa8eb3cb1d8afff73 Mon Sep 17 00:00:00 2001 From: Desour Date: Fri, 29 Dec 2023 15:08:22 +0100 Subject: [PATCH 52/85] Fix AsyncRunStep() skipping steps when dtime < 1 ms --- src/server.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index 1949de288..8c084a573 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -604,7 +604,8 @@ void Server::AsyncRunStep(float dtime, bool initial_step) SendBlocks(dtime); } - if ((dtime < 0.001f) && !initial_step) + // If paused, this function is called with a 0.0f literal + if ((dtime == 0.0f) && !initial_step) return; ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG); -- 2.46.0 From 93c2aff2cfb762a310b31d1b985ba5c19bc14152 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 19 Dec 2023 21:41:48 +0100 Subject: [PATCH 53/85] Clean up OS-specific initialization --- src/main.cpp | 42 +++++++++----------- src/porting.cpp | 86 ++++++++++++++++++++++------------------- src/porting.h | 3 ++ src/porting_android.cpp | 39 ++++++++----------- src/porting_android.h | 12 ------ 5 files changed, 84 insertions(+), 98 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d85afe97a..f849e5dc7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -95,7 +95,7 @@ static void set_allowed_options(OptionList *allowed_options); static void print_help(const OptionList &allowed_options); static void print_allowed_options(const OptionList &allowed_options); -static void print_version(); +static void print_version(std::ostream &os); static void print_worldspecs(const std::vector &worldspecs, std::ostream &os, bool print_name = true, bool print_path = true); static void print_modified_quicktune_values(); @@ -143,6 +143,8 @@ int main(int argc, char *argv[]) g_logger.registerThread("Main"); g_logger.addOutputMaxLevel(&stderr_output, LL_ACTION); + porting::osSpecificInit(); + Settings cmd_args; get_env_opts(cmd_args); bool cmd_args_ok = get_cmdline_opts(argc, argv, &cmd_args); @@ -158,10 +160,13 @@ int main(int argc, char *argv[]) if (cmd_args.getFlag("version")) { porting::attachOrCreateConsole(); - print_version(); + print_version(std::cout); return 0; } + // Debug handler + BEGIN_DEBUG_EXCEPTION_HANDLER + if (!setup_log_params(cmd_args)) return 1; @@ -171,22 +176,13 @@ int main(int argc, char *argv[]) } porting::signal_handler_init(); - -#ifdef __ANDROID__ - porting::initAndroid(); - porting::initializePathsAndroid(); -#else porting::initializePaths(); -#endif if (!create_userdata_path()) { errorstream << "Cannot create user data directory" << std::endl; return 1; } - // Debug handler - BEGIN_DEBUG_EXCEPTION_HANDLER - // List gameids if requested if (cmd_args.exists("gameid") && cmd_args.get("gameid") == "list") { list_game_ids(); @@ -215,7 +211,6 @@ int main(int argc, char *argv[]) if (g_settings->getBool("enable_console")) porting::attachOrCreateConsole(); -#ifndef __ANDROID__ // Run unit tests if (cmd_args.getFlag("run-unittests")) { #if BUILD_UNITTESTS @@ -245,7 +240,6 @@ int main(int argc, char *argv[]) return 1; #endif } -#endif // __ANDROID__ GameStartData game_params; #ifdef SERVER @@ -430,19 +424,20 @@ static void print_allowed_options(const OptionList &allowed_options) } } -static void print_version() +static void print_version(std::ostream &os) { - std::cout << PROJECT_NAME_C " " << g_version_hash + os << PROJECT_NAME_C " " << g_version_hash << " (" << porting::getPlatformName() << ")" << std::endl; #ifndef SERVER - std::cout << "Using Irrlicht " IRRLICHT_SDK_VERSION << std::endl; + os << "Using Irrlicht " IRRLICHT_SDK_VERSION << std::endl; #endif #if USE_LUAJIT - std::cout << "Using " << LUAJIT_VERSION << std::endl; + os << "Using " << LUAJIT_VERSION << std::endl; #else - std::cout << "Using " << LUA_RELEASE << std::endl; + os << "Using " << LUA_RELEASE << std::endl; #endif - std::cout << g_build_info << std::endl; + os << "Running on " << porting::get_sysinfo() << std::endl; + os << g_build_info << std::endl; } static void list_game_ids() @@ -718,10 +713,11 @@ static void uninit_common() static void startup_message() { - infostream << PROJECT_NAME_C << " " << g_version_hash - << "\nwith SER_FMT_VER_HIGHEST_READ=" - << (int)SER_FMT_VER_HIGHEST_READ << ", " - << g_build_info << std::endl; + print_version(infostream); + infostream << "SER_FMT_VER_HIGHEST_READ=" << + TOSTRING(SER_FMT_VER_HIGHEST_READ) << + " LATEST_PROTOCOL_VERSION=" << TOSTRING(LATEST_PROTOCOL_VERSION) + << std::endl; } static bool read_config_file(const Settings &cmd_args) diff --git a/src/porting.cpp b/src/porting.cpp index 7784a671f..0d8d86c3e 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -68,9 +68,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "log.h" #include "util/string.h" -#include +#include #include #include +#include #if !defined(SERVER) && defined(_WIN32) // On Windows export some driver-specific variables to encourage Minetest to be @@ -88,7 +89,7 @@ namespace porting Signal handler (grabs Ctrl-C on POSIX systems) */ -bool g_killed = false; +static bool g_killed = false; bool *signal_handler_killstatus() { @@ -96,9 +97,8 @@ bool *signal_handler_killstatus() } #if !defined(_WIN32) // POSIX - #include -void signal_handler(int sig) +static void signal_handler(int sig) { if (!g_killed) { if (sig == SIGINT) { @@ -127,9 +127,8 @@ void signal_handler_init(void) } #else // _WIN32 - #include -BOOL WINAPI event_handler(DWORD sig) +static BOOL WINAPI event_handler(DWORD sig) { switch (sig) { case CTRL_C_EVENT: @@ -164,11 +163,11 @@ void signal_handler_init(void) Path mangler */ -// Default to RUN_IN_PLACE style relative paths -std::string path_share = ".."; -std::string path_user = ".."; -std::string path_locale = path_share + DIR_DELIM + "locale"; -std::string path_cache = path_user + DIR_DELIM + "cache"; +// Nobody should be reading these before initializePaths() is called +std::string path_share = "UNINITIALIZED"; +std::string path_user = "UNINITIALIZED"; +std::string path_locale = "UNINITIALIZED"; +std::string path_cache = "UNINITIALIZED"; std::string getDataPath(const char *subpath) @@ -176,7 +175,7 @@ std::string getDataPath(const char *subpath) return path_share + DIR_DELIM + subpath; } -void pathRemoveFile(char *path, char delim) +[[maybe_unused]] static void pathRemoveFile(char *path, char delim) { // Remove filename and path delimiter int i; @@ -267,9 +266,9 @@ bool getCurrentWorkingDir(char *buf, size_t len) } -bool getExecPathFromProcfs(char *buf, size_t buflen) +static bool getExecPathFromProcfs(char *buf, size_t buflen) { -#ifndef _WIN32 +#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) buflen--; ssize_t len; @@ -396,10 +395,7 @@ bool getCurrentExecPath(char *buf, size_t len) #endif -//// Non-Windows -#if !defined(_WIN32) - -const char *getHomeOrFail() +[[maybe_unused]] static inline const char *getHomeOrFail() { const char *home = getenv("HOME"); // In rare cases the HOME environment variable may be unset @@ -408,8 +404,6 @@ const char *getHomeOrFail() return home; } -#endif - //// Windows #if defined(_WIN32) @@ -449,6 +443,13 @@ bool setSystemPaths() } +//// Android + +#elif defined(__ANDROID__) + +extern bool setSystemPaths(); // defined in porting_android.cpp + + //// Linux #elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) @@ -457,11 +458,7 @@ bool setSystemPaths() char buf[BUFSIZ]; if (!getCurrentExecPath(buf, sizeof(buf))) { -#ifdef __ANDROID__ - errorstream << "Unable to read bindir "<< std::endl; -#else FATAL_ERROR("Unable to read bindir"); -#endif return false; } @@ -470,7 +467,7 @@ bool setSystemPaths() // Find share directory from these. // It is identified by containing the subdirectory "builtin". - std::list trylist; + std::vector trylist; std::string static_sharedir = STATIC_SHAREDIR; if (!static_sharedir.empty() && static_sharedir != ".") trylist.push_back(static_sharedir); @@ -479,12 +476,7 @@ bool setSystemPaths() DIR_DELIM + PROJECT_NAME); trylist.push_back(bindir + DIR_DELIM ".."); -#ifdef __ANDROID__ - trylist.push_back(path_user); -#endif - - for (std::list::const_iterator - i = trylist.begin(); i != trylist.end(); ++i) { + for (auto i = trylist.begin(); i != trylist.end(); ++i) { const std::string &trypath = *i; if (!fs::PathExists(trypath) || !fs::PathExists(trypath + DIR_DELIM + "builtin")) { @@ -503,7 +495,6 @@ bool setSystemPaths() break; } -#ifndef __ANDROID__ const char *const minetest_user_path = getenv("MINETEST_USER_PATH"); if (minetest_user_path && minetest_user_path[0] != '\0') { path_user = std::string(minetest_user_path); @@ -511,7 +502,6 @@ bool setSystemPaths() path_user = std::string(getHomeOrFail()) + DIR_DELIM "." + PROJECT_NAME; } -#endif return true; } @@ -586,10 +576,9 @@ void migrateCachePath() void initializePaths() { #if RUN_IN_PLACE - char buf[BUFSIZ]; - infostream << "Using relative paths (RUN_IN_PLACE)" << std::endl; + char buf[BUFSIZ]; bool success = getCurrentExecPath(buf, sizeof(buf)) || getExecPathFromProcfs(buf, sizeof(buf)); @@ -627,17 +616,18 @@ void initializePaths() path_user = execpath; } path_cache = path_user + DIR_DELIM + "cache"; + #else infostream << "Using system-wide paths (NOT RUN_IN_PLACE)" << std::endl; if (!setSystemPaths()) errorstream << "Failed to get one or more system-wide path" << std::endl; - -# ifdef _WIN32 +# ifdef __ANDROID__ + sanity_check(!path_cache.empty()); +# elif defined(_WIN32) path_cache = path_user + DIR_DELIM + "cache"; # else - // Initialize path_cache // First try $XDG_CACHE_HOME/PROJECT_NAME const char *cache_dir = getenv("XDG_CACHE_HOME"); const char *home_dir = getenv("HOME"); @@ -651,9 +641,11 @@ void initializePaths() // If neither works, use $PATH_USER/cache path_cache = path_user + DIR_DELIM + "cache"; } +# endif // _WIN32 + // Migrate cache folder to new location if possible migrateCachePath(); -# endif // _WIN32 + #endif // RUN_IN_PLACE infostream << "Detected share path: " << path_share << std::endl; @@ -727,6 +719,15 @@ bool secure_rand_fill_buf(void *buf, size_t len) #endif +#ifndef __ANDROID__ + +void osSpecificInit() +{ + // nothing here yet +} + +#endif + void attachOrCreateConsole() { #ifdef _WIN32 @@ -794,6 +795,11 @@ int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...) return c; } +#ifdef __ANDROID__ +// defined in porting_android.cpp +extern void openURIAndroid(const char *url); +#endif + static bool open_uri(const std::string &uri) { if (uri.find_first_of("\r\n") != std::string::npos) { @@ -804,7 +810,7 @@ static bool open_uri(const std::string &uri) #if defined(_WIN32) return (intptr_t)ShellExecuteA(NULL, NULL, uri.c_str(), NULL, NULL, SW_SHOWNORMAL) > 32; #elif defined(__ANDROID__) - openURIAndroid(uri); + openURIAndroid(uri.c_str()); return true; #elif defined(__APPLE__) const char *argv[] = {"open", uri.c_str(), NULL}; diff --git a/src/porting.h b/src/porting.h index 0c7598805..cd710d191 100644 --- a/src/porting.h +++ b/src/porting.h @@ -301,6 +301,9 @@ inline const char *getPlatformName() bool secure_rand_fill_buf(void *buf, size_t len); +// Call once near beginning of main function. +void osSpecificInit(); + // This attaches to the parents process console, or creates a new one if it doesnt exist. void attachOrCreateConsole(); diff --git a/src/porting_android.cpp b/src/porting_android.cpp index 1ed56015f..4d02eb10a 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -39,23 +39,19 @@ with this program; if not, write to the Free Software Foundation, Inc., extern int main(int argc, char *argv[]); +namespace porting { + void cleanupAndroid(); // used here + bool setSystemPaths(); // used in porting.cpp +} + void android_main(android_app *app) { - int retval = 0; porting::app_global = app; Thread::setName("Main"); char *argv[] = {strdup(PROJECT_NAME), strdup("--verbose"), nullptr}; - try { - main(ARRLEN(argv) - 1, argv); - } catch (std::exception &e) { - errorstream << "Uncaught exception in main thread: " << e.what() << std::endl; - retval = -1; - } catch (...) { - errorstream << "Uncaught exception in main thread!" << std::endl; - retval = -1; - } + int retval = main(ARRLEN(argv) - 1, argv); free(argv[0]); free(argv[1]); @@ -65,8 +61,8 @@ void android_main(android_app *app) } namespace porting { -android_app *app_global; -JNIEnv *jnienv; +android_app *app_global = nullptr; +JNIEnv *jnienv = nullptr; jclass nativeActivity; jclass findClass(const std::string &classname) @@ -86,9 +82,8 @@ jclass findClass(const std::string &classname) return (jclass) jnienv->CallObjectMethod(cls, findClass, strClassName); } -void initAndroid() +void osSpecificInit() { - porting::jnienv = nullptr; JavaVM *jvm = app_global->activity->vm; JavaVMAttachArgs lJavaVMAttachArgs; lJavaVMAttachArgs.version = JNI_VERSION_1_6; @@ -108,8 +103,7 @@ void initAndroid() #ifdef GPROF // in the start-up code - __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME_C, - "Initializing GPROF profiler"); + warningstream << "Initializing GPROF profiler" << std::endl; monstartup("libMinetest.so"); #endif } @@ -117,7 +111,7 @@ void initAndroid() void cleanupAndroid() { #ifdef GPROF - errorstream << "Shutting down GPROF profiler" << std::endl; + warningstream << "Shutting down GPROF profiler" << std::endl; setenv("CPUPROFILE", (path_user + DIR_DELIM + "gmon.out").c_str(), 1); moncleanup(); #endif @@ -137,7 +131,7 @@ static std::string readJavaString(jstring j_str) return str; } -void initializePathsAndroid() +bool setSystemPaths() { // Set user and share paths { @@ -149,7 +143,6 @@ void initializePathsAndroid() std::string str = readJavaString((jstring) result); path_user = str; path_share = str; - path_locale = str + DIR_DELIM + "locale"; } // Set cache path @@ -160,9 +153,9 @@ void initializePathsAndroid() "porting::initializePathsAndroid unable to find Java getCachePath method"); jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, getCachePath); path_cache = readJavaString((jstring) result); - - migrateCachePath(); } + + return true; } void showInputDialog(const std::string &acceptButton, const std::string &hint, @@ -183,7 +176,7 @@ void showInputDialog(const std::string &acceptButton, const std::string &hint, jacceptButton, jhint, jcurrent, jeditType); } -void openURIAndroid(const std::string &url) +void openURIAndroid(const char *url) { jmethodID url_open = jnienv->GetMethodID(nativeActivity, "openURI", "(Ljava/lang/String;)V"); @@ -191,7 +184,7 @@ void openURIAndroid(const std::string &url) FATAL_ERROR_IF(url_open == nullptr, "porting::openURIAndroid unable to find Java openURI method"); - jstring jurl = jnienv->NewStringUTF(url.c_str()); + jstring jurl = jnienv->NewStringUTF(url); jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl); } diff --git a/src/porting_android.h b/src/porting_android.h index d8cfc18d4..e5e75481d 100644 --- a/src/porting_android.h +++ b/src/porting_android.h @@ -36,16 +36,6 @@ extern android_app *app_global; // java <-> c++ interaction interface extern JNIEnv *jnienv; -// do initialization required on android only -void initAndroid(); - -void cleanupAndroid(); - -/** - * Initializes path_* variables for Android - */ -void initializePathsAndroid(); - /** * show text input dialog in java * @param acceptButton text to display on accept button @@ -57,8 +47,6 @@ void initializePathsAndroid(); void showInputDialog(const std::string &acceptButton, const std::string &hint, const std::string ¤t, int editType); -void openURIAndroid(const std::string &url); - /** * Opens a share intent to the file at path * -- 2.46.0 From b8dc34909997f0245160776a21dbeb392c3d6296 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 19 Dec 2023 22:01:28 +0100 Subject: [PATCH 54/85] Clean up gettext initialization --- src/gettext.cpp | 155 +++++++++++++++++++--------------------- src/porting_android.cpp | 9 ++- src/porting_android.h | 1 - 3 files changed, 83 insertions(+), 82 deletions(-) diff --git a/src/gettext.cpp b/src/gettext.cpp index 553c9c4c7..1ddcd5ff6 100644 --- a/src/gettext.cpp +++ b/src/gettext.cpp @@ -25,6 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "log.h" +#ifdef _WIN32 +#define setenv(n,v,o) _putenv_s(n,v) +#endif + #if USE_GETTEXT && defined(_MSC_VER) #include #include @@ -37,7 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc., static std::map glb_supported_locales; /******************************************************************************/ -BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr) +static BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr) { char* endptr = 0; int LOCALEID = strtol(pStr, &endptr,16); @@ -78,7 +82,8 @@ BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr) } /******************************************************************************/ -const char* MSVC_LocaleLookup(const char* raw_shortname) { +static const char* MSVC_LocaleLookup(const char* raw_shortname) +{ /* NULL is used to read locale only so we need to return it too */ if (raw_shortname == NULL) return NULL; @@ -102,9 +107,9 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) { last_raw_value = shortname; - if (glb_supported_locales.find(utf8_to_wide(shortname)) != glb_supported_locales.end()) { - last_full_name = wide_to_utf8( - glb_supported_locales[utf8_to_wide(shortname)]); + auto key = utf8_to_wide(shortname); + if (glb_supported_locales.find(key) != glb_supported_locales.end()) { + last_full_name = wide_to_utf8(glb_supported_locales[key]); return last_full_name.c_str(); } @@ -114,6 +119,54 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) { return ""; } +static void MSVC_LocaleWorkaround() +{ + errorstream << "MSVC localization workaround active. " + "Restarting " PROJECT_NAME_C " in a new environment!" << std::endl; + + std::string parameters; + for (int i = 1; i < argc; i++) { + if (i > 1) + parameters += ' '; + parameters += porting::QuoteArgv(argv[i]); + } + + char *ptr_parameters = nullptr; + if (!parameters.empty()) + ptr_parameters = ¶meters[0]; + + // Allow calling without an extension + std::string app_name = argv[0]; + if (app_name.compare(app_name.size() - 4, 4, ".exe") != 0) + app_name += ".exe"; + + STARTUPINFO startup_info = {}; + PROCESS_INFORMATION process_info = {}; + + bool success = CreateProcess(app_name.c_str(), ptr_parameters, + NULL, NULL, false, DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, + NULL, NULL, &startup_info, &process_info); + + if (success) { + exit(0); + // NOTREACHED + } else { + char buffer[1024]; + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), buffer, + sizeof(buffer) - 1, NULL); + + errorstream << "*******************************************************" << std::endl; + errorstream << "CMD: " << app_name << std::endl; + errorstream << "Failed to restart with current locale: " << std::endl; + errorstream << buffer; + errorstream << "Expect language to be broken!" << std::endl; + errorstream << "*******************************************************" << std::endl; + } +} +} + #endif /******************************************************************************/ @@ -123,72 +176,26 @@ void init_gettext(const char *path, const std::string &configured_language, #if USE_GETTEXT // First, try to set user override environment if (!configured_language.empty()) { -#ifndef _WIN32 - // Add user specified locale to environment + // Set LANGUAGE which overrides all others, see + // +#ifndef _MSC_VER setenv("LANGUAGE", configured_language.c_str(), 1); -#ifdef __ANDROID__ - setenv("LANG", configured_language.c_str(), 1); -#endif - // Reload locale with changed environment setlocale(LC_ALL, ""); -#elif defined(_MSC_VER) +#else std::string current_language; const char *env_lang = getenv("LANGUAGE"); if (env_lang) current_language = env_lang; - _putenv(("LANGUAGE=" + configured_language).c_str()); + setenv("LANGUAGE", configured_language.c_str(), 1); SetEnvironmentVariableA("LANGUAGE", configured_language.c_str()); #ifndef SERVER // Hack to force gettext to see the right environment - if (current_language != configured_language) { - errorstream << "MSVC localization workaround active. " - "Restarting " PROJECT_NAME_C " in a new environment!" << std::endl; - - std::string parameters; - for (int i = 1; i < argc; i++) { - if (i > 1) - parameters += ' '; - parameters += porting::QuoteArgv(argv[i]); - } - - char *ptr_parameters = nullptr; - if (!parameters.empty()) - ptr_parameters = ¶meters[0]; - - // Allow calling without an extension - std::string app_name = argv[0]; - if (app_name.compare(app_name.size() - 4, 4, ".exe") != 0) - app_name += ".exe"; - - STARTUPINFO startup_info = {}; - PROCESS_INFORMATION process_info = {}; - - bool success = CreateProcess(app_name.c_str(), ptr_parameters, - NULL, NULL, false, DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, - NULL, NULL, &startup_info, &process_info); - - if (success) { - exit(0); - // NOTREACHED - } else { - char buffer[1024]; - - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), - MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), buffer, - sizeof(buffer) - 1, NULL); - - errorstream << "*******************************************************" << std::endl; - errorstream << "CMD: " << app_name << std::endl; - errorstream << "Failed to restart with current locale: " << std::endl; - errorstream << buffer; - errorstream << "Expect language to be broken!" << std::endl; - errorstream << "*******************************************************" << std::endl; - } - } + if (current_language != configured_language) + MSVC_LocaleWorkaround(); #else errorstream << "*******************************************************" << std::endl; errorstream << "Can't apply locale workaround for server!" << std::endl; @@ -197,15 +204,8 @@ void init_gettext(const char *path, const std::string &configured_language, #endif setlocale(LC_ALL, configured_language.c_str()); -#else // Mingw - _putenv(("LANGUAGE=" + configured_language).c_str()); - setlocale(LC_ALL, ""); -#endif // ifndef _WIN32 - } - else { -#ifdef __ANDROID__ - setenv("LANG", porting::getLanguageAndroid().c_str(), 1); -#endif +#endif // ifdef _MSC_VER + } else { /* set current system default locale */ setlocale(LC_ALL, ""); } @@ -228,18 +228,13 @@ void init_gettext(const char *path, const std::string &configured_language, bindtextdomain(name.c_str(), path); textdomain(name.c_str()); -#if defined(_WIN32) - // Set character encoding for Win32 - char *tdomain = textdomain( (char *) NULL ); - if( tdomain == NULL ) - { - errorstream << "Warning: domainname parameter is the null pointer" << - ", default domain is not set" << std::endl; - tdomain = (char *) "messages"; - } - /* char *codeset = */bind_textdomain_codeset( tdomain, "UTF-8" ); - //errorstream << "Gettext debug: domainname = " << tdomain << "; codeset = "<< codeset << std::endl; -#endif // defined(_WIN32) +#ifdef _WIN32 + // set character encoding + char *tdomain = textdomain(nullptr); + assert(tdomain); + if (tdomain) + bind_textdomain_codeset(tdomain, "UTF-8"); +#endif #else /* set current system default locale */ @@ -247,7 +242,7 @@ void init_gettext(const char *path, const std::string &configured_language, #endif // if USE_GETTEXT /* no matter what locale is used we need number format to be "C" */ - /* to ensure formspec parameters are evaluated correct! */ + /* to ensure formspec parameters are evaluated correctly! */ setlocale(LC_NUMERIC, "C"); infostream << "Message locale is now set to: " diff --git a/src/porting_android.cpp b/src/porting_android.cpp index 4d02eb10a..c8bdad5a0 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -40,7 +40,9 @@ with this program; if not, write to the Free Software Foundation, Inc., extern int main(int argc, char *argv[]); namespace porting { - void cleanupAndroid(); // used here + // used here: + void cleanupAndroid(); + std::string getLanguageAndroid(); bool setSystemPaths(); // used in porting.cpp } @@ -101,6 +103,11 @@ void osSpecificInit() "porting::initAndroid unable to find Java native activity class" << std::endl; + // Set default language + auto lang = getLanguageAndroid(); + unsetenv("LANGUAGE"); + setenv("LANG", lang.c_str(), 1); + #ifdef GPROF // in the start-up code warningstream << "Initializing GPROF profiler" << std::endl; diff --git a/src/porting_android.h b/src/porting_android.h index e5e75481d..d41cc8e4c 100644 --- a/src/porting_android.h +++ b/src/porting_android.h @@ -71,5 +71,4 @@ float getDisplayDensity(); v2u32 getDisplaySize(); #endif -std::string getLanguageAndroid(); } -- 2.46.0 From edd947b645633d352ed5aed0a3e7f8740f0c5aae Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 19 Dec 2023 22:02:04 +0100 Subject: [PATCH 55/85] Enable some runtime hardening on win32 --- src/porting.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/porting.cpp b/src/porting.cpp index 0d8d86c3e..0165af029 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -723,7 +723,13 @@ bool secure_rand_fill_buf(void *buf, size_t len) void osSpecificInit() { - // nothing here yet +#ifdef _WIN32 + // hardening options + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | + BASE_SEARCH_PATH_PERMANENT); + SetProcessDEPPolicy(PROCESS_DEP_ENABLE); +#endif } #endif -- 2.46.0 From 22a16537022ec052c4c2824b5665cd27c36c133e Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Fri, 29 Dec 2023 12:53:27 -0800 Subject: [PATCH 56/85] Perform server occlusion check before a block is loaded or generated (#14148) --- src/clientiface.cpp | 23 +++++++++++++---------- src/map.cpp | 6 +++--- src/map.h | 7 ++++++- src/mapblock.h | 10 +++++++--- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 6171e2116..4484b08d5 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -350,18 +350,21 @@ void RemoteClient::GetNextBlocks ( if (!block->getIsUnderground() && !block->getDayNightDiff()) continue; } + } - /* - Check occlusion cache first. - */ - if (m_blocks_occ.find(p) != m_blocks_occ.end()) - continue; + /* + Check occlusion cache first. + */ + if (m_blocks_occ.find(p) != m_blocks_occ.end()) + continue; - if (m_occ_cull && !block_not_found && - env->getMap().isBlockOccluded(block, cam_pos_nodes, d >= d_cull_opt)) { - m_blocks_occ.insert(p); - continue; - } + /* + Note that we do this even before the block is loaded as this does not depend on its contents. + */ + if (m_occ_cull && + env->getMap().isBlockOccluded(p * MAP_BLOCKSIZE, cam_pos_nodes, d >= d_cull_opt)) { + m_blocks_occ.insert(p); + continue; } /* diff --git a/src/map.cpp b/src/map.cpp index f63732cf4..2402b971c 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1155,7 +1155,7 @@ bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target, return false; } -bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes, bool simple_check) +bool Map::isBlockOccluded(v3s16 pos_relative, v3s16 cam_pos_nodes, bool simple_check) { // Check occlusion for center and all 8 corners of the mapblock // Overshoot a little for less flickering @@ -1172,7 +1172,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes, bool simple_chec v3s16(-1, -1, -1) * bs2, }; - v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2); + v3s16 pos_blockcenter = pos_relative + (MAP_BLOCKSIZE / 2); // Starting step size, value between 1m and sqrt(3)m float step = BS * 1.2f; @@ -1203,7 +1203,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes, bool simple_chec // Additional occlusion check, see comments in that function v3s16 check; - if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) { + if (determineAdditionalOcclusionCheck(cam_pos_nodes, MapBlock::getBox(pos_relative), check)) { // node is always on a side facing the camera, end_offset can be lower if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset, -1.0f, needed_count)) diff --git a/src/map.h b/src/map.h index 543e2e89b..4133bc4a5 100644 --- a/src/map.h +++ b/src/map.h @@ -305,7 +305,12 @@ public: } } - bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes, bool simple_check = false); + bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes, bool simple_check = false) + { + return isBlockOccluded(block->getPosRelative(), cam_pos_nodes, simple_check); + } + bool isBlockOccluded(v3s16 pos_relative, v3s16 cam_pos_nodes, bool simple_check = false); + protected: IGameDef *m_gamedef; diff --git a/src/mapblock.h b/src/mapblock.h index 9a91a05cf..73aeaade7 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -216,10 +216,14 @@ public: return m_pos_relative; } - inline core::aabbox3d getBox() + inline core::aabbox3d getBox() { + return getBox(getPosRelative()); + } + + static inline core::aabbox3d getBox(const v3s16 &pos_relative) { - return core::aabbox3d(getPosRelative(), - getPosRelative() + return core::aabbox3d(pos_relative, + pos_relative + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE) - v3s16(1,1,1)); } -- 2.46.0 From c99196d363d3337b7b7c6392cd6bfb8846a7ed2b Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Fri, 29 Dec 2023 14:18:06 -0800 Subject: [PATCH 57/85] Do not emerge blocks in the active_object_send_range_blocks range (#14152) The active object range is about active objects (not blocks). Activate blocks (and hence any object "in" them) in the cone define by the active object range (and fov) when they are loaded (i.e. visible), otherwise ignore them. --- src/serverenvironment.cpp | 65 ++++++++++++++++++++++++++++++--------- src/serverenvironment.h | 3 +- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 810456b0d..26b0dd1fa 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -335,12 +335,14 @@ void ActiveBlockList::update(std::vector &active_players, s16 active_block_range, s16 active_object_range, std::set &blocks_removed, - std::set &blocks_added) + std::set &blocks_added, + std::set &extra_blocks_added) { /* Create the new list */ std::set newlist = m_forceloaded_list; + std::set extralist; m_abm_list = m_forceloaded_list; for (const PlayerSAO *playersao : active_players) { v3s16 pos = getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS)); @@ -360,29 +362,52 @@ void ActiveBlockList::update(std::vector &active_players, playersao->getEyePosition(), camera_dir, playersao->getFov(), - newlist); + extralist); } } - /* - Find out which blocks on the old list are not on the new list - */ - // Go through old list - for (v3s16 p : m_list) { - // If not on new list, it's been removed - if (newlist.find(p) == newlist.end()) - blocks_removed.insert(p); - } - /* Find out which blocks on the new list are not on the old list */ - // Go through new list for (v3s16 p : newlist) { + // also remove duplicate blocks from the extra list + extralist.erase(p); // If not on old list, it's been added if (m_list.find(p) == m_list.end()) blocks_added.insert(p); } + /* + Find out which blocks on the extra list are not on the old list + */ + for (v3s16 p : extralist) { + // also make sure newlist has all blocks + newlist.insert(p); + // If not on old list, it's been added + if (m_list.find(p) == m_list.end()) + extra_blocks_added.insert(p); + } + + /* + Find out which blocks on the old list are not on the new + extra list + */ + std::set_difference(m_list.begin(), m_list.end(), newlist.begin(), newlist.end(), + std::inserter(blocks_removed, blocks_removed.end())); + + /* + Some sanity checks + */ + assert(newlist.size() >= extralist.size()); + assert(blocks_removed.size() <= m_list.size()); + if (!blocks_added.empty()) + assert(newlist.count(*blocks_added.begin()) > 0); + if (!extra_blocks_added.empty()) { + assert(newlist.count(*extra_blocks_added.begin()) > 0); + assert(blocks_added.count(*extra_blocks_added.begin()) == 0); + } + if (!blocks_removed.empty()) { + assert(newlist.count(*blocks_removed.begin()) == 0); + assert(m_list.count(*blocks_removed.begin()) > 0); + } /* Update m_list @@ -1403,8 +1428,9 @@ void ServerEnvironment::step(float dtime) g_settings->getS16("active_block_range"); std::set blocks_removed; std::set blocks_added; + std::set extra_blocks_added; m_active_blocks.update(players, active_block_range, active_object_range, - blocks_removed, blocks_added); + blocks_removed, blocks_added, extra_blocks_added); /* Handle removed blocks @@ -1440,6 +1466,17 @@ void ServerEnvironment::step(float dtime) activateBlock(block); } + for (const v3s16 &p: extra_blocks_added) { + // only activate if the block is already loaded + MapBlock *block = m_map->getBlockNoCreateNoEx(p); + if (!block) { + m_active_blocks.remove(p); + continue; + } + + activateBlock(block); + } + // Some blocks may be removed again by the code above so do this here m_active_block_gauge->set(m_active_blocks.size()); diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 215fd37ee..bb689ea2c 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -168,7 +168,8 @@ public: s16 active_block_range, s16 active_object_range, std::set &blocks_removed, - std::set &blocks_added); + std::set &blocks_added, + std::set &extra_blocks_added); bool contains(v3s16 p) const { return (m_list.find(p) != m_list.end()); -- 2.46.0 From 431444ba9f6a57b8105adcaeed9451fd77605fc5 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 30 Dec 2023 00:29:09 +0100 Subject: [PATCH 58/85] Extend sanity checks in ActiveBlockList::update also fixes the space indentation --- src/serverenvironment.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 26b0dd1fa..d8972d045 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -394,19 +394,23 @@ void ActiveBlockList::update(std::vector &active_players, std::inserter(blocks_removed, blocks_removed.end())); /* - Some sanity checks - */ + Do some least-effort sanity checks to hopefully catch code bugs. + */ assert(newlist.size() >= extralist.size()); assert(blocks_removed.size() <= m_list.size()); - if (!blocks_added.empty()) - assert(newlist.count(*blocks_added.begin()) > 0); + if (!blocks_added.empty()) { + assert(newlist.count(*blocks_added.begin()) > 0); + assert(blocks_removed.count(*blocks_added.begin()) == 0); + } if (!extra_blocks_added.empty()) { - assert(newlist.count(*extra_blocks_added.begin()) > 0); - assert(blocks_added.count(*extra_blocks_added.begin()) == 0); + assert(newlist.count(*extra_blocks_added.begin()) > 0); + assert(extralist.count(*extra_blocks_added.begin()) > 0); + assert(blocks_added.count(*extra_blocks_added.begin()) == 0); } if (!blocks_removed.empty()) { - assert(newlist.count(*blocks_removed.begin()) == 0); - assert(m_list.count(*blocks_removed.begin()) > 0); + assert(newlist.count(*blocks_removed.begin()) == 0); + assert(extralist.count(*blocks_removed.begin()) == 0); + assert(m_list.count(*blocks_removed.begin()) > 0); } /* -- 2.46.0 From c9ab61aa8cd0ca50bc8c4d90f257bb5ed3b0bb00 Mon Sep 17 00:00:00 2001 From: Alfred Wingate Date: Sun, 31 Dec 2023 17:39:01 +0200 Subject: [PATCH 59/85] Add missing header for gcc-14 https://gcc.gnu.org/gcc-14/porting_to.html Signed-off-by: Alfred Wingate --- src/client/sound/sound_data.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/sound/sound_data.cpp b/src/client/sound/sound_data.cpp index f0cbc6dbf..d2d626e2c 100644 --- a/src/client/sound/sound_data.cpp +++ b/src/client/sound/sound_data.cpp @@ -25,6 +25,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc., #include "sound_data.h" #include "sound_constants.h" +#include namespace sound { -- 2.46.0 From d0753dddb112f717f5b636d194b2d7c3917ff0a8 Mon Sep 17 00:00:00 2001 From: sfence Date: Mon, 1 Jan 2024 22:48:56 +0100 Subject: [PATCH 60/85] Method add_pos for object/player (#14126) --- doc/lua_api.md | 13 +++++++++---- games/devtest/mods/unittests/player.lua | 16 +++++++++++++++ src/client/client.h | 1 + src/client/localplayer.h | 5 +++++ src/network/clientopcodes.cpp | 4 ++-- src/network/clientpackethandler.cpp | 11 +++++++++++ src/network/networkprotocol.h | 6 ++++++ src/script/lua_api/l_object.cpp | 16 +++++++++++++++ src/script/lua_api/l_object.h | 3 +++ src/server.cpp | 7 +++++++ src/server.h | 1 + src/server/player_sao.cpp | 26 ++++++++++++++++++++++++- src/server/player_sao.h | 1 + src/server/serveractiveobject.h | 2 ++ 14 files changed, 105 insertions(+), 7 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 7461127c2..b15040f99 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -7465,15 +7465,20 @@ child will follow movement and rotation of that bone. * Sets the position of the object. * No-op if object is attached. * `pos` is a vector `{x=num, y=num, z=num}` +* `add_pos(pos)`: + * Changes position by adding to the current position. + * No-op if object is attached. + * `pos` is a vector `{x=num, y=num, z=num}`. + * In comparison to using `set_pos`, `add_pos` will avoid synchronization problems. * `get_velocity()`: returns the velocity, a vector. * `add_velocity(vel)` * Changes velocity by adding to the current velocity. * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}` - * In comparison to using get_velocity, adding the velocity and then using - set_velocity, add_velocity is supposed to avoid synchronization problems. - Additionally, players also do not support set_velocity. + * In comparison to using `get_velocity`, adding the velocity and then using + `set_velocity`, `add_velocity` is supposed to avoid synchronization problems. + Additionally, players also do not support `set_velocity`. * If object is a player: - * Does not apply during free_move. + * Does not apply during `free_move`. * Note that since the player speed is normalized at each move step, increasing e.g. Y velocity beyond what would usually be achieved (see: physics overrides) will cause existing X/Z velocity to be reduced. diff --git a/games/devtest/mods/unittests/player.lua b/games/devtest/mods/unittests/player.lua index fa0557960..70b3b6cae 100644 --- a/games/devtest/mods/unittests/player.lua +++ b/games/devtest/mods/unittests/player.lua @@ -68,3 +68,19 @@ local function run_player_meta_tests(player) assert(meta:equals(meta2)) end unittests.register("test_player_meta", run_player_meta_tests, {player=true}) + +-- +-- Player add pos +-- +local function run_player_add_pos_tests(player) + local pos = player:get_pos() + player:add_pos(vector.new(0, 1000, 0)) + local newpos = player:get_pos() + player:add_pos(vector.new(0, -1000, 0)) + local backpos = player:get_pos() + local newdist = vector.distance(pos, newpos) + assert(math.abs(newdist - 1000) <= 1) + assert(vector.distance(pos, backpos) <= 1) +end +unittests.register("test_player_add_pos", run_player_add_pos_tests, {player=true}) + diff --git a/src/client/client.h b/src/client/client.h index 4c49301ce..b40c60828 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -196,6 +196,7 @@ public: void handleCommand_HP(NetworkPacket* pkt); void handleCommand_Breath(NetworkPacket* pkt); void handleCommand_MovePlayer(NetworkPacket* pkt); + void handleCommand_MovePlayerRel(NetworkPacket* pkt); void handleCommand_DeathScreen(NetworkPacket* pkt); void handleCommand_AnnounceMedia(NetworkPacket* pkt); void handleCommand_Media(NetworkPacket* pkt); diff --git a/src/client/localplayer.h b/src/client/localplayer.h index f33aab4b3..1133a3f56 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -129,6 +129,11 @@ public: m_position = position; m_sneak_node_exists = false; } + inline void addPosition(const v3f &added_pos) + { + m_position += added_pos; + m_sneak_node_exists = false; + } v3f getPosition() const { return m_position; } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index bff318155..c3dbe57a9 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -117,13 +117,13 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_SET_SUN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetSun }, // 0x5a { "TOCLIENT_SET_MOON", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetMoon }, // 0x5b { "TOCLIENT_SET_STARS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetStars }, // 0x5c - null_command_handler, + { "TOCLIENT_MOVE_PLAYER_REL", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MovePlayerRel }, // 0x5d, null_command_handler, null_command_handler, { "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60 { "TOCLIENT_FORMSPEC_PREPEND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FormspecPrepend }, // 0x61, { "TOCLIENT_MINIMAP_MODES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MinimapModes }, // 0x62, - { "TOCLIENT_SET_LIGHTING", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SetLighting }, // 0x63, + { "TOCLIENT_SET_LIGHTING", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SetLighting }, // 0x63, }; const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 3c6da09a7..bacd7f8f0 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -635,6 +635,17 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt) m_client_event_queue.push(event); } +void Client::handleCommand_MovePlayerRel(NetworkPacket *pkt) +{ + v3f added_pos; + + *pkt >> added_pos; + + LocalPlayer *player = m_env.getLocalPlayer(); + assert(player); + player->addPosition(added_pos); +} + void Client::handleCommand_DeathScreen(NetworkPacket* pkt) { bool set_camera_point_target; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 0ededba38..44d5e80fc 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -221,6 +221,7 @@ with this program; if not, write to the Free Software Foundation, Inc., [scheduled bump for 5.8.0] PROTOCOL VERSION 44: AO_CMD_SET_BONE_POSITION extended + Add TOCLIENT_MOVE_PLAYER_REL [scheduled bump for 5.9.0] */ @@ -835,6 +836,11 @@ enum ToClientCommand f32 day_opacity */ + TOCLIENT_MOVE_PLAYER_REL = 0x5d, + /* + v3f added_pos + */ + TOCLIENT_SRP_BYTES_S_B = 0x60, /* Belonging to AUTH_MECHANISM_SRP. diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index c9dae1885..11ad7a7fe 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -134,6 +134,21 @@ int ObjectRef::l_set_pos(lua_State *L) return 0; } +// add_pos(self, pos) +int ObjectRef::l_add_pos(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + v3f pos = checkFloatPos(L, 2); + + sao->addPos(pos); + return 0; +} + // move_to(self, pos, continuous) int ObjectRef::l_move_to(lua_State *L) { @@ -2597,6 +2612,7 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, remove), luamethod_aliased(ObjectRef, get_pos, getpos), luamethod_aliased(ObjectRef, set_pos, setpos), + luamethod(ObjectRef, add_pos), luamethod_aliased(ObjectRef, move_to, moveto), luamethod(ObjectRef, punch), luamethod(ObjectRef, right_click), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 4465a823f..d8cc8d604 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -73,6 +73,9 @@ private: // set_pos(self, pos) static int l_set_pos(lua_State *L); + // add_pos(self, pos) + static int l_add_pos(lua_State *L); + // move_to(self, pos, continuous) static int l_move_to(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 8c084a573..da9c5c10d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1961,6 +1961,13 @@ void Server::SendMovePlayer(session_t peer_id) Send(&pkt); } +void Server::SendMovePlayerRel(session_t peer_id, const v3f &added_pos) +{ + NetworkPacket pkt(TOCLIENT_MOVE_PLAYER_REL, 0, peer_id); + pkt << added_pos; + Send(&pkt); +} + void Server::SendPlayerFov(session_t peer_id) { NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id); diff --git a/src/server.h b/src/server.h index 802612934..bec2fe1cf 100644 --- a/src/server.h +++ b/src/server.h @@ -364,6 +364,7 @@ public: void SendPlayerBreath(PlayerSAO *sao); void SendInventory(PlayerSAO *playerSAO, bool incremental); void SendMovePlayer(session_t peer_id); + void SendMovePlayerRel(session_t peer_id, const v3f &added_pos); void SendPlayerSpeed(session_t peer_id, const v3f &added_vel); void SendPlayerFov(session_t peer_id); diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 681841f23..ede3ccb8e 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -352,7 +352,7 @@ void PlayerSAO::setBasePosition(v3f position) void PlayerSAO::setPos(const v3f &pos) { - if(isAttached()) + if (isAttached()) return; // Send mapblock of target location @@ -367,6 +367,30 @@ void PlayerSAO::setPos(const v3f &pos) m_env->getGameDef()->SendMovePlayer(m_peer_id); } +void PlayerSAO::addPos(const v3f &added_pos) +{ + if (isAttached()) + return; + + // Backward compatibility for older clients + if (m_player->protocol_version < 44) { + setPos(getBasePosition() + added_pos); + return; + } + + // Send mapblock of target location + v3f pos = getBasePosition() + added_pos; + v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE); + m_env->getGameDef()->SendBlock(m_peer_id, blockpos); + + setBasePosition(pos); + // Movement caused by this command is always valid + m_last_good_position = getBasePosition(); + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; + m_env->getGameDef()->SendMovePlayerRel(m_peer_id, added_pos); +} + void PlayerSAO::moveTo(v3f pos, bool continuous) { if(isAttached()) diff --git a/src/server/player_sao.h b/src/server/player_sao.h index c0d395f80..854464508 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -90,6 +90,7 @@ public: void step(float dtime, bool send_recommended) override; void setBasePosition(v3f position); void setPos(const v3f &pos) override; + void addPos(const v3f &added_pos) override; void moveTo(v3f pos, bool continuous) override; void setPlayerYaw(const float yaw); // Data should not be sent at player initialization diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index a42096f7b..2911a5549 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -91,6 +91,8 @@ public: virtual void setPos(const v3f &pos) { setBasePosition(pos); } + virtual void addPos(const v3f &added_pos) + { setBasePosition(m_base_position + added_pos); } // continuous: if true, object does not stop immediately at pos virtual void moveTo(v3f pos, bool continuous) { setBasePosition(pos); } -- 2.46.0 From 2c44620e5e344ca2b4acaaaa5408502332ca5aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Mon, 1 Jan 2024 22:49:12 +0100 Subject: [PATCH 61/85] Comply with base64 license terms (#14199) --- src/util/base64.cpp | 1 + src/util/base64.h | 35 ++++++++++++++++++++++------------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/util/base64.cpp b/src/util/base64.cpp index 0c2455222..ac4421f51 100644 --- a/src/util/base64.cpp +++ b/src/util/base64.cpp @@ -2,6 +2,7 @@ base64.cpp and base64.h Copyright (C) 2004-2008 RenĂ© Nyffenegger +Stricter validation added by the Minetest Contributors. This source code is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages diff --git a/src/util/base64.h b/src/util/base64.h index 7f2bf1368..68fa5b743 100644 --- a/src/util/base64.h +++ b/src/util/base64.h @@ -1,20 +1,29 @@ /* -Minetest -Copyright (C) 2013-2017 celeron55, Perttu Ahola +base64.cpp and base64.h -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. +Copyright (C) 2004-2008 RenĂ© Nyffenegger +Minor modifications by the Minetest Contributors. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. +This source code is provided 'as-is', without any express or implied +warranty. In no event will the author be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + +3. This notice may not be removed or altered from any source distribution. + +RenĂ© Nyffenegger rene.nyffenegger@adp-gmbh.ch -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once -- 2.46.0 From 0b423dd061de9a0f7d5b80ea5a98dccd2eecec03 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 3 Jan 2024 17:01:53 +0100 Subject: [PATCH 62/85] Remove reference to defunct gitlab docker image see #14164 --- README.md | 23 +------------------- misc/kubernetes.yml | 53 --------------------------------------------- 2 files changed, 1 insertion(+), 75 deletions(-) delete mode 100644 misc/kubernetes.yml diff --git a/README.md b/README.md index c3706982c..231a5c3fd 100644 --- a/README.md +++ b/README.md @@ -128,28 +128,7 @@ Docker - [Developing minetestserver with Docker](doc/developing/docker.md) -We provide Minetest server Docker images using the GitLab mirror registry. - -Images are built on each commit and available using the following tag scheme: - -* `registry.gitlab.com/minetest/minetest/server:latest` (latest build) -* `registry.gitlab.com/minetest/minetest/server:` (current branch or current tag) -* `registry.gitlab.com/minetest/minetest/server:` (current commit id) - -If you want to test it on a Docker server you can easily run: - - sudo docker run registry.gitlab.com/minetest/minetest/server: - -If you want to use it in a production environment you should use volumes bound to the Docker host -to persist data and modify the configuration: - - sudo docker create -v /home/minetest/data/:/var/lib/minetest/ -v /home/minetest/conf/:/etc/minetest/ registry.gitlab.com/minetest/minetest/server:master - -Data will be written to `/home/minetest/data` on the host, and configuration will be read from `/home/minetest/conf/minetest.conf`. - -**Note:** If you don't understand the previous commands please read the official Docker documentation before use. - -You can also host your Minetest server inside a Kubernetes cluster. See our example implementation in [`misc/kubernetes.yml`](misc/kubernetes.yml). +We provide a Dockerfile that can be used to build the server. Version scheme diff --git a/misc/kubernetes.yml b/misc/kubernetes.yml deleted file mode 100644 index f2b322b8e..000000000 --- a/misc/kubernetes.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: minetest - name: minetest - namespace: default -spec: - selector: - matchLabels: - app: minetest - template: - metadata: - labels: - app: minetest - spec: - containers: - - image: registry.gitlab.com/minetest/minetest/server:master - name: minetest - ports: - - containerPort: 30000 - protocol: UDP - volumeMounts: - - mountPath: /var/lib/minetest - name: minetest-data - - mountPath: /etc/minetest - name: config - restartPolicy: Always - volumes: - - name: minetest-data - persistentVolumeClaim: - claimName: minetest-data - - configMap: - defaultMode: 420 - name: minetest - name: config ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: minetest - name: minetest - namespace: default -spec: - ports: - - name: minetest - port: 30000 - protocol: UDP - selector: - app: minetest - type: NodePort -- 2.46.0 From de4cc5c20ac241a4ec9f8da4108eda212c1927fa Mon Sep 17 00:00:00 2001 From: ROllerozxa Date: Sat, 16 Dec 2023 19:11:52 +0100 Subject: [PATCH 63/85] Fix tonemapping effect --- .../shaders/second_stage/opengl_fragment.glsl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl index 45e99928e..5ec58a087 100644 --- a/client/shaders/second_stage/opengl_fragment.glsl +++ b/client/shaders/second_stage/opengl_fragment.glsl @@ -68,13 +68,15 @@ vec3 uncharted2Tonemap(vec3 x) vec4 applyToneMapping(vec4 color) { - const float exposureBias = 2.0; + color = vec4(pow(color.rgb, vec3(2.2)), color.a); + const float gamma = 1.6; + const float exposureBias = 5.5; color.rgb = uncharted2Tonemap(exposureBias * color.rgb); // Precalculated white_scale from //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); vec3 whiteScale = vec3(1.036015346); color.rgb *= whiteScale; - return color; + return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a); } vec3 applySaturation(vec3 color, float factor) @@ -130,6 +132,12 @@ void main(void) color = applyBloom(color, uv); #endif + + color.rgb = clamp(color.rgb, vec3(0.), vec3(1.)); + + // return to sRGB colorspace (approximate) + color.rgb = pow(color.rgb, vec3(1.0 / 2.2)); + #ifdef ENABLE_BLOOM_DEBUG if (uv.x > 0.5 || uv.y > 0.5) #endif @@ -140,11 +148,6 @@ void main(void) #endif } - color.rgb = clamp(color.rgb, vec3(0.), vec3(1.)); - - // return to sRGB colorspace (approximate) - color.rgb = pow(color.rgb, vec3(1.0 / 2.2)); - #ifdef ENABLE_DITHERING // Apply dithering just before quantisation color.rgb += screen_space_dither(gl_FragCoord.xy); -- 2.46.0 From 8e9d7611aef259089d832b8cf1134bef711c6918 Mon Sep 17 00:00:00 2001 From: ROllerozxa Date: Sat, 16 Dec 2023 19:15:34 +0100 Subject: [PATCH 64/85] Apply saturation even if tonemapping is disabled --- client/shaders/second_stage/opengl_fragment.glsl | 5 +++-- doc/lua_api.md | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl index 5ec58a087..3b0d4c844 100644 --- a/client/shaders/second_stage/opengl_fragment.glsl +++ b/client/shaders/second_stage/opengl_fragment.glsl @@ -78,6 +78,7 @@ vec4 applyToneMapping(vec4 color) color.rgb *= whiteScale; return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a); } +#endif vec3 applySaturation(vec3 color, float factor) { @@ -86,7 +87,6 @@ vec3 applySaturation(vec3 color, float factor) float brightness = dot(color, vec3(0.2125, 0.7154, 0.0721)); return mix(vec3(brightness), color, factor); } -#endif #ifdef ENABLE_DITHERING // From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf @@ -144,8 +144,9 @@ void main(void) { #if ENABLE_TONE_MAPPING color = applyToneMapping(color); - color.rgb = applySaturation(color.rgb, saturation); #endif + + color.rgb = applySaturation(color.rgb, saturation); } #ifdef ENABLE_DITHERING diff --git a/doc/lua_api.md b/doc/lua_api.md index b15040f99..a85bb8152 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -8028,9 +8028,8 @@ child will follow movement and rotation of that bone. * Passing no arguments resets lighting to its default values. * `light_definition` is a table with the following optional fields: * `saturation` sets the saturation (vividness; default: `1.0`). - values > 1 increase the saturation - values in [0,1) decrease the saturation - * This value has no effect on clients who have the "Tone Mapping" shader disabled. + * values > 1 increase the saturation + * values in [0,1] decrease the saturation * `shadows` is a table that controls ambient shadows * `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness) * This value has no effect on clients who have the "Dynamic Shadows" shader disabled. -- 2.46.0 From a22b1700a448ec9042eb49da4537ec456213682a Mon Sep 17 00:00:00 2001 From: fluxionary <25628292+fluxionary@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:56:07 -0800 Subject: [PATCH 65/85] Legible Lua profiler (#14142) --- builtin/profiler/instrumentation.lua | 18 +++++++++++++++++- builtin/profiler/reporter.lua | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/builtin/profiler/instrumentation.lua b/builtin/profiler/instrumentation.lua index 80f1c66af..0ffd9e6a9 100644 --- a/builtin/profiler/instrumentation.lua +++ b/builtin/profiler/instrumentation.lua @@ -46,11 +46,18 @@ local register_functions = { register_on_mapblocks_changed = 0, } +local function regex_escape(s) + return s:gsub("(%W)", "%%%1") +end + --- -- Create an unique instrument name. -- Generate a missing label with a running index number. -- local counts = {} +local worldmods_path = regex_escape(core.get_worldpath()) +local user_path = regex_escape(core.get_user_path()) +local builtin_path = regex_escape(core.get_builtin_path()) local function generate_name(def) local class, label, func_name = def.class, def.label, def.func_name if label then @@ -65,7 +72,16 @@ local function generate_name(def) local index_id = def.mod .. (class or func_name) local index = counts[index_id] or 1 counts[index_id] = index + 1 - return format("%s[%d] %s", class or func_name, index, class and func_name or ""):trim() + local info = debug.getinfo(def.func) + local modpath = regex_escape(core.get_modpath(def.mod) or "") + local source = info.source + if modpath ~= "" then + source = source:gsub(modpath, def.mod) + end + source = source:gsub(worldmods_path, "") + source = source:gsub(builtin_path, "builtin" .. DIR_DELIM) + source = source:gsub(user_path, "") + return format("%s[%d] %s#%s", class or func_name, index, source, info.linedefined) end --- diff --git a/builtin/profiler/reporter.lua b/builtin/profiler/reporter.lua index 5928a3718..2e24a1fe0 100644 --- a/builtin/profiler/reporter.lua +++ b/builtin/profiler/reporter.lua @@ -77,7 +77,7 @@ local Formatter = { end } -local widths = { 55, 9, 9, 9, 5, 5, 5 } +local widths = { 80, 9, 9, 9, 5, 5, 5 } local txt_row_format = sprintf(" %%-%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds", unpack(widths)) local HR = {} -- 2.46.0 From 3eab5e9002b6457625e4e466637d284a4b9862f0 Mon Sep 17 00:00:00 2001 From: DS Date: Wed, 3 Jan 2024 21:56:38 +0100 Subject: [PATCH 66/85] Replace clientmap's MeshBufListList with a hashmap --- src/client/clientmap.cpp | 88 ++++++++++++++++++++++------------------ src/client/clientmap.h | 19 --------- 2 files changed, 49 insertions(+), 58 deletions(-) diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index b831724b3..7224c6490 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -34,33 +34,43 @@ with this program; if not, write to the Free Software Foundation, Inc., #include -// struct MeshBufListList -void MeshBufListList::clear() -{ - for (auto &list : lists) - list.clear(); -} +namespace { + // A helper struct + struct MeshBufListMaps + { + struct MaterialHash + { + size_t operator()(const video::SMaterial &m) const noexcept + { + // Only hash first texture. Simple and fast. + return std::hash{}(m.TextureLayers[0].Texture); + } + }; -void MeshBufListList::add(scene::IMeshBuffer *buf, v3s16 position, u8 layer) -{ - // Append to the correct layer - std::vector &list = lists[layer]; - const video::SMaterial &m = buf->getMaterial(); - for (MeshBufList &l : list) { - // comparing a full material is quite expensive so we don't do it if - // not even first texture is equal - if (l.m.TextureLayers[0].Texture != m.TextureLayers[0].Texture) - continue; + using MeshBufListMap = std::unordered_map< + video::SMaterial, + std::vector>, + MaterialHash>; - if (l.m == m) { - l.bufs.emplace_back(position, buf); - return; + std::array maps; + + void clear() + { + for (auto &map : maps) + map.clear(); } - } - MeshBufList l; - l.m = m; - l.bufs.emplace_back(position, buf); - list.emplace_back(l); + + void add(scene::IMeshBuffer *buf, v3s16 position, u8 layer) + { + assert(layer < MAX_TILE_LAYERS); + + // Append to the correct layer + auto &map = maps[layer]; + const video::SMaterial &m = buf->getMaterial(); + auto &bufs = map[m]; // default constructs if non-existent + bufs.emplace_back(position, buf); + } + }; } static void on_settings_changed(const std::string &name, void *data) @@ -737,7 +747,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) Draw the selected MapBlocks */ - MeshBufListList grouped_buffers; + MeshBufListMaps grouped_buffers; std::vector draw_order; video::SMaterial previous_material; @@ -793,7 +803,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } else { // otherwise, group buffers across meshes - // using MeshBufListList + // using MeshBufListMaps for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { scene::IMesh *mesh = block_mesh->getMesh(layer); assert(mesh); @@ -819,11 +829,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } // Capture draw order for all solid meshes - for (auto &lists : grouped_buffers.lists) { - for (MeshBufList &list : lists) { + for (auto &map : grouped_buffers.maps) { + for (auto &list : map) { // iterate in reverse to draw closest blocks first - for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it) { - draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin()); + for (auto it = list.second.rbegin(); it != list.second.rend(); ++it) { + draw_order.emplace_back(it->first, it->second, it != list.second.rbegin()); } } } @@ -1103,7 +1113,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, u32 drawcall_count = 0; u32 vertex_count = 0; - MeshBufListList grouped_buffers; + MeshBufListMaps grouped_buffers; std::vector draw_order; @@ -1144,7 +1154,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, } else { // otherwise, group buffers across meshes - // using MeshBufListList + // using MeshBufListMaps MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); @@ -1167,18 +1177,18 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, } u32 buffer_count = 0; - for (auto &lists : grouped_buffers.lists) - for (MeshBufList &list : lists) - buffer_count += list.bufs.size(); + for (auto &map : grouped_buffers.maps) + for (auto &list : map) + buffer_count += list.second.size(); draw_order.reserve(draw_order.size() + buffer_count); // Capture draw order for all solid meshes - for (auto &lists : grouped_buffers.lists) { - for (MeshBufList &list : lists) { + for (auto &map : grouped_buffers.maps) { + for (auto &list : map) { // iterate in reverse to draw closest blocks first - for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it) - draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin()); + for (auto it = list.second.rbegin(); it != list.second.rend(); ++it) + draw_order.emplace_back(it->first, it->second, it != list.second.rbegin()); } } diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 13c320d9a..eb9bf7d39 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -37,25 +37,6 @@ struct MapDrawControl bool show_wireframe = false; }; -struct MeshBufList -{ - video::SMaterial m; - std::vector> bufs; -}; - -struct MeshBufListList -{ - /*! - * Stores the mesh buffers of the world. - * The array index is the material's layer. - * The vector part groups vertices by material. - */ - std::vector lists[MAX_TILE_LAYERS]; - - void clear(); - void add(scene::IMeshBuffer *buf, v3s16 position, u8 layer); -}; - class Client; class ITextureSource; class PartialMeshBuffer; -- 2.46.0 From c9cd0d20ef01edafde3b1d758097af402fff1364 Mon Sep 17 00:00:00 2001 From: DS Date: Wed, 3 Jan 2024 21:57:00 +0100 Subject: [PATCH 67/85] Use AL_SOFT_direct_channels_remix extension for non-positional stereo sounds (#14195) --- builtin/settingtypes.txt | 5 ++ src/client/CMakeLists.txt | 1 + src/client/sound/al_extensions.cpp | 58 +++++++++++++++++++++++ src/client/sound/al_extensions.h | 38 +++++++++++++++ src/client/sound/al_helpers.cpp | 2 +- src/client/sound/al_helpers.h | 2 +- src/client/sound/ogg_file.cpp | 2 +- src/client/sound/ogg_file.h | 2 +- src/client/sound/playing_sound.cpp | 15 +++++- src/client/sound/playing_sound.h | 6 ++- src/client/sound/proxy_sound_manager.cpp | 2 +- src/client/sound/proxy_sound_manager.h | 2 +- src/client/sound/sound_constants.h | 2 +- src/client/sound/sound_data.cpp | 2 +- src/client/sound/sound_data.h | 2 +- src/client/sound/sound_manager.cpp | 7 +-- src/client/sound/sound_manager.h | 9 ++-- src/client/sound/sound_manager_messages.h | 2 +- src/client/sound/sound_openal.cpp | 2 +- src/client/sound/sound_singleton.cpp | 2 +- src/client/sound/sound_singleton.h | 2 +- src/defaultsettings.cpp | 1 + 22 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 src/client/sound/al_extensions.cpp create mode 100644 src/client/sound/al_extensions.h diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 0af96cc33..fbc18e318 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1870,6 +1870,11 @@ texture_min_size (Base texture size) int 64 1 32768 # Systems with a low-end GPU (or no GPU) would benefit from smaller values. client_mesh_chunk (Client Mesh Chunksize) int 1 1 16 +[**Sound] +# Comma-separated list of AL and ALC extensions that should not be used. +# Useful for testing. See al_extensions.[h,cpp] for details. +sound_extensions_blacklist (Sound Extensions Blacklist) string + [**Font] font_bold (Font bold by default) bool false diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 5ec61795a..846a5b782 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -2,6 +2,7 @@ set(sound_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/sound.cpp) if(USE_SOUND) set(sound_SRCS ${sound_SRCS} + ${CMAKE_CURRENT_SOURCE_DIR}/sound/al_extensions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sound/al_helpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sound/ogg_file.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sound/playing_sound.cpp diff --git a/src/client/sound/al_extensions.cpp b/src/client/sound/al_extensions.cpp new file mode 100644 index 000000000..bc696be35 --- /dev/null +++ b/src/client/sound/al_extensions.cpp @@ -0,0 +1,58 @@ +/* +Minetest +Copyright (C) 2023 DS + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "al_extensions.h" + +#include "settings.h" +#include "util/string.h" +#include + +namespace sound { + +ALExtensions::ALExtensions(const ALCdevice *deviceHandle [[maybe_unused]]) +{ + auto blacklist_vec = str_split(g_settings->get("sound_extensions_blacklist"), ','); + for (auto &s : blacklist_vec) { + s = trim(s); + } + std::unordered_set blacklist; + blacklist.insert(blacklist_vec.begin(), blacklist_vec.end()); + + { + constexpr const char *ext_name = "AL_SOFT_direct_channels_remix"; + bool blacklisted = blacklist.find(ext_name) != blacklist.end(); + if (blacklisted) + infostream << "ALExtensions: Blacklisted: " << ext_name << std::endl; +#ifndef AL_SOFT_direct_channels_remix + infostream << "ALExtensions: Not compiled with: " << ext_name << std::endl; +#else + bool found = alIsExtensionPresent(ext_name); + if (found) + infostream << "ALExtensions: Found: " << ext_name << std::endl; + else + infostream << "ALExtensions: Not found: " << ext_name << std::endl; + + if (found && !blacklisted) { + have_ext_AL_SOFT_direct_channels_remix = true; + } +#endif + } +} + +} diff --git a/src/client/sound/al_extensions.h b/src/client/sound/al_extensions.h new file mode 100644 index 000000000..cfa270d06 --- /dev/null +++ b/src/client/sound/al_extensions.h @@ -0,0 +1,38 @@ +/* +Minetest +Copyright (C) 2023 DS + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "al_helpers.h" + +namespace sound { + +/** + * Struct for AL and ALC extensions + */ +struct ALExtensions +{ + explicit ALExtensions(const ALCdevice *deviceHandle [[maybe_unused]]); + +#ifdef AL_SOFT_direct_channels_remix + bool have_ext_AL_SOFT_direct_channels_remix = false; +#endif +}; + +} diff --git a/src/client/sound/al_helpers.cpp b/src/client/sound/al_helpers.cpp index 3db3c6f61..e3b7bda94 100644 --- a/src/client/sound/al_helpers.cpp +++ b/src/client/sound/al_helpers.cpp @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/al_helpers.h b/src/client/sound/al_helpers.h index 5f0ba0089..566d9deeb 100644 --- a/src/client/sound/al_helpers.h +++ b/src/client/sound/al_helpers.h @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/ogg_file.cpp b/src/client/sound/ogg_file.cpp index 46c3427de..62cc71327 100644 --- a/src/client/sound/ogg_file.cpp +++ b/src/client/sound/ogg_file.cpp @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/ogg_file.h b/src/client/sound/ogg_file.h index 177357337..5054a6e64 100644 --- a/src/client/sound/ogg_file.h +++ b/src/client/sound/ogg_file.h @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/playing_sound.cpp b/src/client/sound/playing_sound.cpp index a1570d42f..3cc41fc50 100644 --- a/src/client/sound/playing_sound.cpp +++ b/src/client/sound/playing_sound.cpp @@ -18,12 +18,13 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "playing_sound.h" +#include "al_extensions.h" #include "debug.h" #include #include @@ -32,7 +33,8 @@ namespace sound { PlayingSound::PlayingSound(ALuint source_id, std::shared_ptr data, bool loop, f32 volume, f32 pitch, f32 start_time, - const std::optional> &pos_vel_opt) + const std::optional> &pos_vel_opt, + const ALExtensions &exts [[maybe_unused]]) : m_source_id(source_id), m_data(std::move(data)), m_looping(loop), m_is_positional(pos_vel_opt.has_value()) { @@ -113,6 +115,15 @@ PlayingSound::PlayingSound(ALuint source_id, std::shared_ptr dat alSource3f(m_source_id, AL_POSITION, 0.0f, 0.0f, 0.0f); alSource3f(m_source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f); warn_if_al_error("PlayingSound::PlayingSound at making position-less"); + +#ifdef AL_SOFT_direct_channels_remix + // Play directly on stereo output channels if possible. Improves sound quality. + if (exts.have_ext_AL_SOFT_direct_channels_remix + && m_data->m_decode_info.is_stereo) { + alSourcei(m_source_id, AL_DIRECT_CHANNELS_SOFT, AL_REMIX_UNMATCHED_SOFT); + warn_if_al_error("PlayingSound::PlayingSound at setting AL_DIRECT_CHANNELS_SOFT"); + } +#endif } setGain(volume); setPitch(pitch); diff --git a/src/client/sound/playing_sound.h b/src/client/sound/playing_sound.h index 1fbf37e3e..dcc680e09 100644 --- a/src/client/sound/playing_sound.h +++ b/src/client/sound/playing_sound.h @@ -18,13 +18,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "sound_data.h" +namespace sound { struct ALExtensions; } namespace sound { @@ -51,7 +52,8 @@ class PlayingSound final public: PlayingSound(ALuint source_id, std::shared_ptr data, bool loop, f32 volume, f32 pitch, f32 start_time, - const std::optional> &pos_vel_opt); + const std::optional> &pos_vel_opt, + const ALExtensions &exts [[maybe_unused]]); ~PlayingSound() noexcept { diff --git a/src/client/sound/proxy_sound_manager.cpp b/src/client/sound/proxy_sound_manager.cpp index ede603e67..266e68e00 100644 --- a/src/client/sound/proxy_sound_manager.cpp +++ b/src/client/sound/proxy_sound_manager.cpp @@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/proxy_sound_manager.h b/src/client/sound/proxy_sound_manager.h index 53299e045..edad70baa 100644 --- a/src/client/sound/proxy_sound_manager.h +++ b/src/client/sound/proxy_sound_manager.h @@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/sound_constants.h b/src/client/sound/sound_constants.h index 94d74b86c..d55575320 100644 --- a/src/client/sound/sound_constants.h +++ b/src/client/sound/sound_constants.h @@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/sound_data.cpp b/src/client/sound/sound_data.cpp index d2d626e2c..261549d38 100644 --- a/src/client/sound/sound_data.cpp +++ b/src/client/sound/sound_data.cpp @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/sound_data.h b/src/client/sound/sound_data.h index 36052c161..67fcd7756 100644 --- a/src/client/sound/sound_data.h +++ b/src/client/sound/sound_data.h @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/sound_manager.cpp b/src/client/sound/sound_manager.cpp index 97537cc0f..679d3a155 100644 --- a/src/client/sound/sound_manager.cpp +++ b/src/client/sound/sound_manager.cpp @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ @@ -181,7 +181,7 @@ std::shared_ptr OpenALSoundManager::createPlayingSound( } auto sound = std::make_shared(source_id, std::move(lsnd), loop, - volume, pitch, start_time, pos_vel_opt); + volume, pitch, start_time, pos_vel_opt, m_exts); sound->play(); @@ -271,7 +271,8 @@ OpenALSoundManager::OpenALSoundManager(SoundManagerSingleton *smg, Thread("OpenALSoundManager"), m_fallback_path_provider(std::move(fallback_path_provider)), m_device(smg->m_device.get()), - m_context(smg->m_context.get()) + m_context(smg->m_context.get()), + m_exts(m_device) { SANITY_CHECK(!!m_fallback_path_provider); diff --git a/src/client/sound/sound_manager.h b/src/client/sound/sound_manager.h index 8d0ccf837..2a631db02 100644 --- a/src/client/sound/sound_manager.h +++ b/src/client/sound/sound_manager.h @@ -18,13 +18,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "playing_sound.h" +#include "al_extensions.h" #include "sound_constants.h" #include "sound_manager_messages.h" #include "../sound.h" @@ -51,8 +52,10 @@ class OpenALSoundManager final : public Thread private: std::unique_ptr m_fallback_path_provider; - ALCdevice *m_device; - ALCcontext *m_context; + ALCdevice *const m_device; + ALCcontext *const m_context; + + const ALExtensions m_exts; // time in seconds until which removeDeadSounds will be called again f32 m_time_until_dead_removal = REMOVE_DEAD_SOUNDS_INTERVAL; diff --git a/src/client/sound/sound_manager_messages.h b/src/client/sound/sound_manager_messages.h index 41f4ffac1..e33729705 100644 --- a/src/client/sound/sound_manager_messages.h +++ b/src/client/sound/sound_manager_messages.h @@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/sound_openal.cpp b/src/client/sound/sound_openal.cpp index 89f4b57ad..d44a1fbe2 100644 --- a/src/client/sound/sound_openal.cpp +++ b/src/client/sound/sound_openal.cpp @@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/sound_singleton.cpp b/src/client/sound/sound_singleton.cpp index 4a600243c..dff339be0 100644 --- a/src/client/sound/sound_singleton.cpp +++ b/src/client/sound/sound_singleton.cpp @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/client/sound/sound_singleton.h b/src/client/sound/sound_singleton.h index 5168c1c9b..32cd2d4f8 100644 --- a/src/client/sound/sound_singleton.h +++ b/src/client/sound/sound_singleton.h @@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along -with this program; ifnot, write to the Free Software Foundation, Inc., +with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 4505bdc2c..48bc76fdb 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -42,6 +42,7 @@ void set_default_settings() settings->setDefault("sound_volume", "0.8"); settings->setDefault("sound_volume_unfocused", "0.3"); settings->setDefault("mute_sound", "false"); + settings->setDefault("sound_extensions_blacklist", ""); settings->setDefault("enable_mesh_cache", "false"); settings->setDefault("mesh_generation_interval", "0"); settings->setDefault("mesh_generation_threads", "0"); -- 2.46.0 From 995c19287468a5733956fdba028f940901d4d1d5 Mon Sep 17 00:00:00 2001 From: grorp Date: Wed, 3 Jan 2024 21:58:58 +0100 Subject: [PATCH 68/85] Don't apply gui_scaling & DPI twice to table[] / textlist[] scrollbar (#14206) --- src/gui/guiTable.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/gui/guiTable.cpp b/src/gui/guiTable.cpp index b5042802a..d84107450 100644 --- a/src/gui/guiTable.cpp +++ b/src/gui/guiTable.cpp @@ -60,7 +60,7 @@ GUITable::GUITable(gui::IGUIEnvironment *env, m_rowheight = MYMAX(m_rowheight, 1); } - const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE); + const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE) * 1.5f; m_scrollbar = new GUIScrollBar(Environment, this, -1, core::rect(RelativeRect.getWidth() - s, 0, @@ -77,18 +77,6 @@ GUITable::GUITable(gui::IGUIEnvironment *env, setTabStop(true); setTabOrder(-1); updateAbsolutePosition(); -#ifdef HAVE_TOUCHSCREENGUI - float density = 1; // dp scaling is applied by the skin -#else - float density = RenderingEngine::getDisplayDensity(); -#endif - core::rect relative_rect = m_scrollbar->getRelativePosition(); - s32 width = (relative_rect.getWidth() / (2.0 / 3.0)) * density * - g_settings->getFloat("gui_scaling", 0.5f, 20.0f); - m_scrollbar->setRelativePosition(core::rect( - relative_rect.LowerRightCorner.X-width,relative_rect.UpperLeftCorner.Y, - relative_rect.LowerRightCorner.X,relative_rect.LowerRightCorner.Y - )); } GUITable::~GUITable() -- 2.46.0 From e17455cb2231ac1075dc349429716908aca9be06 Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Fri, 5 Jan 2024 06:39:11 +0700 Subject: [PATCH 69/85] Remove server's address and port from pause menu (#14082) --- src/client/game.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/client/game.cpp b/src/client/game.cpp index 0ba2dac44..32c4bbcab 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -4524,19 +4524,16 @@ void Game::showPauseMenu() << "\n" << strgettext("Game info:") << "\n"; const std::string &address = client->getAddressName(); - static const std::string mode = strgettext("- Mode: "); + os << strgettext("- Mode: "); if (!simple_singleplayer_mode) { - Address serverAddress = client->getServerAddress(); - if (!address.empty()) { - os << mode << strgettext("Remote server") << "\n" - << strgettext("- Address: ") << address; - } else { - os << mode << strgettext("Hosting server"); - } - os << "\n" << strgettext("- Port: ") << serverAddress.getPort() << "\n"; + if (address.empty()) + os << strgettext("Hosting server"); + else + os << strgettext("Remote server"); } else { - os << mode << strgettext("Singleplayer") << "\n"; + os << strgettext("Singleplayer"); } + os << "\n"; if (simple_singleplayer_mode || address.empty()) { static const std::string on = strgettext("On"); static const std::string off = strgettext("Off"); -- 2.46.0 From 05a53cd330017d97112bb1b054652f80b9ee28a2 Mon Sep 17 00:00:00 2001 From: grorp Date: Fri, 5 Jan 2024 00:39:40 +0100 Subject: [PATCH 70/85] Touchscreen: Recognize double-taps as double-clicks (#14187) --- src/gui/modalMenu.cpp | 127 ++++++++++++++++++++++-------------------- src/gui/modalMenu.h | 24 +++----- 2 files changed, 77 insertions(+), 74 deletions(-) diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 5d4e03baf..8d46c70f3 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -51,11 +51,8 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, setVisible(true); m_menumgr->createdMenu(this); - m_doubleclickdetect[0].time = 0; - m_doubleclickdetect[1].time = 0; - - m_doubleclickdetect[0].pos = v2s32(0, 0); - m_doubleclickdetect[1].pos = v2s32(0, 0); + m_last_touch.time = 0; + m_last_touch.pos = v2s32(0, 0); } GUIModalMenu::~GUIModalMenu() @@ -109,7 +106,18 @@ void GUIModalMenu::quitMenu() #endif } -bool GUIModalMenu::DoubleClickDetection(const SEvent &event) +static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) +{ + while (tocheck) { + if (tocheck == parent) { + return true; + } + tocheck = tocheck->getParent(); + } + return false; +} + +bool GUIModalMenu::remapDoubleClick(const SEvent &event) { /* The following code is for capturing double-clicks of the mouse button * and translating the double-click into an EET_KEY_INPUT_EVENT event @@ -124,55 +132,37 @@ bool GUIModalMenu::DoubleClickDetection(const SEvent &event) if (!m_remap_dbl_click) return false; - if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { - m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos; - m_doubleclickdetect[0].time = m_doubleclickdetect[1].time; + if (event.EventType != EET_MOUSE_INPUT_EVENT || + event.MouseInput.Event != EMIE_LMOUSE_DOUBLE_CLICK) + return false; - m_doubleclickdetect[1].pos = m_pointer; - m_doubleclickdetect[1].time = porting::getTimeMs(); - } else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { - u64 delta = porting::getDeltaMs( - m_doubleclickdetect[0].time, porting::getTimeMs()); - if (delta > 400) - return false; + // Only exit if the double-click happened outside the menu. + gui::IGUIElement *hovered = + Environment->getRootGUIElement()->getElementFromPoint(m_pointer); + if (isChild(hovered, this)) + return false; - double squaredistance = m_doubleclickdetect[0].pos. - getDistanceFromSQ(m_doubleclickdetect[1].pos); + // Translate double-click to escape. + SEvent translated{}; + translated.EventType = EET_KEY_INPUT_EVENT; + translated.KeyInput.Key = KEY_ESCAPE; + translated.KeyInput.Control = false; + translated.KeyInput.Shift = false; + translated.KeyInput.PressedDown = true; + translated.KeyInput.Char = 0; + OnEvent(translated); - if (squaredistance > (30 * 30)) { - return false; - } - - SEvent translated{}; - // translate doubleclick to escape - translated.EventType = EET_KEY_INPUT_EVENT; - translated.KeyInput.Key = KEY_ESCAPE; - translated.KeyInput.Control = false; - translated.KeyInput.Shift = false; - translated.KeyInput.PressedDown = true; - translated.KeyInput.Char = 0; - OnEvent(translated); - - return true; - } - - return false; + return true; } -static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) +bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try) { - while (tocheck) { - if (tocheck == parent) { - return true; - } - tocheck = tocheck->getParent(); - } - return false; -} + IGUIElement *target; + if (!second_try) + target = Environment->getFocus(); + else + target = m_touch_hovered.get(); -bool GUIModalMenu::simulateMouseEvent( - gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event) -{ SEvent mouse_event{}; // value-initialized, not unitialized mouse_event.EventType = EET_MOUSE_INPUT_EVENT; mouse_event.MouseInput.X = m_pointer.X; @@ -190,6 +180,11 @@ bool GUIModalMenu::simulateMouseEvent( mouse_event.MouseInput.Event = EMIE_LMOUSE_LEFT_UP; mouse_event.MouseInput.ButtonStates = 0; break; + case ETIE_COUNT: + // ETIE_COUNT is used for double-tap events. + mouse_event.MouseInput.Event = EMIE_LMOUSE_DOUBLE_CLICK; + mouse_event.MouseInput.ButtonStates = EMBSM_LEFT; + break; default: return false; } @@ -209,6 +204,9 @@ bool GUIModalMenu::simulateMouseEvent( } while (false); m_simulated_mouse = false; + if (!retval && !second_try) + return simulateMouseEvent(touch_event, true); + return retval; } @@ -293,12 +291,28 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) leave(); enter(hovered); } - gui::IGUIElement *focused = Environment->getFocus(); - bool ret = simulateMouseEvent(focused, event.TouchInput.Event); - if (!ret && m_touch_hovered != focused) - ret = simulateMouseEvent(m_touch_hovered.get(), event.TouchInput.Event); + bool ret = simulateMouseEvent(event.TouchInput.Event); if (event.TouchInput.Event == ETIE_LEFT_UP) leave(); + + // Detect double-taps and convert them into double-click events. + if (event.TouchInput.Event == ETIE_PRESSED_DOWN) { + u64 time_now = porting::getTimeMs(); + u64 time_delta = porting::getDeltaMs(m_last_touch.time, time_now); + + v2s32 pos_delta = m_pointer - m_last_touch.pos; + f32 distance_sq = (f32)pos_delta.X * pos_delta.X + + (f32)pos_delta.Y * pos_delta.Y; + + if (time_delta < 400 && distance_sq < (30 * 30)) { + // ETIE_COUNT is used for double-tap events. + simulateMouseEvent(ETIE_COUNT); + } + + m_last_touch.time = time_now; + m_last_touch.pos = m_pointer; + } + return ret; } else if (event.TouchInput.touchedCount == 2) { if (event.TouchInput.Event != ETIE_LEFT_UP) @@ -325,13 +339,8 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) m_touch_hovered.reset(); } - gui::IGUIElement *hovered = - Environment->getRootGUIElement()->getElementFromPoint(m_pointer); - if (!isChild(hovered, this)) { - if (DoubleClickDetection(event)) { - return true; - } - } + if (remapDoubleClick(event)) + return true; } return false; diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h index 30eda8bc9..bfa5d4ac8 100644 --- a/src/gui/modalMenu.h +++ b/src/gui/modalMenu.h @@ -69,13 +69,6 @@ protected: virtual std::wstring getLabelByID(s32 id) = 0; virtual std::string getNameByID(s32 id) = 0; - /** - * check if event is part of a double click - * @param event event to evaluate - * @return true/false if a doubleclick was detected - */ - bool DoubleClickDetection(const SEvent &event); - // Stores the last known pointer type. PointerType m_pointer_type = PointerType::Mouse; // Stores the last known pointer position. @@ -97,13 +90,6 @@ protected: bool m_simulated_mouse = false; private: - struct clickpos - { - v2s32 pos; - s64 time; - }; - clickpos m_doubleclickdetect[2]; - IMenuManager *m_menumgr; /* If true, remap a double-click (or double-tap) action to ESC. This is so * that, for example, Android users can double-tap to close a formspec. @@ -112,6 +98,8 @@ private: * and the default value for the setting is true. */ bool m_remap_dbl_click; + bool remapDoubleClick(const SEvent &event); + // This might be necessary to expose to the implementation if it // wants to launch other menus bool m_allow_focus_removal = false; @@ -120,7 +108,13 @@ private: irr_ptr m_touch_hovered; - bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event); + bool simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try=false); void enter(gui::IGUIElement *element); void leave(); + + // Used to detect double-taps and convert them into double-click events. + struct { + v2s32 pos; + s64 time; + } m_last_touch; }; -- 2.46.0 From 34ce86a8f50c1dd4c8b2cc5c94bbd1f13a38c0a9 Mon Sep 17 00:00:00 2001 From: Maintainer_ <131448485+FoxLoveFire@users.noreply.github.com> Date: Fri, 5 Jan 2024 04:39:56 +0500 Subject: [PATCH 71/85] Fix GameUI text staying visible during shutdown. (#14197) --- src/client/game.cpp | 3 +++ src/client/gameui.cpp | 33 +++++++++++++++++++++++++++++++++ src/client/gameui.h | 1 + 3 files changed, 37 insertions(+) diff --git a/src/client/game.cpp b/src/client/game.cpp index 32c4bbcab..1a538a7f5 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1333,6 +1333,9 @@ void Game::shutdown() if (formspec) formspec->quitMenu(); + // Clear text when exiting. + m_game_ui->clearText(); + #ifdef HAVE_TOUCHSCREENGUI g_touchscreengui->hide(); #endif diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index a4ab44e60..3ea1e2624 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -334,3 +334,36 @@ void GameUI::deleteFormspec() m_formname.clear(); } + +void GameUI::clearText() +{ + if (m_guitext_chat) { + m_guitext_chat->remove(); + m_guitext_chat = nullptr; + } + + if (m_guitext) { + m_guitext->remove(); + m_guitext = nullptr; + } + + if (m_guitext2) { + m_guitext2->remove(); + m_guitext2 = nullptr; + } + + if (m_guitext_info) { + m_guitext_info->remove(); + m_guitext_info = nullptr; + } + + if (m_guitext_status) { + m_guitext_status->remove(); + m_guitext_status = nullptr; + } + + if (m_guitext_profiler) { + m_guitext_profiler->remove(); + m_guitext_profiler = nullptr; + } +} diff --git a/src/client/gameui.h b/src/client/gameui.h index 589328a28..f97ee7e50 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -106,6 +106,7 @@ public: const std::string &getFormspecName() { return m_formname; } GUIFormSpecMenu *&getFormspecGUI() { return m_formspec; } void deleteFormspec(); + void clearText(); private: Flags m_flags; -- 2.46.0 From 15f73258fd9f7e7bc6ee398a0e70df877be62b1e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 5 Jan 2024 00:40:11 +0100 Subject: [PATCH 72/85] Don't run CDB update_detector more than once (#14214) --- builtin/mainmenu/content/update_detector.lua | 10 +++++++++- doc/menu_lua_api.md | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/builtin/mainmenu/content/update_detector.lua b/builtin/mainmenu/content/update_detector.lua index d184272e4..558a0fabb 100644 --- a/builtin/mainmenu/content/update_detector.lua +++ b/builtin/mainmenu/content/update_detector.lua @@ -28,6 +28,13 @@ end local has_fetched = false local latest_releases +do + local tmp = core.get_once("cdb_latest_releases") + if tmp then + latest_releases = core.deserialize(tmp, true) + has_fetched = latest_releases ~= nil + end +end local function fetch_latest_releases() @@ -89,8 +96,9 @@ local function fetch() has_fetched = false return end - latest_releases = lowercase_keys(releases) + core.set_once("cdb_latest_releases", core.serialize(latest_releases)) + if update_detector.get_count() > 0 then local maintab = ui.find_by_name("maintab") if not maintab.hidden then diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index 6c3777f5f..67f7edc69 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -38,7 +38,9 @@ Functions --------- * `core.start()` + * start game session * `core.close()` + * exit engine * `core.get_min_supp_proto()` * returns the minimum supported network protocol version * `core.get_max_supp_proto()` @@ -53,6 +55,10 @@ Functions * Android only. Shares file using the share popup * `core.get_version()` (possible in async calls) * returns current core version +* `core.set_once(key, value)`: + * save a string value that persists even if menu is closed +* `core.get_once(key)`: + * get a string value saved by above function, or `nil` -- 2.46.0 From c2c8d4d4100100cc5f68eabf42d431887a514571 Mon Sep 17 00:00:00 2001 From: Zughy <63455151+Zughy@users.noreply.github.com> Date: Fri, 5 Jan 2024 21:10:07 +0100 Subject: [PATCH 73/85] Remove controls listed in the pause menu (no touchscreen) (#13282) --- builtin/mainmenu/settings/dlg_settings.lua | 4 +- builtin/settingtypes.txt | 2 +- minetest.conf.example | 3 +- src/client/game.cpp | 45 +++------------------- src/settings_translation_file.cpp | 2 +- 5 files changed, 11 insertions(+), 45 deletions(-) diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index 01ff2dcb0..320b3dfc3 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -47,13 +47,13 @@ end local change_keys = { - query_text = "Change Keys", + query_text = "Controls", requires = { keyboard_mouse = true, }, get_formspec = function(self, avail_w) local btn_w = math.min(avail_w, 3) - return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Change Keys")), 0.8 + return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8 end, on_submit = function(self, fields) if fields.btn_change_keys then diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index fbc18e318..09bde2a4e 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -90,7 +90,7 @@ # Smooths rotation of camera, also called look or mouse smoothing. 0 to disable. camera_smoothing (Camera smoothing) float 0.0 0.0 0.99 -# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Change Keys. +# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Controls. # # Requires: keyboard_mouse cinematic_camera_smoothing (Camera smoothing in cinematic mode) float 0.7 0.0 0.99 diff --git a/minetest.conf.example b/minetest.conf.example index d88abe92a..72b3d2046 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -22,7 +22,7 @@ # type: float min: 0 max: 0.99 # camera_smoothing = 0.0 -# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Change Keys. +# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Controls. # type: float min: 0 max: 0.99 # cinematic_camera_smoothing = 0.7 @@ -3636,4 +3636,3 @@ # See https://github.com/minetest/irrlicht/blob/master/include/Keycodes.h # type: key # keymap_decrease_viewing_range_min = - - diff --git a/src/client/game.cpp b/src/client/game.cpp index 1a538a7f5..622e8bb07 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -4457,41 +4457,6 @@ void Game::showPauseMenu() "- touch&drag, tap 2nd finger\n" " --> place single item to slot\n" ); -#else - static const std::string control_text_template = strgettext("Controls:\n" - "- %s: move forwards\n" - "- %s: move backwards\n" - "- %s: move left\n" - "- %s: move right\n" - "- %s: jump/climb up\n" - "- %s: dig/punch/use\n" - "- %s: place/use\n" - "- %s: sneak/climb down\n" - "- %s: drop item\n" - "- %s: inventory\n" - "- Mouse: turn/look\n" - "- Mouse wheel: select item\n" - "- %s: chat\n" - ); - - char control_text_buf[600]; - - porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(), - GET_KEY_NAME(keymap_forward), - GET_KEY_NAME(keymap_backward), - GET_KEY_NAME(keymap_left), - GET_KEY_NAME(keymap_right), - GET_KEY_NAME(keymap_jump), - GET_KEY_NAME(keymap_dig), - GET_KEY_NAME(keymap_place), - GET_KEY_NAME(keymap_sneak), - GET_KEY_NAME(keymap_drop), - GET_KEY_NAME(keymap_inventory), - GET_KEY_NAME(keymap_chat) - ); - - std::string control_text = std::string(control_text_buf); - str_formspec_escape(control_text); #endif float ypos = simple_singleplayer_mode ? 0.7f : 0.1f; @@ -4516,14 +4481,16 @@ void Game::showPauseMenu() } #endif os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" - << strgettext("Change Keys") << "]"; + << strgettext("Controls") << "]"; #endif os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;" << strgettext("Exit to Menu") << "]"; os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;" - << strgettext("Exit to OS") << "]" - << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]" - << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n" + << strgettext("Exit to OS") << "]"; +#ifdef HAVE_TOUCHSCREENGUI + os << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"; +#endif + os << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n" << "\n" << strgettext("Game info:") << "\n"; const std::string &address = client->getAddressName(); diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index adcac066b..409cd7061 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -8,7 +8,7 @@ fake_function() { gettext("Camera smoothing"); gettext("Smooths rotation of camera, also called look or mouse smoothing. 0 to disable."); gettext("Camera smoothing in cinematic mode"); - gettext("Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Change Keys."); + gettext("Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Controls."); gettext("Build inside player"); gettext("If enabled, you can place nodes at the position (feet + eye level) where you stand.\nThis is helpful when working with nodeboxes in small areas."); gettext("Aux1 key for climbing/descending"); -- 2.46.0 From e04f61897995f29a0d060fa634659cc7525c1b6f Mon Sep 17 00:00:00 2001 From: Artem <79601745+Sovenok-Hacker@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:35:51 +0300 Subject: [PATCH 74/85] Add "--needed" to Arch command to avoid reinstalling packages --- doc/compiling/linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/compiling/linux.md b/doc/compiling/linux.md index d2e30f74e..ee137dd3d 100644 --- a/doc/compiling/linux.md +++ b/doc/compiling/linux.md @@ -30,7 +30,7 @@ For openSUSE users: For Arch users: - sudo pacman -S base-devel libcurl-gnutls cmake libxi libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd gettext + sudo pacman -S --needed base-devel libcurl-gnutls cmake libxi libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd gettext For Alpine users: -- 2.46.0 From 6550bc252ff89e052fed5a3a3d3ec44d42a0c810 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 2 Jan 2024 14:32:52 +0100 Subject: [PATCH 75/85] Fix logic in porting::attachOrCreateConsole() No functional change but now the comment is actually correct. --- src/porting.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/porting.cpp b/src/porting.cpp index 0165af029..76c3ae985 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -737,12 +737,14 @@ void osSpecificInit() void attachOrCreateConsole() { #ifdef _WIN32 - static bool consoleAllocated = false; - const bool redirected = (_fileno(stdout) == -2 || _fileno(stdout) == -1); // If output is redirected to e.g a file - if (!consoleAllocated && redirected && (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole())) { - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - consoleAllocated = true; + static bool once = false; + const bool redirected = _fileno(stdout) >= 0; // If output is redirected to e.g a file + if (!once && !redirected) { + if (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()) { + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + } + once = true; } #endif } -- 2.46.0 From 3fbe42c3a219731869a6ece9621d47b55eed0259 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 2 Jan 2024 14:57:36 +0100 Subject: [PATCH 76/85] Add unittest to check thread_local destructor brokenness --- src/unittest/test_threading.cpp | 86 +++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp index 65ef7c02d..9d7b7c808 100644 --- a/src/unittest/test_threading.cpp +++ b/src/unittest/test_threading.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "test.h" #include +#include #include "threading/semaphore.h" #include "threading/thread.h" @@ -32,6 +33,7 @@ public: void testStartStopWait(); void testAtomicSemaphoreThread(); + void testTLS(); }; static TestThreading g_test_instance; @@ -40,6 +42,7 @@ void TestThreading::runTests(IGameDef *gamedef) { TEST(testStartStopWait); TEST(testAtomicSemaphoreThread); + TEST(testTLS); } class SimpleTestThread : public Thread { @@ -156,3 +159,86 @@ void TestThreading::testAtomicSemaphoreThread() UASSERT(val == num_threads * 0x10000); } + + +static volatile bool g_tls_broken; + +class TLSTestThread : public Thread { +public: + TLSTestThread() : Thread("TLSTest") + { + } + +private: + struct TestObject { + TestObject() { + for (u32 i = 0; i < buffer_size; i++) + buffer[i] = (i % 2) ? 0xa1 : 0x1a; + } + ~TestObject() { + for (u32 i = 0; i < buffer_size; i++) { + const u8 expect = (i % 2) ? 0xa1 : 0x1a; + if (buffer[i] != expect) { + std::cout << "At offset " << i << " expected " << (int)expect + << " but found " << (int)buffer[i] << std::endl; + g_tls_broken = true; + break; + } + // If we're unlucky the loop might actually just crash. + // probably the user will realize the test failure :) + } + } + // larger objects seem to surface corruption more easily + static constexpr u32 buffer_size = 576; + u8 buffer[buffer_size]; + }; + + void *run() { + thread_local TestObject foo; + while (!stopRequested()) + sleep_ms(1); + return nullptr; + } +}; + +/* + What are we actually testing here? + + MinGW with gcc has a long-standing unsolved bug where memory belonging to + thread-local variables is freed *before* the destructors are called. + Needless to say this leads to unreliable crashes whenever a thread exits. + It does not affect MSVC or MinGW+clang and as far as we know no other platforms + are affected either. + + Related reports and information: + * (2018) + * (2017) + * maybe + + Note that this is different from , + which affected only 32-bit MinGW. It was caused by incorrect calling convention + and fixed in GCC in 2020. + + Occurrences in Minetest: + * (2020) + * (2022) + * (2023) +*/ +void TestThreading::testTLS() +{ + static const int num_threads = 10; + + for (int j = 0; j < num_threads; j++) { + g_tls_broken = false; + + TLSTestThread thread; + thread.start(); + thread.stop(); + thread.wait(); + + if (g_tls_broken) { + std::cout << "While running test thread " << j << std::endl; + UASSERT(!g_tls_broken); + } + } +} -- 2.46.0 From 8674dc831d7344e7e00d4a533a6323e0d70ec1ee Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 2 Jan 2024 15:56:13 +0100 Subject: [PATCH 77/85] Avoid unused argument spam with MinGW-clang --- src/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3780e31b2..c1f446cb1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -773,7 +773,9 @@ else() endif() if(MINGW) - set(OTHER_FLAGS "${OTHER_FLAGS} -mthreads -fexceptions") + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(OTHER_FLAGS "${OTHER_FLAGS} -mthreads") + endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WIN32_WINNT=0x0601 -DWIN32_LEAN_AND_MEAN") endif() @@ -813,7 +815,7 @@ else() endif() if(MINGW) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mwindows") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-mwindows") endif() endif() -- 2.46.0 From 7c7ae79f9f80a383d38c21e352a3ff33aba0c1af Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 2 Jan 2024 17:50:44 +0100 Subject: [PATCH 78/85] Fix native thread handle usage on win32 --- src/threading/thread.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp index ef639ffef..6fef3683c 100644 --- a/src/threading/thread.cpp +++ b/src/threading/thread.cpp @@ -59,6 +59,8 @@ DEALINGS IN THE SOFTWARE. #include #endif +// See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method +#define win32_native_handle() ((HANDLE) getThreadHandle()) Thread::Thread(const std::string &name) : m_name(name), @@ -81,9 +83,8 @@ Thread::~Thread() m_running = false; #if defined(_WIN32) - // See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method - TerminateThread((HANDLE) m_thread_obj->native_handle(), 0); - CloseHandle((HANDLE) m_thread_obj->native_handle()); + TerminateThread(win32_native_handle(), 0); + CloseHandle(win32_native_handle()); #else // We need to pthread_kill instead on Android since NDKv5's pthread // implementation is incomplete. @@ -261,13 +262,9 @@ bool Thread::bindToProcessor(unsigned int proc_number) return false; -#elif _MSC_VER +#elif defined(_WIN32) - return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number); - -#elif __MINGW32__ - - return SetThreadAffinityMask(pthread_gethandle(getThreadHandle()), 1 << proc_number); + return SetThreadAffinityMask(win32_native_handle(), 1 << proc_number); #elif __FreeBSD_version >= 702106 || defined(__linux__) || defined(__DragonFly__) @@ -320,13 +317,9 @@ bool Thread::bindToProcessor(unsigned int proc_number) bool Thread::setPriority(int prio) { -#ifdef _MSC_VER +#ifdef _WIN32 - return SetThreadPriority(getThreadHandle(), prio); - -#elif __MINGW32__ - - return SetThreadPriority(pthread_gethandle(getThreadHandle()), prio); + return SetThreadPriority(win32_native_handle(), prio); #else -- 2.46.0 From 8db4ba9e58f30e1515f529039cf679a026a4fa0b Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 2 Jan 2024 18:08:52 +0100 Subject: [PATCH 79/85] Fix some console window behavior on Windows --- src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index f849e5dc7..67b004508 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -213,6 +213,7 @@ int main(int argc, char *argv[]) // Run unit tests if (cmd_args.getFlag("run-unittests")) { + porting::attachOrCreateConsole(); #if BUILD_UNITTESTS if (cmd_args.exists("test-module")) return run_tests(cmd_args.get("test-module")) ? 0 : 1; @@ -228,6 +229,7 @@ int main(int argc, char *argv[]) // Run benchmarks if (cmd_args.getFlag("run-benchmarks")) { + porting::attachOrCreateConsole(); #if BUILD_BENCHMARKS if (cmd_args.exists("test-module")) return run_benchmarks(cmd_args.get("test-module").c_str()) ? 0 : 1; @@ -635,6 +637,7 @@ static bool use_debugger(int argc, char *argv[]) continue; new_args.push_back(argv[i]); } + new_args.push_back("--console"); new_args.push_back(nullptr); #ifdef _WIN32 -- 2.46.0 From bd42cc2c773f81d6c635de79cb9576d1ac811b32 Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Sat, 6 Jan 2024 18:43:46 -0800 Subject: [PATCH 80/85] Ensure deterministic client occlusion culling and minor improvements (#14212) * Ensure deterministic client occlusion culling * Increase culling optimize distance slightly * More accurate culling when sampling --- builtin/settingtypes.txt | 2 +- src/defaultsettings.cpp | 2 +- src/map.cpp | 15 ++++++++------- src/map.h | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 09bde2a4e..784035e3c 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2103,7 +2103,7 @@ server_side_occlusion_culling (Server-side occlusion culling) bool true # rendering glitches (missing blocks). # This is especially useful for very large viewing range (upwards of 500). # Stated in MapBlocks (16 nodes). -block_cull_optimize_distance (Block cull optimize distance) int 20 2 2047 +block_cull_optimize_distance (Block cull optimize distance) int 25 2 2047 [**Mapgen] diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 48bc76fdb..66b058115 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -397,7 +397,7 @@ void set_default_settings() // This causes frametime jitter on client side, or does it? settings->setDefault("max_block_send_distance", "12"); settings->setDefault("block_send_optimize_distance", "4"); - settings->setDefault("block_cull_optimize_distance", "20"); + settings->setDefault("block_cull_optimize_distance", "25"); settings->setDefault("server_side_occlusion_culling", "true"); settings->setDefault("csm_restriction_flags", "62"); settings->setDefault("csm_restriction_noderange", "0"); diff --git a/src/map.cpp b/src/map.cpp index 2402b971c..9d9737832 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1193,13 +1193,14 @@ bool Map::isBlockOccluded(v3s16 pos_relative, v3s16 cam_pos_nodes, bool simple_c // this is a HACK, we should think of a more precise algorithm u32 needed_count = 2; - v3s16 random_point(myrand_range(-bs2, bs2), myrand_range(-bs2, bs2), myrand_range(-bs2, bs2)); - if (!isOccluded(cam_pos_nodes, pos_blockcenter + random_point, step, stepfac, - start_offset, end_offset, needed_count)) - return false; - - if (simple_check) - return true; + // This should be only used in server occlusion cullung. + // The client recalculates the complete drawlist periodically, + // and random sampling could lead to visible flicker. + if (simple_check) { + v3s16 random_point(myrand_range(-bs2, bs2), myrand_range(-bs2, bs2), myrand_range(-bs2, bs2)); + return isOccluded(cam_pos_nodes, pos_blockcenter + random_point, step, stepfac, + start_offset, end_offset, 1); + } // Additional occlusion check, see comments in that function v3s16 check; diff --git a/src/map.h b/src/map.h index 4133bc4a5..6f1730d60 100644 --- a/src/map.h +++ b/src/map.h @@ -305,9 +305,9 @@ public: } } - bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes, bool simple_check = false) + bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) { - return isBlockOccluded(block->getPosRelative(), cam_pos_nodes, simple_check); + return isBlockOccluded(block->getPosRelative(), cam_pos_nodes, false); } bool isBlockOccluded(v3s16 pos_relative, v3s16 cam_pos_nodes, bool simple_check = false); -- 2.46.0 From 171f911237ba937b70f7dab01a1ff5cbaef7d48b Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Sun, 7 Jan 2024 19:00:04 +0700 Subject: [PATCH 81/85] Android: Add selection dialog (drop down/combo box) (#13814) - The handling of IGUIComboBox uses the new setAndSendSelected() method. - getDialogState() is now getInputDialogState() and returns the state of the input dialog. - getLastDialogType() is added and returns current/last shown dialog's type. - getInputDialogState() now returns an enum instead of int. - getAndroidUIInput() now returns void instead of bool. - New data types (enum) are added: (1) GameActivity.DialogType (Java) and porting::AndroidDialogType (C++) (2) GameActivity.DialogState (Java) and porting::AndroidDialogState (C++) - When showing a text input dialog, there is no custom accept button text any more. - showDialog()/showDialogUI() for text input is now showTextInputDialog()/showTextInputDialogUI(). - showInputDialog()/showDialogUI() for text input is now showTextInputDialog()/showTextInputDialogUI(). - getDialogValue()/getInputDialogValue() is now getDialogMessage()/getInputDialogMessage(). Co-authored-by: Gregor Parzefall <82708541+grorp@users.noreply.github.com> --- .../net/minetest/minetest/GameActivity.java | 59 ++++++++++++--- src/client/game.cpp | 16 ++-- src/gui/guiFormSpecMenu.cpp | 62 ++++++++------- src/gui/guiFormSpecMenu.h | 2 +- src/gui/guiPasswordChange.cpp | 23 +++--- src/gui/guiPasswordChange.h | 2 +- src/gui/modalMenu.cpp | 45 +++++++---- src/gui/modalMenu.h | 7 +- src/porting_android.cpp | 74 ++++++++++++++---- src/porting_android.h | 75 ++++++++++++++----- 10 files changed, 259 insertions(+), 106 deletions(-) diff --git a/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/android/app/src/main/java/net/minetest/minetest/GameActivity.java index caac0637d..f2ff09f3d 100644 --- a/android/app/src/main/java/net/minetest/minetest/GameActivity.java +++ b/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -51,8 +51,13 @@ public class GameActivity extends NativeActivity { System.loadLibrary("minetest"); } - private int messageReturnCode = -1; + enum DialogType { TEXT_INPUT, SELECTION_INPUT } + enum DialogState { DIALOG_SHOWN, DIALOG_INPUTTED, DIALOG_CANCELED } + + private DialogType lastDialogType = DialogType.TEXT_INPUT; + private DialogState inputDialogState = DialogState.DIALOG_CANCELED; private String messageReturnValue = ""; + private int selectionReturnValue = 0; @Override public void onCreate(Bundle savedInstanceState) { @@ -85,11 +90,17 @@ public class GameActivity extends NativeActivity { // Ignore the back press so Minetest can handle it } - public void showDialog(String acceptButton, String hint, String current, int editType) { - runOnUiThread(() -> showDialogUI(hint, current, editType)); + public void showTextInputDialog(String hint, String current, int editType) { + runOnUiThread(() -> showTextInputDialogUI(hint, current, editType)); } - private void showDialogUI(String hint, String current, int editType) { + public void showSelectionInputDialog(String[] optionList, int selectedIdx) { + runOnUiThread(() -> showSelectionInputDialogUI(optionList, selectedIdx)); + } + + private void showTextInputDialogUI(String hint, String current, int editType) { + lastDialogType = DialogType.TEXT_INPUT; + inputDialogState = DialogState.DIALOG_SHOWN; final AlertDialog.Builder builder = new AlertDialog.Builder(this); LinearLayout container = new LinearLayout(this); container.setOrientation(LinearLayout.VERTICAL); @@ -114,7 +125,7 @@ public class GameActivity extends NativeActivity { // For multi-line, do not submit the text after pressing Enter key if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) { imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); - messageReturnCode = 0; + inputDialogState = DialogState.DIALOG_INPUTTED; messageReturnValue = editText.getText().toString(); alertDialog.dismiss(); return true; @@ -128,29 +139,55 @@ public class GameActivity extends NativeActivity { doneButton.setText(R.string.ime_dialog_done); doneButton.setOnClickListener((view -> { imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); - messageReturnCode = 0; + inputDialogState = DialogState.DIALOG_INPUTTED; messageReturnValue = editText.getText().toString(); alertDialog.dismiss(); })); } alertDialog.setOnCancelListener(dialog -> { getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + inputDialogState = DialogState.DIALOG_CANCELED; messageReturnValue = current; - messageReturnCode = -1; }); alertDialog.show(); editText.requestFocusTryShow(); } - public int getDialogState() { - return messageReturnCode; + public void showSelectionInputDialogUI(String[] optionList, int selectedIdx) { + lastDialogType = DialogType.SELECTION_INPUT; + inputDialogState = DialogState.DIALOG_SHOWN; + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setSingleChoiceItems(optionList, selectedIdx, (dialog, selection) -> { + inputDialogState = DialogState.DIALOG_INPUTTED; + selectionReturnValue = selection; + dialog.dismiss(); + }); + builder.setOnCancelListener(dialog -> { + inputDialogState = DialogState.DIALOG_CANCELED; + selectionReturnValue = selectedIdx; + }); + AlertDialog alertDialog = builder.create(); + alertDialog.show(); } - public String getDialogValue() { - messageReturnCode = -1; + public int getLastDialogType() { + return lastDialogType.ordinal(); + } + + public int getInputDialogState() { + return inputDialogState.ordinal(); + } + + public String getDialogMessage() { + inputDialogState = DialogState.DIALOG_CANCELED; return messageReturnValue; } + public int getDialogSelection() { + inputDialogState = DialogState.DIALOG_CANCELED; + return selectionReturnValue; + } + public float getDensity() { return getResources().getDisplayMetrics().density; } diff --git a/src/client/game.cpp b/src/client/game.cpp index 622e8bb07..40fea4307 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2277,7 +2277,7 @@ void Game::openConsole(float scale, const wchar_t *line) assert(scale > 0.0f && scale <= 1.0f); #ifdef __ANDROID__ - porting::showInputDialog(gettext("ok"), "", "", 2); + porting::showTextInputDialog("", "", 2); m_android_chat_open = true; #else if (gui_chat_console->isOpenInhibited()) @@ -2293,15 +2293,19 @@ void Game::openConsole(float scale, const wchar_t *line) #ifdef __ANDROID__ void Game::handleAndroidChatInput() { - if (m_android_chat_open && porting::getInputDialogState() == 0) { - std::string text = porting::getInputDialogValue(); - client->typeChatMessage(utf8_to_wide(text)); - m_android_chat_open = false; + // It has to be a text input + if (m_android_chat_open && porting::getLastInputDialogType() == porting::TEXT_INPUT) { + porting::AndroidDialogState dialogState = porting::getInputDialogState(); + if (dialogState == porting::DIALOG_INPUTTED) { + std::string text = porting::getInputDialogMessage(); + client->typeChatMessage(utf8_to_wide(text)); + } + if (dialogState != porting::DIALOG_SHOWN) + m_android_chat_open = false; } } #endif - void Game::toggleFreeMove() { bool free_move = !g_settings->getBool("free_move"); diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index c53c65ded..b268574bf 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3497,46 +3497,58 @@ void GUIFormSpecMenu::legacySortElements(std::list::iterator from } #ifdef __ANDROID__ -bool GUIFormSpecMenu::getAndroidUIInput() +void GUIFormSpecMenu::getAndroidUIInput() { - if (!hasAndroidUIInput()) - return false; + porting::AndroidDialogState dialogState = getAndroidUIInputState(); + if (dialogState == porting::DIALOG_SHOWN) { + return; + } else if (dialogState == porting::DIALOG_CANCELED) { + m_jni_field_name.clear(); + return; + } - // still waiting - if (porting::getInputDialogState() == -1) - return true; + porting::AndroidDialogType dialog_type = porting::getLastInputDialogType(); std::string fieldname = m_jni_field_name; m_jni_field_name.clear(); for (const FieldSpec &field : m_fields) { if (field.fname != fieldname) - continue; + continue; // Iterate until found IGUIElement *element = getElementFromId(field.fid, true); - if (!element || element->getType() != irr::gui::EGUIET_EDIT_BOX) - return false; + if (!element) + return; - gui::IGUIEditBox *editbox = (gui::IGUIEditBox *)element; - std::string text = porting::getInputDialogValue(); - editbox->setText(utf8_to_wide(text).c_str()); + auto element_type = element->getType(); + if (dialog_type == porting::TEXT_INPUT && element_type == irr::gui::EGUIET_EDIT_BOX) { + gui::IGUIEditBox *editbox = (gui::IGUIEditBox *)element; + std::string text = porting::getInputDialogMessage(); + editbox->setText(utf8_to_wide(text).c_str()); - bool enter_after_edit = false; - auto iter = field_enter_after_edit.find(fieldname); - if (iter != field_enter_after_edit.end()) { - enter_after_edit = iter->second; - } - if (enter_after_edit && editbox->getParent()) { - SEvent enter; - enter.EventType = EET_GUI_EVENT; - enter.GUIEvent.Caller = editbox; - enter.GUIEvent.Element = nullptr; - enter.GUIEvent.EventType = gui::EGET_EDITBOX_ENTER; - editbox->getParent()->OnEvent(enter); + bool enter_after_edit = false; + auto iter = field_enter_after_edit.find(fieldname); + if (iter != field_enter_after_edit.end()) { + enter_after_edit = iter->second; + } + if (enter_after_edit && editbox->getParent()) { + SEvent enter; + enter.EventType = EET_GUI_EVENT; + enter.GUIEvent.Caller = editbox; + enter.GUIEvent.Element = nullptr; + enter.GUIEvent.EventType = gui::EGET_EDITBOX_ENTER; + editbox->getParent()->OnEvent(enter); + } + } else if (dialog_type == porting::SELECTION_INPUT && + element_type == irr::gui::EGUIET_COMBO_BOX) { + auto dropdown = (gui::IGUIComboBox *) element; + int selected = porting::getInputDialogSelection(); + dropdown->setAndSendSelected(selected); } + + return; // Early-return after found } - return false; } #endif diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index eb464747f..9f5685df6 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -286,7 +286,7 @@ public: core::rect getAbsoluteRect(); #ifdef __ANDROID__ - bool getAndroidUIInput(); + void getAndroidUIInput(); #endif protected: diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp index e100643a4..b1264ddfe 100644 --- a/src/gui/guiPasswordChange.cpp +++ b/src/gui/guiPasswordChange.cpp @@ -264,14 +264,19 @@ std::string GUIPasswordChange::getNameByID(s32 id) } #ifdef __ANDROID__ -bool GUIPasswordChange::getAndroidUIInput() +void GUIPasswordChange::getAndroidUIInput() { - if (!hasAndroidUIInput()) - return false; + porting::AndroidDialogState dialogState = getAndroidUIInputState(); + if (dialogState == porting::DIALOG_SHOWN) { + return; + } else if (dialogState == porting::DIALOG_CANCELED) { + m_jni_field_name.clear(); + return; + } - // still waiting - if (porting::getInputDialogState() == -1) - return true; + // It has to be a text input + if (porting::getLastInputDialogType() != porting::TEXT_INPUT) + return; gui::IGUIElement *e = nullptr; if (m_jni_field_name == "old_password") @@ -283,10 +288,10 @@ bool GUIPasswordChange::getAndroidUIInput() m_jni_field_name.clear(); if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX) - return false; + return; - std::string text = porting::getInputDialogValue(); + std::string text = porting::getInputDialogMessage(); e->setText(utf8_to_wide(text).c_str()); - return false; + return; } #endif diff --git a/src/gui/guiPasswordChange.h b/src/gui/guiPasswordChange.h index 452702add..8dc670cc2 100644 --- a/src/gui/guiPasswordChange.h +++ b/src/gui/guiPasswordChange.h @@ -45,7 +45,7 @@ public: bool OnEvent(const SEvent &event); #ifdef __ANDROID__ - bool getAndroidUIInput(); + void getAndroidUIInput(); #endif protected: diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 8d46c70f3..b260f17d5 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -268,11 +268,34 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) if (((gui::IGUIEditBox *)hovered)->isPasswordBox()) type = 3; - porting::showInputDialog(gettext("OK"), "", - wide_to_utf8(((gui::IGUIEditBox *)hovered)->getText()), type); + porting::showTextInputDialog("", + wide_to_utf8(((gui::IGUIEditBox *) hovered)->getText()), type); return retval; } } + + if (event.EventType == EET_GUI_EVENT) { + if (event.GUIEvent.EventType == gui::EGET_LISTBOX_OPENED) { + gui::IGUIComboBox *dropdown = (gui::IGUIComboBox *) event.GUIEvent.Caller; + + std::string field_name = getNameByID(dropdown->getID()); + if (field_name.empty()) + return false; + + m_jni_field_name = field_name; + + s32 selected_idx = dropdown->getSelected(); + s32 option_size = dropdown->getItemCount(); + std::string list_of_options[option_size]; + + for (s32 i = 0; i < option_size; i++) { + list_of_options[i] = wide_to_utf8(dropdown->getItem(i)); + } + + porting::showComboBoxDialog(list_of_options, option_size, selected_idx); + return true; // Prevent the Irrlicht dropdown from opening. + } + } #endif // Convert touch events into mouse events. @@ -347,22 +370,12 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) } #ifdef __ANDROID__ -bool GUIModalMenu::hasAndroidUIInput() +porting::AndroidDialogState GUIModalMenu::getAndroidUIInputState() { - // no dialog shown + // No dialog is shown if (m_jni_field_name.empty()) - return false; + return porting::DIALOG_CANCELED; - // still waiting - if (porting::getInputDialogState() == -1) - return true; - - // no value abort dialog processing - if (porting::getInputDialogState() != 0) { - m_jni_field_name.clear(); - return false; - } - - return true; + return porting::getInputDialogState(); } #endif diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h index bfa5d4ac8..9bb55ffec 100644 --- a/src/gui/modalMenu.h +++ b/src/gui/modalMenu.h @@ -22,6 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "irr_ptr.h" #include "util/string.h" +#ifdef __ANDROID__ + #include +#endif enum class PointerType { Mouse, @@ -59,8 +62,8 @@ public: virtual bool OnEvent(const SEvent &event) { return false; }; virtual bool pausesGame() { return false; } // Used for pause menu #ifdef __ANDROID__ - virtual bool getAndroidUIInput() { return false; } - bool hasAndroidUIInput(); + virtual void getAndroidUIInput() {}; + porting::AndroidDialogState getAndroidUIInputState(); #endif PointerType getPointerType() { return m_pointer_type; }; diff --git a/src/porting_android.cpp b/src/porting_android.cpp index c8bdad5a0..43c2cea35 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -165,22 +165,41 @@ bool setSystemPaths() return true; } -void showInputDialog(const std::string &acceptButton, const std::string &hint, - const std::string ¤t, int editType) +void showTextInputDialog(const std::string &hint, const std::string ¤t, int editType) { - jmethodID showdialog = jnienv->GetMethodID(nativeActivity, "showDialog", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); + jmethodID showdialog = jnienv->GetMethodID(nativeActivity, "showTextInputDialog", + "(Ljava/lang/String;Ljava/lang/String;I)V"); FATAL_ERROR_IF(showdialog == nullptr, - "porting::showInputDialog unable to find Java showDialog method"); + "porting::showTextInputDialog unable to find Java showTextInputDialog method"); - jstring jacceptButton = jnienv->NewStringUTF(acceptButton.c_str()); jstring jhint = jnienv->NewStringUTF(hint.c_str()); jstring jcurrent = jnienv->NewStringUTF(current.c_str()); jint jeditType = editType; jnienv->CallVoidMethod(app_global->activity->clazz, showdialog, - jacceptButton, jhint, jcurrent, jeditType); + jhint, jcurrent, jeditType); +} + +void showComboBoxDialog(const std::string optionList[], s32 listSize, s32 selectedIdx) +{ + jmethodID showdialog = jnienv->GetMethodID(nativeActivity, "showSelectionInputDialog", + "([Ljava/lang/String;I)V"); + + FATAL_ERROR_IF(showdialog == nullptr, + "porting::showComboBoxDialog unable to find Java showSelectionInputDialog method"); + + jclass jStringClass = jnienv->FindClass("java/lang/String"); + jobjectArray jOptionList = jnienv->NewObjectArray(listSize, jStringClass, NULL); + jint jselectedIdx = selectedIdx; + + for (s32 i = 0; i < listSize; i ++) { + jnienv->SetObjectArrayElement(jOptionList, i, + jnienv->NewStringUTF(optionList[i].c_str())); + } + + jnienv->CallVoidMethod(app_global->activity->clazz, showdialog, jOptionList, + jselectedIdx); } void openURIAndroid(const char *url) @@ -207,30 +226,53 @@ void shareFileAndroid(const std::string &path) jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl); } -int getInputDialogState() +AndroidDialogType getLastInputDialogType() { - jmethodID dialogstate = jnienv->GetMethodID(nativeActivity, - "getDialogState", "()I"); + jmethodID lastdialogtype = jnienv->GetMethodID(nativeActivity, + "getLastDialogType", "()I"); - FATAL_ERROR_IF(dialogstate == nullptr, - "porting::getInputDialogState unable to find Java getDialogState method"); + FATAL_ERROR_IF(lastdialogtype == nullptr, + "porting::getLastInputDialogType unable to find Java getLastDialogType method"); - return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate); + int dialogType = jnienv->CallIntMethod(app_global->activity->clazz, lastdialogtype); + return static_cast(dialogType); } -std::string getInputDialogValue() +AndroidDialogState getInputDialogState() +{ + jmethodID inputdialogstate = jnienv->GetMethodID(nativeActivity, + "getInputDialogState", "()I"); + + FATAL_ERROR_IF(inputdialogstate == nullptr, + "porting::getInputDialogState unable to find Java getInputDialogState method"); + + int dialogState = jnienv->CallIntMethod(app_global->activity->clazz, inputdialogstate); + return static_cast(dialogState); +} + +std::string getInputDialogMessage() { jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity, - "getDialogValue", "()Ljava/lang/String;"); + "getDialogMessage", "()Ljava/lang/String;"); FATAL_ERROR_IF(dialogvalue == nullptr, - "porting::getInputDialogValue unable to find Java getDialogValue method"); + "porting::getInputDialogMessage unable to find Java getDialogMessage method"); jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, dialogvalue); return readJavaString((jstring) result); } +int getInputDialogSelection() +{ + jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity, "getDialogSelection", "()I"); + + FATAL_ERROR_IF(dialogvalue == nullptr, + "porting::getInputDialogSelection unable to find Java getDialogSelection method"); + + return jnienv->CallIntMethod(app_global->activity->clazz, dialogvalue); +} + #ifndef SERVER float getDisplayDensity() { diff --git a/src/porting_android.h b/src/porting_android.h index d41cc8e4c..980f43ed1 100644 --- a/src/porting_android.h +++ b/src/porting_android.h @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #ifndef __ANDROID__ -#error this include has to be included on android port only! +#error This header has to be included on Android port only! #endif #include @@ -30,22 +30,28 @@ with this program; if not, write to the Free Software Foundation, Inc., #include namespace porting { -// java app +// Java app extern android_app *app_global; -// java <-> c++ interaction interface +// Java <-> C++ interaction interface extern JNIEnv *jnienv; /** - * show text input dialog in java - * @param acceptButton text to display on accept button - * @param hint hint to show - * @param current initial value to display - * @param editType type of texfield - * (1==multiline text input; 2==single line text input; 3=password field) + * Show a text input dialog in Java + * @param hint Hint to be shown + * @param current Initial value to be displayed + * @param editType Type of the text field + * (1 = multi-line text input; 2 = single-line text input; 3 = password field) */ -void showInputDialog(const std::string &acceptButton, - const std::string &hint, const std::string ¤t, int editType); +void showTextInputDialog(const std::string &hint, const std::string ¤t, int editType); + +/** + * Show a selection dialog in Java + * @param optionList The list of options + * @param listSize Size of the list + * @param selectedIdx Selected index + */ +void showComboBoxDialog(const std::string optionList[], s32 listSize, s32 selectedIdx); /** * Opens a share intent to the file at path @@ -54,17 +60,48 @@ void showInputDialog(const std::string &acceptButton, */ void shareFileAndroid(const std::string &path); -/** - * WORKAROUND for not working callbacks from java -> c++ - * get current state of input dialog +/* + * Types of Android input dialog: + * 1. Text input (single/multi-line text and password field) + * 2. Selection input (combo box) */ -int getInputDialogState(); +enum AndroidDialogType { TEXT_INPUT, SELECTION_INPUT }; -/** - * WORKAROUND for not working callbacks from java -> c++ - * get text in current input dialog +/* + * WORKAROUND for not working callbacks from Java -> C++ + * Get the type of the last input dialog */ -std::string getInputDialogValue(); +AndroidDialogType getLastInputDialogType(); + +/* + * States of Android input dialog: + * 1. The dialog is currently shown. + * 2. The dialog has its input sent. + * 3. The dialog is canceled/dismissed. + */ +enum AndroidDialogState { DIALOG_SHOWN, DIALOG_INPUTTED, DIALOG_CANCELED }; + +/* + * WORKAROUND for not working callbacks from Java -> C++ + * Get the state of the input dialog + */ +AndroidDialogState getInputDialogState(); + +/* + * WORKAROUND for not working callbacks from Java -> C++ + * Get the text in the current/last input dialog + * This function clears the dialog state (set to canceled). Make sure to save + * the dialog state before calling this function. + */ +std::string getInputDialogMessage(); + +/* + * WORKAROUND for not working callbacks from Java -> C++ + * Get the selection in the current/last input dialog + * This function clears the dialog state (set to canceled). Make sure to save + * the dialog state before calling this function. + */ +int getInputDialogSelection(); #ifndef SERVER float getDisplayDensity(); -- 2.46.0 From dc7fb26921993fd710b81d828afe7afee95eb31d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 1 Jan 2024 15:35:46 +0100 Subject: [PATCH 82/85] Extend capabilities of Address class --- src/client/game.cpp | 3 +- src/network/address.cpp | 78 +++++++++++++++++------------------ src/network/address.h | 27 ++++++++---- src/unittest/test_address.cpp | 52 +++++++++++++++++++++++ 4 files changed, 111 insertions(+), 49 deletions(-) diff --git a/src/client/game.cpp b/src/client/game.cpp index 40fea4307..518718223 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1621,7 +1621,8 @@ bool Game::connectToServer(const GameStartData &start_data, try { connect_address.Resolve(start_data.address.c_str()); - if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY + if (connect_address.isAny()) { + // replace with localhost IP if (connect_address.isIPv6()) { IPv6AddressBytes addr_bytes; addr_bytes.bytes[15] = 1; diff --git a/src/network/address.cpp b/src/network/address.cpp index df1e3b852..846008078 100644 --- a/src/network/address.cpp +++ b/src/network/address.cpp @@ -84,7 +84,7 @@ Address::Address(const IPv6AddressBytes *ipv6_bytes, u16 port) } // Equality (address family, IP and port must be equal) -bool Address::operator==(const Address &other) +bool Address::operator==(const Address &other) const { if (other.m_addr_family != m_addr_family || other.m_port != m_port) return false; @@ -101,44 +101,60 @@ bool Address::operator==(const Address &other) return false; } -void Address::Resolve(const char *name) +void Address::Resolve(const char *name, Address *fallback) { if (!name || name[0] == 0) { if (m_addr_family == AF_INET) setAddress(static_cast(0)); else if (m_addr_family == AF_INET6) setAddress(static_cast(nullptr)); + if (fallback) + *fallback = Address(); return; } - struct addrinfo *resolved, hints; + const auto ©_from_ai = [] (const struct addrinfo *ai, Address *to) { + if (ai->ai_family == AF_INET) { + struct sockaddr_in *t = (struct sockaddr_in *)ai->ai_addr; + to->m_addr_family = AF_INET; + to->m_address.ipv4 = t->sin_addr; + } else if (ai->ai_family == AF_INET6) { + struct sockaddr_in6 *t = (struct sockaddr_in6 *)ai->ai_addr; + to->m_addr_family = AF_INET6; + to->m_address.ipv6 = t->sin6_addr; + } else { + to->m_addr_family = 0; + } + }; + + struct addrinfo hints; memset(&hints, 0, sizeof(hints)); - // Setup hints + // set a type, so every unique address is only returned once + hints.ai_socktype = SOCK_DGRAM; if (g_settings->getBool("enable_ipv6")) { // AF_UNSPEC allows both IPv6 and IPv4 addresses to be returned hints.ai_family = AF_UNSPEC; } else { hints.ai_family = AF_INET; } + hints.ai_flags = AI_ADDRCONFIG; // Do getaddrinfo() - int e = getaddrinfo(name, NULL, &hints, &resolved); + struct addrinfo *resolved = nullptr; + int e = getaddrinfo(name, nullptr, &hints, &resolved); if (e != 0) throw ResolveError(gai_strerror(e)); + assert(resolved); // Copy data - if (resolved->ai_family == AF_INET) { - struct sockaddr_in *t = (struct sockaddr_in *)resolved->ai_addr; - m_addr_family = AF_INET; - m_address.ipv4 = t->sin_addr; - } else if (resolved->ai_family == AF_INET6) { - struct sockaddr_in6 *t = (struct sockaddr_in6 *)resolved->ai_addr; - m_addr_family = AF_INET6; - m_address.ipv6 = t->sin6_addr; - } else { - m_addr_family = 0; + copy_from_ai(resolved, this); + if (fallback) { + *fallback = Address(); + if (resolved->ai_next) + copy_from_ai(resolved->ai_next, fallback); } + freeaddrinfo(resolved); } @@ -151,28 +167,11 @@ std::string Address::serializeString() const return str; } -struct in_addr Address::getAddress() const -{ - return m_address.ipv4; -} - -struct in6_addr Address::getAddress6() const -{ - return m_address.ipv6; -} - -u16 Address::getPort() const -{ - return m_port; -} - -bool Address::isZero() const +bool Address::isAny() const { if (m_addr_family == AF_INET) { return m_address.ipv4.s_addr == 0; - } - - if (m_addr_family == AF_INET6) { + } else if (m_addr_family == AF_INET6) { static const char zero[16] = {0}; return memcmp(m_address.ipv6.s6_addr, zero, 16) == 0; } @@ -218,18 +217,19 @@ void Address::print(std::ostream& s) const bool Address::isLocalhost() const { - if (isIPv6()) { + if (m_addr_family == AF_INET6) { static const u8 localhost_bytes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; static const u8 mapped_ipv4_localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 0}; - auto addr = m_address.ipv6.s6_addr; + auto *addr = m_address.ipv6.s6_addr; return memcmp(addr, localhost_bytes, 16) == 0 || memcmp(addr, mapped_ipv4_localhost, 13) == 0; + } else if (m_addr_family == AF_INET) { + auto addr = ntohl(m_address.ipv4.s_addr); + return (addr >> 24) == 0x7f; } - - auto addr = ntohl(m_address.ipv4.s_addr); - return (addr >> 24) == 0x7f; + return false; } diff --git a/src/network/address.h b/src/network/address.h index 03528c53b..2fbf419a7 100644 --- a/src/network/address.h +++ b/src/network/address.h @@ -47,21 +47,29 @@ public: Address(u8 a, u8 b, u8 c, u8 d, u16 port); Address(const IPv6AddressBytes *ipv6_bytes, u16 port); - bool operator==(const Address &address); - bool operator!=(const Address &address) { return !(*this == address); } + bool operator==(const Address &address) const; + bool operator!=(const Address &address) const { return !(*this == address); } - struct in_addr getAddress() const; - struct in6_addr getAddress6() const; - u16 getPort() const; int getFamily() const { return m_addr_family; } + bool isValid() const { return m_addr_family != 0; } bool isIPv6() const { return m_addr_family == AF_INET6; } - bool isZero() const; + struct in_addr getAddress() const { return m_address.ipv4; } + struct in6_addr getAddress6() const { return m_address.ipv6; } + u16 getPort() const { return m_port; } + void print(std::ostream &s) const; std::string serializeString() const; + + // Is this an address that binds to all interfaces (like INADDR_ANY)? + bool isAny() const; + // Is this an address referring to the local host? bool isLocalhost() const; - // Resolve() may throw ResolveError (address is unchanged in this case) - void Resolve(const char *name); + // `name`: hostname or numeric IP + // `fallback`: fallback IP to try gets written here + // any empty name resets the IP to the "any address" + // may throw ResolveError (address is unchanged in this case) + void Resolve(const char *name, Address *fallback = nullptr); void setAddress(u32 address); void setAddress(u8 a, u8 b, u8 c, u8 d); @@ -75,5 +83,6 @@ private: struct in_addr ipv4; struct in6_addr ipv6; } m_address; - u16 m_port = 0; // Port is separate from sockaddr structures + // port is separate from in_addr structures + u16 m_port = 0; }; diff --git a/src/unittest/test_address.cpp b/src/unittest/test_address.cpp index f46135577..dd77d0264 100644 --- a/src/unittest/test_address.cpp +++ b/src/unittest/test_address.cpp @@ -32,14 +32,36 @@ public: void runTests(IGameDef *gamedef); + void testBasic(); void testIsLocalhost(); + void testResolve(); }; static TestAddress g_test_instance; void TestAddress::runTests(IGameDef *gamedef) { + TEST(testBasic); TEST(testIsLocalhost); + TEST(testResolve); +} + +void TestAddress::testBasic() +{ + Address tmp; + + UASSERT(!tmp.isValid()); + UASSERTEQ(int, tmp.getFamily(), 0); + + tmp = Address(static_cast(0), 0); + UASSERT(tmp.isValid()); + UASSERTEQ(int, tmp.getFamily(), AF_INET); + UASSERT(tmp.isAny()); + + tmp = Address(static_cast(nullptr), 0); + UASSERT(tmp.isValid()); + UASSERTEQ(int, tmp.getFamily(), AF_INET6); + UASSERT(tmp.isAny()); } void TestAddress::testIsLocalhost() @@ -65,3 +87,33 @@ void TestAddress::testIsLocalhost() memcpy(ipv6Bytes->bytes, &ipv6RawAddr[0], 16); UASSERT(!Address(ipv6Bytes.get(), 0).isLocalhost()) } + +void TestAddress::testResolve() +{ + // Empty test + { + Address tmp(1, 2, 3, 4, 5); + tmp.Resolve(""); + + UASSERT(tmp.isValid()); + UASSERT(tmp.isAny()); + } + + // Localhost test + Address result, fallback; + result.Resolve("localhost", &fallback); + + UASSERT(result.isValid()); + UASSERT(result.isLocalhost()); + + if (fallback.isValid()) { + UASSERT(fallback.isLocalhost()); + + // the result should be ::1 and 127.0.0.1 so the fallback addr should be + // of a different family + UASSERTCMP(int, !=, result.getFamily(), fallback.getFamily()); + } else if (g_settings->getBool("enable_ipv6")) { + warningstream << "Couldn't verify Address::Resolve fallback (no IPv6?)" + << std::endl; + } +} -- 2.46.0 From 20692d54dea378278fbceb4c072225f0c3d12abe Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 1 Jan 2024 16:08:53 +0100 Subject: [PATCH 83/85] Some minor cleanups for UDPSocket class --- src/network/socket.cpp | 27 ++++++++++++++++++--------- src/network/socket.h | 17 +++++++++-------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/network/socket.cpp b/src/network/socket.cpp index 07fcf7062..210e7aef3 100644 --- a/src/network/socket.cpp +++ b/src/network/socket.cpp @@ -73,6 +73,7 @@ void sockets_cleanup() // On Windows, cleanup sockets after use WSACleanup(); #endif + g_sockets_initialized = false; } /* @@ -87,10 +88,18 @@ UDPSocket::UDPSocket(bool ipv6) bool UDPSocket::init(bool ipv6, bool noExceptions) { if (!g_sockets_initialized) { - tracestream << "Sockets not initialized" << std::endl; + verbosestream << "Sockets not initialized" << std::endl; return false; } + if (m_handle >= 0) { + auto msg = "Cannot initialize socket twice"; + verbosestream << msg << std::endl; + if (noExceptions) + return false; + throw SocketException(msg); + } + // Use IPv6 if specified m_addr_family = ipv6 ? AF_INET6 : AF_INET; m_handle = socket(m_addr_family, SOCK_DGRAM, IPPROTO_UDP); @@ -101,7 +110,7 @@ bool UDPSocket::init(bool ipv6, bool noExceptions) << std::endl; } - if (m_handle <= 0) { + if (m_handle < 0) { if (noExceptions) { return false; } @@ -131,11 +140,13 @@ UDPSocket::~UDPSocket() << std::endl; } + if (m_handle >= 0) { #ifdef _WIN32 - closesocket(m_handle); + closesocket(m_handle); #else - close(m_handle); + close(m_handle); #endif + } } void UDPSocket::Bind(Address addr) @@ -251,9 +262,12 @@ void UDPSocket::Send(const Address &destination, const void *data, int size) int UDPSocket::Receive(Address &sender, void *data, int size) { // Return on timeout + assert(m_timeout_ms >= 0); if (!WaitData(m_timeout_ms)) return -1; + size = MYMAX(size, 0); + int received; if (m_addr_family == AF_INET6) { struct sockaddr_in6 address; @@ -311,11 +325,6 @@ int UDPSocket::Receive(Address &sender, void *data, int size) return received; } -int UDPSocket::GetHandle() -{ - return m_handle; -} - void UDPSocket::setTimeoutMs(int timeout_ms) { m_timeout_ms = timeout_ms; diff --git a/src/network/socket.h b/src/network/socket.h index d34186b44..c3758a9d8 100644 --- a/src/network/socket.h +++ b/src/network/socket.h @@ -34,23 +34,24 @@ class UDPSocket { public: UDPSocket() = default; - - UDPSocket(bool ipv6); + UDPSocket(bool ipv6); // calls init() ~UDPSocket(); - void Bind(Address addr); - bool init(bool ipv6, bool noExceptions = false); + void Bind(Address addr); + void Send(const Address &destination, const void *data, int size); // Returns -1 if there is no data int Receive(Address &sender, void *data, int size); - int GetHandle(); // For debugging purposes only void setTimeoutMs(int timeout_ms); // Returns true if there is data, false if timeout occurred bool WaitData(int timeout_ms); + // Debugging purposes only + int GetHandle() const { return m_handle; }; + private: - int m_handle; - int m_timeout_ms; - int m_addr_family; + int m_handle = -1; + int m_timeout_ms = -1; + unsigned short m_addr_family = 0; }; -- 2.46.0 From 2c390b5473687c3569961d1f80aa542e823500db Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 1 Jan 2024 18:15:58 +0100 Subject: [PATCH 84/85] Rework client connecting and enable fallback address use --- src/client/client.cpp | 40 +++++++++++++++++----- src/client/client.h | 17 ++++----- src/client/game.cpp | 53 ++++++++++++++++++++--------- src/client/game.h | 2 ++ src/network/clientpackethandler.cpp | 1 + 5 files changed, 81 insertions(+), 32 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 8d204931c..ef3308d03 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -97,7 +97,6 @@ void PacketCounter::print(std::ostream &o) const Client::Client( const char *playername, const std::string &password, - const std::string &address_name, MapDrawControl &control, IWritableTextureSource *tsrc, IWritableShaderSource *shsrc, @@ -106,7 +105,6 @@ Client::Client( ISoundManager *sound, MtEventManager *event, RenderingEngine *rendering_engine, - bool ipv6, GameUI *game_ui, ELoginRegister allow_login_or_register ): @@ -123,8 +121,6 @@ Client::Client( tsrc, this ), m_particle_manager(std::make_unique(&m_env)), - m_con(new con::Connection(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this)), - m_address_name(address_name), m_allow_login_or_register(allow_login_or_register), m_server_ser_ver(SER_FMT_VER_INVALID), m_last_chat_message_sent(time(NULL)), @@ -338,7 +334,8 @@ bool Client::isShutdown() Client::~Client() { m_shutdown = true; - m_con->Disconnect(); + if (m_con) + m_con->Disconnect(); deleteAuthData(); @@ -381,13 +378,32 @@ Client::~Client() m_sounds_client_to_server.clear(); } -void Client::connect(Address address, bool is_local_server) +void Client::connect(const Address &address, const std::string &address_name, + bool is_local_server) { - initLocalMapSaving(address, m_address_name, is_local_server); + if (m_con) { + // can't do this if the connection has entered auth phase + sanity_check(m_state == LC_Created && m_proto_ver == 0); + infostream << "Client connection will be recreated" << std::endl; + + m_access_denied = false; + m_access_denied_reconnect = false; + m_access_denied_reason.clear(); + } + + m_address_name = address_name; + m_con.reset(new con::Connection(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, + address.isIPv6(), this)); + + infostream << "Connecting to server at "; + address.print(infostream); + infostream << std::endl; // Since we use TryReceive() a timeout here would be ineffective anyway m_con->SetTimeoutMs(0); m_con->Connect(address); + + initLocalMapSaving(address, m_address_name, is_local_server); } void Client::step(float dtime) @@ -908,6 +924,10 @@ void Client::initLocalMapSaving(const Address &address, if (!g_settings->getBool("enable_local_map_saving") || is_local_server) { return; } + if (m_localdb) { + infostream << "Local map saving already running" << std::endl; + return; + } std::string world_path; #define set_world_path(hostname) \ @@ -935,6 +955,8 @@ void Client::ReceiveAll() NetworkPacket pkt; u64 start_ms = porting::getTimeMs(); const u64 budget = 10; + + FATAL_ERROR_IF(!m_con, "Networking not initialized"); for(;;) { // Limit time even if there would be huge amounts of data to // process @@ -1767,7 +1789,7 @@ ClientEvent *Client::getClientEvent() const Address Client::getServerAddress() { - return m_con->GetPeerAddress(PEER_ID_SERVER); + return m_con ? m_con->GetPeerAddress(PEER_ID_SERVER) : Address(); } float Client::mediaReceiveProgress() @@ -1873,11 +1895,13 @@ void Client::afterContentReceived() float Client::getRTT() { + assert(m_con); return m_con->getPeerStat(PEER_ID_SERVER,con::AVG_RTT); } float Client::getCurRate() { + assert(m_con); return (m_con->getLocalStat(con::CUR_INC_RATE) + m_con->getLocalStat(con::CUR_DL_RATE)); } diff --git a/src/client/client.h b/src/client/client.h index b40c60828..3a630c27c 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -122,7 +122,6 @@ public: Client( const char *playername, const std::string &password, - const std::string &address_name, MapDrawControl &control, IWritableTextureSource *tsrc, IWritableShaderSource *shsrc, @@ -131,7 +130,6 @@ public: ISoundManager *sound, MtEventManager *event, RenderingEngine *rendering_engine, - bool ipv6, GameUI *game_ui, ELoginRegister allow_login_or_register ); @@ -155,11 +153,8 @@ public: bool isShutdown(); - /* - The name of the local player should already be set when - calling this, as it is sent in the initialization. - */ - void connect(Address address, bool is_local_server); + void connect(const Address &address, const std::string &address_name, + bool is_local_server); /* Stuff that references the environment is valid only as @@ -351,7 +346,7 @@ public: bool activeObjectsReceived() const { return m_activeobjects_received; } - u16 getProtoVersion() + u16 getProtoVersion() const { return m_proto_ver; } bool m_simple_singleplayer_mode; @@ -363,6 +358,10 @@ public: float getRTT(); float getCurRate(); + // has the server ever replied to us, used for connection retry/fallback + bool hasServerReplied() const { + return getProtoVersion() != 0; // (set in TOCLIENT_HELLO) + } Minimap* getMinimap() { return m_minimap; } void setCamera(Camera* camera) { m_camera = camera; } @@ -415,8 +414,10 @@ public: void showMinimap(bool show = true); + // IP and port we're connected to const Address getServerAddress(); + // Hostname of the connected server (but can also be a numerical IP) const std::string &getAddressName() const { return m_address_name; diff --git a/src/client/game.cpp b/src/client/game.cpp index 518718223..e04ec1674 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1613,13 +1613,15 @@ bool Game::connectToServer(const GameStartData &start_data, *connect_ok = false; // Let's not be overly optimistic *connection_aborted = false; bool local_server_mode = false; + const auto &address_name = start_data.address; showOverlayMessage(N_("Resolving address..."), 0, 15); Address connect_address(0, 0, 0, 0, start_data.socket_port); + Address fallback_address; try { - connect_address.Resolve(start_data.address.c_str()); + connect_address.Resolve(address_name.c_str(), &fallback_address); if (connect_address.isAny()) { // replace with localhost IP @@ -1639,45 +1641,58 @@ bool Game::connectToServer(const GameStartData &start_data, return false; } - if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) { + // this shouldn't normally happen since Address::Resolve() checks for enable_ipv6 + if (g_settings->getBool("enable_ipv6")) { + // empty + } else if (connect_address.isIPv6()) { *error_message = fmtgettext("Unable to connect to %s because IPv6 is disabled", connect_address.serializeString().c_str()); errorstream << *error_message << std::endl; return false; + } else if (fallback_address.isIPv6()) { + fallback_address = Address(); } + fallback_address.setPort(connect_address.getPort()); + if (fallback_address.isValid()) { + infostream << "Resolved two addresses for \"" << address_name + << "\" isIPv6[0]=" << connect_address.isIPv6() + << " isIPv6[1]=" << fallback_address.isIPv6() << std::endl; + } else { + infostream << "Resolved one address for \"" << address_name + << "\" isIPv6=" << connect_address.isIPv6() << std::endl; + } + + try { client = new Client(start_data.name.c_str(), - start_data.password, start_data.address, + start_data.password, *draw_control, texture_src, shader_src, itemdef_manager, nodedef_manager, sound_manager.get(), eventmgr, - m_rendering_engine, connect_address.isIPv6(), m_game_ui.get(), + m_rendering_engine, m_game_ui.get(), start_data.allow_login_or_register); - client->migrateModStorage(); } catch (const BaseException &e) { *error_message = fmtgettext("Error creating client: %s", e.what()); errorstream << *error_message << std::endl; return false; } + client->migrateModStorage(); client->m_simple_singleplayer_mode = simple_singleplayer_mode; - infostream << "Connecting to server at "; - connect_address.print(infostream); - infostream << std::endl; - - client->connect(connect_address, - simple_singleplayer_mode || local_server_mode); - /* Wait for server to accept connection */ + client->connect(connect_address, address_name, + simple_singleplayer_mode || local_server_mode); + try { input->clear(); FpsControl fps_control; f32 dtime; f32 wait_time = 0; // in seconds + bool did_fallback = false; fps_control.reset(); @@ -1712,8 +1727,15 @@ bool Game::connectToServer(const GameStartData &start_data, } wait_time += dtime; - // Only time out if we aren't waiting for the server we started - if (!start_data.address.empty() && wait_time > 10) { + if (local_server_mode) { + // never time out + } else if (wait_time > GAME_FALLBACK_TIMEOUT && !did_fallback) { + if (!client->hasServerReplied() && fallback_address.isValid()) { + client->connect(fallback_address, address_name, + simple_singleplayer_mode || local_server_mode); + } + did_fallback = true; + } else if (wait_time > GAME_CONNECTION_TIMEOUT) { *error_message = gettext("Connection timed out."); errorstream << *error_message << std::endl; break; @@ -1723,8 +1745,7 @@ bool Game::connectToServer(const GameStartData &start_data, showOverlayMessage(N_("Connecting to server..."), dtime, 20); } } catch (con::PeerNotFoundException &e) { - // TODO: Should something be done here? At least an info/error - // message? + warningstream << "This should not happen. Please report a bug." << std::endl; return false; } diff --git a/src/client/game.h b/src/client/game.h index d87e747c5..c1e8bb0a5 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -43,6 +43,8 @@ struct CameraOrientation { f32 camera_pitch; // "up/down" }; +#define GAME_FALLBACK_TIMEOUT 1.8f +#define GAME_CONNECTION_TIMEOUT 10.0f void the_game(bool *kill, InputHandler *input, diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index bacd7f8f0..7b048ea6d 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -163,6 +163,7 @@ void Client::handleCommand_AuthAccept(NetworkPacket* pkt) m_state = LC_Init; } + void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt) { deleteAuthData(); -- 2.46.0 From 2766c70ad3b32f61b6f8f5bbcb2af9cfd38c6cef Mon Sep 17 00:00:00 2001 From: cx384 Date: Sun, 7 Jan 2024 21:49:26 +0100 Subject: [PATCH 85/85] Fix dividing by zero crashes in texture modifiers --- src/client/tile.cpp | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 55a34d432..eb055c2ce 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1598,6 +1598,13 @@ bool TextureSource::generateImagePart(std::string part_of_name, u32 frame_count = stoi(sf.next(":")); u32 frame_index = stoi(sf.next(":")); + if (frame_count == 0){ + errorstream << "generateImagePart(): invalid frame_count " + << "for part_of_name=\"" << part_of_name + << "\", using frame_count = 1 instead." << std::endl; + frame_count = 1; + } + if (baseimg == NULL){ errorstream<<"generateImagePart(): baseimg != NULL " <<"for part_of_name=\""<getDimension(), color); } else { apply_screen(baseimg, v2u32(0, 0), baseimg->getDimension(), color); @@ -1899,6 +1906,13 @@ bool TextureSource::generateImagePart(std::string part_of_name, u32 x0 = stoi(sf.next(",")); u32 y0 = stoi(sf.next(":")); + if (w0 == 0 || h0 == 0) { + errorstream << "generateImagePart(): invalid width or height " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + core::dimension2d img_dim = baseimg->getDimension(); core::dimension2d tile_dim(v2u32(img_dim) / v2u32(w0, h0)); @@ -1965,7 +1979,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, } /* [hsl:hue:saturation:lightness - or + or [colorizehsl:hue:saturation:lightness Adjust the hue, saturation, and lightness of the base image. Like @@ -1978,7 +1992,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, will be converted to a grayscale image as though seen through a colored glass, like "Colorize" in GIMP. */ - else if (str_starts_with(part_of_name, "[hsl:") || + else if (str_starts_with(part_of_name, "[hsl:") || str_starts_with(part_of_name, "[colorizehsl:")) { if (baseimg == nullptr) { @@ -1995,7 +2009,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, Strfnd sf(part_of_name); sf.next(":"); - s32 hue = mystoi(sf.next(":"), -180, 360); + s32 hue = mystoi(sf.next(":"), -180, 360); s32 saturation = sf.at_end() ? defaultSaturation : mystoi(sf.next(":"), -100, 1000); s32 lightness = sf.at_end() ? 0 : mystoi(sf.next(":"), -100, 100); @@ -2005,7 +2019,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, } /* [overlay:filename - or + or [hardlight:filename "A.png^[hardlight:B.png" is the same as "B.png^[overlay:A.Png" @@ -2069,7 +2083,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, s32 contrast = mystoi(sf.next(":"), -127, 127); s32 brightness = sf.at_end() ? 0 : mystoi(sf.next(":"), -127, 127); - apply_brightness_contrast(baseimg, v2u32(0, 0), + apply_brightness_contrast(baseimg, v2u32(0, 0), baseimg->getDimension(), brightness, contrast); } else @@ -2347,14 +2361,14 @@ static void apply_overlay(video::IImage *blend, video::IImage *dst, v2s32 blend_layer_pos = hardlight ? dst_pos : blend_pos; v2s32 base_layer_pos = hardlight ? blend_pos : dst_pos; - for (u32 y = 0; y < size.Y; y++) + for (u32 y = 0; y < size.Y; y++) for (u32 x = 0; x < size.X; x++) { s32 base_x = x + base_layer_pos.X; s32 base_y = y + base_layer_pos.Y; video::SColor blend_c = blend_layer->getPixel(x + blend_layer_pos.X, y + blend_layer_pos.Y); - video::SColor base_c = base_layer->getPixel(base_x, base_y); + video::SColor base_c = base_layer->getPixel(base_x, base_y); double blend_r = blend_c.getRed() / 255.0; double blend_g = blend_c.getGreen() / 255.0; double blend_b = blend_c.getBlue() / 255.0; @@ -2373,7 +2387,7 @@ static void apply_overlay(video::IImage *blend, video::IImage *dst, } } -/* +/* Adjust the brightness and contrast of the base image. Conceptually like GIMP's "Brightness-Contrast" feature but allows brightness to be @@ -2387,17 +2401,17 @@ static void apply_brightness_contrast(video::IImage *dst, v2u32 dst_pos, v2u32 s // (we could technically allow -128/128 here as that would just result in 0 slope) double norm_c = core::clamp(contrast, -127, 127) / 128.0; double norm_b = core::clamp(brightness, -127, 127) / 127.0; - + // Scale brightness so its range is -127.5 to 127.5, otherwise brightness // adjustments will outputs values from 0.5 to 254.5 instead of 0 to 255. double scaled_b = brightness * 127.5 / 127; - // Calculate a contrast slope such that that no colors will get clamped due + // Calculate a contrast slope such that that no colors will get clamped due // to the brightness setting. // This allows the texture modifier to used as a brightness modifier without // the user having to calculate a contrast to avoid clipping at that brightness. double slope = 1 - fabs(norm_b); - + // Apply the user's contrast adjustment to the calculated slope, such that // -127 will make it near-vertical and +127 will make it horizontal double angle = atan(slope); -- 2.46.0