From e62927ed71c557cd885fce03fbc34bb6020089a3 Mon Sep 17 00:00:00 2001 From: BlockMen Date: Thu, 12 Feb 2015 02:55:50 +0100 Subject: [PATCH 001/183] Fix gettext on MSVC --- src/gettext.cpp | 26 +++++++++++++------------- src/gettext.h | 7 +++++-- src/util/string.cpp | 23 +++++++++++++---------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/gettext.cpp b/src/gettext.cpp index 688a22570..fc7569418 100644 --- a/src/gettext.cpp +++ b/src/gettext.cpp @@ -26,24 +26,24 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #if USE_GETTEXT && defined(_MSC_VER) -#include +#include #include #include #include "filesys.h" -#define setlocale(category,localename) \ - setlocale(category,MSVC_LocaleLookup(localename)) +#define setlocale(category, localename) \ + setlocale(category, MSVC_LocaleLookup(localename)) -static std::map glb_supported_locales; +static std::map glb_supported_locales; /******************************************************************************/ BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr) { char* endptr = 0; - int LOCALEID = strtol(pStr,&endptr,16); + int LOCALEID = strtol(pStr, &endptr,16); wchar_t buffer[LOCALE_NAME_MAX_LENGTH]; - memset(buffer,0,sizeof(buffer)); + memset(buffer, 0, sizeof(buffer)); if (GetLocaleInfoW( LOCALEID, LOCALE_SISO639LANGNAME, @@ -52,7 +52,7 @@ BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr) std::wstring name = buffer; - memset(buffer,0,sizeof(buffer)); + memset(buffer, 0, sizeof(buffer)); GetLocaleInfoW( LOCALEID, LOCALE_SISO3166CTRYNAME, @@ -61,7 +61,7 @@ BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr) std::wstring country = buffer; - memset(buffer,0,sizeof(buffer)); + memset(buffer, 0, sizeof(buffer)); GetLocaleInfoW( LOCALEID, LOCALE_SENGLISHLANGUAGENAME, @@ -96,7 +96,7 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) { } if (first_use) { - EnumSystemLocalesA(UpdateLocaleCallback,LCID_SUPPORTED | LCID_ALTERNATE_SORTS); + EnumSystemLocalesA(UpdateLocaleCallback, LCID_SUPPORTED | LCID_ALTERNATE_SORTS); first_use = false; } @@ -148,8 +148,8 @@ void init_gettext(const char *path, const std::string &configured_language) { if (current_language_var != configured_language) { STARTUPINFO startupinfo; PROCESS_INFORMATION processinfo; - memset(&startupinfo,0,sizeof(startupinfo)); - memset(&processinfo,0,sizeof(processinfo)); + memset(&startupinfo, 0, sizeof(startupinfo)); + memset(&processinfo, 0, sizeof(processinfo)); errorstream << "MSVC localization workaround active restating minetest in new environment!" << std::endl; std::string parameters = ""; @@ -169,7 +169,7 @@ void init_gettext(const char *path, const std::string &configured_language) { /** users may start by short name in commandline without extention **/ std::string appname = argv[0]; - if (appname.substr(appname.length() -4) != ".exe") { + if (appname.substr(appname.length() - 4) != ".exe") { appname += ".exe"; } @@ -260,7 +260,7 @@ void init_gettext(const char *path, const std::string &configured_language) { /* no matter what locale is used we need number format to be "C" */ /* to ensure formspec parameters are evaluated correct! */ - setlocale(LC_NUMERIC,"C"); + setlocale(LC_NUMERIC, "C"); infostream << "Message locale is now set to: " << setlocale(LC_ALL, 0) << std::endl; } diff --git a/src/gettext.h b/src/gettext.h index 8e6282887..dce45fa3a 100644 --- a/src/gettext.h +++ b/src/gettext.h @@ -41,16 +41,19 @@ void init_gettext(const char *path, const std::string &configured_language); extern const wchar_t *narrow_to_wide_c(const char *mbs); extern std::wstring narrow_to_wide(const std::string &mbs); - // You must free the returned string! inline const wchar_t *wgettext(const char *str) { return narrow_to_wide_c(gettext(str)); } +// Gettext under MSVC needs this strange way. Just don't ask... inline std::wstring wstrgettext(const std::string &text) { - return narrow_to_wide(gettext(text.c_str())); + const wchar_t *tmp = wgettext(text.c_str()); + std::wstring retval = (std::wstring)tmp; + delete[] tmp; + return retval; } inline std::string strgettext(const std::string &text) diff --git a/src/util/string.cpp b/src/util/string.cpp index b4908d62d..00499ff32 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -98,11 +98,13 @@ const wchar_t *narrow_to_wide_c(const char *mbs) { wchar_t *wcs = NULL; #if defined(_WIN32) - int wcl = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) mbs, -1, NULL, 0); - if (!wcl) - return NULL; - wcs = new wchar_t[wcl]; - MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) mbs, -1, (WCHAR *) wcs, wcl); + int nResult = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) mbs, -1, 0, 0); + if (nResult == 0) { + errorstream << "gettext: MultiByteToWideChar returned null" << std::endl; + } else { + wcs = new wchar_t[nResult]; + MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) mbs, -1, (WCHAR *) wcs, nResult); + } #else size_t wcl = mbstowcs(NULL, mbs, 0); if (wcl == (size_t) -1) @@ -120,12 +122,13 @@ const wchar_t *narrow_to_wide_c(const char *mbs) std::wstring narrow_to_wide(const std::string& mbs) { - const wchar_t *wcs = narrow_to_wide_c(mbs.c_str()); - if (!wcs) + size_t wcl = mbs.size(); + Buffer wcs(wcl + 1); + size_t l = mbstowcs(*wcs, mbs.c_str(), wcl); + if (l == (size_t)(-1)) return L""; - std::wstring wstr(wcs); - delete [] wcs; - return wstr; + wcs[l] = 0; + return *wcs; } #ifdef __ANDROID__ From 61588a43dd61b48383823b7fa948ece4d8dd357e Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 12 Feb 2015 22:03:24 +0100 Subject: [PATCH 002/183] Fix crash on passing false as value in table to table.copy(t) Fixes #2293. --- builtin/common/misc_helpers.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index deeba788e..d9ebc39c3 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -545,12 +545,11 @@ function table.copy(t, seen) seen = seen or {} seen[t] = n for k, v in pairs(t) do - n[type(k) ~= "table" and k or seen[k] or table.copy(k, seen)] = - type(v) ~= "table" and v or seen[v] or table.copy(v, seen) + n[(type(k) == "table" and (seen[k] or table.copy(k, seen))) or k] = + (type(v) == "table" and (seen[v] or table.copy(v, seen))) or v end return n end - -------------------------------------------------------------------------------- -- mainmenu only functions -------------------------------------------------------------------------------- From 9dbca41385750f656cef6af9ded58b6113916425 Mon Sep 17 00:00:00 2001 From: ngosang Date: Tue, 27 Jan 2015 01:17:04 +0100 Subject: [PATCH 003/183] Fix Exit to OS button focus in Pause Menu --- src/game.cpp | 2 +- src/guiFormSpecMenu.cpp | 7 +++++-- src/guiFormSpecMenu.h | 10 +++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index fe3e838b2..ba28aa789 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1157,7 +1157,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL); - + (*cur_formspec)->setFocus(L"btn_continue"); (*cur_formspec)->doPause = true; } diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 20c9ecde7..3f285fa5e 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -97,6 +97,7 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, m_form_src(fsrc), m_text_dst(tdst), m_formspec_version(0), + m_focused_element(L""), m_font(NULL) #ifdef __ANDROID__ ,m_JavaDialogFieldName(L"") @@ -1757,8 +1758,6 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element) <getDynamicData(); } + //set focus + if (!m_focused_element.empty()) + mydata.focused_fieldname = m_focused_element; + //preserve focus gui::IGUIElement *focused_element = Environment->getFocus(); if (focused_element && focused_element->getParent() == this) { diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 6d6c07453..48cb5c553 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -246,13 +246,20 @@ public: m_allowclose = value; } - void lockSize(bool lock,v2u32 basescreensize=v2u32(0,0)) { + void lockSize(bool lock,v2u32 basescreensize=v2u32(0,0)) + { m_lock = lock; m_lockscreensize = basescreensize; } void removeChildren(); void setInitialFocus(); + + void setFocus(std::wstring elementname) + { + m_focused_element = elementname; + } + /* Remove and re-add (or reposition) stuff */ @@ -348,6 +355,7 @@ private: IFormSource *m_form_src; TextDest *m_text_dst; unsigned int m_formspec_version; + std::wstring m_focused_element; typedef struct { bool explicit_size; From 60fa5807b90498bf052bce1a7552f2ec794eb0d4 Mon Sep 17 00:00:00 2001 From: est31 Date: Tue, 6 Jan 2015 21:46:00 +0100 Subject: [PATCH 004/183] README.txt: Simplify initial build steps by using git to fetch sources Also simplify wget steps and apt-get install zlib1g-dev libjsoncpp-dev --- README.txt | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/README.txt b/README.txt index 0999bd165..774dc6639 100644 --- a/README.txt +++ b/README.txt @@ -103,18 +103,32 @@ Compiling on GNU/Linux: ----------------------- Install dependencies. Here's an example for Debian/Ubuntu: -$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev +$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libjsoncpp-dev -Download source, extract (this is the URL to the latest of source repository, which might not work at all times): -$ wget https://github.com/minetest/minetest/tarball/master -O master.tar.gz -$ tar xf master.tar.gz -$ cd minetest-minetest-286edd4 (or similar) +You can install git for easily keeping your copy up to date. +If you dont want git, read below on how to get the source without git. +This is an example for installing git on Debian/Ubuntu: +$ apt-get install git-core -Download minetest_game (otherwise only the "Minimal development test" game is available) +Download source (this is the URL to the latest of source repository, which might not work at all times) using git: +$ git clone --depth 1 https://github.com/minetest/minetest.git +$ cd minetest + +Download minetest_game (otherwise only the "Minimal development test" game is available) using git: $ cd games/ -$ wget https://github.com/minetest/minetest_game/tarball/master -O minetest_game.tar.gz -$ tar xf minetest_game.tar.gz -$ mv minetest-minetest_game-* minetest_game +$ git clone --depth 1 https://github.com/minetest/minetest_game.git +$ cd .. + +Download source, without using git: +$ wget https://github.com/minetest/minetest/archive/master.tar.gz +$ tar xf master.tar.gz +$ cd minetest-master + +Download minetest_game, without using git: +$ cd games/ +$ wget https://github.com/minetest/minetest_game/archive/master.tar.gz +$ tar xf master.tar.gz +$ mv minetest_game-master minetest_game $ cd .. Build a version that runs directly from the source directory: From 93e5ab367abe9f68cf1fe3ed8a198f563d9452af Mon Sep 17 00:00:00 2001 From: Markus Koschany Date: Tue, 20 Jan 2015 10:41:51 +0100 Subject: [PATCH 005/183] Fix FTBFS on GNU/Hurd platform Minetest fails to build on GNU/Hurd due to a name clash with OSX/Apple, both are defining the __MACH__ keyword. This commit fixes the issue. --- src/cguittfont/irrUString.h | 2 +- src/jthread/jevent.h | 4 ++-- src/jthread/jsemaphore.h | 4 ++-- src/jthread/pthread/jevent.cpp | 2 +- src/jthread/pthread/jsemaphore.cpp | 20 ++++++++++---------- src/porting.h | 8 ++++---- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/cguittfont/irrUString.h b/src/cguittfont/irrUString.h index c175c792a..eb7abe5a1 100644 --- a/src/cguittfont/irrUString.h +++ b/src/cguittfont/irrUString.h @@ -45,7 +45,7 @@ #define __BYTE_ORDER 0 #define __LITTLE_ENDIAN 0 #define __BIG_ENDIAN 1 -#elif __MACH__ +#elif defined(__MACH__) && defined(__APPLE__) #include #elif defined(__FreeBSD__) #include diff --git a/src/jthread/jevent.h b/src/jthread/jevent.h index f97e09ca0..9ea7ebde8 100644 --- a/src/jthread/jevent.h +++ b/src/jthread/jevent.h @@ -30,7 +30,7 @@ #ifdef _WIN32 #include -#elif __MACH__ +#elif defined(__MACH__) && defined(__APPLE__) #include #include #include @@ -43,7 +43,7 @@ class Event { #ifdef _WIN32 HANDLE hEvent; -#elif __MACH__ +#elif defined(__MACH__) && defined(__APPLE__) semaphore_t sem; #else sem_t sem; diff --git a/src/jthread/jsemaphore.h b/src/jthread/jsemaphore.h index 4ab006aea..32e9bc2f2 100644 --- a/src/jthread/jsemaphore.h +++ b/src/jthread/jsemaphore.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #define MAX_SEMAPHORE_COUNT 1024 -#elif __MACH__ +#elif defined(__MACH__) && defined(__APPLE__) #include #include #include @@ -52,7 +52,7 @@ public: private: #if defined(WIN32) HANDLE m_hSemaphore; -#elif __MACH__ +#elif defined(__MACH__) && defined(__APPLE__) semaphore_t m_semaphore; int semcount; #else diff --git a/src/jthread/pthread/jevent.cpp b/src/jthread/pthread/jevent.cpp index 6a45a37d2..e1d40f4c1 100644 --- a/src/jthread/pthread/jevent.cpp +++ b/src/jthread/pthread/jevent.cpp @@ -29,7 +29,7 @@ #define UNUSED(expr) do { (void)(expr); } while (0) -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) #undef sem_t #define sem_t semaphore_t #undef sem_init diff --git a/src/jthread/pthread/jsemaphore.cpp b/src/jthread/pthread/jsemaphore.cpp index 16e001e92..15281ba64 100644 --- a/src/jthread/pthread/jsemaphore.cpp +++ b/src/jthread/pthread/jsemaphore.cpp @@ -20,13 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "jthread/jsemaphore.h" -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) #include #endif #define UNUSED(expr) do { (void)(expr); } while (0) -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) #undef sem_t #undef sem_init #undef sem_wait @@ -44,7 +44,7 @@ JSemaphore::JSemaphore() { int sem_init_retval = sem_init(&m_semaphore,0,0); assert(sem_init_retval == 0); UNUSED(sem_init_retval); -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) semcount = 0; #endif } @@ -73,7 +73,7 @@ void JSemaphore::Post() { int sem_post_retval = sem_post(&m_semaphore); assert(sem_post_retval == 0); UNUSED(sem_post_retval); -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) pthread_mutex_lock(&semcount_mutex); semcount++; pthread_mutex_unlock(&semcount_mutex); @@ -84,7 +84,7 @@ void JSemaphore::Wait() { int sem_wait_retval = sem_wait(&m_semaphore); assert(sem_wait_retval == 0); UNUSED(sem_wait_retval); -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) pthread_mutex_lock(&semcount_mutex); semcount--; pthread_mutex_unlock(&semcount_mutex); @@ -92,7 +92,7 @@ void JSemaphore::Wait() { } bool JSemaphore::Wait(unsigned int time_ms) { -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) mach_timespec_t waittime; waittime.tv_sec = time_ms / 1000; waittime.tv_nsec = 1000000 * (time_ms % 1000); @@ -106,14 +106,14 @@ bool JSemaphore::Wait(unsigned int time_ms) { return false; } -#ifndef __MACH__ +#if !(defined(__MACH__) && defined(__APPLE__)) waittime.tv_nsec = ((time_ms % 1000) * 1000 * 1000) + (now.tv_usec * 1000); waittime.tv_sec = (time_ms / 1000) + (waittime.tv_nsec / (1000*1000*1000)) + now.tv_sec; waittime.tv_nsec %= 1000*1000*1000; #endif errno = 0; -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) int sem_wait_retval = semaphore_timedwait(m_semaphore, waittime); if (sem_wait_retval == KERN_OPERATION_TIMED_OUT) { errno = ETIMEDOUT; @@ -128,7 +128,7 @@ bool JSemaphore::Wait(unsigned int time_ms) { if (sem_wait_retval == 0) { -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) pthread_mutex_lock(&semcount_mutex); semcount--; pthread_mutex_unlock(&semcount_mutex); @@ -144,7 +144,7 @@ bool JSemaphore::Wait(unsigned int time_ms) { int JSemaphore::GetValue() { int retval = 0; -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) pthread_mutex_lock(&semcount_mutex); retval = semcount; pthread_mutex_unlock(&semcount_mutex); diff --git a/src/porting.h b/src/porting.h index 8387453e8..3d2677564 100644 --- a/src/porting.h +++ b/src/porting.h @@ -60,7 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include //for uintptr_t - #if (defined(linux) || defined(__linux)) && !defined(_GNU_SOURCE) +#if (defined(linux) || defined(__linux) || defined(__GNU__)) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -228,7 +228,7 @@ void initIrrlicht(irr::IrrlichtDevice * ); #else // Posix #include #include -#ifdef __MACH__ +#if defined(__MACH__) && defined(__APPLE__) #include #include #endif @@ -258,7 +258,7 @@ void initIrrlicht(irr::IrrlichtDevice * ); { struct timespec ts; // from http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x -#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time +#if defined(__MACH__) && defined(__APPLE__) // 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); @@ -358,7 +358,7 @@ inline u32 getDeltaMs(u32 old_time_ms, u32 new_time_ms) inline void setThreadName(const char *name) { pthread_setname_np(name); } -#elif defined(_WIN32) +#elif defined(_WIN32) || defined(__GNU__) inline void setThreadName(const char* name) {} #else #warning "Unrecognized platform, thread names will not be available." From 9e9688fc613a74e81aa5ce544482b512071c4677 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Wed, 11 Feb 2015 02:27:43 -0500 Subject: [PATCH 006/183] Fix Android build of narrow_to_wide --- src/util/string.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/util/string.cpp b/src/util/string.cpp index 00499ff32..de669b473 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -62,20 +62,21 @@ int wctomb(char *s, wchar_t wc) int mbtowc(wchar_t *pwc, const char *s, size_t n) { - wchar_t *intermediate = narrow_to_wide(s); + const wchar_t *tmp = narrow_to_wide_c(s); - if (intermediate.length() > 0) { - *pwc = intermediate[0]; + if (tmp[0] != '\0') { + *pwc = tmp[0]; return 1; } else { return -1; } } + // You must free the returned string! const wchar_t *narrow_to_wide_c(const char *mbs) { size_t mbl = strlen(mbs); - wchar_t wcs = new wchar_t[mbl + 1]; + wchar_t *wcs = new wchar_t[mbl + 1]; for (size_t i = 0; i < mbl; i++) { if (((unsigned char) mbs[i] > 31) && From 7f078582094b6fef067294b4db7a58abc65ed2c6 Mon Sep 17 00:00:00 2001 From: Rui Date: Wed, 11 Feb 2015 13:42:58 +0900 Subject: [PATCH 007/183] Fix tab_mods.lua: default screenshot patch https://forum.minetest.net/viewtopic.php?f=6&t=11201 Fixed this bug. --- builtin/mainmenu/tab_mods.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua index d16ecca8c..e00f580bb 100644 --- a/builtin/mainmenu/tab_mods.lua +++ b/builtin/mainmenu/tab_mods.lua @@ -57,7 +57,7 @@ local function get_formspec(tabview, name, tabdata) end if modscreenshot == nil then - modscreenshot = modstore.basetexturedir .. "no_screenshot.png" + modscreenshot = defaulttexturedir .. "no_screenshot.png" end retval = retval From c7249f59833c42faf5407108a25bd65bae893b95 Mon Sep 17 00:00:00 2001 From: Rui Date: Thu, 12 Feb 2015 19:26:26 +0900 Subject: [PATCH 008/183] Fix store.lua bug: default screenshot --- builtin/mainmenu/store.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/mainmenu/store.lua b/builtin/mainmenu/store.lua index f0ddfce8f..999125d6e 100644 --- a/builtin/mainmenu/store.lua +++ b/builtin/mainmenu/store.lua @@ -391,7 +391,7 @@ function modstore.getscreenshot(ypos,listentry) listentry.details.screenshot_url == "") then if listentry.texturename == nil then - listentry.texturename = modstore.basetexturedir .. "no_screenshot.png" + listentry.texturename = defaulttexturedir .. "no_screenshot.png" end return "image[0,".. ypos .. ";3,2;" .. From 878e9f759481948af73e5129c5a79e44425a534e Mon Sep 17 00:00:00 2001 From: ngosang Date: Thu, 22 Jan 2015 17:09:29 +0100 Subject: [PATCH 009/183] Fix .zip extraction (mod store) --- src/script/lua_api/l_mainmenu.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 28c3d193d..0d8365106 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -859,19 +859,19 @@ int ModApiMainMenu::l_extract_zip(lua_State *L) unsigned int number_of_files = files_in_zip->getFileCount(); - for (unsigned int i=0; i < number_of_files; i++) { + for (unsigned int i=0; i < number_of_files; i++) { std::string fullpath = destination; fullpath += DIR_DELIM; fullpath += files_in_zip->getFullFileName(i).c_str(); + std::string fullpath_dir = fs::RemoveLastPathComponent(fullpath); - if (files_in_zip->isDirectory(i)) { - if (! fs::CreateAllDirs(fullpath) ) { + if (!files_in_zip->isDirectory(i)) { + if (!fs::PathExists(fullpath_dir) && !fs::CreateAllDirs(fullpath_dir)) { fs->removeFileArchive(fs->getFileArchiveCount()-1); lua_pushboolean(L,false); return 1; } - } - else { + io::IReadFile* toread = opened_zip->createAndOpenFile(i); FILE *targetfile = fopen(fullpath.c_str(),"wb"); @@ -883,7 +883,7 @@ int ModApiMainMenu::l_extract_zip(lua_State *L) } char read_buffer[1024]; - unsigned int total_read = 0; + long total_read = 0; while (total_read < toread->getSize()) { From 7f6fc148bd6f8337781efe59fd4f189fa94a2fda Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Wed, 11 Feb 2015 09:57:35 +0100 Subject: [PATCH 010/183] Fix issue #2279. ok @zeno- --- src/game.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index ba28aa789..a1e2b807a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3218,10 +3218,13 @@ void Game::updateCamera(VolatileRunFlags *flags, u32 busy_time, v3s16 old_camera_offset = camera->getOffset(); if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CAMERA_MODE])) { - camera->toggleCameraMode(); GenericCAO *playercao = player->getCAO(); - assert(playercao != NULL); + // If playercao not loaded, don't change camera + if (playercao == NULL) + return; + + camera->toggleCameraMode(); playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); } From 2b635a892cf6f484c327307f24a91dca472f4fd4 Mon Sep 17 00:00:00 2001 From: ngosang Date: Wed, 21 Jan 2015 02:50:33 +0100 Subject: [PATCH 011/183] Minor fixes in translations --- builtin/mainmenu/tab_mods.lua | 2 +- builtin/mainmenu/tab_multiplayer.lua | 6 +++--- builtin/mainmenu/tab_simple_main.lua | 6 +++--- src/game.cpp | 2 +- src/guiKeyChangeMenu.cpp | 10 +++++----- src/guiPasswordChange.cpp | 12 ++++++------ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua index e00f580bb..901f14553 100644 --- a/builtin/mainmenu/tab_mods.lua +++ b/builtin/mainmenu/tab_mods.lua @@ -96,7 +96,7 @@ local function get_formspec(tabview, name, tabdata) else --show dependencies - retval = retval .. ",Depends:," + retval = retval .. "," .. fgettext("Depends:") .. "," local toadd = modmgr.get_dependencies(selected_mod.path) diff --git a/builtin/mainmenu/tab_multiplayer.lua b/builtin/mainmenu/tab_multiplayer.lua index c44fd0144..734cb5d3e 100644 --- a/builtin/mainmenu/tab_multiplayer.lua +++ b/builtin/mainmenu/tab_multiplayer.lua @@ -59,9 +59,9 @@ local function get_formspec(tabview, name, tabdata) "text,align=right;" .. -- clients "text,align=center,padding=0.25;" .. -- "/" "text,align=right,padding=0.25;" .. -- clients_max - image_column("Creative mode", "creative") .. ",padding=1;" .. - image_column("Damage enabled", "damage") .. ",padding=0.25;" .. - image_column("PvP enabled", "pvp") .. ",padding=0.25;" .. + image_column(fgettext("Creative mode"), "creative") .. ",padding=1;" .. + image_column(fgettext("Damage enabled"), "damage") .. ",padding=0.25;" .. + image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. "text,padding=1]" -- name else retval = retval .. "tablecolumns[text]" diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua index cab1702cf..87bd551c0 100644 --- a/builtin/mainmenu/tab_simple_main.lua +++ b/builtin/mainmenu/tab_simple_main.lua @@ -42,9 +42,9 @@ local function get_formspec(tabview, name, tabdata) "text,align=right;" .. -- clients "text,align=center,padding=0.25;" .. -- "/" "text,align=right,padding=0.25;" .. -- clients_max - image_column("Creative mode", "creative") .. ",padding=1;" .. - image_column("Damage enabled", "damage") .. ",padding=0.25;" .. - image_column("PvP enabled", "pvp") .. ",padding=0.25;" .. + image_column(fgettext("Creative mode"), "creative") .. ",padding=1;" .. + image_column(fgettext("Damage enabled"), "damage") .. ",padding=0.25;" .. + image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. "text,padding=1]" -- name else retval = retval .. "tablecolumns[text]" diff --git a/src/game.cpp b/src/game.cpp index a1e2b807a..896973515 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1074,7 +1074,7 @@ static void show_deathscreen(GUIFormSpecMenu **cur_formspec, std::string(FORMSPEC_VERSION_STRING) + SIZE_TAG "bgcolor[#320000b4;true]" - "label[4.85,1.35;You died.]" + "label[4.85,1.35;" + gettext("You died.") + "]" "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]" ; diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp index 9bc8118a1..4cd9f36d9 100644 --- a/src/guiKeyChangeMenu.cpp +++ b/src/guiKeyChangeMenu.cpp @@ -156,8 +156,8 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) } { - s32 option_x = offset.X + 10; - s32 option_y = offset.Y; + s32 option_x = offset.X; + s32 option_y = offset.Y + 5; u32 option_w = 180; { core::rect rect(0, 0, option_w, 30); @@ -171,9 +171,9 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) } { - s32 option_x = offset.X + 10; - s32 option_y = offset.Y; - u32 option_w = 220; + s32 option_x = offset.X; + s32 option_y = offset.Y + 5; + u32 option_w = 280; { core::rect rect(0, 0, option_w, 30); rect += topleft + v2s32(option_x, option_y); diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp index d9a5cc48b..0e19f571d 100644 --- a/src/guiPasswordChange.cpp +++ b/src/guiPasswordChange.cpp @@ -103,8 +103,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) */ s32 ypos = 50; { - core::rect rect(0, 0, 110, 20); - rect += topleft_client + v2s32(35, ypos+6); + core::rect rect(0, 0, 150, 20); + rect += topleft_client + v2s32(25, ypos+6); text = wgettext("Old Password"); Environment->addStaticText(text, rect, false, true, this, -1); delete[] text; @@ -119,8 +119,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) } ypos += 50; { - core::rect rect(0, 0, 110, 20); - rect += topleft_client + v2s32(35, ypos+6); + core::rect rect(0, 0, 150, 20); + rect += topleft_client + v2s32(25, ypos+6); text = wgettext("New Password"); Environment->addStaticText(text, rect, false, true, this, -1); delete[] text; @@ -134,8 +134,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) } ypos += 50; { - core::rect rect(0, 0, 110, 20); - rect += topleft_client + v2s32(35, ypos+6); + core::rect rect(0, 0, 150, 20); + rect += topleft_client + v2s32(25, ypos+6); text = wgettext("Confirm Password"); Environment->addStaticText(text, rect, false, true, this, -1); delete[] text; From ec0bf899ed737435e5ed26cf5f78a1e0c80be248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Van=C4=9Bk?= Date: Thu, 12 Feb 2015 16:21:43 +0100 Subject: [PATCH 012/183] Update czech translation --- po/cs/minetest.po | 734 ++++++++++++++++++++++++++-------------------- 1 file changed, 411 insertions(+), 323 deletions(-) diff --git a/po/cs/minetest.po b/po/cs/minetest.po index ce24e54ae..563275b01 100644 --- a/po/cs/minetest.po +++ b/po/cs/minetest.po @@ -1,24 +1,23 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. +# Czech translations for minetest. +# Copyright (C) 2011 celeron +# This file is distributed under the same license as the minetest. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: minetest\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-12-13 15:24+0100\n" -"PO-Revision-Date: 2013-12-04 11:23+0200\n" -"Last-Translator: Jakub Vaněk \n" -"Language-Team: LANGUAGE \n" +"POT-Creation-Date: 2015-02-12 13:13+0100\n" +"PO-Revision-Date: 2015-02-12 16:16+0100\n" +"Last-Translator: Jakub Vanek \n" +"Language-Team: Czech <>\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Weblate 1.7-dev\n" -#: builtin/fstk/ui.lua:67 +#: builtin/fstk/ui.lua:67 builtin/mainmenu/store.lua:165 msgid "Ok" msgstr "OK" @@ -29,18 +28,18 @@ msgstr "Svět:" #: builtin/mainmenu/dlg_config_world.lua:30 #: builtin/mainmenu/dlg_config_world.lua:32 msgid "Hide Game" -msgstr "Skrýt interní" +msgstr "Skrýt vnitřní" #: builtin/mainmenu/dlg_config_world.lua:36 #: builtin/mainmenu/dlg_config_world.lua:38 msgid "Hide mp content" -msgstr "Skrýt obsah balíčku" +msgstr "Skrýt obsahy balíčků" #: builtin/mainmenu/dlg_config_world.lua:46 msgid "Mod:" -msgstr "Mody:" +msgstr "Mod:" -#: builtin/mainmenu/dlg_config_world.lua:48 +#: builtin/mainmenu/dlg_config_world.lua:48 builtin/mainmenu/tab_mods.lua:99 msgid "Depends:" msgstr "Závislosti:" @@ -82,7 +81,7 @@ msgstr "Seedové číslo" #: builtin/mainmenu/dlg_create_world.lua:56 msgid "Mapgen" -msgstr "Generátor světa" +msgstr "Generátor mapy" #: builtin/mainmenu/dlg_create_world.lua:59 msgid "Game" @@ -94,19 +93,19 @@ msgstr "Vytvořit" #: builtin/mainmenu/dlg_create_world.lua:68 msgid "You have no subgames installed." -msgstr "" +msgstr "Nemáte nainstalované žádné podhry." #: builtin/mainmenu/dlg_create_world.lua:69 msgid "Download one from minetest.net" -msgstr "" +msgstr "Stáhněte si jednu z minetest.net" #: builtin/mainmenu/dlg_create_world.lua:72 msgid "Warning: The minimal development test is meant for developers." -msgstr "" +msgstr "Varování: \"Minimal development test\" je zamýšlen pouze pro vývojáře." #: builtin/mainmenu/dlg_create_world.lua:73 msgid "Download a subgame, such as minetest_game, from minetest.net" -msgstr "" +msgstr "Stáhněte si z minetest.net podhru, například minetest_game." #: builtin/mainmenu/dlg_create_world.lua:97 msgid "A world named \"$1\" already exists" @@ -114,7 +113,7 @@ msgstr "Svět s názvem \"$1\" už existuje" #: builtin/mainmenu/dlg_create_world.lua:116 msgid "No worldname given or no game selected" -msgstr "Nebyla vybrána žádná hra" +msgstr "Nebyla vybrána podhra nebo název" #: builtin/mainmenu/dlg_delete_mod.lua:26 msgid "Are you sure you want to delete \"$1\"?" @@ -122,7 +121,7 @@ msgstr "Skutečně chcete odstranit \"$1\"?" #: builtin/mainmenu/dlg_delete_mod.lua:27 #: builtin/mainmenu/dlg_delete_world.lua:25 -#: builtin/mainmenu/tab_settings.lua:25 +#: builtin/mainmenu/tab_settings.lua:79 msgid "Yes" msgstr "Ano" @@ -156,16 +155,15 @@ msgstr "Přijmout" #: builtin/mainmenu/modmgr.lua:342 msgid "Install Mod: file: \"$1\"" -msgstr "Instalace Modu: ze souboru: \"$1\"" +msgstr "Instalace modu: ze souboru: \"$1\"" #: builtin/mainmenu/modmgr.lua:343 -#, fuzzy msgid "" "\n" "Install Mod: unsupported filetype \"$1\" or broken archive" msgstr "" "\n" -"Instalace Modu: nepodporovaný typ souboru \"$1\"" +"Instalace modu: špatný archiv nebo nepodporovaný typ souboru \"$1\"" #: builtin/mainmenu/modmgr.lua:363 msgid "Failed to install $1 to $2" @@ -173,60 +171,49 @@ msgstr "Selhala instalace $1 do $2" #: builtin/mainmenu/modmgr.lua:366 msgid "Install Mod: unable to find suitable foldername for modpack $1" -msgstr "" -"Install Mod: nenalezen vhodný adresář s příslušným názvem pro balíček modů $1" +msgstr "Instalace modu: nenalezen vhodný adresář s příslušným názvem pro balíček $1" #: builtin/mainmenu/modmgr.lua:386 msgid "Install Mod: unable to find real modname for: $1" -msgstr "Install Mod: Nenašel jsem skutečné jméno modu: $1" +msgstr "Instalace modu: nenašel jsem skutečné jméno modu: $1" #: builtin/mainmenu/store.lua:88 msgid "Unsorted" -msgstr "" +msgstr "Neřazené" -#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584 +#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580 msgid "Search" -msgstr "" +msgstr "Hledání" -#: builtin/mainmenu/store.lua:125 -#, fuzzy -msgid "Downloading" -msgstr "Stáhnout" +#: builtin/mainmenu/store.lua:126 +msgid "Downloading $1, please wait..." +msgstr "Stahuji $1, prosím čekejte..." -#: builtin/mainmenu/store.lua:127 -msgid "please wait..." -msgstr "" - -#: builtin/mainmenu/store.lua:159 +#: builtin/mainmenu/store.lua:160 msgid "Successfully installed:" -msgstr "" +msgstr "Úspěšně nainstalováno:" -#: builtin/mainmenu/store.lua:163 -#, fuzzy +#: builtin/mainmenu/store.lua:162 msgid "Shortname:" -msgstr "Název světa" +msgstr "Zkratka:" -#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866 -msgid "ok" -msgstr "" - -#: builtin/mainmenu/store.lua:476 +#: builtin/mainmenu/store.lua:472 msgid "Rating" msgstr "Hodnocení" -#: builtin/mainmenu/store.lua:501 +#: builtin/mainmenu/store.lua:497 msgid "re-Install" -msgstr "přeinstalovat" +msgstr "Přeinstalovat" -#: builtin/mainmenu/store.lua:503 +#: builtin/mainmenu/store.lua:499 msgid "Install" msgstr "Instalovat" -#: builtin/mainmenu/store.lua:522 +#: builtin/mainmenu/store.lua:518 msgid "Close store" -msgstr "" +msgstr "Zavřít obchod" -#: builtin/mainmenu/store.lua:530 +#: builtin/mainmenu/store.lua:526 msgid "Page $1 of $2" msgstr "Strana $1 z $2" @@ -248,7 +235,7 @@ msgstr "Bývalí přispěvatelé" #: builtin/mainmenu/tab_mods.lua:30 msgid "Installed Mods:" -msgstr "Nainstalované Mody:" +msgstr "Nainstalované mody:" #: builtin/mainmenu/tab_mods.lua:39 msgid "Online mod repository" @@ -268,7 +255,7 @@ msgstr "Přejmenovat" #: builtin/mainmenu/tab_mods.lua:95 msgid "Uninstall selected modpack" -msgstr "Odinstalovat označený balíček modů" +msgstr "Odinstalovat označený balíček" #: builtin/mainmenu/tab_mods.lua:106 msgid "Uninstall selected mod" @@ -276,39 +263,53 @@ msgstr "Odinstalovat vybraný mod" #: builtin/mainmenu/tab_mods.lua:121 msgid "Select Mod File:" -msgstr "Vybrat Soubor s Modem:" +msgstr "Vybrat soubor s modem:" #: builtin/mainmenu/tab_mods.lua:165 msgid "Mods" msgstr "Mody" #: builtin/mainmenu/tab_multiplayer.lua:23 -msgid "Address/Port" -msgstr "Adresa/port" +msgid "Address / Port :" +msgstr "Adresa / Port :" -#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37 -#: builtin/mainmenu/tab_simple_main.lua:25 -msgid "Name/Password" -msgstr "Jméno/Heslo" +#: builtin/mainmenu/tab_multiplayer.lua:24 +msgid "Name / Password :" +msgstr "Jméno / Heslo :" #: builtin/mainmenu/tab_multiplayer.lua:29 #: builtin/mainmenu/tab_simple_main.lua:30 msgid "Public Serverlist" -msgstr "Veřejný seznam serverů" +msgstr "Seznam veřejných serverů" #: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26 #: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230 msgid "Delete" -msgstr "Vymazat" +msgstr "Smazat" #: builtin/mainmenu/tab_multiplayer.lua:38 #: builtin/mainmenu/tab_simple_main.lua:34 msgid "Connect" msgstr "Připojit" -#: builtin/mainmenu/tab_multiplayer.lua:252 +#: builtin/mainmenu/tab_multiplayer.lua:62 +#: builtin/mainmenu/tab_simple_main.lua:45 +msgid "Creative mode" +msgstr "Kreativní mód" + +#: builtin/mainmenu/tab_multiplayer.lua:63 +#: builtin/mainmenu/tab_simple_main.lua:46 +msgid "Damage enabled" +msgstr "Poškození povoleno" + +#: builtin/mainmenu/tab_multiplayer.lua:64 +#: builtin/mainmenu/tab_simple_main.lua:47 +msgid "PvP enabled" +msgstr "PvP povoleno" + +#: builtin/mainmenu/tab_multiplayer.lua:247 msgid "Client" -msgstr "Hra více hráčů" +msgstr "Klient" #: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86 msgid "New" @@ -320,18 +321,18 @@ msgstr "Nastavit" #: builtin/mainmenu/tab_server.lua:29 msgid "Start Game" -msgstr "Začít hru" +msgstr "Spustit hru" #: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89 msgid "Select World:" msgstr "Vyber svět:" -#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63 +#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:75 #: builtin/mainmenu/tab_singleplayer.lua:90 msgid "Creative Mode" msgstr "Kreativní mód" -#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65 +#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:77 #: builtin/mainmenu/tab_singleplayer.lua:92 msgid "Enable Damage" msgstr "Povolit poškození" @@ -340,13 +341,17 @@ msgstr "Povolit poškození" msgid "Public" msgstr "Veřejný" +#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25 +msgid "Name/Password" +msgstr "Jméno/Heslo" + #: builtin/mainmenu/tab_server.lua:45 msgid "Bind Address" -msgstr "" +msgstr "Svázat adresu" #: builtin/mainmenu/tab_server.lua:47 msgid "Port" -msgstr "" +msgstr "Port" #: builtin/mainmenu/tab_server.lua:51 msgid "Server Port" @@ -354,140 +359,153 @@ msgstr "Port serveru" #: builtin/mainmenu/tab_server.lua:174 msgid "Server" -msgstr "Místní server" +msgstr "Server" + +#: builtin/mainmenu/tab_settings.lua:21 +msgid "No Filter" +msgstr "Žádný filtr" + +#: builtin/mainmenu/tab_settings.lua:22 +msgid "Bilinear Filter" +msgstr "Bilineární filtr" #: builtin/mainmenu/tab_settings.lua:23 +msgid "Trilinear Filter" +msgstr "Trilineární filtr" + +#: builtin/mainmenu/tab_settings.lua:32 +msgid "No Mipmap" +msgstr "Žádné Mipmapy" + +#: builtin/mainmenu/tab_settings.lua:33 +msgid "Mipmap" +msgstr "Mipmapa" + +#: builtin/mainmenu/tab_settings.lua:34 +msgid "Mipmap + Aniso. Filter" +msgstr "Mipmapa + Anizo. filtr" + +#: builtin/mainmenu/tab_settings.lua:77 msgid "Are you sure to reset your singleplayer world?" -msgstr "" +msgstr "Jste si jisti, že chcete resetovat místní svět?" -#: builtin/mainmenu/tab_settings.lua:27 +#: builtin/mainmenu/tab_settings.lua:81 msgid "No!!!" -msgstr "" +msgstr "Ne!!!" -#: builtin/mainmenu/tab_settings.lua:134 +#: builtin/mainmenu/tab_settings.lua:181 msgid "Smooth Lighting" -msgstr "Hladké osvětlení" +msgstr "Plynulé osvětlení" -#: builtin/mainmenu/tab_settings.lua:136 +#: builtin/mainmenu/tab_settings.lua:183 msgid "Enable Particles" -msgstr "Povolit Částice" +msgstr "Povolit částice" -#: builtin/mainmenu/tab_settings.lua:138 +#: builtin/mainmenu/tab_settings.lua:185 msgid "3D Clouds" -msgstr "3D Mraky" +msgstr "3D mraky" -#: builtin/mainmenu/tab_settings.lua:140 -#, fuzzy +#: builtin/mainmenu/tab_settings.lua:187 msgid "Fancy Trees" -msgstr "Pěkné stromy" +msgstr "Ozdobné stromy" -#: builtin/mainmenu/tab_settings.lua:142 +#: builtin/mainmenu/tab_settings.lua:189 msgid "Opaque Water" msgstr "Neprůhledná voda" -#: builtin/mainmenu/tab_settings.lua:144 -#, fuzzy +#: builtin/mainmenu/tab_settings.lua:191 msgid "Connected Glass" -msgstr "Připojit" +msgstr "Propojené sklo" -#: builtin/mainmenu/tab_settings.lua:149 +#: builtin/mainmenu/tab_settings.lua:193 +msgid "Node Highlighting" +msgstr "Zvýraznění bloků" + +#: builtin/mainmenu/tab_settings.lua:196 +msgid "Texturing:" +msgstr "Texturování:" + +#: builtin/mainmenu/tab_settings.lua:201 +msgid "Rendering:" +msgstr "Renderování:" + +#: builtin/mainmenu/tab_settings.lua:205 msgid "Restart minetest for driver change to take effect" -msgstr "" +msgstr "Aby se změna ovladače projevila, restartujte Minetest." -#: builtin/mainmenu/tab_settings.lua:151 -msgid "Mip-Mapping" -msgstr "Mip-Mapování" - -#: builtin/mainmenu/tab_settings.lua:153 -msgid "Anisotropic Filtering" -msgstr "Anizotropní filtrování" - -#: builtin/mainmenu/tab_settings.lua:155 -msgid "Bi-Linear Filtering" -msgstr "Bilineární filtrování" - -#: builtin/mainmenu/tab_settings.lua:157 -msgid "Tri-Linear Filtering" -msgstr "Trilineární filtrování" - -#: builtin/mainmenu/tab_settings.lua:160 +#: builtin/mainmenu/tab_settings.lua:207 msgid "Shaders" msgstr "Shadery" -#: builtin/mainmenu/tab_settings.lua:164 +#: builtin/mainmenu/tab_settings.lua:212 msgid "Change keys" msgstr "Změnit nastavení kláves" -#: builtin/mainmenu/tab_settings.lua:167 -#, fuzzy +#: builtin/mainmenu/tab_settings.lua:215 msgid "Reset singleplayer world" -msgstr "Hra jednoho hráče" +msgstr "Reset místního světa" -#: builtin/mainmenu/tab_settings.lua:171 +#: builtin/mainmenu/tab_settings.lua:219 msgid "GUI scale factor" -msgstr "" +msgstr "Měřítko GUI" -#: builtin/mainmenu/tab_settings.lua:175 +#: builtin/mainmenu/tab_settings.lua:223 msgid "Scaling factor applied to menu elements: " -msgstr "" +msgstr "Měřítko aplikované na prvky menu: " -#: builtin/mainmenu/tab_settings.lua:181 +#: builtin/mainmenu/tab_settings.lua:229 msgid "Touch free target" -msgstr "" +msgstr "Středový kurzor" -#: builtin/mainmenu/tab_settings.lua:187 +#: builtin/mainmenu/tab_settings.lua:235 msgid "Touchthreshold (px)" -msgstr "" +msgstr "Dosah dotyku (px)" -#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208 -#, fuzzy +#: builtin/mainmenu/tab_settings.lua:242 builtin/mainmenu/tab_settings.lua:256 msgid "Bumpmapping" -msgstr "Mip-Mapování" +msgstr "Bump mapování" -#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209 +#: builtin/mainmenu/tab_settings.lua:244 builtin/mainmenu/tab_settings.lua:257 msgid "Generate Normalmaps" -msgstr "" +msgstr "Generovat normálové mapy" -#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210 +#: builtin/mainmenu/tab_settings.lua:246 builtin/mainmenu/tab_settings.lua:258 msgid "Parallax Occlusion" -msgstr "" +msgstr "Parallax Occlusion" -#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211 +#: builtin/mainmenu/tab_settings.lua:248 builtin/mainmenu/tab_settings.lua:259 msgid "Waving Water" -msgstr "" +msgstr "Vlnění vody" -#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212 +#: builtin/mainmenu/tab_settings.lua:250 builtin/mainmenu/tab_settings.lua:260 msgid "Waving Leaves" -msgstr "" +msgstr "Vlnění listů" -#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213 +#: builtin/mainmenu/tab_settings.lua:252 builtin/mainmenu/tab_settings.lua:261 msgid "Waving Plants" -msgstr "" +msgstr "Vlnění rostlin" -#: builtin/mainmenu/tab_settings.lua:255 +#: builtin/mainmenu/tab_settings.lua:287 msgid "To enable shaders the OpenGL driver needs to be used." -msgstr "Pro povolení shaderů je nutné používat OpenGL ovladač." +msgstr "Pro povolení shaderů musíte používat OpenGL ovladač." -#: builtin/mainmenu/tab_settings.lua:330 +#: builtin/mainmenu/tab_settings.lua:398 msgid "Settings" msgstr "Nastavení" -#: builtin/mainmenu/tab_simple_main.lua:67 +#: builtin/mainmenu/tab_simple_main.lua:79 msgid "Fly mode" -msgstr "" +msgstr "Létací režim" -#: builtin/mainmenu/tab_simple_main.lua:71 -#, fuzzy +#: builtin/mainmenu/tab_simple_main.lua:83 msgid "Start Singleplayer" -msgstr "Hra jednoho hráče" +msgstr "Start místní hry" -#: builtin/mainmenu/tab_simple_main.lua:72 -#, fuzzy +#: builtin/mainmenu/tab_simple_main.lua:84 msgid "Config mods" -msgstr "Nastavit" +msgstr "Nastavení modů" -#: builtin/mainmenu/tab_simple_main.lua:191 -#, fuzzy +#: builtin/mainmenu/tab_simple_main.lua:203 msgid "Main" msgstr "Hlavní nabídka" @@ -497,7 +515,7 @@ msgstr "Hrát" #: builtin/mainmenu/tab_singleplayer.lua:224 msgid "Singleplayer" -msgstr "Hra jednoho hráče" +msgstr "Místní hra" #: builtin/mainmenu/tab_texturepacks.lua:49 msgid "Select texture pack:" @@ -508,43 +526,168 @@ msgid "No information available" msgstr "Informace nejsou dostupné" #: builtin/mainmenu/tab_texturepacks.lua:114 -#, fuzzy msgid "Texturepacks" msgstr "Balíčky textur" -#: src/client.cpp:2726 +#: src/client.cpp:2788 +msgid "Loading textures..." +msgstr "Načítám textury..." + +#: src/client.cpp:2798 +msgid "Rebuilding shaders..." +msgstr "Sestavuji shadery..." + +#: src/client.cpp:2805 +msgid "Initializing nodes..." +msgstr "Inicializuji bloky..." + +#: src/client.cpp:2820 msgid "Item textures..." -msgstr "Textury předmětů..." +msgstr "Textury věcí..." + +#: src/client.cpp:2845 +msgid "Done!" +msgstr "Hotovo!" #: src/fontengine.cpp:70 src/fontengine.cpp:226 msgid "needs_fallback_font" msgstr "no" -#: src/game.cpp:1063 +#: src/game.cpp:1057 src/guiFormSpecMenu.cpp:2006 +msgid "Proceed" +msgstr "Pokračovat" + +#: src/game.cpp:1077 +msgid "You died." +msgstr "Zemřel jsi." + +#: src/game.cpp:1078 msgid "Respawn" -msgstr "Oživení" +msgstr "Znovu stvořit" -#: src/game.cpp:2250 +#: src/game.cpp:1097 +msgid "" +"Default Controls:\n" +"No menu visible:\n" +"- single tap: button activate\n" +"- double tap: place/use\n" +"- slide finger: look around\n" +"Menu/Inventory visible:\n" +"- double tap (outside):\n" +" -->close\n" +"- touch stack, touch slot:\n" +" --> move stack\n" +"- touch&drag, tap 2nd finger\n" +" --> place single item to slot\n" +msgstr "" +"Výchozí ovládání:\n" +"Bez menu:\n" +"- klik: aktivace tlačítka\n" +"- dvojklik: položit/použít\n" +"- pohyb prstem: rozhlížení\n" +"Menu/Inventář zobrazen:\n" +"- dvojklik (mimo):\n" +" -->zavřít\n" +"- stisk hromádky, přihrádky :\n" +" --> přesunutí hromádky\n" +"- stisk a přesun, klik druhým prstem\n" +" --> umístit samostatnou věc do přihrádky\n" + +#: src/game.cpp:1111 +msgid "" +"Default Controls:\n" +"- WASD: move\n" +"- Space: jump/climb\n" +"- Shift: sneak/go down\n" +"- Q: drop item\n" +"- I: inventory\n" +"- Mouse: turn/look\n" +"- Mouse left: dig/punch\n" +"- Mouse right: place/use\n" +"- Mouse wheel: select item\n" +"- T: chat\n" +msgstr "" +"Výchozí ovládání:\n" +"- WASD: pohyb\n" +"- Mezera: skákání/šplhání\n" +"- Shift: plížení\n" +"- Q: zahodit věc\n" +"- I: inventář\n" +"- Myš: otáčení,rozhlížení\n" +"- Myš(levé tl.): kopat, štípat\n" +"- Myš(pravé tl.): položit, použít\n" +"- Myš(kolečko): vybrat přihrádku\n" +"- T: chat\n" + +#: src/game.cpp:1130 +msgid "Continue" +msgstr "Pokračovat" + +#: src/game.cpp:1134 +msgid "Change Password" +msgstr "Změnit heslo" + +#: src/game.cpp:1139 +msgid "Sound Volume" +msgstr "Hlasitost" + +#: src/game.cpp:1141 +msgid "Change Keys" +msgstr "Změnit klávesy" + +#: src/game.cpp:1144 +msgid "Exit to Menu" +msgstr "Odejít do nabídky" + +#: src/game.cpp:1146 +msgid "Exit to OS" +msgstr "Ukončit hru" + +#: src/game.cpp:1809 +msgid "Shutting down..." +msgstr "Vypínání..." + +#: src/game.cpp:1858 +msgid "Loading..." +msgstr "Nahrávám..." + +#: src/game.cpp:1915 +msgid "Creating server..." +msgstr "Vytvářím server..." + +#: src/game.cpp:1952 +msgid "Creating client..." +msgstr "Vytvářím klienta..." + +#: src/game.cpp:2125 +msgid "Resolving address..." +msgstr "Překládám adresu..." + +#: src/game.cpp:2216 +msgid "Connecting to server..." +msgstr "Připojuji se k serveru..." + +#: src/game.cpp:2274 msgid "Item definitions..." -msgstr "Definice předmětů..." +msgstr "Definice věcí..." -#: src/game.cpp:2255 +#: src/game.cpp:2279 msgid "Node definitions..." msgstr "Definice bloků..." -#: src/game.cpp:2262 +#: src/game.cpp:2286 msgid "Media..." msgstr "Média..." -#: src/game.cpp:2267 +#: src/game.cpp:2291 msgid " KB/s" -msgstr "" +msgstr " KB/s" -#: src/game.cpp:2271 +#: src/game.cpp:2295 msgid " MB/s" -msgstr "" +msgstr " MB/s" -#: src/game.cpp:4220 +#: src/game.cpp:4210 msgid "" "\n" "Check debug.txt for details." @@ -552,18 +695,17 @@ msgstr "" "\n" "Pro detaily se podívejte do debug.txt." -#: src/guiFormSpecMenu.cpp:2055 -msgid "Proceed" -msgstr "Pokračovat" - -#: src/guiFormSpecMenu.cpp:2846 +#: src/guiFormSpecMenu.cpp:2797 msgid "Enter " -msgstr "" +msgstr "Zadejte " + +#: src/guiFormSpecMenu.cpp:2817 +msgid "ok" +msgstr "OK" #: src/guiKeyChangeMenu.cpp:125 msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)" -msgstr "" -"Nastavení kláves. (Pokud tohle menu nebude v pořádku, odstraňte nastavení z " +msgstr "Nastavení kláves (Pokud tohle menu nebude v pořádku, odstraňte nastavení z " "minetest.conf)" #: src/guiKeyChangeMenu.cpp:165 @@ -572,7 +714,7 @@ msgstr "\"Použít\" = slézt dolů" #: src/guiKeyChangeMenu.cpp:180 msgid "Double tap \"jump\" to toggle fly" -msgstr "Dvě zmáčknutí klávesy \"skok\" zapnou létání" +msgstr "Dvojstisk klávesy \"skok\" zapne létání" #: src/guiKeyChangeMenu.cpp:296 msgid "Key already in use" @@ -592,11 +734,11 @@ msgstr "Vzad" #: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229 msgid "Left" -msgstr "Vlevo" +msgstr "Doleva" #: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229 msgid "Right" -msgstr "Vpravo" +msgstr "Doprava" #: src/guiKeyChangeMenu.cpp:401 msgid "Use" @@ -608,7 +750,7 @@ msgstr "Skok" #: src/guiKeyChangeMenu.cpp:403 msgid "Sneak" -msgstr "Plížení" +msgstr "Plížit se" #: src/guiKeyChangeMenu.cpp:404 msgid "Drop" @@ -636,11 +778,11 @@ msgstr "Létání" #: src/guiKeyChangeMenu.cpp:410 msgid "Toggle fast" -msgstr "Rychlý pohyb" +msgstr "Turbo" #: src/guiKeyChangeMenu.cpp:411 msgid "Toggle noclip" -msgstr "Noclip" +msgstr "Duch" #: src/guiKeyChangeMenu.cpp:412 msgid "Range select" @@ -650,25 +792,25 @@ msgstr "Změna dohledu" msgid "Print stacks" msgstr "Vypsat hromádky" -#: src/guiPasswordChange.cpp:106 +#: src/guiPasswordChange.cpp:108 msgid "Old Password" msgstr "Staré heslo" -#: src/guiPasswordChange.cpp:122 +#: src/guiPasswordChange.cpp:124 msgid "New Password" msgstr "Nové heslo" -#: src/guiPasswordChange.cpp:137 +#: src/guiPasswordChange.cpp:139 msgid "Confirm Password" msgstr "Potvrdit heslo" -#: src/guiPasswordChange.cpp:153 +#: src/guiPasswordChange.cpp:155 msgid "Change" msgstr "Změnit" -#: src/guiPasswordChange.cpp:162 +#: src/guiPasswordChange.cpp:164 msgid "Passwords do not match!" -msgstr "Hesla si neodpovídají!" +msgstr "Hesla se neshodují!" #: src/guiVolumeChange.cpp:106 msgid "Sound Volume: " @@ -740,7 +882,7 @@ msgstr "Shift" #: src/keycode.cpp:227 msgid "Convert" -msgstr "Převádět" +msgstr "Convert" #: src/keycode.cpp:227 msgid "Escape" @@ -748,7 +890,7 @@ msgstr "Esc" #: src/keycode.cpp:227 msgid "Final" -msgstr "Konečný" +msgstr "Final" #: src/keycode.cpp:227 msgid "Junja" @@ -760,7 +902,7 @@ msgstr "Kanji" #: src/keycode.cpp:227 msgid "Nonconvert" -msgstr "Nepřevádět" +msgstr "Nonconvert" #: src/keycode.cpp:228 msgid "End" @@ -812,7 +954,7 @@ msgstr "Pomoc" #: src/keycode.cpp:230 msgid "Insert" -msgstr "Vložit" +msgstr "Insert" #: src/keycode.cpp:230 msgid "Snapshot" @@ -966,165 +1108,111 @@ msgstr "PA1" msgid "Zoom" msgstr "Přiblížení" -#: src/main.cpp:1681 +#: src/main.cpp:1688 msgid "Main Menu" msgstr "Hlavní nabídka" -#: src/main.cpp:1719 +#: src/main.cpp:1726 msgid "Player name too long." -msgstr "" +msgstr "Jméno hráče je příliš dlouhé." -#: src/main.cpp:1757 +#: src/main.cpp:1764 msgid "Connection error (timed out?)" msgstr "Chyba spojení (vypršel čas?)" -#: src/main.cpp:1919 +#: src/main.cpp:1929 msgid "No world selected and no address provided. Nothing to do." -msgstr "" -"Nebyl vybrán žádný svět a nebyla poskytnuta žádná adresa. Nemám co dělat." +msgstr "Nebyl vybrán žádný svět a nebyla poskytnuta žádná adresa. Nemám co dělat." -#: src/main.cpp:1926 +#: src/main.cpp:1936 msgid "Provided world path doesn't exist: " -msgstr "" +msgstr "Uvedená cesta ke světu neexistuje: " -#: src/main.cpp:1935 +#: src/main.cpp:1945 msgid "Could not find or load game \"" msgstr "Hru nebylo možné nahrát nebo najít \"" -#: src/main.cpp:1953 +#: src/main.cpp:1963 msgid "Invalid gamespec." msgstr "Neplatná specifikace hry." -#~ msgid "Left click: Move all items, Right click: Move single item" -#~ msgstr "" -#~ "Levý klik: Přesunout všechny předměty, Pravý klik: Přesunout jeden předmět" +msgid "Downloading" +msgstr "Stahuji" -#~ msgid "" -#~ "Default Controls:\n" -#~ "- WASD: move\n" -#~ "- Space: jump/climb\n" -#~ "- Shift: sneak/go down\n" -#~ "- Q: drop item\n" -#~ "- I: inventory\n" -#~ "- Mouse: turn/look\n" -#~ "- Mouse left: dig/punch\n" -#~ "- Mouse right: place/use\n" -#~ "- Mouse wheel: select item\n" -#~ "- T: chat\n" -#~ msgstr "" -#~ "Výchozí ovládání:\n" -#~ "- WASD: pohyb\n" -#~ "- Mezera: skákání/šplhání\n" -#~ "- Shift: plížení\n" -#~ "- Q: zahodit\n" -#~ "- I: inventář\n" -#~ "- Myš: otáčení,rozhlížení\n" -#~ "- Myš(levé tl.): kopat, štípat\n" -#~ "- Myš(pravé tl.): položit, použít\n" -#~ "- Myš(kolečko): vybrat předmět\n" -#~ "- T: chat\n" - -#~ msgid "Exit to OS" -#~ msgstr "Ukončit hru" - -#~ msgid "Exit to Menu" -#~ msgstr "Odejít do Nabídky" - -#~ msgid "Sound Volume" -#~ msgstr "Hlasitost" - -#~ msgid "Change Password" -#~ msgstr "Změnit heslo" - -#~ msgid "Continue" -#~ msgstr "Pokračovat" - -#~ msgid "You died." -#~ msgstr "Zemřel jsi." - -#~ msgid "Shutting down stuff..." -#~ msgstr "Vypínám to..." - -#~ msgid "Connecting to server..." -#~ msgstr "Připojuji se k serveru..." - -#~ msgid "Resolving address..." -#~ msgstr "Překládám adresu..." - -#~ msgid "Creating client..." -#~ msgstr "Vytvářím klienta..." - -#~ msgid "Creating server...." -#~ msgstr "Vytvářím server..." - -#~ msgid "Loading..." -#~ msgstr "Nahrávám..." - -#~ msgid "Local install" -#~ msgstr "Místní instalace" - -#~ msgid "Add mod:" -#~ msgstr "Přidat mod:" - -#~ msgid "MODS" -#~ msgstr "MODY" - -#~ msgid "TEXTURE PACKS" -#~ msgstr "BALÍČKY TEXTUR" - -#~ msgid "SINGLE PLAYER" -#~ msgstr "HRA JEDNOHO HRÁČE" - -#~ msgid "Finite Liquid" -#~ msgstr "Konečná voda" - -#~ msgid "Preload item visuals" -#~ msgstr "Přednačíst textury předmětů" - -#~ msgid "SETTINGS" -#~ msgstr "NASTAVENÍ" - -#~ msgid "Password" -#~ msgstr "Heslo" - -#~ msgid "Name" -#~ msgstr "Jméno" - -#~ msgid "START SERVER" -#~ msgstr "MÍSTNÍ SERVER" - -#~ msgid "Favorites:" -#~ msgstr "Oblíbené:" - -#~ msgid "CLIENT" -#~ msgstr "KLIENT" - -#~ msgid "<<-- Add mod" -#~ msgstr "<<-- Přidat mod" - -#~ msgid "Remove selected mod" -#~ msgstr "Odstranit vybraný mod" - -#~ msgid "EDIT GAME" -#~ msgstr "UPRAVIT HRU" - -#~ msgid "new game" -#~ msgstr "nová hra" - -#~ msgid "edit game" -#~ msgstr "upravit hru" - -#~ msgid "Mods:" -#~ msgstr "Mody:" - -#~ msgid "Games" -#~ msgstr "Hry" - -#~ msgid "GAMES" -#~ msgstr "HRY" +#~ msgid "Game Name" +#~ msgstr "Název hry" #~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\"" #~ msgstr "Gamemgr: Nepovedlo se zkopírovat mod \"$1\" do hry \"$2\"" -#~ msgid "Game Name" -#~ msgstr "Název hry" +#~ msgid "GAMES" +#~ msgstr "HRY" + +#~ msgid "Games" +#~ msgstr "Hry" + +#~ msgid "Mods:" +#~ msgstr "Mody:" + +#~ msgid "edit game" +#~ msgstr "upravit hru" + +#~ msgid "new game" +#~ msgstr "nová hra" + +#~ msgid "EDIT GAME" +#~ msgstr "UPRAVIT HRU" + +#~ msgid "Remove selected mod" +#~ msgstr "Odstranit vybraný mod" + +#~ msgid "<<-- Add mod" +#~ msgstr "<<-- Přidat mod" + +#~ msgid "CLIENT" +#~ msgstr "KLIENT" + +#~ msgid "Favorites:" +#~ msgstr "Oblíbené:" + +#~ msgid "START SERVER" +#~ msgstr "MÍSTNÍ SERVER" + +#~ msgid "Name" +#~ msgstr "Jméno" + +#~ msgid "Password" +#~ msgstr "Heslo" + +#~ msgid "SETTINGS" +#~ msgstr "NASTAVENÍ" + +#~ msgid "Preload item visuals" +#~ msgstr "Přednačíst textury předmětů" + +#~ msgid "Finite Liquid" +#~ msgstr "Konečná voda" + +#~ msgid "SINGLE PLAYER" +#~ msgstr "HRA JEDNOHO HRÁČE" + +#~ msgid "TEXTURE PACKS" +#~ msgstr "BALÍČKY TEXTUR" + +#~ msgid "MODS" +#~ msgstr "MODY" + +#~ msgid "Add mod:" +#~ msgstr "Přidat mod:" + +#~ msgid "Local install" +#~ msgstr "Místní instalace" + +#~ msgid "Left click: Move all items, Right click: Move single item" +#~ msgstr "Levý klik: Přesunout všechny předměty, Pravý klik: Přesunout jeden předmět" + +#~ msgid "Anisotropic Filtering" +#~ msgstr "Anizotropní filtrování" + +#~ msgid "Mip-Mapping" +#~ msgstr "Mip-Mapování" From f92540e8adf70f09fa400d0b2abf97ef762b0759 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Thu, 12 Feb 2015 19:36:02 +0100 Subject: [PATCH 013/183] Add german and french translation for minetest.desktop This fixes #1573 --- misc/minetest.desktop | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/minetest.desktop b/misc/minetest.desktop index 36decb43f..896404789 100644 --- a/misc/minetest.desktop +++ b/misc/minetest.desktop @@ -2,6 +2,8 @@ Name=Minetest GenericName=Minetest Comment=Multiplayer infinite-world block sandbox +Comment[fr]=Jeu multijoueurs de type bac à sable avec des mondes infinis +Comment[de]=Mehrspieler-Sandkastenspiel mit unendlichen Blockwelten Exec=minetest Icon=minetest-icon Terminal=false From 4875213168ed877ab708e81d539923109977a5c8 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Tue, 17 Feb 2015 10:29:44 +0100 Subject: [PATCH 014/183] Grab GUIChatConsole::m_font, fixes segfault when changing font_size --- src/guiChatConsole.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index bdce7c872..8210e0bf4 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -99,7 +99,7 @@ GUIChatConsole::GUIChatConsole( { core::dimension2d dim = m_font->getDimension(L"M"); m_fontsize = v2u32(dim.Width, dim.Height); - dstream << "Font size: " << m_fontsize.X << " " << m_fontsize.Y << std::endl; + m_font->grab(); } m_fontsize.X = MYMAX(m_fontsize.X, 1); m_fontsize.Y = MYMAX(m_fontsize.Y, 1); @@ -109,7 +109,10 @@ GUIChatConsole::GUIChatConsole( } GUIChatConsole::~GUIChatConsole() -{} +{ + if (m_font) + m_font->drop(); +} void GUIChatConsole::openConsole(f32 height) { From 4208fdfd2273b715eeddb8b9877c2def8346d447 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Tue, 17 Feb 2015 20:09:36 +0100 Subject: [PATCH 015/183] Fix unused (and so, broken) enable_rollback_recording. This option must be reloaded at server loop but loaded when server starts, for data consistency (not a hot load variable) --- builtin/game/chatcommands.lua | 10 ++++++++++ minetest.conf.example | 1 + src/map.cpp | 4 ++-- src/script/lua_api/l_rollback.cpp | 10 ++++++++++ src/server.cpp | 13 ++++++------- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 2d94817e8..91b685fdf 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -570,6 +570,9 @@ core.register_chatcommand("rollback_check", { .. " seconds=86400=24h, limit=5)", privs = {rollback=true}, func = function(name, param) + if not core.setting_getbool("enable_rollback_recording") then + return false, "Rollback functions are disabled." + end local range, seconds, limit = param:match("(%d+) *(%d*) *(%d*)") range = tonumber(range) or 0 @@ -583,6 +586,10 @@ core.register_chatcommand("rollback_check", { local name = puncher:get_player_name() core.chat_send_player(name, "Checking " .. core.pos_to_string(pos) .. "...") local actions = core.rollback_get_node_actions(pos, range, seconds, limit) + if not actions then + core.chat_send_player(name, "Rollback functions are disabled") + return + end local num_actions = #actions if num_actions == 0 then core.chat_send_player(name, "Nobody has touched" @@ -614,6 +621,9 @@ core.register_chatcommand("rollback", { description = "revert actions of a player; default for is 60", privs = {rollback=true}, func = function(name, param) + if not core.setting_getbool("enable_rollback_recording") then + return false, "Rollback functions are disabled." + end local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)") if not target_name then local player_name = nil diff --git a/minetest.conf.example b/minetest.conf.example index eb883fa8e..eefe3b7bc 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -337,6 +337,7 @@ # If true, disable cheat prevention in multiplayer #disable_anticheat = false # If true, actions are recorded for rollback +# This option is only read when server starts #enable_rollback_recording = false # Handling for deprecated lua api calls: # "legacy" = (try to) mimic old behaviour (default for release). diff --git a/src/map.cpp b/src/map.cpp index d8f018742..efa53ca08 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1863,11 +1863,11 @@ void Map::transformLiquids(std::map & modified_blocks) // Find out whether there is a suspect for this action std::string suspect; - if(m_gamedef->rollback()){ + if(m_gamedef->rollback()) { suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1); } - if(!suspect.empty()){ + if(m_gamedef->rollback() && !suspect.empty()){ // Blame suspect RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true); // Get old node for rollback diff --git a/src/script/lua_api/l_rollback.cpp b/src/script/lua_api/l_rollback.cpp index f55a72d1b..5744e6813 100644 --- a/src/script/lua_api/l_rollback.cpp +++ b/src/script/lua_api/l_rollback.cpp @@ -44,6 +44,9 @@ int ModApiRollback::l_rollback_get_node_actions(lua_State *L) int limit = luaL_checknumber(L, 4); Server *server = getServer(L); IRollbackManager *rollback = server->getRollbackManager(); + if (rollback == NULL) { + return 0; + } std::list actions = rollback->getNodeActors(pos, range, seconds, limit); std::list::iterator iter = actions.begin(); @@ -80,6 +83,13 @@ int ModApiRollback::l_rollback_revert_actions_by(lua_State *L) int seconds = luaL_checknumber(L, 2); Server *server = getServer(L); IRollbackManager *rollback = server->getRollbackManager(); + + // If rollback is disabled, tell it's not a success. + if (rollback == NULL) { + lua_pushboolean(L, false); + lua_newtable(L); + return 2; + } std::list actions = rollback->getRevertActions(actor, seconds); std::list log; bool success = server->rollbackRevertActions(actions, &log); diff --git a/src/server.cpp b/src/server.cpp index 399c41b70..2d84ad032 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -244,9 +244,6 @@ Server::Server( std::string ban_path = m_path_world + DIR_DELIM "ipban.txt"; m_banmanager = new BanManager(ban_path); - // Create rollback manager - m_rollback = new RollbackManager(m_path_world, this); - ModConfiguration modconf(m_path_world); m_mods = modconf.getMods(); std::vector unsatisfied_mods = modconf.getUnsatisfiedMods(); @@ -354,6 +351,12 @@ Server::Server( // Initialize mapgens m_emerge->initMapgens(); + m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording"); + if (m_enable_rollback_recording) { + // Create rollback manager + m_rollback = new RollbackManager(m_path_world, this); + } + // Give environment reference to scripting api m_script->initializeEnvironment(m_env); @@ -1108,10 +1111,6 @@ void Server::AsyncRunStep(bool initial_step) counter = 0.0; m_emerge->startThreads(); - - // Update m_enable_rollback_recording here too - m_enable_rollback_recording = - g_settings->getBool("enable_rollback_recording"); } } From bb603ff18ebc62fdd5799118eee318747f60744a Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Wed, 18 Feb 2015 11:45:23 +0200 Subject: [PATCH 016/183] Use fixed size for builtin menus on non-android platforms --- builtin/mainmenu/init.lua | 6 +++++- src/game.cpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index dfaa04d3c..d008ec8b0 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -139,7 +139,11 @@ local function init_globals() tv_main:add(tab_credits) tv_main:set_global_event_handler(main_event_handler) - tv_main:set_fixed_size(false) + if PLATFORM ~= "Android" then + tv_main:set_fixed_size(true) + else + tv_main:set_fixed_size(false) + end if not (PLATFORM == "Android") then tv_main:set_tab(core.setting_get("maintab_LAST")) diff --git a/src/game.cpp b/src/game.cpp index 896973515..8e88fbc8f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1043,7 +1043,11 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, } } +#ifdef __ANDROID__ #define SIZE_TAG "size[11,5.5]" +#else +#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop +#endif static void show_chat_menu(GUIFormSpecMenu **cur_formspec, InventoryManager *invmgr, IGameDef *gamedef, From 6f688c50ee12fa731cbd5e5846d4f1e74e8ad7b7 Mon Sep 17 00:00:00 2001 From: BlockMen Date: Sat, 14 Feb 2015 20:16:09 +0100 Subject: [PATCH 017/183] Fix font_size under windows --- src/constants.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/constants.h b/src/constants.h index 9a7bb9d86..53a2608bd 100644 --- a/src/constants.h +++ b/src/constants.h @@ -100,7 +100,13 @@ with this program; if not, write to the Free Software Foundation, Inc., /* GUI related things */ -#define TTF_DEFAULT_FONT_SIZE (14) + +// TODO: implement dpi-based scaling for windows and remove this hack +#if defined(_WIN32) + #define TTF_DEFAULT_FONT_SIZE (18) +#else + #define TTF_DEFAULT_FONT_SIZE (14) +#endif #define DEFAULT_FONT_SIZE (10) #endif From 678546308ef2cf4b45c557dc5c227354a216486f Mon Sep 17 00:00:00 2001 From: BlockMen Date: Wed, 18 Feb 2015 12:37:53 +0100 Subject: [PATCH 018/183] Increase default font_size --- minetest.conf.example | 6 +++--- src/constants.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index eefe3b7bc..88e2db8ab 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -252,16 +252,16 @@ #freetype = true # Path to TrueTypeFont or bitmap #font_path = fonts/liberationsans.ttf -#font_size = 13 +#font_size = 15 # Font shadow offset, if 0 then shadow will not be drawn #font_shadow = 1 # Font shadow alpha (opaqueness, between 0 and 255) #font_shadow_alpha = 128 #mono_font_path = fonts/liberationmono.ttf -#mono_font_size = 13 +#mono_font_size = 15 # This font will be used for certain languages #fallback_font_path = fonts/DroidSansFallbackFull.ttf -#fallback_font_size = 13 +#fallback_font_size = 15 #fallback_font_shadow = 1 #fallback_font_shadow_alpha = 128 # Override language. When no value is provided (default) system language is used. diff --git a/src/constants.h b/src/constants.h index 53a2608bd..d7163bf68 100644 --- a/src/constants.h +++ b/src/constants.h @@ -105,7 +105,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if defined(_WIN32) #define TTF_DEFAULT_FONT_SIZE (18) #else - #define TTF_DEFAULT_FONT_SIZE (14) + #define TTF_DEFAULT_FONT_SIZE (15) #endif #define DEFAULT_FONT_SIZE (10) From 9ef2e5000f6df5a0c16e0343c6af59967150db42 Mon Sep 17 00:00:00 2001 From: fz72 Date: Tue, 17 Feb 2015 16:53:49 +0100 Subject: [PATCH 019/183] Fix map_seed not changed when creating a new world after login to another --- builtin/mainmenu/dlg_create_world.lua | 4 ++-- src/emerge.cpp | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua index 32e1fbf83..b42d119e0 100644 --- a/builtin/mainmenu/dlg_create_world.lua +++ b/builtin/mainmenu/dlg_create_world.lua @@ -90,6 +90,8 @@ local function create_world_buttonhandler(this, fields) local message = nil + core.setting_set("fixed_map_seed", fields["te_seed"]) + if not menudata.worldlist:uid_exists_raw(worldname) then core.setting_set("mg_name",fields["dd_mapgen"]) message = core.create_world(worldname,gameindex) @@ -97,8 +99,6 @@ local function create_world_buttonhandler(this, fields) message = fgettext("A world named \"$1\" already exists", worldname) end - core.setting_set("fixed_map_seed", fields["te_seed"]) - if message ~= nil then gamedata.errormessage = message else diff --git a/src/emerge.cpp b/src/emerge.cpp index c485caffa..a697bcb07 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -361,7 +361,10 @@ void EmergeManager::loadParamsFromSettings(Settings *settings) std::string seed_str; const char *setname = (settings == g_settings) ? "fixed_map_seed" : "seed"; - if (settings->getNoEx(setname, seed_str) && !seed_str.empty()) { + if (!settings->getNoEx("seed", seed_str)) { + g_settings->getNoEx(setname, seed_str); + } + if (!seed_str.empty()) { params.seed = read_seed(seed_str.c_str()); } else { params.seed = From 82bfa2ee7b90f678b687fb42405ca775c517be13 Mon Sep 17 00:00:00 2001 From: est31 Date: Tue, 17 Feb 2015 01:37:14 +0100 Subject: [PATCH 020/183] Server: announce MIN/MAX protocol version supported to serverlist. Client: check serverlist Client now informs about incompatible servers from the list, this permits to prevent the protocol movements. Server announces its supported protocol versions to master server --- builtin/common/misc_helpers.lua | 8 +++- builtin/mainmenu/common.lua | 56 ++++++++++++++++++++++++++-- builtin/mainmenu/tab_multiplayer.lua | 13 ++++++- builtin/mainmenu/tab_simple_main.lua | 7 +++- doc/menu_lua_api.txt | 12 +++++- src/script/lua_api/l_mainmenu.cpp | 28 ++++++++++++++ src/script/lua_api/l_mainmenu.h | 6 +++ src/serverlist.cpp | 11 +++++- 8 files changed, 129 insertions(+), 12 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index d9ebc39c3..39fca7d1e 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -564,7 +564,7 @@ if INIT == "mainmenu" then return nil end - function fgettext(text, ...) + function fgettext_ne(text, ...) text = core.gettext(text) local arg = {n=select('#', ...), ...} if arg.n >= 1 then @@ -586,7 +586,11 @@ if INIT == "mainmenu" then end text = result end - return core.formspec_escape(text) + return text + end + + function fgettext(text, ...) + return core.formspec_escape(fgettext_ne(text, ...)) end end diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index 549c0967b..f32d77f2a 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -16,9 +16,15 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -------------------------------------------------------------------------------- -- Global menu data ---------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- menudata = {} +-------------------------------------------------------------------------------- +-- Local cached values +-------------------------------------------------------------------------------- +local min_supp_proto = core.get_min_supp_proto() +local max_supp_proto = core.get_max_supp_proto() + -------------------------------------------------------------------------------- -- Menu helper functions -------------------------------------------------------------------------------- @@ -42,6 +48,25 @@ function image_column(tooltip, flagname) "1=" .. core.formspec_escape(defaulttexturedir .. "server_flags_" .. flagname .. ".png") end +-------------------------------------------------------------------------------- +function order_favorite_list(list) + local res = {} + --orders the favorite list after support + for i=1,#list,1 do + local fav = list[i] + if is_server_protocol_compat(fav.proto_min, fav.proto_max) then + table.insert(res, fav) + end + end + for i=1,#list,1 do + local fav = list[i] + if not is_server_protocol_compat(fav.proto_min, fav.proto_max) then + table.insert(res, fav) + end + end + return res +end + -------------------------------------------------------------------------------- function render_favorite(spec,render_details) local text = "" @@ -68,6 +93,7 @@ function render_favorite(spec,render_details) end local details = "" + local grey_out = not is_server_protocol_compat(spec.proto_max, spec.proto_min) if spec.clients ~= nil and spec.clients_max ~= nil then local clients_color = '' @@ -87,11 +113,17 @@ function render_favorite(spec,render_details) clients_color = '#ffba97' -- 90-100%: orange end + if grey_out then + clients_color = '#aaaaaa' + end + details = details .. clients_color .. ',' .. render_client_count(spec.clients) .. ',' .. '/,' .. render_client_count(spec.clients_max) .. ',' + elseif grey_out then + details = details .. '#aaaaaa,?,/,?,' else details = details .. ',?,/,?,' end @@ -114,7 +146,7 @@ function render_favorite(spec,render_details) details = details .. "0," end - return details .. text + return details .. (grey_out and '#aaaaaa,' or ',') .. text end -------------------------------------------------------------------------------- @@ -195,7 +227,7 @@ function asyncOnlineFavourites() nil, function(result) if core.setting_getbool("public_serverlist") then - menudata.favorites = result + menudata.favorites = order_favorite_list(result) core.event_handler("Refresh") end end @@ -225,3 +257,21 @@ function text2textlist(xpos,ypos,width,height,tl_name,textlen,text,transparency) return retval end + +-------------------------------------------------------------------------------- +function is_server_protocol_compat(proto_min, proto_max) + return not ((min_supp_proto > (proto_max or 24)) or (max_supp_proto < (proto_min or 13))) +end +-------------------------------------------------------------------------------- +function is_server_protocol_compat_or_error(proto_min, proto_max) + if not is_server_protocol_compat(proto_min, proto_max) then + gamedata.errormessage = fgettext_ne("Protocol version mismatch, server " .. + ((proto_min ~= proto_max) and "supports protocols between $1 and $2" or "enforces protocol version $1") .. + ", we " .. + ((min_supp_proto ~= max_supp_proto) and "support protocols between version $3 and $4." or "only support protocol version $3"), + proto_min or 13, proto_max or 24, min_supp_proto, max_supp_proto) + return false + end + + return true +end diff --git a/builtin/mainmenu/tab_multiplayer.lua b/builtin/mainmenu/tab_multiplayer.lua index 734cb5d3e..f9ac78f17 100644 --- a/builtin/mainmenu/tab_multiplayer.lua +++ b/builtin/mainmenu/tab_multiplayer.lua @@ -62,6 +62,7 @@ local function get_formspec(tabview, name, tabdata) image_column(fgettext("Creative mode"), "creative") .. ",padding=1;" .. image_column(fgettext("Damage enabled"), "damage") .. ",padding=0.25;" .. image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. + "color,span=1;" .. "text,padding=1]" -- name else retval = retval .. "tablecolumns[text]" @@ -88,7 +89,6 @@ end -------------------------------------------------------------------------------- local function main_button_handler(tabview, fields, name, tabdata) - if fields["te_name"] ~= nil then gamedata.playername = fields["te_name"] core.setting_set("name", fields["te_name"]) @@ -98,6 +98,10 @@ local function main_button_handler(tabview, fields, name, tabdata) local event = core.explode_table_event(fields["favourites"]) if event.type == "DCL" then if event.row <= #menudata.favorites then + if not is_server_protocol_compat_or_error(menudata.favorites[event.row].proto_min, + menudata.favorites[event.row].proto_max) then + return true + end gamedata.address = menudata.favorites[event.row].address gamedata.port = menudata.favorites[event.row].port gamedata.playername = fields["te_name"] @@ -189,7 +193,7 @@ local function main_button_handler(tabview, fields, name, tabdata) local current_favourite = core.get_table_index("favourites") if current_favourite == nil then return end core.delete_favorite(current_favourite) - menudata.favorites = core.get_favorites() + menudata.favorites = order_favorite_list(core.get_favorites()) tabdata.fav_selected = nil core.setting_set("address","") @@ -214,6 +218,11 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.servername = menudata.favorites[fav_idx].name gamedata.serverdescription = menudata.favorites[fav_idx].description + + if not is_server_protocol_compat_or_error(menudata.favorites[fav_idx].proto_min, + menudata.favorites[fav_idx].proto_max)then + return true + end else gamedata.servername = "" gamedata.serverdescription = "" diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua index 87bd551c0..b9a6b650f 100644 --- a/builtin/mainmenu/tab_simple_main.lua +++ b/builtin/mainmenu/tab_simple_main.lua @@ -45,6 +45,7 @@ local function get_formspec(tabview, name, tabdata) image_column(fgettext("Creative mode"), "creative") .. ",padding=1;" .. image_column(fgettext("Damage enabled"), "damage") .. ",padding=0.25;" .. image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. + "color,span=1;" .. "text,padding=1]" -- name else retval = retval .. "tablecolumns[text]" @@ -87,7 +88,6 @@ local function get_formspec(tabview, name, tabdata) end -------------------------------------------------------------------------------- - local function main_button_handler(tabview, fields, name, tabdata) if fields["btn_start_singleplayer"] then @@ -159,6 +159,11 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.servername = menudata.favorites[fav_idx].name gamedata.serverdescription = menudata.favorites[fav_idx].description + + if not is_server_protocol_compat_or_error(menudata.favorites[fav_idx].proto_min, + menudata.favorites[fav_idx].proto_max) then + return true + end else gamedata.servername = "" gamedata.serverdescription = "" diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index 5c0f90df6..f76124a0d 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -197,9 +197,11 @@ core.delete_world(index) Helpers: core.gettext(string) -> string ^ look up the translation of a string in the gettext message catalog -fgettext(string, ...) -> string +fgettext_ne(string, ...) ^ call core.gettext(string), replace "$1"..."$9" with the given -^ extra arguments, call core.formspec_escape and return the result +^ extra arguments and return the result +fgettext(string, ...) -> string +^ same as fgettext_ne(), but calls core.formspec_escape before returning result core.parse_json(string[, nullvalue]) -> something (possible in async calls) ^ see core.parse_json (lua_api.txt) dump(obj, dumped={}) @@ -211,6 +213,12 @@ string:trim() core.is_yes(arg) (possible in async calls) ^ returns whether arg can be interpreted as yes +Version compat: +core.get_min_supp_proto() +^ returns the minimum supported network protocol version +core.get_max_supp_proto() +^ returns the maximum supported network protocol version + Async: core.handle_async(async_job,parameters,finished) ^ execute a function asynchronously diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 0d8365106..2bed2a255 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -472,6 +472,7 @@ int ModApiMainMenu::l_get_favorites(lua_State *L) for (unsigned int i = 0; i < servers.size(); i++) { + lua_pushnumber(L,index); lua_newtable(L); @@ -509,6 +510,18 @@ int ModApiMainMenu::l_get_favorites(lua_State *L) lua_settable(L, top_lvl2); } + if (servers[i]["proto_min"].asString().size()) { + lua_pushstring(L,"proto_min"); + lua_pushinteger(L,servers[i]["proto_min"].asInt()); + lua_settable(L, top_lvl2); + } + + if (servers[i]["proto_max"].asString().size()) { + lua_pushstring(L,"proto_max"); + lua_pushinteger(L,servers[i]["proto_max"].asInt()); + lua_settable(L, top_lvl2); + } + if (servers[i]["password"].asString().size()) { lua_pushstring(L,"password"); lua_pushboolean(L,servers[i]["password"].asBool()); @@ -1082,6 +1095,19 @@ int ModApiMainMenu::l_get_screen_info(lua_State *L) return 1; } +/******************************************************************************/ +int ModApiMainMenu::l_get_min_supp_proto(lua_State *L) +{ + lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MIN); + return 1; +} + +int ModApiMainMenu::l_get_max_supp_proto(lua_State *L) +{ + lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MAX); + return 1; +} + /******************************************************************************/ int ModApiMainMenu::l_do_async_callback(lua_State *L) { @@ -1142,6 +1168,8 @@ void ModApiMainMenu::Initialize(lua_State *L, int top) API_FCT(gettext); API_FCT(get_video_drivers); API_FCT(get_screen_info); + API_FCT(get_min_supp_proto); + API_FCT(get_max_supp_proto); API_FCT(do_async_callback); } diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h index ff61dd97a..8b21a93aa 100644 --- a/src/script/lua_api/l_mainmenu.h +++ b/src/script/lua_api/l_mainmenu.h @@ -137,6 +137,12 @@ private: static int l_get_video_drivers(lua_State *L); + //version compatibility + static int l_get_min_supp_proto(lua_State *L); + + static int l_get_max_supp_proto(lua_State *L); + + // async static int l_do_async_callback(lua_State *L); diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 472a6b85c..a3353340e 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "porting.h" #include "log.h" +#include "network/networkprotocol.h" #include "json/json.h" #include "convert_json.h" #include "httpfetch.h" @@ -67,8 +68,11 @@ std::vector getLocal() std::vector getOnline() { - Json::Value root = fetchJsonValue( - (g_settings->get("serverlist_url") + "/list").c_str(), NULL); + std::ostringstream geturl; + geturl << g_settings->get("serverlist_url") << + "/list?proto_version_min=" << CLIENT_PROTOCOL_VERSION_MIN << + "&proto_version_max=" << CLIENT_PROTOCOL_VERSION_MAX; + Json::Value root = fetchJsonValue(geturl.str(), NULL); std::vector server_list; @@ -205,9 +209,12 @@ void sendAnnounce(const std::string &action, server["address"] = g_settings->get("server_address"); } if (action != "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"] = minetest_version_simple; + 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["url"] = g_settings->get("server_url"); server["creative"] = g_settings->getBool("creative_mode"); server["damage"] = g_settings->getBool("enable_damage"); From 45ff8569d7655b480456884745db4a23a07aa722 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Wed, 18 Feb 2015 16:26:23 +0100 Subject: [PATCH 021/183] Fix serverlist include --- src/serverlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serverlist.cpp b/src/serverlist.cpp index a3353340e..6732e5ac9 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "porting.h" #include "log.h" -#include "network/networkprotocol.h" +#include "clientserver.h" #include "json/json.h" #include "convert_json.h" #include "httpfetch.h" From b0df67d9c062a08a93d24c304bdbc72b0ced0898 Mon Sep 17 00:00:00 2001 From: Novatux Date: Wed, 18 Feb 2015 16:48:58 +0100 Subject: [PATCH 022/183] Add modname convention checking Fixes #2037 --- builtin/mainmenu/dlg_config_world.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua index a15e4c11f..4d13faea8 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/dlg_config_world.lua @@ -16,6 +16,9 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -------------------------------------------------------------------------------- +local function modname_valid(name) + return not name:find("[^a-z0-9_]") +end local function get_formspec(data) @@ -195,10 +198,12 @@ local function handle_buttons(this, fields) for i,mod in ipairs(rawlist) do if not mod.is_modpack and mod.typ ~= "game_mod" then - if mod.enabled then - worldfile:set("load_mod_"..mod.name, "true") + if modname_valid(mod.name) then + worldfile:set("load_mod_"..mod.name, tostring(mod.enabled)) else - worldfile:set("load_mod_"..mod.name, "false") + if mod.enabled then + gamedata.errormessage = fgettext_ne("Failed to enable mod \"$1\" as it contains disallowed characters. Only chararacters [a-z0-9_] are allowed.", mod.name) + end end mods["load_mod_"..mod.name] = nil end From 7993a403f2c17a215e4895ba1848aaf69bb61980 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Wed, 18 Feb 2015 19:50:37 +0200 Subject: [PATCH 023/183] Bump version to 0.4.12 --- CMakeLists.txt | 4 ++-- build/android/Makefile | 2 +- doc/lua_api.txt | 2 +- doc/menu_lua_api.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c64b2e76..98d9aee0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,14 +12,14 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 11) +set(VERSION_PATCH 12) set(VERSION_PATCH_ORIG ${VERSION_PATCH}) if(VERSION_EXTRA) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) else() # Comment the following line during release - set(VERSION_PATCH ${VERSION_PATCH}-dev) + #set(VERSION_PATCH ${VERSION_PATCH}-dev) endif() set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") diff --git a/build/android/Makefile b/build/android/Makefile index 68625b6a7..6027982ed 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -26,7 +26,7 @@ GAMES_TO_COPY = minetest_game # Android Version code # Increase for each build! ################################################################################ -ANDROID_VERSION_CODE = 5 +ANDROID_VERSION_CODE = 6 ################################################################################ # toolchain config for arm old processors diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 586c520fc..d2d885880 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Modding API Reference 0.4.11 +Minetest Lua Modding API Reference 0.4.12 ========================================= * More information at * Developer Wiki: diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index f76124a0d..e5ba46d4c 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Mainmenu API Reference 0.4.11 +Minetest Lua Mainmenu API Reference 0.4.12 ======================================== Introduction From b12f569fc6b78c1e657bc0cfd8518b997d5f83c7 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 14 Mar 2015 19:57:21 +0100 Subject: [PATCH 024/183] Releasing android --- build/android/AndroidManifest.xml.template | 2 +- build/android/Makefile | 2 +- .../{org => net}/minetest/minetest/MinetestAssetCopy.java | 2 +- .../{org => net}/minetest/minetest/MinetestTextEntry.java | 2 +- .../{org => net}/minetest/minetest/MtNativeActivity.java | 2 +- src/porting_android.cpp | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) rename build/android/src/{org => net}/minetest/minetest/MinetestAssetCopy.java (99%) rename build/android/src/{org => net}/minetest/minetest/MinetestTextEntry.java (98%) rename build/android/src/{org => net}/minetest/minetest/MtNativeActivity.java (98%) diff --git a/build/android/AndroidManifest.xml.template b/build/android/AndroidManifest.xml.template index a0ca7993c..0f75ca648 100644 --- a/build/android/AndroidManifest.xml.template +++ b/build/android/AndroidManifest.xml.template @@ -1,6 +1,6 @@ diff --git a/build/android/Makefile b/build/android/Makefile index 6027982ed..ef9376df3 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -26,7 +26,7 @@ GAMES_TO_COPY = minetest_game # Android Version code # Increase for each build! ################################################################################ -ANDROID_VERSION_CODE = 6 +ANDROID_VERSION_CODE = 10 ################################################################################ # toolchain config for arm old processors diff --git a/build/android/src/org/minetest/minetest/MinetestAssetCopy.java b/build/android/src/net/minetest/minetest/MinetestAssetCopy.java similarity index 99% rename from build/android/src/org/minetest/minetest/MinetestAssetCopy.java rename to build/android/src/net/minetest/minetest/MinetestAssetCopy.java index 45dc6373a..5776e77b5 100644 --- a/build/android/src/org/minetest/minetest/MinetestAssetCopy.java +++ b/build/android/src/net/minetest/minetest/MinetestAssetCopy.java @@ -1,4 +1,4 @@ -package org.minetest.minetest; +package net.minetest.minetest; import java.io.BufferedReader; import java.io.File; diff --git a/build/android/src/org/minetest/minetest/MinetestTextEntry.java b/build/android/src/net/minetest/minetest/MinetestTextEntry.java similarity index 98% rename from build/android/src/org/minetest/minetest/MinetestTextEntry.java rename to build/android/src/net/minetest/minetest/MinetestTextEntry.java index db175a483..68dc73274 100644 --- a/build/android/src/org/minetest/minetest/MinetestTextEntry.java +++ b/build/android/src/net/minetest/minetest/MinetestTextEntry.java @@ -1,4 +1,4 @@ -package org.minetest.minetest; +package net.minetest.minetest; import android.app.Activity; import android.app.AlertDialog; diff --git a/build/android/src/org/minetest/minetest/MtNativeActivity.java b/build/android/src/net/minetest/minetest/MtNativeActivity.java similarity index 98% rename from build/android/src/org/minetest/minetest/MtNativeActivity.java rename to build/android/src/net/minetest/minetest/MtNativeActivity.java index ba7d62169..2bfcef93c 100644 --- a/build/android/src/org/minetest/minetest/MtNativeActivity.java +++ b/build/android/src/net/minetest/minetest/MtNativeActivity.java @@ -1,4 +1,4 @@ -package org.minetest.minetest; +package net.minetest.minetest; import android.app.NativeActivity; import android.content.Intent; diff --git a/src/porting_android.cpp b/src/porting_android.cpp index 96c9385a6..6871ce465 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -71,10 +71,10 @@ void android_main(android_app *app) /* TODO this doesn't work as expected, no idea why but there's a workaround */ /* for it right now */ extern "C" { - JNIEXPORT void JNICALL Java_org_minetest_MtNativeActivity_putMessageBoxResult( + JNIEXPORT void JNICALL Java_net_minetest_MtNativeActivity_putMessageBoxResult( JNIEnv * env, jclass thiz, jstring text) { - errorstream << "Java_org_minetest_MtNativeActivity_putMessageBoxResult got: " + errorstream << "Java_net_minetest_MtNativeActivity_putMessageBoxResult got: " << std::string((const char*)env->GetStringChars(text,0)) << std::endl; } @@ -138,7 +138,7 @@ void initAndroid() exit(-1); } - nativeActivity = findClass("org/minetest/minetest/MtNativeActivity"); + nativeActivity = findClass("net/minetest/minetest/MtNativeActivity"); if (nativeActivity == 0) { errorstream << "porting::initAndroid unable to find java native activity class" << From a740a48f62261073190a92e9b60256da13817923 Mon Sep 17 00:00:00 2001 From: Craig Robbins Date: Mon, 2 Mar 2015 13:16:01 +1000 Subject: [PATCH 025/183] Fix narrow_to_wide_c (ANDROID) * Ensure converted string is NUL terminated * Restore logic to that used prior to 9e2a9b5 --- src/util/string.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/util/string.cpp b/src/util/string.cpp index de669b473..a29bce94b 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -78,16 +78,18 @@ const wchar_t *narrow_to_wide_c(const char *mbs) size_t mbl = strlen(mbs); wchar_t *wcs = new wchar_t[mbl + 1]; - for (size_t i = 0; i < mbl; i++) { + size_t i, dest_i = 0; + for (i = 0; i < mbl; i++) { if (((unsigned char) mbs[i] > 31) && ((unsigned char) mbs[i] < 127)) { - wcs[i] = wide_chars[(unsigned char) mbs[i] - 32]; + wcs[dest_i++] = wide_chars[(unsigned char) mbs[i] - 32]; } //handle newline else if (mbs[i] == '\n') { - wcs[i] = L'\n'; + wcs[dest_i++] = L'\n'; } } + wcs[dest_i] = '\0'; return wcs; } From 0429ec4cfde636dd8dda3d1a0dcf6e175c821196 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 15 Mar 2015 11:01:15 +0100 Subject: [PATCH 026/183] Android: update makefile and backport language fix from master --- build/android/Makefile | 11 +++- src/game.cpp | 4 +- src/gettext.h | 16 ++--- src/util/string.cpp | 129 ++++++++++++++++++++--------------------- src/util/string.h | 10 ++-- 5 files changed, 89 insertions(+), 81 deletions(-) diff --git a/build/android/Makefile b/build/android/Makefile index ef9376df3..f53f7f9f5 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -26,7 +26,8 @@ GAMES_TO_COPY = minetest_game # Android Version code # Increase for each build! ################################################################################ -ANDROID_VERSION_CODE = 10 +# Play Store actual version (15/03/15): 10 +ANDROID_VERSION_CODE = 11 ################################################################################ # toolchain config for arm old processors @@ -743,9 +744,13 @@ $(ROOT)/jni/src/android_version.h : >> ${ROOT}/jni/src/android_version.h; \ export GITHASH=$$(git rev-parse --short=8 HEAD); \ if [ "x$$GITHASH" = "x" ] ; then \ - export GITHASH=gUnknown; \ + export GITHASH=""; \ fi; \ - echo "#define CMAKE_VERSION_GITHASH \"$$GITHASH\"" \ + export GITTAG=$$(git describe --abbrev=0 --tags); \ + if [ "x$$GITTAG" = "x" ] ; then \ + export GITTAG=""; \ + fi; \ + echo "#define CMAKE_VERSION_GITHASH \"$$GITTAG-$$GITHASH-Android\"" \ >> ${ROOT}/jni/src/android_version.h; \ echo "#define CMAKE_VERSION_STRING STR(VERSION_MAJOR)\".\"STR(VERSION_MINOR)\ \".\"STR(VERSION_PATCH)" \ diff --git a/src/game.cpp b/src/game.cpp index 8e88fbc8f..7fb99ef3e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4117,7 +4117,9 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime) fps_timings->last_time = time; } - +// Note: This will free (using delete[])! \p msg. If you want to use it later, +// pass a copy of it to this function +// Note: \p msg must be allocated using new (not malloc()) void Game::showOverlayMessage(const wchar_t *msg, float dtime, int percent, bool draw_clouds) { diff --git a/src/gettext.h b/src/gettext.h index dce45fa3a..8235efa8a 100644 --- a/src/gettext.h +++ b/src/gettext.h @@ -23,31 +23,31 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" // for USE_GETTEXT #if USE_GETTEXT -#include + #include #else -#define gettext(String) String + #define gettext(String) String #endif #define _(String) gettext(String) -#define gettext_noop(String) String -#define N_(String) gettext_noop (String) +#define gettext_noop(String) (String) +#define N_(String) gettext_noop((String)) #ifdef _MSC_VER -void init_gettext(const char *path, const std::string &configured_language, int argc, char** argv); +void init_gettext(const char *path, const std::string &configured_language, + int argc, char** argv); #else void init_gettext(const char *path, const std::string &configured_language); #endif -extern const wchar_t *narrow_to_wide_c(const char *mbs); -extern std::wstring narrow_to_wide(const std::string &mbs); +extern wchar_t *narrow_to_wide_c(const char *str); // You must free the returned string! +// The returned string is allocated using new inline const wchar_t *wgettext(const char *str) { return narrow_to_wide_c(gettext(str)); } -// Gettext under MSVC needs this strange way. Just don't ask... inline std::wstring wstrgettext(const std::string &text) { const wchar_t *tmp = wgettext(text.c_str()); diff --git a/src/util/string.cpp b/src/util/string.cpp index a29bce94b..956a1ac44 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -22,9 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "numeric.h" #include "log.h" -#include "../sha1.h" -#include "../base64.h" -#include "../hex.h" +#include "sha1.h" +#include "base64.h" +#include "hex.h" #include "../porting.h" #include @@ -32,13 +32,36 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -#if defined(_WIN32) -#include // MultiByteToWideChar -#endif - static bool parseHexColorString(const std::string &value, video::SColor &color); static bool parseNamedColorString(const std::string &value, video::SColor &color); + +// You must free the returned string! +// The returned string is allocated using new +wchar_t *narrow_to_wide_c(const char *str) +{ + wchar_t* nstr = 0; +#if defined(_WIN32) + int nResult = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) str, -1, 0, 0); + if (nResult == 0) { + errorstream<<"gettext: MultiByteToWideChar returned null"<?@" @@ -62,80 +85,55 @@ int wctomb(char *s, wchar_t wc) int mbtowc(wchar_t *pwc, const char *s, size_t n) { - const wchar_t *tmp = narrow_to_wide_c(s); + std::wstring intermediate = narrow_to_wide(s); - if (tmp[0] != '\0') { - *pwc = tmp[0]; + if (intermediate.length() > 0) { + *pwc = intermediate[0]; return 1; - } else { + } + else { return -1; } } -// You must free the returned string! -const wchar_t *narrow_to_wide_c(const char *mbs) -{ - size_t mbl = strlen(mbs); - wchar_t *wcs = new wchar_t[mbl + 1]; +std::wstring narrow_to_wide(const std::string &mbs) { + size_t wcl = mbs.size(); - size_t i, dest_i = 0; - for (i = 0; i < mbl; i++) { - if (((unsigned char) mbs[i] > 31) && - ((unsigned char) mbs[i] < 127)) { - wcs[dest_i++] = wide_chars[(unsigned char) mbs[i] - 32]; + std::wstring retval = L""; + + for (unsigned int i = 0; i < wcl; i++) { + if (((unsigned char) mbs[i] >31) && + ((unsigned char) mbs[i] < 127)) { + + retval += wide_chars[(unsigned char) mbs[i] -32]; } //handle newline else if (mbs[i] == '\n') { - wcs[dest_i++] = L'\n'; + retval += L'\n'; } } - wcs[dest_i] = '\0'; - return wcs; + return retval; } -#else +#else // not Android -// You must free the returned string! -const wchar_t *narrow_to_wide_c(const char *mbs) -{ - wchar_t *wcs = NULL; -#if defined(_WIN32) - int nResult = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) mbs, -1, 0, 0); - if (nResult == 0) { - errorstream << "gettext: MultiByteToWideChar returned null" << std::endl; - } else { - wcs = new wchar_t[nResult]; - MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) mbs, -1, (WCHAR *) wcs, nResult); - } -#else - size_t wcl = mbstowcs(NULL, mbs, 0); - if (wcl == (size_t) -1) - return NULL; - wcs = new wchar_t[wcl + 1]; - size_t l = mbstowcs(wcs, mbs, wcl); - assert(l != (size_t) -1); // Should never happen if the last call worked - wcs[l] = '\0'; -#endif - - return wcs; -} - -#endif - -std::wstring narrow_to_wide(const std::string& mbs) +std::wstring narrow_to_wide(const std::string &mbs) { size_t wcl = mbs.size(); Buffer wcs(wcl + 1); - size_t l = mbstowcs(*wcs, mbs.c_str(), wcl); - if (l == (size_t)(-1)) + size_t len = mbstowcs(*wcs, mbs.c_str(), wcl); + if (len == (size_t)(-1)) return L""; - wcs[l] = 0; + wcs[len] = 0; return *wcs; } +#endif + #ifdef __ANDROID__ -std::string wide_to_narrow(const std::wstring& wcs) { + +std::string wide_to_narrow(const std::wstring &wcs) { size_t mbl = wcs.size()*4; std::string retval = ""; @@ -160,17 +158,18 @@ std::string wide_to_narrow(const std::wstring& wcs) { return retval; } -#else -std::string wide_to_narrow(const std::wstring& wcs) + +#else // not Android + +std::string wide_to_narrow(const std::wstring &wcs) { - size_t mbl = wcs.size()*4; + size_t mbl = wcs.size() * 4; SharedBuffer mbs(mbl+1); - size_t l = wcstombs(*mbs, wcs.c_str(), mbl); - if(l == (size_t)(-1)) { + size_t len = wcstombs(*mbs, wcs.c_str(), mbl); + if (len == (size_t)(-1)) return "Character conversion failed!"; - } else - mbs[l] = 0; + mbs[len] = 0; return *mbs; } @@ -183,7 +182,7 @@ std::string wide_to_narrow(const std::wstring& wcs) // compatibility with password-less players). std::string translatePassword(std::string playername, std::wstring password) { - if(password.length() == 0) + if (password.length() == 0) return ""; std::string slt = playername + wide_to_narrow(password); diff --git a/src/util/string.h b/src/util/string.h index 388184ca4..dc520e3a8 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -36,11 +36,13 @@ struct FlagDesc { u32 flag; }; -// You must free the returned string! -const wchar_t *narrow_to_wide_c(const char *mbs); -std::wstring narrow_to_wide(const std::string& mbs); -std::string wide_to_narrow(const std::wstring& wcs); +// You must free the returned string! +// The returned string is allocated using new +wchar_t *narrow_to_wide_c(const char *str); + +std::wstring narrow_to_wide(const std::string &mbs); +std::string wide_to_narrow(const std::wstring &wcs); std::string translatePassword(std::string playername, std::wstring password); std::string urlencode(std::string str); std::string urldecode(std::string str); From 0c0248a19cd356eb51240c5ba8ffd17b6f7d89f4 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 16 Mar 2015 17:32:30 +0100 Subject: [PATCH 027/183] Android: Fix auto-entry of server address and port in mainmenu Fixes #2497. --- builtin/mainmenu/tab_simple_main.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua index b9a6b650f..995c72132 100644 --- a/builtin/mainmenu/tab_simple_main.lua +++ b/builtin/mainmenu/tab_simple_main.lua @@ -98,12 +98,12 @@ local function main_button_handler(tabview, fields, name, tabdata) end if fields["favourites"] ~= nil then - local event = core.explode_textlist_event(fields["favourites"]) + local event = core.explode_table_event(fields["favourites"]) if event.type == "CHG" then - if event.index <= #menudata.favorites then - local address = menudata.favorites[event.index].address - local port = menudata.favorites[event.index].port + if event.row <= #menudata.favorites then + local address = menudata.favorites[event.row].address + local port = menudata.favorites[event.row].port if address ~= nil and port ~= nil then @@ -111,7 +111,7 @@ local function main_button_handler(tabview, fields, name, tabdata) core.setting_set("remote_port",port) end - tabdata.fav_selected = event.index + tabdata.fav_selected = event.row end end return true From 315b00d15081d1f56f0e2de22a4ff1a393ab7f22 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 16 Mar 2015 20:37:07 +0100 Subject: [PATCH 028/183] Bump android version code --- build/android/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/android/Makefile b/build/android/Makefile index f53f7f9f5..bfc24bc85 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -26,8 +26,8 @@ GAMES_TO_COPY = minetest_game # Android Version code # Increase for each build! ################################################################################ -# Play Store actual version (15/03/15): 10 -ANDROID_VERSION_CODE = 11 +# Play Store actual version (16/03/15): 11 +ANDROID_VERSION_CODE = 12 ################################################################################ # toolchain config for arm old processors From 7968f1ddaa67432719d5becdda5ca8bec58faa47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Sun, 20 Aug 2017 17:20:11 +0200 Subject: [PATCH 029/183] New version scheme (#6292) * Version changes: current dev version is now 0.4.17 * This change permit to have multi branches with various versions * Dev version is 0.4.17-dev and next release will be 0.4.17 --- CMakeLists.txt | 2 +- doc/client_lua_api.md | 2 +- doc/lua_api.txt | 2 +- doc/menu_lua_api.txt | 2 +- util/bump_version.sh | 144 +++++++++++++++++++++++------------------- 5 files changed, 82 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a0dcb688..c64ac897b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(PROJECT_NAME_CAPITALIZED "Minetest") # Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 16) +set(VERSION_PATCH 17) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index b3e494cc1..436f22b41 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -1,4 +1,4 @@ -Minetest Lua Client Modding API Reference 0.4.15 +Minetest Lua Client Modding API Reference 0.4.17 ================================================ * More information at * Developer Wiki: diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ae8263c6c..8c02ebb85 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Modding API Reference 0.4.16 +Minetest Lua Modding API Reference 0.4.17 ========================================= * More information at * Developer Wiki: diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index 074bc962d..eb0d2ed6c 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Mainmenu API Reference 0.4.16 +Minetest Lua Mainmenu API Reference 0.4.17 ======================================== Introduction diff --git a/util/bump_version.sh b/util/bump_version.sh index 948561ac3..35cad78a7 100755 --- a/util/bump_version.sh +++ b/util/bump_version.sh @@ -1,6 +1,4 @@ -#!/bin/bash - -die() { echo "$@" 1>&2 ; exit 1; } +#!/bin/bash -e prompt_for_number() { local prompt_text=$1 @@ -16,7 +14,50 @@ prompt_for_number() { done } +# On a release the following actions are performed +# * DEVELOPMENT_BUILD is set to false +# * android versionCode is bumped +# * Commit the changes +# * Tag with current version +perform_release() { + sed -i -re "s/^set\(DEVELOPMENT_BUILD TRUE\)$/set(DEVELOPMENT_BUILD FALSE)/" CMakeLists.txt + sed -i -re "s/versionCode [0-9]+$/versionCode $NEW_ANDROID_VERSION_CODE/" build/android/build.gradle + + git add -f CMakeLists.txt build/android/build.gradle + + git commit -m "Bump version to $RELEASE_VERSION" + + echo "Tagging $RELEASE_VERSION" + + git tag -a "$RELEASE_VERSION" -m "$RELEASE_VERSION" +} + +# After release +# * Set DEVELOPMENT_BUILD to true +# * Bump version in CMakeLists and docs +# * Commit the changes +back_to_devel() { + echo 'Creating "return back to development" commit' + + sed -i -re 's/^set\(DEVELOPMENT_BUILD FALSE\)$/set(DEVELOPMENT_BUILD TRUE)/' CMakeLists.txt + + sed -i -re "s/^set\(VERSION_MAJOR [0-9]+\)$/set(VERSION_MAJOR $NEXT_VERSION_MAJOR)/" CMakeLists.txt + + sed -i -re "s/^set\(VERSION_MINOR [0-9]+\)$/set(VERSION_MINOR $NEXT_VERSION_MINOR)/" CMakeLists.txt + + sed -i -re "s/^set\(VERSION_PATCH [0-9]+\)$/set(VERSION_PATCH $NEXT_VERSION_PATCH)/" CMakeLists.txt + + sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/lua_api.txt + + sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/menu_lua_api.txt + + sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/client_lua_api.md + + git add -f CMakeLists.txt doc/lua_api.txt doc/menu_lua_api.txt doc/client_lua_api.md + + git commit -m "Continue with $NEXT_VERSION-dev" +} ################################## # Switch to top minetest directory ################################## @@ -29,93 +70,64 @@ cd ${0%/*}/.. ####################### # Make sure all the files we need exist -grep -q -E '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt || die "error: Could not find CMakeLists.txt" -grep -q -E '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt || die "error: Could not find CMakeLists.txt" -grep -q -E '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt || die "error: Could not find CMakeLists.txt" -grep -q -E 'versionCode [0-9]+$' build/android/build.gradle || die "error: Could not find Android version code" +grep -q -E '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt +grep -q -E '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt +grep -q -E '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt +grep -q -E 'versionCode [0-9]+$' build/android/build.gradle VERSION_MAJOR=$(grep -E '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) VERSION_MINOR=$(grep -E '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) VERSION_PATCH=$(grep -E '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) ANDROID_VERSION_CODE=$(grep -E 'versionCode [0-9]+$' build/android/build.gradle | tr -dC 0-9) -echo "Current Minetest version: $VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" +RELEASE_VERSION="$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" + +echo "Current Minetest version: $RELEASE_VERSION" echo "Current Android version code: $ANDROID_VERSION_CODE" - -######################## -# Prompt for new version -######################## - -NEW_VERSION_MAJOR=$VERSION_MAJOR -NEW_VERSION_MINOR=$VERSION_MINOR -NEW_VERSION_PATCH=$(expr $VERSION_PATCH + 1) - -NEW_VERSION_MAJOR=$(prompt_for_number "Set major" $NEW_VERSION_MAJOR) - -if [ "$NEW_VERSION_MAJOR" != "$VERSION_MAJOR" ]; then - NEW_VERSION_MINOR=0 - NEW_VERSION_PATCH=0 -fi - -NEW_VERSION_MINOR=$(prompt_for_number "Set minor" $NEW_VERSION_MINOR) - -if [ "$NEW_VERSION_MINOR" != "$VERSION_MINOR" ]; then - NEW_VERSION_PATCH=0 -fi - -NEW_VERSION_PATCH=$(prompt_for_number "Set patch" $NEW_VERSION_PATCH) - NEW_ANDROID_VERSION_CODE=$(expr $ANDROID_VERSION_CODE + 1) NEW_ANDROID_VERSION_CODE=$(prompt_for_number "Set android version code" $NEW_ANDROID_VERSION_CODE) -NEW_VERSION="$NEW_VERSION_MAJOR.$NEW_VERSION_MINOR.$NEW_VERSION_PATCH" - - echo -echo "New version: $NEW_VERSION" echo "New android version code: $NEW_ANDROID_VERSION_CODE" +######################## +# Perform release +######################## -####################################### -# Replace version everywhere and commit -####################################### +perform_release -sed -i -re "s/^set\(VERSION_MAJOR [0-9]+\)$/set(VERSION_MAJOR $NEW_VERSION_MAJOR)/" CMakeLists.txt || die "Failed to update VERSION_MAJOR" +######################## +# Prompt for next version +######################## -sed -i -re "s/^set\(VERSION_MINOR [0-9]+\)$/set(VERSION_MINOR $NEW_VERSION_MINOR)/" CMakeLists.txt || die "Failed to update VERSION_MINOR" +NEXT_VERSION_MAJOR=$VERSION_MAJOR +NEXT_VERSION_MINOR=$VERSION_MINOR +NEXT_VERSION_PATCH=$(expr $VERSION_PATCH + 1) -sed -i -re "s/^set\(VERSION_PATCH [0-9]+\)$/set(VERSION_PATCH $NEW_VERSION_PATCH)/" CMakeLists.txt || die "Failed to update VERSION_PATCH" +NEXT_VERSION_MAJOR=$(prompt_for_number "Set next major" $NEXT_VERSION_MAJOR) -sed -i -re "s/^set\(DEVELOPMENT_BUILD TRUE\)$/set(DEVELOPMENT_BUILD FALSE)/" CMakeLists.txt || die "Failed to unset DEVELOPMENT_BUILD" +if [ "$NEXT_VERSION_MAJOR" != "$VERSION_MAJOR" ]; then + NEXT_VERSION_MINOR=0 + NEXT_VERSION_PATCH=0 +fi -sed -i -re "s/versionCode [0-9]+$/versionCode $NEW_ANDROID_VERSION_CODE/" build/android/build.gradle || die "Failed to update Android version code" +NEXT_VERSION_MINOR=$(prompt_for_number "Set next minor" $NEXT_VERSION_MINOR) -sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEW_VERSION/g" doc/lua_api.txt || die "Failed to update doc/lua_api.txt" +if [ "$NEXT_VERSION_MINOR" != "$VERSION_MINOR" ]; then + NEXT_VERSION_PATCH=0 +fi -sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEW_VERSION/g" doc/menu_lua_api.txt || die "Failed to update doc/menu_lua_api.txt" +NEXT_VERSION_PATCH=$(prompt_for_number "Set next patch" $NEXT_VERSION_PATCH) -git add -f CMakeLists.txt build/android/build.gradle doc/lua_api.txt doc/menu_lua_api.txt || die "git add failed" +NEXT_VERSION="$NEXT_VERSION_MAJOR.$NEXT_VERSION_MINOR.$NEXT_VERSION_PATCH" -git commit -m "Bump version to $NEW_VERSION" || die "git commit failed" +echo +echo "New version: $NEXT_VERSION" -############ -# Create tag -############ +######################## +# Return back to devel +######################## -echo "Tagging $NEW_VERSION" - -git tag -a "$NEW_VERSION" -m "$NEW_VERSION" || die 'Adding tag failed' - -###################### -# Create revert commit -###################### - -echo 'Creating "revert to development" commit' - -sed -i -re 's/^set\(DEVELOPMENT_BUILD FALSE\)$/set(DEVELOPMENT_BUILD TRUE)/' CMakeLists.txt || die 'Failed to set DEVELOPMENT_BUILD' - -git add -f CMakeLists.txt || die 'git add failed' - -git commit -m "Continue with $NEW_VERSION-dev" || die 'git commit failed' +back_to_devel From 41b7823057bdaddd760f932dce802719301c3a0f Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 9 Sep 2017 20:33:28 +0100 Subject: [PATCH 030/183] Fix branch being labelled as 0.4.17 instead of 0.4.17-dev You should use tags instead of this branch to track the latest release --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c64ac897b..9f0fda614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(VERSION_PATCH 17) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases -set(DEVELOPMENT_BUILD FALSE) +set(DEVELOPMENT_BUILD TRUE) set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") if(VERSION_EXTRA) From 87b94518207381da10344d9fe08269687da36de5 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 20 Nov 2017 19:27:06 +0100 Subject: [PATCH 031/183] Revert version scheme changes This reverts commit 41b7823057bdaddd760f932dce802719301c3a0f. This reverts commit 7968f1ddaa67432719d5becdda5ca8bec58faa47. --- CMakeLists.txt | 4 +- doc/client_lua_api.md | 2 +- doc/lua_api.txt | 2 +- doc/menu_lua_api.txt | 2 +- util/bump_version.sh | 144 +++++++++++++++++++----------------------- 5 files changed, 71 insertions(+), 83 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f0fda614..1a0dcb688 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,11 +13,11 @@ set(PROJECT_NAME_CAPITALIZED "Minetest") # Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 17) +set(VERSION_PATCH 16) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases -set(DEVELOPMENT_BUILD TRUE) +set(DEVELOPMENT_BUILD FALSE) set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") if(VERSION_EXTRA) diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index 436f22b41..b3e494cc1 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -1,4 +1,4 @@ -Minetest Lua Client Modding API Reference 0.4.17 +Minetest Lua Client Modding API Reference 0.4.15 ================================================ * More information at * Developer Wiki: diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 8c02ebb85..ae8263c6c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Modding API Reference 0.4.17 +Minetest Lua Modding API Reference 0.4.16 ========================================= * More information at * Developer Wiki: diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index eb0d2ed6c..074bc962d 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Mainmenu API Reference 0.4.17 +Minetest Lua Mainmenu API Reference 0.4.16 ======================================== Introduction diff --git a/util/bump_version.sh b/util/bump_version.sh index 35cad78a7..948561ac3 100755 --- a/util/bump_version.sh +++ b/util/bump_version.sh @@ -1,4 +1,6 @@ -#!/bin/bash -e +#!/bin/bash + +die() { echo "$@" 1>&2 ; exit 1; } prompt_for_number() { local prompt_text=$1 @@ -14,50 +16,7 @@ prompt_for_number() { done } -# On a release the following actions are performed -# * DEVELOPMENT_BUILD is set to false -# * android versionCode is bumped -# * Commit the changes -# * Tag with current version -perform_release() { - sed -i -re "s/^set\(DEVELOPMENT_BUILD TRUE\)$/set(DEVELOPMENT_BUILD FALSE)/" CMakeLists.txt - sed -i -re "s/versionCode [0-9]+$/versionCode $NEW_ANDROID_VERSION_CODE/" build/android/build.gradle - - git add -f CMakeLists.txt build/android/build.gradle - - git commit -m "Bump version to $RELEASE_VERSION" - - echo "Tagging $RELEASE_VERSION" - - git tag -a "$RELEASE_VERSION" -m "$RELEASE_VERSION" -} - -# After release -# * Set DEVELOPMENT_BUILD to true -# * Bump version in CMakeLists and docs -# * Commit the changes -back_to_devel() { - echo 'Creating "return back to development" commit' - - sed -i -re 's/^set\(DEVELOPMENT_BUILD FALSE\)$/set(DEVELOPMENT_BUILD TRUE)/' CMakeLists.txt - - sed -i -re "s/^set\(VERSION_MAJOR [0-9]+\)$/set(VERSION_MAJOR $NEXT_VERSION_MAJOR)/" CMakeLists.txt - - sed -i -re "s/^set\(VERSION_MINOR [0-9]+\)$/set(VERSION_MINOR $NEXT_VERSION_MINOR)/" CMakeLists.txt - - sed -i -re "s/^set\(VERSION_PATCH [0-9]+\)$/set(VERSION_PATCH $NEXT_VERSION_PATCH)/" CMakeLists.txt - - sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/lua_api.txt - - sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/menu_lua_api.txt - - sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/g" doc/client_lua_api.md - - git add -f CMakeLists.txt doc/lua_api.txt doc/menu_lua_api.txt doc/client_lua_api.md - - git commit -m "Continue with $NEXT_VERSION-dev" -} ################################## # Switch to top minetest directory ################################## @@ -70,64 +29,93 @@ cd ${0%/*}/.. ####################### # Make sure all the files we need exist -grep -q -E '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt -grep -q -E '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt -grep -q -E '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt -grep -q -E 'versionCode [0-9]+$' build/android/build.gradle +grep -q -E '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt || die "error: Could not find CMakeLists.txt" +grep -q -E '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt || die "error: Could not find CMakeLists.txt" +grep -q -E '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt || die "error: Could not find CMakeLists.txt" +grep -q -E 'versionCode [0-9]+$' build/android/build.gradle || die "error: Could not find Android version code" VERSION_MAJOR=$(grep -E '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) VERSION_MINOR=$(grep -E '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) VERSION_PATCH=$(grep -E '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) ANDROID_VERSION_CODE=$(grep -E 'versionCode [0-9]+$' build/android/build.gradle | tr -dC 0-9) -RELEASE_VERSION="$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" - -echo "Current Minetest version: $RELEASE_VERSION" +echo "Current Minetest version: $VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" echo "Current Android version code: $ANDROID_VERSION_CODE" + +######################## +# Prompt for new version +######################## + +NEW_VERSION_MAJOR=$VERSION_MAJOR +NEW_VERSION_MINOR=$VERSION_MINOR +NEW_VERSION_PATCH=$(expr $VERSION_PATCH + 1) + +NEW_VERSION_MAJOR=$(prompt_for_number "Set major" $NEW_VERSION_MAJOR) + +if [ "$NEW_VERSION_MAJOR" != "$VERSION_MAJOR" ]; then + NEW_VERSION_MINOR=0 + NEW_VERSION_PATCH=0 +fi + +NEW_VERSION_MINOR=$(prompt_for_number "Set minor" $NEW_VERSION_MINOR) + +if [ "$NEW_VERSION_MINOR" != "$VERSION_MINOR" ]; then + NEW_VERSION_PATCH=0 +fi + +NEW_VERSION_PATCH=$(prompt_for_number "Set patch" $NEW_VERSION_PATCH) + NEW_ANDROID_VERSION_CODE=$(expr $ANDROID_VERSION_CODE + 1) NEW_ANDROID_VERSION_CODE=$(prompt_for_number "Set android version code" $NEW_ANDROID_VERSION_CODE) +NEW_VERSION="$NEW_VERSION_MAJOR.$NEW_VERSION_MINOR.$NEW_VERSION_PATCH" + + echo +echo "New version: $NEW_VERSION" echo "New android version code: $NEW_ANDROID_VERSION_CODE" -######################## -# Perform release -######################## -perform_release +####################################### +# Replace version everywhere and commit +####################################### -######################## -# Prompt for next version -######################## +sed -i -re "s/^set\(VERSION_MAJOR [0-9]+\)$/set(VERSION_MAJOR $NEW_VERSION_MAJOR)/" CMakeLists.txt || die "Failed to update VERSION_MAJOR" -NEXT_VERSION_MAJOR=$VERSION_MAJOR -NEXT_VERSION_MINOR=$VERSION_MINOR -NEXT_VERSION_PATCH=$(expr $VERSION_PATCH + 1) +sed -i -re "s/^set\(VERSION_MINOR [0-9]+\)$/set(VERSION_MINOR $NEW_VERSION_MINOR)/" CMakeLists.txt || die "Failed to update VERSION_MINOR" -NEXT_VERSION_MAJOR=$(prompt_for_number "Set next major" $NEXT_VERSION_MAJOR) +sed -i -re "s/^set\(VERSION_PATCH [0-9]+\)$/set(VERSION_PATCH $NEW_VERSION_PATCH)/" CMakeLists.txt || die "Failed to update VERSION_PATCH" -if [ "$NEXT_VERSION_MAJOR" != "$VERSION_MAJOR" ]; then - NEXT_VERSION_MINOR=0 - NEXT_VERSION_PATCH=0 -fi +sed -i -re "s/^set\(DEVELOPMENT_BUILD TRUE\)$/set(DEVELOPMENT_BUILD FALSE)/" CMakeLists.txt || die "Failed to unset DEVELOPMENT_BUILD" -NEXT_VERSION_MINOR=$(prompt_for_number "Set next minor" $NEXT_VERSION_MINOR) +sed -i -re "s/versionCode [0-9]+$/versionCode $NEW_ANDROID_VERSION_CODE/" build/android/build.gradle || die "Failed to update Android version code" -if [ "$NEXT_VERSION_MINOR" != "$VERSION_MINOR" ]; then - NEXT_VERSION_PATCH=0 -fi +sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEW_VERSION/g" doc/lua_api.txt || die "Failed to update doc/lua_api.txt" -NEXT_VERSION_PATCH=$(prompt_for_number "Set next patch" $NEXT_VERSION_PATCH) +sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEW_VERSION/g" doc/menu_lua_api.txt || die "Failed to update doc/menu_lua_api.txt" -NEXT_VERSION="$NEXT_VERSION_MAJOR.$NEXT_VERSION_MINOR.$NEXT_VERSION_PATCH" +git add -f CMakeLists.txt build/android/build.gradle doc/lua_api.txt doc/menu_lua_api.txt || die "git add failed" -echo -echo "New version: $NEXT_VERSION" +git commit -m "Bump version to $NEW_VERSION" || die "git commit failed" -######################## -# Return back to devel -######################## +############ +# Create tag +############ -back_to_devel +echo "Tagging $NEW_VERSION" + +git tag -a "$NEW_VERSION" -m "$NEW_VERSION" || die 'Adding tag failed' + +###################### +# Create revert commit +###################### + +echo 'Creating "revert to development" commit' + +sed -i -re 's/^set\(DEVELOPMENT_BUILD FALSE\)$/set(DEVELOPMENT_BUILD TRUE)/' CMakeLists.txt || die 'Failed to set DEVELOPMENT_BUILD' + +git add -f CMakeLists.txt || die 'git add failed' + +git commit -m "Continue with $NEW_VERSION-dev" || die 'git commit failed' From cba783f7faf517cb747177b2854567de5604c65d Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Sat, 3 Jun 2017 17:59:17 -0400 Subject: [PATCH 032/183] Fix segmentation fault with tool capabilities (#5899) --- src/game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game.cpp b/src/game.cpp index ff473e022..75d1f7cc8 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3597,7 +3597,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) } else if (pointed.type == POINTEDTHING_NODE) { ToolCapabilities playeritem_toolcap = playeritem.getToolCapabilities(itemdef_manager); - if (playeritem.name.empty()) { + if (playeritem.name.empty() && hand_def.tool_capabilities != NULL) { playeritem_toolcap = *hand_def.tool_capabilities; } handlePointingAtNode(pointed, playeritem_def, playeritem_toolcap, dtime); From e665e75e778d59c1ff8d55bd435f6ec3d7b3e2a3 Mon Sep 17 00:00:00 2001 From: red-001 Date: Tue, 6 Jun 2017 16:02:44 +0100 Subject: [PATCH 033/183] Fix typos/mistakes in the documentation for colour related functions. (#5936) --- doc/client_lua_api.md | 8 ++++---- doc/lua_api.txt | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index b3e494cc1..ab72bbccf 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -1117,15 +1117,15 @@ The following functions provide escape sequences: `minetest.get_color_escape_sequence(color) .. message .. minetest.get_color_escape_sequence("#ffffff")` -* `color.get_background_escape_sequence(color)` +* `minetest.get_background_escape_sequence(color)` * `color` is a [ColorString](#colorstring) * The escape sequence sets the background of the whole text element to `color`. Only defined for item descriptions and tooltips. -* `color.strip_foreground_colors(str)` +* `minetest.strip_foreground_colors(str)` * Removes foreground colors added by `get_color_escape_sequence`. -* `color.strip_background_colors(str)` +* `minetest.strip_background_colors(str)` * Removes background colors added by `get_background_escape_sequence`. -* `color.strip_colors(str)` +* `minetest.strip_colors(str)` * Removes all color escape sequences. `ColorString` diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ae8263c6c..f3d3b1bca 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2060,15 +2060,15 @@ The following functions provide escape sequences: `minetest.get_color_escape_sequence(color) .. message .. minetest.get_color_escape_sequence("#ffffff")` -* `color.get_background_escape_sequence(color)` +* `minetest.get_background_escape_sequence(color)` * `color` is a ColorString * The escape sequence sets the background of the whole text element to `color`. Only defined for item descriptions and tooltips. -* `color.strip_foreground_colors(str)` +* `minetest.strip_foreground_colors(str)` * Removes foreground colors added by `get_color_escape_sequence`. -* `color.strip_background_colors(str)` +* `minetest.strip_background_colors(str)` * Removes background colors added by `get_background_escape_sequence`. -* `color.strip_colors(str)` +* `minetest.strip_colors(str)` * Removes all color escape sequences. Spatial Vectors From f736226c1a289c52cb59e5639ce4a39ad6451ac6 Mon Sep 17 00:00:00 2001 From: DS Date: Wed, 7 Jun 2017 19:11:28 +0200 Subject: [PATCH 034/183] make ret variable in /builtin/mainmenu/tab_credits.lua local (#5942) --- builtin/mainmenu/tab_credits.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua index 0774433b6..b02352257 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_credits.lua @@ -74,7 +74,7 @@ local previous_contributors = { } local function buildCreditList(source) - ret = {} + local ret = {} for i = 1, #source do ret[i] = core.formspec_escape(source[i]) end From c399f5a5419efdafec766268af008ed24418fcd4 Mon Sep 17 00:00:00 2001 From: red-001 Date: Fri, 9 Jun 2017 20:39:25 +0100 Subject: [PATCH 035/183] Fix sending color codes to clients that don't support them. (#5950) Also remove `disable_escape_sequences` since it's not needed anymore. --- builtin/common/misc_helpers.lua | 54 +++++++++++---------------------- builtin/settingtypes.txt | 5 --- src/server.cpp | 17 ++++++----- 3 files changed, 28 insertions(+), 48 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 68481f7c8..0bdd4b02a 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -642,44 +642,26 @@ end local ESCAPE_CHAR = string.char(0x1b) --- Client-side mods don't have access to settings -if core.settings and core.settings:get_bool("disable_escape_sequences") then - - function core.get_color_escape_sequence(color) - return "" - end - - function core.get_background_escape_sequence(color) - return "" - end - - function core.colorize(color, message) - return message - end - -else - - function core.get_color_escape_sequence(color) - return ESCAPE_CHAR .. "(c@" .. color .. ")" - end - - function core.get_background_escape_sequence(color) - return ESCAPE_CHAR .. "(b@" .. color .. ")" - end - - function core.colorize(color, message) - local lines = tostring(message):split("\n", true) - local color_code = core.get_color_escape_sequence(color) - - for i, line in ipairs(lines) do - lines[i] = color_code .. line - end - - return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff") - end - +function core.get_color_escape_sequence(color) + return ESCAPE_CHAR .. "(c@" .. color .. ")" end +function core.get_background_escape_sequence(color) + return ESCAPE_CHAR .. "(b@" .. color .. ")" +end + +function core.colorize(color, message) + local lines = tostring(message):split("\n", true) + local color_code = core.get_color_escape_sequence(color) + + for i, line in ipairs(lines) do + lines[i] = color_code .. line + end + + return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff") +end + + function core.strip_foreground_colors(str) return (str:gsub(ESCAPE_CHAR .. "%(c@[^)]+%)", "")) end diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index ba3339d32..5182cb3f7 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -721,11 +721,6 @@ server_announce (Announce server) bool false # If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net. serverlist_url (Serverlist URL) string servers.minetest.net -# Disable escape sequences, e.g. chat coloring. -# Use this if you want to run a server with pre-0.4.14 clients and you want to disable -# the escape sequences generated by mods. -disable_escape_sequences (Disable escape sequences) bool false - [*Network] # Network port to listen (UDP). diff --git a/src/server.cpp b/src/server.cpp index 1e8e6a5d2..e9ccc3d79 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1643,15 +1643,18 @@ void Server::SendInventory(PlayerSAO* playerSAO) void Server::SendChatMessage(u16 peer_id, const std::wstring &message) { DSTACK(FUNCTION_NAME); - - NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); - pkt << message; - if (peer_id != PEER_ID_INEXISTENT) { + NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); + + if (m_clients.getProtocolVersion(peer_id) < 27) + pkt << unescape_enriched(message); + else + pkt << message; + Send(&pkt); - } - else { - m_clients.sendToAll(&pkt); + } else { + for (u16 id : m_clients.getClientIDs()) + SendChatMessage(id, message); } } From 14b039f0b4c8bddc1d4760dc7277f72392385d7d Mon Sep 17 00:00:00 2001 From: DS Date: Sat, 10 Jun 2017 13:49:28 +0200 Subject: [PATCH 036/183] fix an example in lua_api (#5604) --- doc/lua_api.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f3d3b1bca..2bbf18310 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2711,9 +2711,9 @@ and `minetest.auth_reload` call the authetification handler. * Example query for `"default:gold_ingot"` will return table: { - [1]={type = "cooking", width = 3, output = "default:gold_ingot", + [1]={method = "cooking", width = 3, output = "default:gold_ingot", items = {1 = "default:gold_lump"}}, - [2]={type = "normal", width = 1, output = "default:gold_ingot 9", + [2]={method = "normal", width = 1, output = "default:gold_ingot 9", items = {1 = "default:goldblock"}} } * `minetest.handle_node_drops(pos, drops, digger)` From 0664b5f77226b7a2308e85898295378e04158923 Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 10 Jun 2017 12:49:44 +0100 Subject: [PATCH 037/183] Add a server-sided way to remove color codes from incoming chat messages (#5948) These code be generated by CSM, a modded client or just copy and pasted by the player. Changes - Update configuration example and setting translation file. - Remove colour codes before logging chat. - Add setting to remove colour codes before processing the chat. --- builtin/settingtypes.txt | 4 ++++ minetest.conf.example | 14 +++++++------- src/defaultsettings.cpp | 1 + src/server.cpp | 7 +++++-- src/server.h | 2 +- src/settings_translation_file.cpp | 10 +++++----- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 5182cb3f7..0da66750a 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -721,6 +721,10 @@ server_announce (Announce server) bool false # If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net. serverlist_url (Serverlist URL) string servers.minetest.net +# Remove color codes from incoming chat messages +# Use this to stop players from being able to use color in their messages +strip_color_codes (Strip color codes) bool false + [*Network] # Network port to listen (UDP). diff --git a/minetest.conf.example b/minetest.conf.example index c933047dd..79f7f68f9 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -344,8 +344,8 @@ # serverlist_file = favoriteservers.txt # Maximum size of the out chat queue. 0 to disable queueing and -1 to make the queue size unlimited -# type: int min: -1 -max_out_chat_queue_size = 20 +# type: int +# max_out_chat_queue_size = 20 ## Graphics @@ -555,7 +555,7 @@ max_out_chat_queue_size = 20 # type: int # screenH = 600 -# Save the window size automatically when modified. +# Save window size automatically when modified. # type: bool # autosave_screensize = true @@ -867,11 +867,10 @@ max_out_chat_queue_size = 20 # type: string # serverlist_url = servers.minetest.net -# Disable escape sequences, e.g. chat coloring. -# Use this if you want to run a server with pre-0.4.14 clients and you want to disable -# the escape sequences generated by mods. +# Remove color codes from incoming chat messages +# Use this to stop players from being able to use color in their messages # type: bool -# disable_escape_sequences = false +# strip_color_codes = false ## Network @@ -1841,3 +1840,4 @@ max_out_chat_queue_size = 20 # Print the engine's profiling data in regular intervals (in seconds). 0 = disable. Useful for developers. # type: int # profiler_print_interval = 0 + diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 0a44069fd..3378e8b4a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -252,6 +252,7 @@ void set_default_settings(Settings *settings) // Server settings->setDefault("disable_escape_sequences", "false"); + settings->setDefault("strip_color_codes", "false"); // Network settings->setDefault("enable_ipv6", "true"); diff --git a/src/server.cpp b/src/server.cpp index e9ccc3d79..a0c4e30d8 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2873,12 +2873,15 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt) } std::wstring Server::handleChat(const std::string &name, const std::wstring &wname, - const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player) + std::wstring wmessage, bool check_shout_priv, RemotePlayer *player) { // If something goes wrong, this player is to blame RollbackScopeActor rollback_scope(m_rollback, std::string("player:") + name); + if (g_settings->getBool("strip_color_codes")) + wmessage = unescape_enriched(wmessage); + if (player) { switch (player->canSendChatMessage()) { case RPLAYER_CHATRESULT_FLOODING: { @@ -2933,7 +2936,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna /* Send the message to others */ - actionstream << "CHAT: " << wide_to_narrow(line) << std::endl; + actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl; std::vector clients = m_clients.getClientIDs(); diff --git a/src/server.h b/src/server.h index 2e735e77c..e3a4291d9 100644 --- a/src/server.h +++ b/src/server.h @@ -486,7 +486,7 @@ private: // This returns the answer to the sender of wmessage, or "" if there is none std::wstring handleChat(const std::string &name, const std::wstring &wname, - const std::wstring &wmessage, + std::wstring wmessage_input, bool check_shout_priv = false, RemotePlayer *player = NULL); void handleAdminChat(const ChatEventChat *evt); diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index 383da33a7..684b125b9 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -34,7 +34,7 @@ fake_function() { gettext("Random input"); gettext("Enable random user input (only used for testing)."); gettext("Continuous forward"); - gettext("Continuous forward movement (only used for testing)."); + gettext("Continuous forward movement, toggled by autoforward key."); gettext("Enable Joysticks"); gettext("Enable Joysticks"); gettext("Joystick ID"); @@ -87,8 +87,8 @@ fake_function() { gettext("Key for increasing the volume.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3"); gettext("Dec. volume key"); gettext("Key for decreasing the volume.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3"); - gettext("Autorun key"); - gettext("Key for toggling autorun.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3"); + gettext("Autoforward key"); + gettext("Key for toggling autoforward.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3"); gettext("Cinematic mode key"); gettext("Key for toggling cinematic mode.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3"); gettext("Minimap key"); @@ -362,8 +362,8 @@ fake_function() { gettext("Automaticaly report to the serverlist."); gettext("Serverlist URL"); gettext("Announce to this serverlist.\nIf you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net."); - gettext("Disable escape sequences"); - gettext("Disable escape sequences, e.g. chat coloring.\nUse this if you want to run a server with pre-0.4.14 clients and you want to disable\nthe escape sequences generated by mods."); + gettext("Strip color codes"); + gettext("Remove color codes from incoming chat messages\nUse this to stop players from being able to use color in their messages"); gettext("Network"); gettext("Server port"); gettext("Network port to listen (UDP).\nThis value will be overridden when starting from the main menu."); From 7aa52fe4e195ad9d66f0bda51688b81d31391346 Mon Sep 17 00:00:00 2001 From: paramat Date: Sun, 4 Jun 2017 22:28:32 +0100 Subject: [PATCH 038/183] (Re)spawn players within 'mapgen_limit' Previously, findSpawnPos() did not take the 'mapgen_limit' setting into account, a small limit often resulted in a spawn out in the void. Use the recently added 'calcMapgenEdges()' to get max spawn range through a new mapgenParams function 'getSpawnRangeMax()'. Previously, when a player respawned into a world, 'objectpos_over_limit()' was used as a check, which was inaccurate. Use the recently added 'saoPosOverLimit()' to get exact mapgen edges. Also fix default value of 'm_sao_limit_min'. --- src/mapgen.cpp | 25 +++++++++++++++++-------- src/mapgen.h | 14 +++++++++++--- src/server.cpp | 4 +++- src/serverenvironment.cpp | 3 ++- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 1f7f98621..1aa3be302 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -1053,12 +1053,13 @@ void MapgenParams::writeParams(Settings *settings) const // 'mapgen_limit'), and corresponding exact limits for SAO entities. void MapgenParams::calcMapgenEdges() { + if (m_mapgen_edges_calculated) + return; + // Central chunk offset, in blocks s16 ccoff_b = -chunksize / 2; - // Chunksize, in nodes s32 csize_n = chunksize * MAP_BLOCKSIZE; - // Minp/maxp of central chunk, in nodes s16 ccmin = ccoff_b * MAP_BLOCKSIZE; s16 ccmax = ccmin + csize_n - 1; @@ -1077,21 +1078,21 @@ void MapgenParams::calcMapgenEdges() s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0); s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0); // Mapgen edges, in nodes - // These values may be useful later as additional class members - s16 mapgen_edge_min = ccmin - numcmin * csize_n; - s16 mapgen_edge_max = ccmax + numcmax * csize_n; + mapgen_edge_min = ccmin - numcmin * csize_n; + mapgen_edge_max = ccmax + numcmax * csize_n; // SAO position limits, in Irrlicht units m_sao_limit_min = mapgen_edge_min * BS - 3.0f; m_sao_limit_max = mapgen_edge_max * BS + 3.0f; + + m_mapgen_edges_calculated = true; } bool MapgenParams::saoPosOverLimit(const v3f &p) { - if (!m_sao_limit_calculated) { + if (!m_mapgen_edges_calculated) calcMapgenEdges(); - m_sao_limit_calculated = true; - } + return p.X < m_sao_limit_min || p.X > m_sao_limit_max || p.Y < m_sao_limit_min || @@ -1099,3 +1100,11 @@ bool MapgenParams::saoPosOverLimit(const v3f &p) p.Z < m_sao_limit_min || p.Z > m_sao_limit_max; } + + +s32 MapgenParams::getSpawnRangeMax() +{ + calcMapgenEdges(); + + return MYMIN(-mapgen_edge_min, mapgen_edge_max); +} diff --git a/src/mapgen.h b/src/mapgen.h index 222838011..9bfdb22de 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -131,6 +131,9 @@ struct MapgenParams { BiomeParams *bparams; + s16 mapgen_edge_min; + s16 mapgen_edge_max; + MapgenParams() : mgtype(MAPGEN_DEFAULT), chunksize(5), @@ -139,9 +142,12 @@ struct MapgenParams { mapgen_limit(MAX_MAP_GENERATION_LIMIT), flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS), bparams(NULL), - m_sao_limit_min(MAX_MAP_GENERATION_LIMIT * BS), + + mapgen_edge_min(-MAX_MAP_GENERATION_LIMIT), + mapgen_edge_max(MAX_MAP_GENERATION_LIMIT), + m_sao_limit_min(-MAX_MAP_GENERATION_LIMIT * BS), m_sao_limit_max(MAX_MAP_GENERATION_LIMIT * BS), - m_sao_limit_calculated(false) + m_mapgen_edges_calculated(false) { } @@ -151,12 +157,14 @@ struct MapgenParams { virtual void writeParams(Settings *settings) const; bool saoPosOverLimit(const v3f &p); + s32 getSpawnRangeMax(); + private: void calcMapgenEdges(); float m_sao_limit_min; float m_sao_limit_max; - bool m_sao_limit_calculated; + bool m_mapgen_edges_calculated; }; diff --git a/src/server.cpp b/src/server.cpp index a0c4e30d8..83022e95b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -3516,10 +3516,12 @@ v3f Server::findSpawnPos() } bool is_good = false; + // Limit spawn range to mapgen edges (determined by 'mapgen_limit') + s32 range_max = map.getMapgenParams()->getSpawnRangeMax(); // Try to find a good place a few times for(s32 i = 0; i < 4000 && !is_good; i++) { - s32 range = 1 + i; + s32 range = MYMIN(1 + i, range_max); // We're going to try to throw the player to this position v2s16 nodepos2d = v2s16( -range + (myrand() % (range * 2)), diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index cbdc747d1..5e39ce6a5 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -579,7 +579,8 @@ PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player, // If the player exists, ensure that they respawn inside legal bounds // This fixes an assert crash when the player can't be added // to the environment - if (objectpos_over_limit(playersao->getBasePosition())) { + ServerMap &map = getServerMap(); + if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) { actionstream << "Respawn position for player \"" << player->getName() << "\" outside limits, resetting" << std::endl; playersao->setBasePosition(m_server->findSpawnPos()); From 5f796f7a042576f848e969c77b25b4b9e4112b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Mon, 19 Jun 2017 14:10:30 +0200 Subject: [PATCH 039/183] Verify HudSetParams input when hotbar textures are set (#6013) * Verify HudSetParams input when hotbar textures are set This fix #6011 --- src/network/clientpackethandler.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 59669fe6d..8935ed90c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1172,9 +1172,21 @@ void Client::handleCommand_HudSetParam(NetworkPacket* pkt) player->hud_hotbar_itemcount = hotbar_itemcount; } else if (param == HUD_PARAM_HOTBAR_IMAGE) { + // If value not empty verify image exists in texture source + if (value != "" && !getTextureSource()->isKnownSourceImage(value)) { + errorstream << "Server sent wrong Hud hotbar image (sent value: '" + << value << "')" << std::endl; + return; + } player->hotbar_image = value; } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) { + // If value not empty verify image exists in texture source + if (value != "" && !getTextureSource()->isKnownSourceImage(value)) { + errorstream << "Server sent wrong Hud hotbar selected image (sent value: '" + << value << "')" << std::endl; + return; + } player->hotbar_selected_image = value; } } From 03bc584f5738e21ef6c07e7c0928d0083be9995e Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 19 Jun 2017 16:30:26 +0200 Subject: [PATCH 040/183] find_nodes_in_area: Extend maximal count to U32_MAX (#5277) Extend documentation, limit area volume Remove u16 count limitation * Prevent integer overflow, replace minp/maxp with pos1/pos2 --- doc/lua_api.txt | 9 +++--- src/script/lua_api/l_env.cpp | 62 +++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 2bbf18310..e893d6461 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2462,12 +2462,13 @@ and `minetest.auth_reload` call the authetification handler. * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * `search_center` is an optional boolean (default: `false`) If true `pos` is also checked for the nodes -* `minetest.find_nodes_in_area(minp, maxp, nodenames)`: returns a list of positions - * returns as second value a table with the count of the individual nodes found +* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of positions * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` -* `minetest.find_nodes_in_area_under_air(minp, maxp, nodenames)`: returns a list of positions - * returned positions are nodes with a node air above + * First return value: Table with all node positions + * Second return value: Table with the count of each node with the node name as index +* `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a list of positions * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` + * Return value: Table with all node positions with a node air above * `minetest.get_perlin(noiseparams)` * `minetest.get_perlin(seeddiff, octaves, persistence, scale)` * Return world-specific perlin noise (`int(worldseed)+seeddiff`) diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index b8b6bc5ba..e7284b035 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -651,38 +651,51 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) INodeDefManager *ndef = getServer(L)->ndef(); v3s16 minp = read_v3s16(L, 1); v3s16 maxp = read_v3s16(L, 2); + sortBoxVerticies(minp, maxp); + + v3s16 cube = maxp - minp + 1; + + /* Limit for too large areas, assume default values + * and give tolerances of 1 node on each side + * (chunksize * MAP_BLOCKSIZE + 2)^3 = 551368 + */ + if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 551368) { + luaL_error(L, "find_nodes_in_area(): area volume" + " exceeds allowed value of 551368"); + return 0; + } + std::set filter; - if(lua_istable(L, 3)) { - int table = 3; + if (lua_istable(L, 3)) { lua_pushnil(L); - while(lua_next(L, table) != 0) { + while (lua_next(L, 3) != 0) { // key at index -2 and value at index -1 luaL_checktype(L, -1, LUA_TSTRING); ndef->getIds(lua_tostring(L, -1), filter); // removes value, keeps key for next iteration lua_pop(L, 1); } - } else if(lua_isstring(L, 3)) { + } else if (lua_isstring(L, 3)) { ndef->getIds(lua_tostring(L, 3), filter); } - std::map individual_count; + std::unordered_map individual_count; lua_newtable(L); u64 i = 0; for (s16 x = minp.X; x <= maxp.X; x++) - for (s16 y = minp.Y; y <= maxp.Y; y++) - for (s16 z = minp.Z; z <= maxp.Z; z++) { - v3s16 p(x, y, z); - content_t c = env->getMap().getNodeNoEx(p).getContent(); - if (filter.count(c) != 0) { - push_v3s16(L, p); - lua_rawseti(L, -2, ++i); - individual_count[c]++; - } + for (s16 y = minp.Y; y <= maxp.Y; y++) + for (s16 z = minp.Z; z <= maxp.Z; z++) { + v3s16 p(x, y, z); + content_t c = env->getMap().getNodeNoEx(p).getContent(); + if (filter.count(c) != 0) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + individual_count[c]++; + } } lua_newtable(L); - for (std::set::iterator it = filter.begin(); + for (std::set::const_iterator it = filter.begin(); it != filter.end(); ++it) { lua_pushnumber(L, individual_count[*it]); lua_setfield(L, -2, ndef->get(*it).name.c_str()); @@ -706,12 +719,25 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) INodeDefManager *ndef = getServer(L)->ndef(); v3s16 minp = read_v3s16(L, 1); v3s16 maxp = read_v3s16(L, 2); + sortBoxVerticies(minp, maxp); + + v3s16 cube = maxp - minp + 1; + + /* Limit for too large areas, assume default values + * and give tolerances of 1 node on each side + * (chunksize * MAP_BLOCKSIZE + 2)^3 = 551368 + */ + if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 551368) { + luaL_error(L, "find_nodes_in_area_under_air(): area volume" + " exceeds allowed value of 551368"); + return 0; + } + std::set filter; if (lua_istable(L, 3)) { - int table = 3; lua_pushnil(L); - while(lua_next(L, table) != 0) { + while (lua_next(L, 3) != 0) { // key at index -2 and value at index -1 luaL_checktype(L, -1, LUA_TSTRING); ndef->getIds(lua_tostring(L, -1), filter); @@ -732,7 +758,7 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) for (; y <= maxp.Y; y++) { v3s16 psurf(x, y + 1, z); content_t csurf = env->getMap().getNodeNoEx(psurf).getContent(); - if(c != CONTENT_AIR && csurf == CONTENT_AIR && + if (c != CONTENT_AIR && csurf == CONTENT_AIR && filter.count(c) != 0) { push_v3s16(L, v3s16(x, y, z)); lua_rawseti(L, -2, ++i); From 322e5aaf9285e9686101393967f1a3c1e7db986c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Tue, 20 Jun 2017 09:19:56 +0000 Subject: [PATCH 041/183] Automatic item and node colorization (#5640) * Automatic item and node colorization Now nodes with a palette yield colored item stacks, and colored items place colored nodes by default. The client predicts the colorization. * Backwards compatibility * Use nil * Style fixes * Fix code style * Document changes --- builtin/game/falling.lua | 6 ++-- builtin/game/item.lua | 50 +++++++++++++++++++++++--- doc/lua_api.txt | 13 ++++--- src/game.cpp | 57 ++++++++++++++++++++++-------- src/inventory.cpp | 8 ++--- src/inventory.h | 5 +-- src/script/lua_api/l_inventory.cpp | 11 +++--- src/script/lua_api/l_inventory.h | 2 +- 8 files changed, 114 insertions(+), 38 deletions(-) diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index b1beb1ab0..1ac4f7081 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -93,7 +93,7 @@ core.register_entity(":__builtin:falling_node", { core.remove_node(np) if nd and nd.buildable_to == false then -- Add dropped items - local drops = core.get_node_drops(n2.name, "") + local drops = core.get_node_drops(n2, "") for _, dropped_item in pairs(drops) do core.add_item(np, dropped_item) end @@ -145,9 +145,9 @@ function core.spawn_falling_node(pos) end local function drop_attached_node(p) - local nn = core.get_node(p).name + local n = core.get_node(p) core.remove_node(p) - for _, item in pairs(core.get_node_drops(nn, "")) do + for _, item in pairs(core.get_node_drops(n, "")) do local pos = { x = p.x + math.random()/2 - 0.25, y = p.y + math.random()/2 - 0.25, diff --git a/builtin/game/item.lua b/builtin/game/item.lua index e36745f93..f6de2c339 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -155,12 +155,35 @@ function core.yaw_to_dir(yaw) return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)} end -function core.get_node_drops(nodename, toolname) +function core.get_node_drops(node, toolname) + -- Compatibility, if node is string + local nodename = node + local param2 = 0 + -- New format, if node is table + if (type(node) == "table") then + nodename = node.name + param2 = node.param2 + end local def = core.registered_nodes[nodename] local drop = def and def.drop if drop == nil then -- default drop - return {nodename} + local stack = ItemStack(nodename) + if def then + local type = def.paramtype2 + if (type == "color") or (type == "colorfacedir") or + (type == "colorwallmounted") then + local meta = stack:get_meta() + local color_part = param2 + if (type == "colorfacedir") then + color_part = math.floor(color_part / 32) * 32; + elseif (type == "colorwallmounted") then + color_part = math.floor(color_part / 8) * 8; + end + meta:set_int("palette_index", color_part) + end + end + return {stack:to_string()} elseif type(drop) == "string" then -- itemstring drop return {drop} @@ -258,7 +281,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) .. def.name .. " at " .. core.pos_to_string(place_to)) local oldnode = core.get_node(place_to) - local newnode = {name = def.name, param1 = 0, param2 = param2} + local newnode = {name = def.name, param1 = 0, param2 = param2 or 0} -- Calculate direction for wall mounted stuff like torches and signs if def.place_param2 ~= nil then @@ -286,6 +309,25 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) end end + local metatable = itemstack:get_meta():to_table().fields + + -- Transfer color information + if metatable.palette_index and not def.place_param2 then + local color_divisor = nil + if def.paramtype2 == "color" then + color_divisor = 1 + elseif def.paramtype2 == "colorwallmounted" then + color_divisor = 8 + elseif def.paramtype2 == "colorfacedir" then + color_divisor = 32 + end + if color_divisor then + local color = math.floor(metatable.palette_index / color_divisor) + local other = newnode.param2 % color_divisor + newnode.param2 = color * color_divisor + other + end + end + -- Check if the node is attached and if it can be placed there if core.get_item_group(def.name, "attached_node") ~= 0 and not builtin_shared.check_attached_node(place_to, newnode) then @@ -474,7 +516,7 @@ function core.node_dig(pos, node, digger) .. node.name .. " at " .. core.pos_to_string(pos)) local wielded = digger:get_wielded_item() - local drops = core.get_node_drops(node.name, wielded:get_name()) + local drops = core.get_node_drops(node, wielded:get_name()) local wdef = wielded:get_definition() local tp = wielded:get_tool_capabilities() diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e893d6461..aefcba064 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -531,9 +531,11 @@ for conversion. If the `ItemStack`'s metadata contains the `color` field, it will be lost on placement, because nodes on the map can only use palettes. -If the `ItemStack`'s metadata contains the `palette_index` field, you -currently must manually convert between it and the node's `param2` with -custom `on_place` and `on_dig` callbacks. +If the `ItemStack`'s metadata contains the `palette_index` field, it is +automatically transferred between node and item forms by the engine, +when a player digs or places a colored node. +You can disable this feature by setting the `drop` field of the node +to itself (without metadata). ### Colored items in craft recipes Craft recipes only support item strings, but fortunately item strings @@ -3323,8 +3325,9 @@ An `InvRef` is a reference to an inventory. * `add_item(listname, stack)`: add item somewhere in list, returns leftover `ItemStack` * `room_for_item(listname, stack):` returns `true` if the stack of items can be fully added to the list -* `contains_item(listname, stack)`: returns `true` if the stack of items - can be fully taken from the list +* `contains_item(listname, stack, [match_meta])`: returns `true` if + the stack of items can be fully taken from the list. + If `match_meta` is false, only the items' names are compared (default: `false`). * `remove_item(listname, stack)`: take as many items as specified from the list, returns the items that were actually removed (as an `ItemStack`) -- note that any item metadata is ignored, so attempting to remove a specific unique diff --git a/src/game.cpp b/src/game.cpp index 75d1f7cc8..9bb560172 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -774,8 +774,8 @@ public: }; -bool nodePlacementPrediction(Client &client, - const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos) +bool nodePlacementPrediction(Client &client, const ItemDefinition &playeritem_def, + const ItemStack &playeritem, v3s16 nodepos, v3s16 neighbourpos) { std::string prediction = playeritem_def.node_placement_prediction; INodeDefManager *nodedef = client.ndef(); @@ -818,11 +818,13 @@ bool nodePlacementPrediction(Client &client, return false; } + const ContentFeatures &predicted_f = nodedef->get(id); + // Predict param2 for facedir and wallmounted nodes u8 param2 = 0; - if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED || - nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) { + if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || + predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { v3s16 dir = nodepos - neighbourpos; if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { @@ -834,8 +836,8 @@ bool nodePlacementPrediction(Client &client, } } - if (nodedef->get(id).param_type_2 == CPT2_FACEDIR || - nodedef->get(id).param_type_2 == CPT2_COLORED_FACEDIR) { + if (predicted_f.param_type_2 == CPT2_FACEDIR || + predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) { v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS); if (abs(dir.X) > abs(dir.Z)) { @@ -848,7 +850,7 @@ bool nodePlacementPrediction(Client &client, assert(param2 <= 5); //Check attachment if node is in group attached_node - if (((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0) { + if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) { static v3s16 wallmounted_dirs[8] = { v3s16(0, 1, 0), v3s16(0, -1, 0), @@ -859,8 +861,8 @@ bool nodePlacementPrediction(Client &client, }; v3s16 pp; - if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED || - nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) + if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || + predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) pp = p + wallmounted_dirs[param2]; else pp = p + v3s16(0, -1, 0); @@ -869,6 +871,28 @@ bool nodePlacementPrediction(Client &client, return false; } + // Apply color + if ((predicted_f.param_type_2 == CPT2_COLOR + || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR + || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) { + const std::string &indexstr = playeritem.metadata.getString( + "palette_index", 0); + if (!indexstr.empty()) { + s32 index = mystoi(indexstr); + if (predicted_f.param_type_2 == CPT2_COLOR) { + param2 = index; + } else if (predicted_f.param_type_2 + == CPT2_COLORED_WALLMOUNTED) { + // param2 = pure palette index + other + param2 = (index & 0xf8) | (param2 & 0x07); + } else if (predicted_f.param_type_2 + == CPT2_COLORED_FACEDIR) { + // param2 = pure palette index + other + param2 = (index & 0xe0) | (param2 & 0x1f); + } + } + } + // Add node to client map MapNode n(id, 0, param2); @@ -1277,8 +1301,9 @@ protected: const core::line3d &shootline, bool liquids_pointable, bool look_for_object, const v3s16 &camera_offset); void handlePointingAtNothing(const ItemStack &playerItem); - void handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def, - const ToolCapabilities &playeritem_toolcap, f32 dtime); + void handlePointingAtNode(const PointedThing &pointed, + const ItemDefinition &playeritem_def, const ItemStack &playeritem, + const ToolCapabilities &playeritem_toolcap, f32 dtime); void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, const v3f &player_position, bool show_debug); void handleDigging(const PointedThing &pointed, const v3s16 &nodepos, @@ -3600,7 +3625,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) if (playeritem.name.empty() && hand_def.tool_capabilities != NULL) { playeritem_toolcap = *hand_def.tool_capabilities; } - handlePointingAtNode(pointed, playeritem_def, playeritem_toolcap, dtime); + handlePointingAtNode(pointed, playeritem_def, playeritem, + playeritem_toolcap, dtime); } else if (pointed.type == POINTEDTHING_OBJECT) { handlePointingAtObject(pointed, playeritem, player_position, show_debug); } else if (isLeftPressed()) { @@ -3735,8 +3761,9 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem) } -void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def, - const ToolCapabilities &playeritem_toolcap, f32 dtime) +void Game::handlePointingAtNode(const PointedThing &pointed, + const ItemDefinition &playeritem_def, const ItemStack &playeritem, + const ToolCapabilities &playeritem_toolcap, f32 dtime) { v3s16 nodepos = pointed.node_undersurface; v3s16 neighbourpos = pointed.node_abovesurface; @@ -3796,7 +3823,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio // If the wielded item has node placement prediction, // make that happen bool placed = nodePlacementPrediction(*client, - playeritem_def, + playeritem_def, playeritem, nodepos, neighbourpos); if (placed) { diff --git a/src/inventory.cpp b/src/inventory.cpp index 8617f7263..ec8f3db72 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -659,7 +659,7 @@ bool InventoryList::roomForItem(const ItemStack &item_) const return false; } -bool InventoryList::containsItem(const ItemStack &item) const +bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const { u32 count = item.count; if(count == 0) @@ -670,9 +670,9 @@ bool InventoryList::containsItem(const ItemStack &item) const { if(count == 0) break; - if(i->name == item.name) - { - if(i->count >= count) + if (i->name == item.name + && (!match_meta || (i->metadata == item.metadata))) { + if (i->count >= count) return true; else count -= i->count; diff --git a/src/inventory.h b/src/inventory.h index a9fef3b05..51664e8d3 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -223,9 +223,10 @@ public: // Checks whether there is room for a given item bool roomForItem(const ItemStack &item) const; - // Checks whether the given count of the given item name + // Checks whether the given count of the given item // exists in this inventory list. - bool containsItem(const ItemStack &item) const; + // If match_meta is false, only the items' names are compared. + bool containsItem(const ItemStack &item, bool match_meta) const; // Removes the given count of the given item name from // this inventory list. Walks the list in reverse order. diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index f5e76a7b6..e92197c14 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -325,8 +325,8 @@ int InvRef::l_room_for_item(lua_State *L) return 1; } -// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false -// Returns true if the list contains the given count of the given item name +// contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false +// Returns true if the list contains the given count of the given item int InvRef::l_contains_item(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -334,8 +334,11 @@ int InvRef::l_contains_item(lua_State *L) const char *listname = luaL_checkstring(L, 2); ItemStack item = read_item(L, 3, getServer(L)->idef()); InventoryList *list = getlist(L, ref, listname); - if(list){ - lua_pushboolean(L, list->containsItem(item)); + bool match_meta = false; + if (lua_isboolean(L, 4)) + match_meta = lua_toboolean(L, 4); + if (list) { + lua_pushboolean(L, list->containsItem(item, match_meta)); } else { lua_pushboolean(L, false); } diff --git a/src/script/lua_api/l_inventory.h b/src/script/lua_api/l_inventory.h index 91d41c0d0..502827a11 100644 --- a/src/script/lua_api/l_inventory.h +++ b/src/script/lua_api/l_inventory.h @@ -93,7 +93,7 @@ private: // Returns true if the item completely fits into the list static int l_room_for_item(lua_State *L); - // contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false + // contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false // Returns true if the list contains the given count of the given item name static int l_contains_item(lua_State *L); From cfa62166944f6d414d1c09756fef226dabcc5056 Mon Sep 17 00:00:00 2001 From: Zeno- Date: Tue, 20 Jun 2017 20:36:58 +1000 Subject: [PATCH 042/183] Fix console not being properly resized after window size changed (#6020) --- src/guiChatConsole.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index 5bb80bbbe..91ed91301 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -231,6 +231,7 @@ void GUIChatConsole::reformatConsole() s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt if (cols <= 0 || rows <= 0) cols = rows = 0; + recalculateConsolePosition(); m_chat_backend->reformat(cols, rows); } From 849fe19f8cc89dbe92b24e6020e3a1618a3171e2 Mon Sep 17 00:00:00 2001 From: Ezhh Date: Wed, 21 Jun 2017 06:50:57 +0100 Subject: [PATCH 043/183] Fix console resize issue when maximising game window (#6023) --- src/guiChatConsole.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index 91ed91301..ba73a58e2 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -204,8 +204,8 @@ void GUIChatConsole::draw() // scale current console height to new window size if (m_screensize.Y != 0) m_height = m_height * screensize.Y / m_screensize.Y; - m_desired_height = m_desired_height_fraction * m_screensize.Y; m_screensize = screensize; + m_desired_height = m_desired_height_fraction * m_screensize.Y; reformatConsole(); } From 0c91c65a1147049a0a97ef3120a025401f60c5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Wed, 21 Jun 2017 08:47:31 +0000 Subject: [PATCH 044/183] Fix render order of overlays (#6008) * Fix render order of overlays * Use C++11 loops * Fix time_t --- src/clientmap.cpp | 79 +++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 6cd24ffc6..d00443c62 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -290,49 +290,45 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) struct MeshBufList { - /*! - * Specifies in which layer the list is. - * All lists which are in a lower layer are rendered before this list. - */ - u8 layer; video::SMaterial m; std::vector bufs; }; struct MeshBufListList { - std::vector lists; + /*! + * 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() { - lists.clear(); + for (int l = 0; l < MAX_TILE_LAYERS; l++) + lists[l].clear(); } void add(scene::IMeshBuffer *buf, u8 layer) { + // Append to the correct layer + std::vector &list = lists[layer]; const video::SMaterial &m = buf->getMaterial(); - for(std::vector::iterator i = lists.begin(); - i != lists.end(); ++i){ - MeshBufList &l = *i; - + 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.TextureLayer[0].Texture != m.TextureLayer[0].Texture) continue; - if(l.layer != layer) - continue; - if (l.m == m) { l.bufs.push_back(buf); return; } } MeshBufList l; - l.layer = layer; l.m = m; l.bufs.push_back(buf); - lists.push_back(l); + list.push_back(l); } }; @@ -360,7 +356,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) Measuring time is very useful for long delays when the machine is swapping a lot. */ - int time1 = time(0); + std::time_t time1 = time(0); /* Get animation parameters @@ -476,35 +472,32 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } } - std::vector &lists = drawbufs.lists; + // Render all layers in order + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + std::vector &lists = drawbufs.lists[layer]; - int timecheck_counter = 0; - for (std::vector::iterator i = lists.begin(); - i != lists.end(); ++i) { - timecheck_counter++; - if (timecheck_counter > 50) { - timecheck_counter = 0; - int time2 = time(0); - if (time2 > time1 + 4) { - infostream << "ClientMap::renderMap(): " - "Rendering takes ages, returning." - << std::endl; - return; + int timecheck_counter = 0; + for (MeshBufList &list : lists) { + timecheck_counter++; + if (timecheck_counter > 50) { + timecheck_counter = 0; + std::time_t time2 = time(0); + if (time2 > time1 + 4) { + infostream << "ClientMap::renderMap(): " + "Rendering takes ages, returning." + << std::endl; + return; + } + } + + driver->setMaterial(list.m); + + for (scene::IMeshBuffer *buf : list.bufs) { + driver->drawMeshBuffer(buf); + vertex_count += buf->getVertexCount(); + meshbuffer_count++; } } - - MeshBufList &list = *i; - - driver->setMaterial(list.m); - - for (std::vector::iterator j = list.bufs.begin(); - j != list.bufs.end(); ++j) { - scene::IMeshBuffer *buf = *j; - driver->drawMeshBuffer(buf); - vertex_count += buf->getVertexCount(); - meshbuffer_count++; - } - } } // ScopeProfiler From 6e0557e20c7347f2196c8c76f84d647f8699ec78 Mon Sep 17 00:00:00 2001 From: paramat Date: Fri, 23 Jun 2017 21:49:26 +0100 Subject: [PATCH 045/183] Mgv7: Avoid divide-by-zero errors Some settings of paramters can cause mgv7 variables to be -inf, nan or -nan. This can cause massive vertical columns of water to appear above sea level. --- src/mapgen_v7.cpp | 14 ++++++++------ src/mapgen_v7.h | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 5e9bc4aa3..ae500d5f8 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -57,7 +57,7 @@ MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge) this->spflags = params->spflags; this->cave_width = params->cave_width; this->float_mount_density = params->float_mount_density; - this->float_mount_height = params->float_mount_height; + float_mount_height_lim = MYMAX(params->float_mount_height, 1.0f); this->floatland_level = params->floatland_level; this->shadow_limit = params->shadow_limit; this->cavern_limit = params->cavern_limit; @@ -376,7 +376,8 @@ float MapgenV7::baseTerrainLevelFromMap(int index) bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z) { - float mnt_h_n = NoisePerlin2D(&noise_mount_height->np, x, z, seed); + float mnt_h_n = + MYMAX(NoisePerlin2D(&noise_mount_height->np, x, z, seed), 1.0f); float density_gradient = -((float)y / mnt_h_n); float mnt_n = NoisePerlin3D(&noise_mountain->np, x, y, z, seed); @@ -386,7 +387,7 @@ bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z) bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y) { - float mounthn = noise_mount_height->result[idx_xz]; + float mounthn = MYMAX(noise_mount_height->result[idx_xz], 1.0f); float density_gradient = -((float)y / mounthn); float mountn = noise_mountain->result[idx_xyz]; @@ -398,8 +399,8 @@ bool MapgenV7::getFloatlandMountainFromMap(int idx_xyz, int idx_xz, s16 y) { // Make rim 2 nodes thick to match floatland base terrain float density_gradient = (y >= floatland_level) ? - -pow((float)(y - floatland_level) / float_mount_height, 0.75f) : - -pow((float)(floatland_level - 1 - y) / float_mount_height, 0.75f); + -pow((float)(y - floatland_level) / float_mount_height_lim, 0.75f) : + -pow((float)(floatland_level - 1 - y) / float_mount_height_lim, 0.75f); float floatn = noise_mountain->result[idx_xyz] + float_mount_density; @@ -415,7 +416,8 @@ void MapgenV7::floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max, float n_base = noise_floatland_base->result[idx_xz]; if (n_base > 0.0f) { - float n_base_height = noise_float_base_height->result[idx_xz]; + float n_base_height = + MYMAX(noise_float_base_height->result[idx_xz], 1.0f); float amp = n_base * n_base_height; float ridge = n_base_height / 3.0f; base_min = floatland_level - amp / 1.5f; diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index a69170057..2b79cce51 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -103,6 +103,8 @@ private: Noise *noise_float_base_height; Noise *noise_mountain; Noise *noise_ridge; + + float float_mount_height_lim; }; #endif From a08a93bc9c64ee426132424770fc28bf15457db9 Mon Sep 17 00:00:00 2001 From: paramat Date: Sun, 25 Jun 2017 04:45:40 +0100 Subject: [PATCH 046/183] Mgv7: Clean up divide-by-zero fix --- src/mapgen_v7.cpp | 9 ++++++--- src/mapgen_v7.h | 2 -- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index ae500d5f8..3ac099d32 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -57,13 +57,16 @@ MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge) this->spflags = params->spflags; this->cave_width = params->cave_width; this->float_mount_density = params->float_mount_density; - float_mount_height_lim = MYMAX(params->float_mount_height, 1.0f); this->floatland_level = params->floatland_level; this->shadow_limit = params->shadow_limit; this->cavern_limit = params->cavern_limit; this->cavern_taper = params->cavern_taper; this->cavern_threshold = params->cavern_threshold; + // This is to avoid a divide-by-zero. + // Parameter will be saved to map_meta.txt in limited form. + params->float_mount_height = MYMAX(params->float_mount_height, 1.0f); + // 2D noise noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Z); noise_terrain_alt = new Noise(¶ms->np_terrain_alt, seed, csize.X, csize.Z); @@ -399,8 +402,8 @@ bool MapgenV7::getFloatlandMountainFromMap(int idx_xyz, int idx_xz, s16 y) { // Make rim 2 nodes thick to match floatland base terrain float density_gradient = (y >= floatland_level) ? - -pow((float)(y - floatland_level) / float_mount_height_lim, 0.75f) : - -pow((float)(floatland_level - 1 - y) / float_mount_height_lim, 0.75f); + -pow((float)(y - floatland_level) / float_mount_height, 0.75f) : + -pow((float)(floatland_level - 1 - y) / float_mount_height, 0.75f); float floatn = noise_mountain->result[idx_xyz] + float_mount_density; diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index 2b79cce51..a69170057 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -103,8 +103,6 @@ private: Noise *noise_float_base_height; Noise *noise_mountain; Noise *noise_ridge; - - float float_mount_height_lim; }; #endif From 46ff2e2cefe55d3511da06c210ea16cbbc1c9ee3 Mon Sep 17 00:00:00 2001 From: Jesse McDonald Date: Tue, 27 Jun 2017 05:34:11 -0500 Subject: [PATCH 047/183] Fix for empty key/value when reading item string with wear but no metadata (#6058) --- src/itemstackmetadata.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp index c3d602245..65829fd68 100644 --- a/src/itemstackmetadata.cpp +++ b/src/itemstackmetadata.cpp @@ -28,16 +28,18 @@ void ItemStackMetadata::deSerialize(std::istream &is) m_stringvars.clear(); - if (!in.empty() && in[0] == DESERIALIZE_START) { - Strfnd fnd(in); - fnd.to(1); - while (!fnd.at_end()) { - std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR); - std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR); - m_stringvars[name] = var; + if (!in.empty()) { + if (in[0] == DESERIALIZE_START) { + Strfnd fnd(in); + fnd.to(1); + while (!fnd.at_end()) { + std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR); + std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR); + m_stringvars[name] = var; + } + } else { + // BACKWARDS COMPATIBILITY + m_stringvars[""] = in; } - } else { - // BACKWARDS COMPATIBILITY - m_stringvars[""] = in; } } From 26d0753d870b0b0e2c5e03c817a4c2bdb3eebfc9 Mon Sep 17 00:00:00 2001 From: paramat Date: Wed, 28 Jun 2017 09:35:46 +0100 Subject: [PATCH 048/183] Mgv7: Fix undefined 'float_mount_height' Commit cad10ce3b747b721fd63784915e05f12bc488128 altered the parameter 'float_mount_height' but was missing the necessary line in the constructor to get the altered value from 'params'. Fixes 3D floatland terrain generating everywhere. --- src/mapgen_v7.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 3ac099d32..44a42948b 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -66,6 +66,7 @@ MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge) // This is to avoid a divide-by-zero. // Parameter will be saved to map_meta.txt in limited form. params->float_mount_height = MYMAX(params->float_mount_height, 1.0f); + this->float_mount_height = params->float_mount_height; // 2D noise noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Z); From c352ff71e86fd3d40414cbe3a374c9563495ca1a Mon Sep 17 00:00:00 2001 From: stujones11 Date: Wed, 7 Jun 2017 18:52:38 +0100 Subject: [PATCH 049/183] Tile material: Add 'TILE_MATERIAL_OPAQUE', use for drawtype 'NDT_NORMAL' Prevents normal drawtype nodes having transparency. Avoids clients cheating by using 'x-ray' texture packs with transparent textures. --- src/client/tile.h | 10 ++++++---- src/nodedef.cpp | 16 ++++++++++++++-- src/shader.cpp | 34 +++++++++++++++------------------- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/client/tile.h b/src/client/tile.h index 15854fb71..66ca8be1d 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -159,7 +159,8 @@ enum MaterialType{ TILE_MATERIAL_LIQUID_TRANSPARENT, TILE_MATERIAL_LIQUID_OPAQUE, TILE_MATERIAL_WAVING_LEAVES, - TILE_MATERIAL_WAVING_PLANTS + TILE_MATERIAL_WAVING_PLANTS, + TILE_MATERIAL_OPAQUE }; // Material flags @@ -243,6 +244,10 @@ struct TileLayer void applyMaterialOptions(video::SMaterial &material) const { switch (material_type) { + case TILE_MATERIAL_OPAQUE: + case TILE_MATERIAL_LIQUID_OPAQUE: + material.MaterialType = video::EMT_SOLID; + break; case TILE_MATERIAL_BASIC: case TILE_MATERIAL_WAVING_LEAVES: case TILE_MATERIAL_WAVING_PLANTS: @@ -252,9 +257,6 @@ struct TileLayer case TILE_MATERIAL_LIQUID_TRANSPARENT: material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; - case TILE_MATERIAL_LIQUID_OPAQUE: - material.MaterialType = video::EMT_SOLID; - break; } material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false; diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 98b34ea9e..17162b344 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -682,6 +682,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc switch (drawtype) { default: case NDT_NORMAL: + material_type = (alpha == 255) ? + TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA; solidness = 2; break; case NDT_AIRLIKE: @@ -778,6 +780,16 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc tile_shader[j] = shdsrc->getShader("nodes_shader", material_type, drawtype); } + u8 overlay_material = material_type; + if (overlay_material == TILE_MATERIAL_OPAQUE) + overlay_material = TILE_MATERIAL_BASIC; + else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE) + overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT; + u32 overlay_shader[6]; + for (u16 j = 0; j < 6; j++) { + overlay_shader[j] = shdsrc->getShader("nodes_shader", + overlay_material, drawtype); + } // Tiles (fill in f->tiles[]) for (u16 j = 0; j < 6; j++) { @@ -786,8 +798,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc tdef[j].backface_culling, material_type); if (tdef_overlay[j].name != "") fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j], - tile_shader[j], tsettings.use_normal_texture, - tdef[j].backface_culling, material_type); + overlay_shader[j], tsettings.use_normal_texture, + tdef[j].backface_culling, overlay_material); } // Special tiles (fill in f->special_tiles[]) diff --git a/src/shader.cpp b/src/shader.cpp index 66f32c9a1..f52ca69ef 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -535,24 +535,19 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaderinfo.material_type = material_type; shaderinfo.drawtype = drawtype; shaderinfo.material = video::EMT_SOLID; - switch(material_type){ - case TILE_MATERIAL_BASIC: - shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - break; - case TILE_MATERIAL_ALPHA: - shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - break; - case TILE_MATERIAL_LIQUID_TRANSPARENT: - shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - break; - case TILE_MATERIAL_LIQUID_OPAQUE: - shaderinfo.base_material = video::EMT_SOLID; - break; - case TILE_MATERIAL_WAVING_LEAVES: - shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - break; - case TILE_MATERIAL_WAVING_PLANTS: - shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + switch (material_type) { + case TILE_MATERIAL_OPAQUE: + case TILE_MATERIAL_LIQUID_OPAQUE: + shaderinfo.base_material = video::EMT_SOLID; + break; + case TILE_MATERIAL_ALPHA: + case TILE_MATERIAL_LIQUID_TRANSPARENT: + shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + break; + case TILE_MATERIAL_BASIC: + case TILE_MATERIAL_WAVING_LEAVES: + case TILE_MATERIAL_WAVING_PLANTS: + shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; break; } @@ -646,7 +641,8 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp "TILE_MATERIAL_LIQUID_TRANSPARENT", "TILE_MATERIAL_LIQUID_OPAQUE", "TILE_MATERIAL_WAVING_LEAVES", - "TILE_MATERIAL_WAVING_PLANTS" + "TILE_MATERIAL_WAVING_PLANTS", + "TILE_MATERIAL_OPAQUE" }; for (int i = 0; i < 6; i++){ From ebf9dda2e6150e07a1e55c376043a34e6b4c70aa Mon Sep 17 00:00:00 2001 From: stujones11 Date: Sat, 1 Jul 2017 17:01:07 +0100 Subject: [PATCH 050/183] Include TILE_MATERIAL_OPAQUE in shaders header (#6086) --- src/shader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shader.cpp b/src/shader.cpp index f52ca69ef..e525aa0aa 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -645,7 +645,7 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp "TILE_MATERIAL_OPAQUE" }; - for (int i = 0; i < 6; i++){ + for (int i = 0; i < 7; i++){ shaders_header += "#define "; shaders_header += materialTypes[i]; shaders_header += " "; From 070ab6654ab4d8ab4a15a99698666f27552d6937 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 1 Apr 2017 20:38:14 +0200 Subject: [PATCH 051/183] Sneak: Stripped down version Fix taking damage caused by sneaking over a nodebox gap. Fix strange behaviour on stair nodeboxes. Enable jumping from node edges while sneaking. Enable movement around corners while sneaking on a 1-node-high groove in a wall. --- src/localplayer.cpp | 400 +++++++++++++++++++------------------------- src/localplayer.h | 104 ++++++------ 2 files changed, 223 insertions(+), 281 deletions(-) diff --git a/src/localplayer.cpp b/src/localplayer.cpp index b587f7bbb..ace0b992f 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -67,13 +67,6 @@ LocalPlayer::LocalPlayer(Client *client, const char *name): hurt_tilt_timer(0.0f), hurt_tilt_strength(0.0f), m_position(0,0,0), - m_sneak_node(32767,32767,32767), - m_sneak_node_bb_ymax(0), // To support temporary option for old move code - m_sneak_node_bb_top(0,0,0,0,0,0), - m_sneak_node_exists(false), - m_need_to_get_new_sneak_node(true), - m_sneak_ladder_detected(false), - m_ledge_detected(false), m_old_node_below(32767,32767,32767), m_old_node_below_type("air"), m_can_jump(false), @@ -96,88 +89,134 @@ LocalPlayer::~LocalPlayer() { } -static aabb3f getTopBoundingBox(const std::vector &nodeboxes) +static aabb3f getNodeBoundingBox(const std::vector &nodeboxes) { + if (nodeboxes.size() == 0) + return aabb3f(0, 0, 0, 0, 0, 0); + aabb3f b_max; - b_max.reset(-BS, -BS, -BS); - for (std::vector::const_iterator it = nodeboxes.begin(); - it != nodeboxes.end(); ++it) { - aabb3f box = *it; - if (box.MaxEdge.Y > b_max.MaxEdge.Y) - b_max = box; - else if (box.MaxEdge.Y == b_max.MaxEdge.Y) - b_max.addInternalBox(box); - } - return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge); + + std::vector::const_iterator it = nodeboxes.begin(); + b_max = aabb3f(it->MinEdge, it->MaxEdge); + + ++it; + for (; it != nodeboxes.end(); ++it) + b_max.addInternalBox(*it); + + return b_max; } -#define GETNODE(map, p3, v2, y, valid) \ - (map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid) - -// pos is the node the player is standing inside(!) -static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos) +bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, + const v3f &sneak_max) { - // Detects a structure known as "sneak ladder" or "sneak elevator" - // that relies on bugs to provide a fast means of vertical transportation, - // the bugs have since been fixed but this function remains to keep it working. - // NOTE: This is just entirely a huge hack and causes way too many problems. - bool is_valid_position; + static const v3s16 dir9_center[9] = { + v3s16( 0, 0, 0), + v3s16( 1, 0, 0), + v3s16(-1, 0, 0), + v3s16( 0, 0, 1), + v3s16( 0, 0, -1), + v3s16( 1, 0, 1), + v3s16(-1, 0, 1), + v3s16( 1, 0, -1), + v3s16(-1, 0, -1) + }; + + INodeDefManager *nodemgr = m_client->ndef(); MapNode node; - // X/Z vectors for 4 neighboring nodes - static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) }; + bool is_valid_position; + bool new_sneak_node_exists = m_sneak_node_exists; - for (u16 i = 0; i < ARRLEN(vecs); i++) { - const v2s16 vec = vecs[i]; + // We want the top of the sneak node to be below the players feet + f32 position_y_mod = 0.05 * BS; + if (m_sneak_node_exists) + position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod; - // walkability of bottom & top node should differ - node = GETNODE(map, pos, vec, 0, &is_valid_position); - if (!is_valid_position) - continue; - bool w = nodemgr->get(node).walkable; - node = GETNODE(map, pos, vec, 1, &is_valid_position); - if (!is_valid_position || w == nodemgr->get(node).walkable) + // Get position of current standing node + const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS); + + if (current_node != m_sneak_node) { + new_sneak_node_exists = false; + } else { + node = map->getNodeNoEx(current_node, &is_valid_position); + if (!is_valid_position || !nodemgr->get(node).walkable) + new_sneak_node_exists = false; + } + + // Keep old sneak node + if (new_sneak_node_exists) + return true; + + // Get new sneak node + m_sneak_ladder_detected = false; + f32 min_distance_f = 100000.0 * BS; + + for (s16 d = 0; d < 9; d++) { + const v3s16 p = current_node + dir9_center[d]; + const v3f pf = intToFloat(p, BS); + const v2f diff(position.X - pf.X, position.Z - pf.Z); + f32 distance_f = diff.getLength(); + + if (distance_f > min_distance_f || + fabs(diff.X) > (.5 + .1) * BS + sneak_max.X || + fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z) continue; - // check one more node above OR below with corresponding walkability - node = GETNODE(map, pos, vec, -1, &is_valid_position); - bool ok = is_valid_position && w != nodemgr->get(node).walkable; - if (!ok) { - node = GETNODE(map, pos, vec, 2, &is_valid_position); - ok = is_valid_position && w == nodemgr->get(node).walkable; + + // The node to be sneaked on has to be walkable + node = map->getNodeNoEx(p, &is_valid_position); + if (!is_valid_position || !nodemgr->get(node).walkable) + continue; + // And the node(s) above have to be nonwalkable + bool ok = true; + if (!physics_override_sneak_glitch) { + u16 height = ceilf( + (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS + ); + for (u16 y = 1; y <= height; y++) { + node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position); + if (!is_valid_position || nodemgr->get(node).walkable) { + ok = false; + break; + } + } + } else { + // legacy behaviour: check just one node + node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position); + ok = is_valid_position && !nodemgr->get(node).walkable; } + if (!ok) + continue; - if (ok) - return true; + min_distance_f = distance_f; + m_sneak_node = p; + new_sneak_node_exists = true; } - return false; -} + if (!new_sneak_node_exists) + return false; -static bool detectLedge(Map *map, INodeDefManager *nodemgr, v3s16 pos) -{ - bool is_valid_position; - MapNode node; - // X/Z vectors for 4 neighboring nodes - static const v2s16 vecs[] = {v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1)}; + // Update saved top bounding box of sneak node + node = map->getNodeNoEx(m_sneak_node); + std::vector nodeboxes; + node.getCollisionBoxes(nodemgr, &nodeboxes); + m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes); - for (u16 i = 0; i < ARRLEN(vecs); i++) { - const v2s16 vec = vecs[i]; - - node = GETNODE(map, pos, vec, 1, &is_valid_position); + if (physics_override_sneak_glitch) { + // Detect sneak ladder: + // Node two meters above sneak node must be solid + node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0), + &is_valid_position); if (is_valid_position && nodemgr->get(node).walkable) { - // Ledge exists - node = GETNODE(map, pos, vec, 2, &is_valid_position); - if (is_valid_position && !nodemgr->get(node).walkable) - // Space above ledge exists - return true; + // Node three meters above: must be non-solid + node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0), + &is_valid_position); + m_sneak_ladder_detected = is_valid_position && + !nodemgr->get(node).walkable; } } - - return false; + return true; } -#undef GETNODE - void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector *collision_info) { @@ -193,10 +232,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, v3f position = getPosition(); // Copy parent position if local player is attached - if(isAttached) - { + if (isAttached) { setPosition(overridePosition); - m_sneak_node_exists = false; return; } @@ -208,7 +245,6 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if (free_move) { position += m_speed * dtime; setPosition(position); - m_sneak_node_exists = false; return; } @@ -279,7 +315,6 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, || nodemgr->get(node2.getContent()).climbable) && !free_move; } - /* Collision uncertainty radius Make it a bit larger than the maximum distance of movement @@ -291,59 +326,6 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, // This should always apply, otherwise there are glitches sanity_check(d > pos_max_d); - // Max. distance (X, Z) over border for sneaking determined by collision box - // * 0.49 to keep the center just barely on the node - v3f sneak_max = m_collisionbox.getExtent() * 0.49; - if (m_sneak_ladder_detected) { - // restore legacy behaviour (this makes the m_speed.Y hack necessary) - sneak_max = v3f(0.4 * BS, 0, 0.4 * BS); - } - - /* - If sneaking, keep in range from the last walked node and don't - fall off from it - */ - if (control.sneak && m_sneak_node_exists && - !(fly_allowed && g_settings->getBool("free_move")) && - !in_liquid && !is_climbing && - physics_override_sneak) { - const v3f sn_f = intToFloat(m_sneak_node, BS); - const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge; - const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge; - const v3f old_pos = position; - const v3f old_speed = m_speed; - - position.X = rangelim(position.X, - bmin.X - sneak_max.X, bmax.X + sneak_max.X); - position.Z = rangelim(position.Z, - bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z); - - if (position.X != old_pos.X) - m_speed.X = 0; - if (position.Z != old_pos.Z) - m_speed.Z = 0; - - // Because we keep the player collision box on the node, limiting - // position.Y is not necessary but useful to prevent players from - // being inside a node if sneaking on e.g. the lower part of a stair - if (!m_sneak_ladder_detected) { - position.Y = MYMAX(position.Y, bmax.Y); - } else { - // legacy behaviour that sometimes causes some weird slow sinking - m_speed.Y = MYMAX(m_speed.Y, 0); - } - - if (collision_info != NULL && - m_speed.Y - old_speed.Y > BS) { - // Collide with sneak node, report fall damage - CollisionInfo sn_info; - sn_info.node_p = m_sneak_node; - sn_info.old_speed = old_speed; - sn_info.new_speed = m_speed; - collision_info->push_back(sn_info); - } - } - // TODO: this shouldn't be hardcoded but transmitted from server float player_stepheight = (touching_ground) ? (BS * 0.6f) : (BS * 0.2f); @@ -357,6 +339,10 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, pos_max_d, m_collisionbox, player_stepheight, dtime, &position, &m_speed, accel_f); + bool could_sneak = control.sneak && + !(fly_allowed && g_settings->getBool("free_move")) && + !in_liquid && !is_climbing && + physics_override_sneak; /* If the player's feet touch the topside of any node, this is set to true. @@ -365,110 +351,78 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, */ bool touching_ground_was = touching_ground; touching_ground = result.touching_ground; + bool sneak_can_jump = false; - // We want the top of the sneak node to be below the players feet - f32 position_y_mod; - if (m_sneak_node_exists) - position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS; - else - position_y_mod = (0.5 - 0.05) * BS; - v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS); - /* - Check the nodes under the player to see from which node the - player is sneaking from, if any. If the node from under - the player has been removed, the player falls. - */ - if (m_sneak_node_exists && - nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" && - m_old_node_below_type != "air") { - // Old node appears to have been removed; that is, - // it wasn't air before but now it is - m_need_to_get_new_sneak_node = false; - m_sneak_node_exists = false; - } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") { - // We are on something, so make sure to recalculate the sneak - // node. - m_need_to_get_new_sneak_node = true; + // Max. distance (X, Z) over border for sneaking determined by collision box + // * 0.49 to keep the center just barely on the node + v3f sneak_max = m_collisionbox.getExtent() * 0.49; + + if (m_sneak_ladder_detected) { + // restore legacy behaviour (this makes the m_speed.Y hack necessary) + sneak_max = v3f(0.4 * BS, 0, 0.4 * BS); } - if (m_need_to_get_new_sneak_node && physics_override_sneak) { - v2f player_p2df(position.X, position.Z); - f32 min_distance_f = 100000.0 * BS; - v3s16 new_sneak_node = m_sneak_node; - for(s16 x=-1; x<=1; x++) - for(s16 z=-1; z<=1; z++) - { - v3s16 p = current_node + v3s16(x,0,z); - v3f pf = intToFloat(p, BS); - v2f node_p2df(pf.X, pf.Z); - f32 distance_f = player_p2df.getDistanceFrom(node_p2df); + /* + If sneaking, keep on top of last walked node and don't fall off + */ + if (could_sneak && m_sneak_node_exists) { + const v3f sn_f = intToFloat(m_sneak_node, BS); + const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge; + const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge; + const v3f old_pos = position; + const v3f old_speed = m_speed; + f32 y_diff = bmax.Y - position.Y; - if (distance_f > min_distance_f || - fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X || - fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z) - continue; + // (BS * 0.6f) is the basic stepheight while standing on ground + if (y_diff < BS * 0.6f) { + // Only center player when they're on the node + position.X = rangelim(position.X, + bmin.X - sneak_max.X, bmax.X + sneak_max.X); + position.Z = rangelim(position.Z, + bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z); - - // The node to be sneaked on has to be walkable - node = map->getNodeNoEx(p, &is_valid_position); - if (!is_valid_position || !nodemgr->get(node).walkable) - continue; - // And the node(s) above have to be nonwalkable - bool ok = true; - if (!physics_override_sneak_glitch) { - u16 height = ceilf( - (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS - ); - for (u16 y = 1; y <= height; y++) { - node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position); - if (!is_valid_position || nodemgr->get(node).walkable) { - ok = false; - break; - } - } - } else { - // legacy behaviour: check just one node - node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position); - ok = is_valid_position && !nodemgr->get(node).walkable; - } - if (!ok) - continue; - - min_distance_f = distance_f; - new_sneak_node = p; + if (position.X != old_pos.X) + m_speed.X = 0; + if (position.Z != old_pos.Z) + m_speed.Z = 0; } - bool sneak_node_found = (min_distance_f < 100000.0 * BS); - m_sneak_node = new_sneak_node; - m_sneak_node_exists = sneak_node_found; + if (y_diff > 0 && m_speed.Y < 0 && + (physics_override_sneak_glitch || y_diff < BS * 0.6f)) { + // Move player to the maximal height when falling or when + // the ledge is climbed on the next step. + position.Y = bmax.Y; + m_speed.Y = 0; + } - if (sneak_node_found) { - // Update saved top bounding box of sneak node - MapNode n = map->getNodeNoEx(m_sneak_node); - std::vector nodeboxes; - n.getCollisionBoxes(nodemgr, &nodeboxes); - m_sneak_node_bb_top = getTopBoundingBox(nodeboxes); + // Allow jumping on node edges while sneaking + if (m_speed.Y == 0 || m_sneak_ladder_detected) + sneak_can_jump = true; - m_sneak_ladder_detected = physics_override_sneak_glitch && - detectSneakLadder(map, nodemgr, floatToInt(position, BS)); - } else { - m_sneak_ladder_detected = false; + if (collision_info != NULL && + m_speed.Y - old_speed.Y > BS) { + // Collide with sneak node, report fall damage + CollisionInfo sn_info; + sn_info.node_p = m_sneak_node; + sn_info.old_speed = old_speed; + sn_info.new_speed = m_speed; + collision_info->push_back(sn_info); } } /* - If 'sneak glitch' enabled detect ledge for old sneak-jump - behaviour of climbing onto a ledge 2 nodes up. + Find the next sneak node if necessary */ - if (physics_override_sneak_glitch && control.sneak && control.jump) - m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS)); + bool new_sneak_node_exists = false; + + if (could_sneak) + new_sneak_node_exists = updateSneakNode(map, position, sneak_max); /* Set new position but keep sneak node set */ - bool sneak_node_exists = m_sneak_node_exists; setPosition(position); - m_sneak_node_exists = sneak_node_exists; + m_sneak_node_exists = new_sneak_node_exists; /* Report collisions @@ -501,22 +455,15 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, } } - /* - Update the node last under the player - */ - m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS); - m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name; - /* Check properties of the node on which the player is standing */ const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos())); // Determine if jumping is possible - m_can_jump = touching_ground && !in_liquid; + m_can_jump = (touching_ground && !in_liquid && !is_climbing) + || sneak_can_jump; if (itemgroup_get(f.groups, "disable_jump")) m_can_jump = false; - else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing) - m_can_jump = true; // Jump key pressed while jumping off from a bouncy block if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") && @@ -705,17 +652,8 @@ void LocalPlayer::applyControl(float dtime) */ v3f speedJ = getSpeed(); if(speedJ.Y >= -0.5 * BS) { - if (m_ledge_detected) { - // Limit jump speed to a minimum that allows - // jumping up onto a ledge 2 nodes up. - speedJ.Y = movement_speed_jump * - MYMAX(physics_override_jump, 1.3f); - setSpeed(speedJ); - m_ledge_detected = false; - } else { - speedJ.Y = movement_speed_jump * physics_override_jump; - setSpeed(speedJ); - } + speedJ.Y = movement_speed_jump * physics_override_jump; + setSpeed(speedJ); MtEvent *e = new SimpleTriggerEvent("PlayerJump"); m_client->event()->put(e); diff --git a/src/localplayer.h b/src/localplayer.h index 9cbefae23..32714ff32 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "player.h" #include "environment.h" +#include "constants.h" #include class Client; @@ -44,27 +45,29 @@ public: LocalPlayer(Client *client, const char *name); virtual ~LocalPlayer(); - ClientActiveObject *parent; + ClientActiveObject *parent = nullptr; - u16 hp; - bool isAttached; - bool touching_ground; + // Initialize hp to 0, so that no hearts will be shown if server + // doesn't support health points + u16 hp = 0; + bool isAttached = false; + bool touching_ground = false; // This oscillates so that the player jumps a bit above the surface - bool in_liquid; + bool in_liquid = false; // This is more stable and defines the maximum speed of the player - bool in_liquid_stable; + bool in_liquid_stable = false; // Gets the viscosity of water to calculate friction - u8 liquid_viscosity; - bool is_climbing; - bool swimming_vertical; + u8 liquid_viscosity = 0; + bool is_climbing = false; + bool swimming_vertical = false; - float physics_override_speed; - float physics_override_jump; - float physics_override_gravity; - bool physics_override_sneak; - bool physics_override_sneak_glitch; + float physics_override_speed = 1.0f; + float physics_override_jump = 1.0f; + float physics_override_gravity = 1.0f; + bool physics_override_sneak = true; + bool physics_override_sneak_glitch = false; // Temporary option for old move code - bool physics_override_new_move; + bool physics_override_new_move = true; v3f overridePosition; @@ -83,32 +86,32 @@ public: // Used to check if anything changed and prevent sending packets if not v3f last_position; v3f last_speed; - float last_pitch; - float last_yaw; - unsigned int last_keyPressed; - u8 last_camera_fov; - u8 last_wanted_range; + float last_pitch = 0.0f; + float last_yaw = 0.0f; + unsigned int last_keyPressed = 0; + u8 last_camera_fov = 0; + u8 last_wanted_range = 0; - float camera_impact; + float camera_impact = 0.0f; - bool makes_footstep_sound; + bool makes_footstep_sound = true; - int last_animation; + int last_animation = NO_ANIM; float last_animation_speed; - std::string hotbar_image; - std::string hotbar_selected_image; + std::string hotbar_image = ""; + std::string hotbar_selected_image = ""; - video::SColor light_color; + video::SColor light_color = video::SColor(255, 255, 255, 255); - float hurt_tilt_timer; - float hurt_tilt_strength; + float hurt_tilt_timer = 0.0f; + float hurt_tilt_strength = 0.0f; GenericCAO *getCAO() const { return m_cao; } void setCAO(GenericCAO *toset) { - assert(m_cao == NULL); // Pre-condition + assert(!m_cao); // Pre-condition m_cao = toset; } @@ -140,38 +143,39 @@ public: private: void accelerateHorizontal(const v3f &target_speed, const f32 max_increase); void accelerateVertical(const v3f &target_speed, const f32 max_increase); + bool updateSneakNode(Map *map, const v3f &position, const v3f &sneak_max); v3f m_position; - v3s16 m_sneak_node; - // Stores the max player uplift by m_sneak_node - // To support temporary option for old move code - f32 m_sneak_node_bb_ymax; + v3s16 m_sneak_node = v3s16(32767, 32767, 32767); // Stores the top bounding box of m_sneak_node - aabb3f m_sneak_node_bb_top; + aabb3f m_sneak_node_bb_top = aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); // Whether the player is allowed to sneak - bool m_sneak_node_exists; - // Whether recalculation of m_sneak_node and its top bbox is needed - bool m_need_to_get_new_sneak_node; + bool m_sneak_node_exists = false; // Whether a "sneak ladder" structure is detected at the players pos // see detectSneakLadder() in the .cpp for more info (always false if disabled) - bool m_sneak_ladder_detected; - // Whether a 2-node-up ledge is detected at the players pos, - // see detectLedge() in the .cpp for more info (always false if disabled). - bool m_ledge_detected; + bool m_sneak_ladder_detected = false; + // ***** Variables for temporary option of the old move code ***** + // Stores the max player uplift by m_sneak_node + f32 m_sneak_node_bb_ymax = 0.0f; + // Whether recalculation of m_sneak_node and its top bbox is needed + bool m_need_to_get_new_sneak_node = true; // Node below player, used to determine whether it has been removed, // and its old type - v3s16 m_old_node_below; - std::string m_old_node_below_type; - bool m_can_jump; - u16 m_breath; - f32 m_yaw; - f32 m_pitch; - bool camera_barely_in_ceiling; - aabb3f m_collisionbox; + v3s16 m_old_node_below = v3s16(32767, 32767, 32767); + std::string m_old_node_below_type = "air"; + // ***** End of variables for temporary option ***** - GenericCAO *m_cao; + bool m_can_jump = false; + u16 m_breath = PLAYER_MAX_BREATH; + f32 m_yaw = 0.0f; + f32 m_pitch = 0.0f; + bool camera_barely_in_ceiling = false; + aabb3f m_collisionbox = aabb3f(-BS * 0.30f, 0.0f, -BS * 0.30f, BS * 0.30f, + BS * 1.75f, BS * 0.30f); + + GenericCAO *m_cao = nullptr; Client *m_client; }; From 4be7d8b43a68664b6c349918ab79174ec6a3a24c Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 29 Jul 2017 19:01:14 +0200 Subject: [PATCH 052/183] Noise: Prevent unittest crash caused by division by zero --- src/noise.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/noise.cpp b/src/noise.cpp index b918c9936..abd29f3ec 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -130,7 +130,9 @@ s32 PcgRandom::range(s32 min, s32 max) if (max < min) throw PrngException("Invalid range (max < min)"); - u32 bound = max - min + 1; + // We have to cast to s64 because otherwise this could overflow, + // and signed overflow is undefined behavior. + u32 bound = (s64)max - (s64)min + 1; return range(bound) + min; } From e5311a4d565dece751745f859ac1b8d92da67564 Mon Sep 17 00:00:00 2001 From: Juozas Pocius Date: Wed, 2 Aug 2017 16:40:38 +0300 Subject: [PATCH 053/183] Fix crash when using --go in command line --- src/client/clientlauncher.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index 4ff77bc03..3d82b9cdc 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -81,7 +81,7 @@ protected: scene::ISceneManager *smgr; SubgameSpec gamespec; WorldSpec worldspec; - bool simple_singleplayer_mode; + bool simple_singleplayer_mode = false; // These are set up based on the menu and other things // TODO: Are these required since there's already playername, password, etc From 90a9e4e69fac32f368b275428fa42407ea9b7883 Mon Sep 17 00:00:00 2001 From: Jens Rottmann <30634967+JRottm@users.noreply.github.com> Date: Fri, 4 Aug 2017 21:48:32 +0200 Subject: [PATCH 054/183] Fix player coordinate rounding in collisionMoveSimple() (#6197) To determine the area (nodes) where a player movement took place collisionMoveSimple() first took the old/new player coordinates and rounded them to integers, then added the player character's collision box and implicitely rounded the result. This has 2 problems: Rounding the position and the box seperately, then adding the resulting integers means you get twice the rounding error. And implicit rounding always rounds towards 0.0, unlike floatToInt(), which rounds towards the closest integer. Previous (simplified) behavior: round(pos)+(int)box, for example player at Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2. ==> A character's height of 1.75m always got rounded down to 1m, its width of +/-0.3 even became 0. Fixed by adding the floats first, then rounding properly: round(pos+box) = round(0.9+1.75) = round(2.65) = 3. --- src/collision.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/collision.cpp b/src/collision.cpp index c0891c152..c9a945916 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -258,20 +258,25 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, //TimeTaker tt2("collisionMoveSimple collect boxes"); ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG); - v3s16 oldpos_i = floatToInt(*pos_f, BS); - v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS); - s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1; - s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1; - s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1; - s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1; - s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1; - s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1; + v3f newpos_f = *pos_f + *speed_f * dtime; + v3f minpos_f( + MYMIN(pos_f->X, newpos_f.X), + MYMIN(pos_f->Y, newpos_f.Y), + MYMIN(pos_f->Z, newpos_f.Z) + ); + v3f maxpos_f( + MYMAX(pos_f->X, newpos_f.X), + MYMAX(pos_f->Y, newpos_f.Y), + MYMAX(pos_f->Z, newpos_f.Z) + ); + v3s16 min = floatToInt(minpos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1); + v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1); bool any_position_valid = false; - for(s16 x = min_x; x <= max_x; x++) - for(s16 y = min_y; y <= max_y; y++) - for(s16 z = min_z; z <= max_z; z++) + for(s16 x = min.X; x <= max.X; x++) + for(s16 y = min.Y; y <= max.Y; y++) + for(s16 z = min.Z; z <= max.Z; z++) { v3s16 p(x,y,z); From b3ffe675c33104936dc3914d482942d5685f645e Mon Sep 17 00:00:00 2001 From: Jens Rottmann <30634967+JRottm@users.noreply.github.com> Date: Sat, 5 Aug 2017 01:42:39 +0200 Subject: [PATCH 055/183] Add tiny Y offset in collisionMoveSimple() to tweak performance Another small general problem: the player is always standing exactly on the bondary between 2 nodes e.g. Y=1.5 is exactly between nodes Y=1 and Y=2. floatToInt() and myround() will round +/-n.5 always 'outwards' to +/-(n+1), which means they behave differently depending on where you are: they round upwards above sea level and downwards when underground. This inconsistency comes from the way the coordinates are calculated, independent of the specific C++ code. The result is a tiny bit of lost performance when moving underground, because 1 node level more than necessary is checked for collisions. This can be amended by adding a tiny offset to minpos_f.Y, like @paramat suggested. This is not an elegant solution, but still better than wasting CPU. --- src/collision.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collision.cpp b/src/collision.cpp index c9a945916..4c3bd016d 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -261,7 +261,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, v3f newpos_f = *pos_f + *speed_f * dtime; v3f minpos_f( MYMIN(pos_f->X, newpos_f.X), - MYMIN(pos_f->Y, newpos_f.Y), + MYMIN(pos_f->Y, newpos_f.Y) + 0.01 * BS, // bias rounding, player often at +/-n.5 MYMIN(pos_f->Z, newpos_f.Z) ); v3f maxpos_f( From c789c532bbda266cf2f3f27bac7d4ece9c5022bd Mon Sep 17 00:00:00 2001 From: Fixer Date: Wed, 9 Aug 2017 13:50:16 +0300 Subject: [PATCH 056/183] Full viewing range key message clarified To make it sound less confusing to players --- src/game.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 9bb560172..8958826ad 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2974,8 +2974,8 @@ void Game::decreaseViewRange() void Game::toggleFullViewRange() { static const wchar_t *msg[] = { - L"Disabled full viewing range", - L"Enabled full viewing range" + L"Normal view range", + L"Infinite view range" }; draw_control->range_all = !draw_control->range_all; From 037b01eac7d41de442149ba75a427f8b340b02dd Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 10 Aug 2017 09:41:56 +0200 Subject: [PATCH 057/183] Trigger on_rightclick regardless on the formspec meta field Document behaviour for older clients. --- doc/lua_api.txt | 6 ++++-- src/game.cpp | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index aefcba064..edef75593 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4261,9 +4261,11 @@ Definition tables ^ By default: Calls minetest.register_on_punchnode callbacks ]] on_rightclick = func(pos, node, clicker, itemstack, pointed_thing), --[[ ^ default: nil - ^ if defined, itemstack will hold clicker's wielded item + ^ itemstack will hold clicker's wielded item ^ Shall return the leftover itemstack - ^ Note: pointed_thing can be nil, if a mod calls this function ]] + ^ Note: pointed_thing can be nil, if a mod calls this function + This function does not get triggered by clients <=0.4.16 if the + "formspec" node metadata field is set ]] on_dig = func(pos, node, digger), --[[ ^ default: minetest.node_dig diff --git a/src/game.cpp b/src/game.cpp index 8958826ad..68acc81b7 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3801,6 +3801,11 @@ void Game::handlePointingAtNode(const PointedThing &pointed, if (meta && meta->getString("formspec") != "" && !random_input && !isKeyDown(KeyType::SNEAK)) { + // Report right click to server + if (nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) { + client->interact(3, pointed); + } + infostream << "Launching custom inventory view" << std::endl; InventoryLocation inventoryloc; From 9d40d89d2721359b02c578366ebfd0ef1b91116d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Sat, 24 Jun 2017 18:58:01 +0200 Subject: [PATCH 058/183] Make dropped items colorable --- builtin/game/item.lua | 47 +++++++++++++++++++++++++++++-------------- doc/lua_api.txt | 23 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/builtin/game/item.lua b/builtin/game/item.lua index f6de2c339..1a8bce903 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -155,6 +155,24 @@ function core.yaw_to_dir(yaw) return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)} end +function core.is_colored_paramtype(ptype) + return (ptype == "color") or (ptype == "colorfacedir") or + (ptype == "colorwallmounted") +end + +function core.strip_param2_color(param2, paramtype2) + if not core.is_colored_paramtype(paramtype2) then + return nil + end + if paramtype2 == "colorfacedir" then + param2 = math.floor(param2 / 32) * 32 + elseif paramtype2 == "colorwallmounted" then + param2 = math.floor(param2 / 8) * 8 + end + -- paramtype2 == "color" requires no modification. + return param2 +end + function core.get_node_drops(node, toolname) -- Compatibility, if node is string local nodename = node @@ -166,24 +184,17 @@ function core.get_node_drops(node, toolname) end local def = core.registered_nodes[nodename] local drop = def and def.drop + local ptype = def and def.paramtype2 + -- get color, if there is color (otherwise nil) + local palette_index = core.strip_param2_color(param2, ptype) if drop == nil then -- default drop - local stack = ItemStack(nodename) - if def then - local type = def.paramtype2 - if (type == "color") or (type == "colorfacedir") or - (type == "colorwallmounted") then - local meta = stack:get_meta() - local color_part = param2 - if (type == "colorfacedir") then - color_part = math.floor(color_part / 32) * 32; - elseif (type == "colorwallmounted") then - color_part = math.floor(color_part / 8) * 8; - end - meta:set_int("palette_index", color_part) - end + if palette_index then + local stack = ItemStack(nodename) + stack:get_meta():set_int("palette_index", palette_index) + return {stack:to_string()} end - return {stack:to_string()} + return {nodename} elseif type(drop) == "string" then -- itemstring drop return {drop} @@ -218,6 +229,12 @@ function core.get_node_drops(node, toolname) if good_rarity and good_tool then got_count = got_count + 1 for _, add_item in ipairs(item.items) do + -- add color, if necessary + if item.inherit_color and palette_index then + local stack = ItemStack(add_item) + stack:get_meta():set_int("palette_index", palette_index) + add_item = stack:to_string() + end got_items[#got_items+1] = add_item end if drop.max_items ~= nil and got_count == drop.max_items then diff --git a/doc/lua_api.txt b/doc/lua_api.txt index edef75593..03c825689 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -536,6 +536,21 @@ automatically transferred between node and item forms by the engine, when a player digs or places a colored node. You can disable this feature by setting the `drop` field of the node to itself (without metadata). +To transfer the color to a special drop, you need a drop table. +Example: + + minetest.register_node("mod:stone", { + description = "Stone", + tiles = {"default_stone.png"}, + paramtype2 = "color", + palette = "palette.png", + drop = { + items = { + -- assume that mod:cobblestone also has the same palette + {items = {"mod:cobblestone"}, inherit_color = true }, + } + } + }) ### Colored items in craft recipes Craft recipes only support item strings, but fortunately item strings @@ -2679,6 +2694,13 @@ and `minetest.auth_reload` call the authetification handler. * Convert a vector into a yaw (angle) * `minetest.yaw_to_dir(yaw)` * Convert yaw (angle) to a vector +* `minetest.is_colored_paramtype(ptype)` + * Returns a boolean. Returns `true` if the given `paramtype2` contains color + information (`color`, `colorwallmounted` or `colorfacedir`). +* `minetest.strip_param2_color(param2, paramtype2)` + * Removes everything but the color information from the + given `param2` value. + * Returns `nil` if the given `paramtype2` does not contain color information * `minetest.get_node_drops(nodename, toolname)` * Returns list of item names. * **Note**: This will be removed or modified in a future version. @@ -4216,6 +4238,7 @@ Definition tables { items = {"foo:bar", "baz:frob"}, -- Items to drop. rarity = 1, -- Probability of dropping is 1 / rarity. + inherit_color = true, -- To inherit palette color from the node }, }, }, From 151c19a6be591d19d83cdd3c69583e7bf0828440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Wed, 16 Aug 2017 23:48:29 +0200 Subject: [PATCH 059/183] ClientInterface: add a function to verify (correctly) if user limit was reached (#6258) * ClientInterface: add a function to verify (correctly) if user limit was reached CS_HelloSent is a better indicator of active slots than CS_Created, which are session objects created after init packet reception Switch existing checks to ClientInterface::isUserLimitReached() Use range-based for loop for getClientIds() used function too This will fix #6254 (not the memory overhead if init is flooded) --- src/clientiface.cpp | 10 ++++++++++ src/clientiface.h | 4 +++- src/network/serverpackethandler.cpp | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 68bd4afe7..475397279 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -633,6 +633,16 @@ std::vector ClientInterface::getClientIDs(ClientState min_state) return reply; } +/** + * Verify if user limit was reached. + * User limit count all clients from HelloSent state (MT protocol user) to Active state + * @return true if user limit was reached + */ +bool ClientInterface::isUserLimitReached() +{ + return getClientIDs(CS_HelloSent).size() >= g_settings->getU16("max_users"); +} + void ClientInterface::step(float dtime) { m_print_info_timer += dtime; diff --git a/src/clientiface.h b/src/clientiface.h index d2299c879..6e7429309 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -449,6 +449,9 @@ public: /* get list of active client id's */ std::vector getClientIDs(ClientState min_state=CS_Active); + /* verify is server user limit was reached */ + bool isUserLimitReached(); + /* get list of client player names */ const std::vector &getPlayerNames() const { return m_clients_names; } @@ -493,7 +496,6 @@ public: } static std::string state2Name(ClientState state); - protected: //TODO find way to avoid this functions void lock() { m_clients_mutex.lock(); } diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 5b026bbdb..22456f487 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -211,7 +211,7 @@ void Server::handleCommand_Init(NetworkPacket* pkt) // Enforce user limit. // Don't enforce for users that have some admin right - if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") && + if (m_clients.isUserLimitReached() && !checkPriv(playername, "server") && !checkPriv(playername, "ban") && !checkPriv(playername, "privs") && @@ -520,7 +520,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) // Enforce user limit. // Don't enforce for users that have some admin right - if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") && + if (m_clients.isUserLimitReached() >= g_settings->getU16("max_users") && !checkPriv(playername, "server") && !checkPriv(playername, "ban") && !checkPriv(playername, "privs") && From 12562be393215487280b597b53157c94901e750d Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Thu, 17 Aug 2017 19:15:12 +0200 Subject: [PATCH 060/183] Typo fix in compat code from commit 1d8d01074fdb52946f81110bebf1d001185b394b --- src/network/serverpackethandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 22456f487..62ffdde17 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -520,7 +520,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) // Enforce user limit. // Don't enforce for users that have some admin right - if (m_clients.isUserLimitReached() >= g_settings->getU16("max_users") && + if (m_clients.isUserLimitReached() && !checkPriv(playername, "server") && !checkPriv(playername, "ban") && !checkPriv(playername, "privs") && From d9c7af109ada1e45074d4615b50b8aab2886c2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Mon, 21 Aug 2017 16:07:39 +0200 Subject: [PATCH 061/183] serialize: use a temporary for SerializeException Exception must always use temporary instead of global copied exception instances, it's not recommended and should have undefined issues --- src/util/serialize.cpp | 2 -- src/util/serialize.h | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp index 61d369bc4..a1e6790f4 100644 --- a/src/util/serialize.cpp +++ b/src/util/serialize.cpp @@ -28,8 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -SerializationError eof_ser_err("Attempted read past end of data"); - //// //// BufReader //// diff --git a/src/util/serialize.h b/src/util/serialize.h index e22434191..a864d21ab 100644 --- a/src/util/serialize.h +++ b/src/util/serialize.h @@ -429,8 +429,6 @@ bool deSerializeStringToStruct(std::string valstr, //// BufReader //// -extern SerializationError eof_ser_err; - #define MAKE_BUFREADER_GETNOEX_FXN(T, N, S) \ inline bool get ## N ## NoEx(T *val) \ { \ @@ -446,7 +444,7 @@ extern SerializationError eof_ser_err; { \ T val; \ if (!get ## N ## NoEx(&val)) \ - throw eof_ser_err; \ + throw SerializationError("Attempted read past end of data"); \ return val; \ } @@ -504,7 +502,7 @@ public: inline void getRawData(void *val, size_t len) { if (!getRawDataNoEx(val, len)) - throw eof_ser_err; + throw SerializationError("Attempted read past end of data"); } inline size_t remaining() From 2c450ed93f0217d4950bcd4d2be3d5d1b92d7a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Thu, 24 Aug 2017 06:31:33 +0000 Subject: [PATCH 062/183] Fix Android node selection distance (#6187) --- src/game.cpp | 3 +++ src/touchscreengui.h | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/game.cpp b/src/game.cpp index 68acc81b7..facc68aea 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3550,6 +3550,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) { shootline = g_touchscreengui->getShootline(); + // Scale shootline to the acual distance the player can reach + shootline.end = shootline.start + + shootline.getVector().normalize() * BS * d; shootline.start += intToFloat(camera_offset, BS); shootline.end += intToFloat(camera_offset, BS); } diff --git a/src/touchscreengui.h b/src/touchscreengui.h index f4f1766c9..1f680ccb2 100644 --- a/src/touchscreengui.h +++ b/src/touchscreengui.h @@ -156,6 +156,14 @@ public: double getPitch() { return m_camera_pitch; } + /*! + * Returns a line which describes what the player is pointing at. + * The starting point and looking direction are significant, + * the line should be scaled to match its length to the actual distance + * the player can reach. + * The line starts at the camera and ends on the camera's far plane. + * The coordinates do not contain the camera offset. + */ line3d getShootline() { return m_shootline; } void step(float dtime); @@ -180,6 +188,12 @@ private: double m_camera_yaw_change; double m_camera_pitch; + /*! + * A line starting at the camera and pointing towards the + * selected object. + * The line ends on the camera's far plane. + * The coordinates do not contain the camera offset. + */ line3d m_shootline; rect m_control_pad_rect; From 888b99e1b66595330e207163d549b325eb4c147f Mon Sep 17 00:00:00 2001 From: Paramat Date: Sat, 26 Aug 2017 07:45:09 +0100 Subject: [PATCH 063/183] Android stepheight: Only increase if 'touching ground' (#6313) --- src/localplayer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/localplayer.cpp b/src/localplayer.cpp index ace0b992f..652cebd77 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -330,7 +330,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, float player_stepheight = (touching_ground) ? (BS * 0.6f) : (BS * 0.2f); #ifdef __ANDROID__ - player_stepheight += (0.6f * BS); + if (touching_ground) + player_stepheight += (0.6f * BS); #endif v3f accel_f = v3f(0,0,0); From bb4ef529548d2d27fbea0617347d49eb7ba70b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Courant?= Date: Sun, 27 Aug 2017 19:06:40 +0200 Subject: [PATCH 064/183] Statbars: fix incorrect half-images in non-standard orientations (fixes #6198) --- src/hud.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/hud.cpp b/src/hud.cpp index a2f031b4c..6895349ef 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -407,24 +407,32 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, p += offset; v2s32 steppos; + core::rect srchalfrect, dsthalfrect; switch (drawdir) { case HUD_DIR_RIGHT_LEFT: steppos = v2s32(-1, 0); + srchalfrect = core::rect(srcd.Width / 2, 0, srcd.Width, srcd.Height); + dsthalfrect = core::rect(dstd.Width / 2, 0, dstd.Width, dstd.Height); break; case HUD_DIR_TOP_BOTTOM: steppos = v2s32(0, 1); + srchalfrect = core::rect(0, 0, srcd.Width, srcd.Height / 2); + dsthalfrect = core::rect(0, 0, dstd.Width, dstd.Height / 2); break; case HUD_DIR_BOTTOM_TOP: steppos = v2s32(0, -1); + srchalfrect = core::rect(0, srcd.Height / 2, srcd.Width, srcd.Height); + dsthalfrect = core::rect(0, dstd.Height / 2, dstd.Width, dstd.Height); break; default: steppos = v2s32(1, 0); + srchalfrect = core::rect(0, 0, srcd.Width / 2, srcd.Height); + dsthalfrect = core::rect(0, 0, dstd.Width / 2, dstd.Height); } steppos.X *= dstd.Width; steppos.Y *= dstd.Height; - for (s32 i = 0; i < count / 2; i++) - { + for (s32 i = 0; i < count / 2; i++) { core::rect srcrect(0, 0, srcd.Width, srcd.Height); core::rect dstrect(0,0, dstd.Width, dstd.Height); @@ -433,13 +441,9 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, p += steppos; } - if (count % 2 == 1) - { - core::rect srcrect(0, 0, srcd.Width / 2, srcd.Height); - core::rect dstrect(0,0, dstd.Width / 2, dstd.Height); - - dstrect += p; - draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true); + if (count % 2 == 1) { + dsthalfrect += p; + draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true); } } From e9087d1be7e24557a268e77b3005052058d89adb Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 19 Aug 2017 19:43:02 +0100 Subject: [PATCH 065/183] Fix empty legacy meta being persisted --- src/itemstackmetadata.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp index 65829fd68..f63671425 100644 --- a/src/itemstackmetadata.cpp +++ b/src/itemstackmetadata.cpp @@ -13,11 +13,10 @@ void ItemStackMetadata::serialize(std::ostream &os) const { std::ostringstream os2; os2 << DESERIALIZE_START; - for (StringMap::const_iterator - it = m_stringvars.begin(); - it != m_stringvars.end(); ++it) { - os2 << it->first << DESERIALIZE_KV_DELIM - << it->second << DESERIALIZE_PAIR_DELIM; + for (const auto &stringvar : m_stringvars) { + if (!stringvar.first.empty() || !stringvar.second.empty()) + os2 << stringvar.first << DESERIALIZE_KV_DELIM + << stringvar.second << DESERIALIZE_PAIR_DELIM; } os << serializeJsonStringIfNeeded(os2.str()); } From 5b2461c713889b9832f5b99c85abf87e5d494242 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 11 Sep 2017 16:25:20 +0200 Subject: [PATCH 066/183] Fix core.wrap_text and make its behaviour consistent with the docs Code based on initial implementation by @dsohler. --- builtin/common/misc_helpers.lua | 64 ++++++++------------------------- builtin/mainmenu/common.lua | 2 +- builtin/mainmenu/tab_mods.lua | 2 +- doc/lua_api.txt | 6 ++-- 4 files changed, 21 insertions(+), 53 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 0bdd4b02a..4840dcbaa 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -308,59 +308,25 @@ function core.formspec_escape(text) end -function core.wrap_text(text, charlimit) - local retval = {} - - local current_idx = 1 - - local start,stop = string_find(text, " ", current_idx) - local nl_start,nl_stop = string_find(text, "\n", current_idx) - local gotnewline = false - if nl_start ~= nil and (start == nil or nl_start < start) then - start = nl_start - stop = nl_stop - gotnewline = true - end - local last_line = "" - while start ~= nil do - if string.len(last_line) + (stop-start) > charlimit then - retval[#retval + 1] = last_line - last_line = "" - end - - if last_line ~= "" then - last_line = last_line .. " " - end - - last_line = last_line .. string_sub(text, current_idx, stop - 1) - - if gotnewline then - retval[#retval + 1] = last_line - last_line = "" - gotnewline = false - end - current_idx = stop+1 - - start,stop = string_find(text, " ", current_idx) - nl_start,nl_stop = string_find(text, "\n", current_idx) - - if nl_start ~= nil and (start == nil or nl_start < start) then - start = nl_start - stop = nl_stop - gotnewline = true - end +function core.wrap_text(text, max_length, as_table) + local result = {} + local line = {} + if #text <= max_length then + return as_table and {text} or text end - --add last part of text - if string.len(last_line) + (string.len(text) - current_idx) > charlimit then - retval[#retval + 1] = last_line - retval[#retval + 1] = string_sub(text, current_idx) - else - last_line = last_line .. " " .. string_sub(text, current_idx) - retval[#retval + 1] = last_line + for word in text:gmatch('%S+') do + local cur_length = #table.concat(line, ' ') + if cur_length > 0 and cur_length + #word + 1 >= max_length then + -- word wouldn't fit on current line, move to next line + table.insert(result, table.concat(line, ' ')) + line = {} + end + table.insert(line, word) end - return retval + table.insert(result, table.concat(line, ' ')) + return as_table and result or table.concat(result, '\n') end -------------------------------------------------------------------------------- diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index fa7ae583b..7eb941775 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -250,7 +250,7 @@ end -------------------------------------------------------------------------------- function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency) - local textlines = core.wrap_text(text, textlen) + local textlines = core.wrap_text(text, textlen, true) local retval = "textlist[" .. xpos .. "," .. ypos .. ";" .. width .. "," .. height .. ";" .. tl_name .. ";" diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua index 9510a9e18..7f95355a9 100644 --- a/builtin/mainmenu/tab_mods.lua +++ b/builtin/mainmenu/tab_mods.lua @@ -75,7 +75,7 @@ local function get_formspec(tabview, name, tabdata) if error == nil then local descriptiontext = descriptionfile:read("*all") - descriptionlines = core.wrap_text(descriptiontext, 42) + descriptionlines = core.wrap_text(descriptiontext, 42, true) descriptionfile:close() else descriptionlines = {} diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 03c825689..f0e6931db 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2128,9 +2128,11 @@ Helper functions * e.g. `string:split("a,b", ",") == {"a","b"}` * `string:trim()` * e.g. `string.trim("\n \t\tfoo bar\t ") == "foo bar"` -* `minetest.wrap_text(str, limit)`: returns a string - * Adds new lines to the string to keep it within the specified character limit +* `minetest.wrap_text(str, limit, [as_table])`: returns a string or table + * Adds newlines to the string to keep it within the specified character limit + Note that returned lines may be longer than the limit since it only splits at word borders. * limit: Maximal amount of characters in one line + * as_table: optional, if true return table of lines instead of string * `minetest.pos_to_string({x=X,y=Y,z=Z}, decimal_places))`: returns string `"(X,Y,Z)"` * Convert position to a printable string Optional: 'decimal_places' will round the x, y and z of the pos to the given decimal place. From c2a0333901a696f7dcb67356aeb0206b89be14e6 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 15 Sep 2017 12:19:01 +0200 Subject: [PATCH 067/183] ServerEnv: Clean up object lifecycle handling (#6414) * ServerEnv: Clean up object lifecycle handling --- src/content_sao.cpp | 13 +- src/network/serverpackethandler.cpp | 8 +- src/script/cpp_api/s_base.cpp | 4 + src/script/lua_api/l_env.cpp | 8 +- src/script/lua_api/l_object.cpp | 11 +- src/serverenvironment.cpp | 326 ++++++++++++---------------- src/serverenvironment.h | 11 +- src/serverobject.cpp | 2 +- src/serverobject.h | 26 ++- 9 files changed, 185 insertions(+), 224 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index be1c52fe6..d60a30840 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -59,7 +59,7 @@ public: m_age += dtime; if(m_age > 10) { - m_removed = true; + m_pending_removal = true; return; } @@ -415,7 +415,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) infostream << "Remove SAO " << m_id << "(" << m_init_name << "), outside of limits" << std::endl; m_pending_deactivation = true; - m_removed = true; + m_pending_removal = true; return; } } @@ -555,9 +555,9 @@ int LuaEntitySAO::punch(v3f dir, ServerActiveObject *puncher, float time_from_last_punch) { - if (!m_registered){ + if (!m_registered) { // Delete unknown LuaEntities when punched - m_removed = true; + m_pending_removal = true; return 0; } @@ -601,7 +601,7 @@ int LuaEntitySAO::punch(v3f dir, } if (getHP() == 0) - m_removed = true; + m_pending_removal = true; @@ -1361,11 +1361,10 @@ void PlayerSAO::setWieldIndex(int i) } } -// Erase the peer id and make the object for removal void PlayerSAO::disconnected() { m_peer_id = 0; - m_removed = true; + m_pending_removal = true; } void PlayerSAO::unlinkPlayerSessionAndSave() diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 62ffdde17..452abdea5 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1441,8 +1441,8 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) playersao->noCheatDigStart(p_under); } else if (pointed.type == POINTEDTHING_OBJECT) { - // Skip if object has been removed - if (pointed_object->m_removed) + // Skip if object can't be interacted with anymore + if (pointed_object->isGone()) return; actionstream<getName()<<" punches object " @@ -1600,8 +1600,8 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) if (pointed.type == POINTEDTHING_OBJECT) { // Right click object - // Skip if object has been removed - if (pointed_object->m_removed) + // Skip if object can't be interacted with anymore + if (pointed_object->isGone()) return; actionstream << player->getName() << " right-clicks object " diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 4d7461c5b..2537d895f 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -322,6 +322,10 @@ void ScriptApiBase::objectrefGetOrCreate(lua_State *L, ObjectRef::create(L, cobj); } else { push_objectRef(L, cobj->getId()); + if (cobj->isGone()) + warningstream << "ScriptApiBase::objectrefGetOrCreate(): " + << "Pushing ObjectRef to removed/deactivated object" + << ", this is probably a bug." << std::endl; } } diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index e7284b035..393c4ed5f 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -537,9 +537,11 @@ int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L) std::vector::const_iterator iter = ids.begin(); for(u32 i = 0; iter != ids.end(); ++iter) { ServerActiveObject *obj = env->getActiveObject(*iter); - // Insert object reference into table - script->objectrefGetOrCreate(L, obj); - lua_rawseti(L, -2, ++i); + if (!obj->isGone()) { + // Insert object reference into table + script->objectrefGetOrCreate(L, obj); + lua_rawseti(L, -2, ++i); + } } return 1; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index aaab0d98e..0195dc399 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -137,16 +137,15 @@ int ObjectRef::l_remove(lua_State *L) if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) return 0; - const UNORDERED_SET &child_ids = co->getAttachmentChildIds(); - UNORDERED_SET::const_iterator it; - for (it = child_ids.begin(); it != child_ids.end(); ++it) { + const std::unordered_set &child_ids = co->getAttachmentChildIds(); + for (int child_id : child_ids) { // Child can be NULL if it was deleted earlier - if (ServerActiveObject *child = env->getActiveObject(*it)) + if (ServerActiveObject *child = env->getActiveObject(child_id)) child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); } - verbosestream<<"ObjectRef::l_remove(): id="<getId()<m_removed = true; + verbosestream << "ObjectRef::l_remove(): id=" << co->getId() << std::endl; + co->m_pending_removal = true; return 0; } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 5e39ce6a5..e4fcf626d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1018,26 +1018,18 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode) infostream << "ServerEnvironment::clearObjects(): " << "Removing all active objects" << std::endl; std::vector objects_to_remove; - for (ActiveObjectMap::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - ServerActiveObject* obj = i->second; + for (auto &it : m_active_objects) { + u16 id = it.first; + ServerActiveObject* obj = it.second; if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) continue; - u16 id = i->first; + // Delete static object if block is loaded - if (obj->m_static_exists) { - MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); - if (block) { - block->m_static_objects.remove(id); - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_CLEAR_ALL_OBJECTS); - obj->m_static_exists = false; - } - } + deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true); + // If known by some client, don't delete immediately if (obj->m_known_by_count > 0) { - obj->m_pending_deactivation = true; - obj->m_removed = true; + obj->m_pending_removal = true; continue; } @@ -1054,9 +1046,8 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode) } // Remove references from m_active_objects - for (std::vector::iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); ++i) { - m_active_objects.erase(*i); + for (u16 i : objects_to_remove) { + m_active_objects.erase(i); } // Get list of loaded blocks @@ -1399,16 +1390,14 @@ void ServerEnvironment::step(float dtime) for(ActiveObjectMap::iterator i = m_active_objects.begin(); i != m_active_objects.end(); ++i) { ServerActiveObject* obj = i->second; - // Don't step if is to be removed or stored statically - if(obj->m_removed || obj->m_pending_deactivation) + if (obj->isGone()) continue; + // Step object obj->step(dtime, send_recommended); // Read messages from object - while(!obj->m_messages_out.empty()) - { - m_active_object_messages.push( - obj->m_messages_out.front()); + while (!obj->m_messages_out.empty()) { + m_active_object_messages.push(obj->m_messages_out.front()); obj->m_messages_out.pop(); } } @@ -1420,9 +1409,6 @@ void ServerEnvironment::step(float dtime) if(m_object_management_interval.step(dtime, 0.5)) { ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG); - /* - Remove objects that satisfy (m_removed && m_known_by_count==0) - */ removeRemovedObjects(); } @@ -1542,7 +1528,7 @@ void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius, player_radius_f = 0; /* Go through the object list, - - discard m_removed objects, + - discard removed/deactivated objects, - discard objects that are too far away, - discard objects that are found in current_objects. - add remaining objects to added_objects @@ -1556,8 +1542,7 @@ void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius, if (object == NULL) continue; - // Discard if removed or deactivating - if(object->m_removed || object->m_pending_deactivation) + if (object->isGone()) continue; f32 distance_f = object->getBasePosition(). @@ -1615,7 +1600,7 @@ void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius continue; } - if (object->m_removed || object->m_pending_deactivation) { + if (object->isGone()) { removed_objects.push(id); continue; } @@ -1757,7 +1742,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, } /* - Remove objects that satisfy (m_removed && m_known_by_count==0) + Remove objects that satisfy (isGone() && m_known_by_count==0) */ void ServerEnvironment::removeRemovedObjects() { @@ -1766,62 +1751,54 @@ void ServerEnvironment::removeRemovedObjects() i != m_active_objects.end(); ++i) { u16 id = i->first; ServerActiveObject* obj = i->second; + // This shouldn't happen but check it - if(obj == NULL) - { - infostream<<"NULL object found in ServerEnvironment" - <<" while finding removed objects. id="<m_static_block) << std::endl; } } else { - infostream<<"Failed to emerge block from which an object to " - <<"be deactivated was loaded from. id="<getStaticData(&staticdata_new); StaticObject s_obj(obj->getType(), objectpos, staticdata_new); - block->m_static_objects.insert(id, s_obj); - obj->m_static_block = blockpos_o; - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_STATIC_DATA_ADDED); + // Save to block where object is located + saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED); - // Delete from block where object was located - block = m_map->emergeBlock(old_static_block, false); - if(!block){ - errorstream<<"ServerEnvironment::deactivateFarObjects(): " - <<"Could not delete object id="<m_static_objects.remove(id); - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_STATIC_DATA_REMOVED); continue; } - // If block is active, don't remove + // If block is still active, don't remove if(!force_delete && m_active_blocks.contains(blockpos_o)) continue; - verbosestream<<"ServerEnvironment::deactivateFarObjects(): " - <<"deactivating object id="<m_known_by_count > 0 && !force_delete); @@ -2058,7 +2000,6 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) /* Update the static data */ - if(obj->isStaticAllowed()) { // Create new static object @@ -2069,6 +2010,7 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) bool stays_in_same_block = false; bool data_changed = true; + // Check if static data has changed considerably if (obj->m_static_exists) { if (obj->m_static_block == blockpos_o) stays_in_same_block = true; @@ -2087,88 +2029,29 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) (static_old.pos - objectpos).getLength() < save_movem) data_changed = false; } else { - errorstream<<"ServerEnvironment::deactivateFarObjects(): " - <<"id="<m_static_block)<m_static_block) << std::endl; } } } + /* + While changes are always saved, blocks are only marked as modified + if the object has moved or different staticdata. (see above) + */ bool shall_be_written = (!stays_in_same_block || data_changed); + u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN; // Delete old static object - if(obj->m_static_exists) - { - MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); - if(block) - { - block->m_static_objects.remove(id); - obj->m_static_exists = false; - // Only mark block as modified if data changed considerably - if(shall_be_written) - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_STATIC_DATA_CHANGED); - } - } + deleteStaticFromBlock(obj, id, reason, false); // Add to the block where the object is located in v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); - // Get or generate the block - MapBlock *block = NULL; - try{ - block = m_map->emergeBlock(blockpos); - } catch(InvalidPositionException &e){ - // Handled via NULL pointer - // NOTE: emergeBlock's failure is usually determined by it - // actually returning NULL - } - - if(block) - { - if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { - warningstream << "ServerEnv: Trying to store id = " << obj->getId() - << " statically but block " << PP(blockpos) - << " already contains " - << block->m_static_objects.m_stored.size() - << " objects." - << " Forcing delete." << std::endl; - force_delete = true; - } else { - // If static counterpart already exists in target block, - // remove it first. - // This shouldn't happen because the object is removed from - // the previous block before this according to - // obj->m_static_block, but happens rarely for some unknown - // reason. Unsuccessful attempts have been made to find - // said reason. - if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){ - warningstream<<"ServerEnv: Performing hack #83274" - <m_static_objects.remove(id); - } - // Store static data - u16 store_id = pending_delete ? id : 0; - block->m_static_objects.insert(store_id, s_obj); - - // Only mark block as modified if data changed considerably - if(shall_be_written) - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_STATIC_DATA_CHANGED); - - obj->m_static_exists = true; - obj->m_static_block = block->getPos(); - } - } - else{ - if(!force_delete){ - v3s16 p = floatToInt(objectpos, BS); - errorstream<<"ServerEnv: Could not find or generate " - <<"a block for storing id="<getId() - <<" statically (pos="<m_pending_deactivation = true; continue; } - - verbosestream<<"ServerEnvironment::deactivateFarObjects(): " - <<"object id="<removingFromEnvironment(); @@ -2203,12 +2085,74 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) } // Remove references from m_active_objects - for(std::vector::iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); ++i) { - m_active_objects.erase(*i); + for (u16 i : objects_to_remove) { + m_active_objects.erase(i); } } +void ServerEnvironment::deleteStaticFromBlock( + ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge) +{ + if (!obj->m_static_exists) + return; + + MapBlock *block; + if (no_emerge) + block = m_map->getBlockNoCreateNoEx(obj->m_static_block); + else + block = m_map->emergeBlock(obj->m_static_block, false); + if (!block) { + if (!no_emerge) + errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block) + << " when deleting static data of object from it. id=" << id << std::endl; + return; + } + + block->m_static_objects.remove(id); + if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested + block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason); + + obj->m_static_exists = false; +} + +bool ServerEnvironment::saveStaticToBlock( + v3s16 blockpos, u16 store_id, + ServerActiveObject *obj, const StaticObject &s_obj, + u32 mod_reason) +{ + MapBlock *block = nullptr; + try { + block = m_map->emergeBlock(blockpos); + } catch (InvalidPositionException &e) { + // Handled via NULL pointer + // NOTE: emergeBlock's failure is usually determined by it + // actually returning NULL + } + + if (!block) { + errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block) + << " when saving static data of object to it. id=" << store_id << std::endl; + return false; + } + if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { + warningstream << "ServerEnv: Trying to store id = " << store_id + << " statically but block " << PP(blockpos) + << " already contains " + << block->m_static_objects.m_stored.size() + << " objects." << std::endl; + return false; + } + + block->m_static_objects.insert(store_id, s_obj); + if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested + block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason); + + obj->m_static_exists = true; + obj->m_static_block = blockpos; + + return true; +} + PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name, const std::string &savedir, const Settings &conf) { diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 7c370fd54..f6c2f17fc 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -33,6 +33,7 @@ class PlayerDatabase; class PlayerSAO; class ServerEnvironment; class ActiveBlockModifier; +struct StaticObject; class ServerActiveObject; class Server; class ServerScripting; @@ -362,7 +363,7 @@ private: u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s); /* - Remove all objects that satisfy (m_removed && m_known_by_count==0) + Remove all objects that satisfy (isGone() && m_known_by_count==0) */ void removeRemovedObjects(); @@ -382,6 +383,14 @@ private: */ void deactivateFarObjects(bool force_delete); + /* + A few helpers used by the three above methods + */ + void deleteStaticFromBlock( + ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge); + bool saveStaticToBlock(v3s16 blockpos, u16 store_id, + ServerActiveObject *obj, const StaticObject &s_obj, u32 mod_reason); + /* Member variables */ diff --git a/src/serverobject.cpp b/src/serverobject.cpp index 191247829..b3cb85b83 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos): ActiveObject(0), m_known_by_count(0), - m_removed(false), + m_pending_removal(false), m_pending_deactivation(false), m_static_exists(false), m_static_block(1337,1337,1337), diff --git a/src/serverobject.h b/src/serverobject.h index 38204980e..31af5d6a7 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -210,22 +210,26 @@ public: it anymore. - Removal is delayed to preserve the id for the time during which it could be confused to some other object by some client. - - This is set to true by the step() method when the object wants - to be deleted. - - This can be set to true by anything else too. + - This is usually set to true by the step() method when the object wants + to be deleted but can be set by anything else too. */ - bool m_removed; + bool m_pending_removal = false; /* - This is set to true when an object should be removed from the active - object list but couldn't be removed because the id has to be - reserved for some client. + Same purpose as m_pending_removal but for deactivation. + deactvation = save static data in block, remove active object - The environment checks this periodically. If this is true and also - m_known_by_count is true, object is deleted from the active object - list. + If this is set alongside with m_pending_removal, removal takes + priority. */ - bool m_pending_deactivation; + bool m_pending_deactivation = false; + + /* + A getter that unifies the above to answer the question: + "Can the environment still interact with this object?" + */ + inline bool isGone() const + { return m_pending_removal || m_pending_deactivation; } /* Whether the object's static data has been stored to a block From b1fae4c7beb66b4aa63ad04ac6ba9e145b39ca82 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 16 Sep 2017 21:39:38 +0100 Subject: [PATCH 068/183] Fix Rotate Node Placement (#6424) This properly checks for creative mode or privilege when using fixed rotate_node() function. --- builtin/common/misc_helpers.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 4840dcbaa..1e282f886 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -425,11 +425,15 @@ if INIT == "game" then --Wrapper for rotate_and_place() to check for sneak and assume Creative mode --implies infinite stacks when performing a 6d rotation. -------------------------------------------------------------------------------- - + local creative_mode_cache = core.settings:get_bool("creative_mode") + local function is_creative(name) + return creative_mode_cache or + core.check_player_privs(name, {creative = true}) + end core.rotate_node = function(itemstack, placer, pointed_thing) core.rotate_and_place(itemstack, placer, pointed_thing, - core.settings:get_bool("creative_mode"), + is_creative(placer:get_player_name()), {invert_wall = placer:get_player_control().sneak}) return itemstack end From b8f473be21b932c0c0cdb9ae3bfb4243f262a763 Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 18 Sep 2017 05:08:56 +0100 Subject: [PATCH 069/183] Leveled nodebox: Change levels from 1/63rds to 1/64ths Add missing documentation of leveled nodebox to lua_api.txt, plus a little cleaning up nearby. --- doc/lua_api.txt | 5 +++++ src/mapnode.cpp | 15 ++++----------- src/mapnode.h | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f0e6931db..8e750c5a4 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -809,6 +809,11 @@ node definition: 0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y- facedir modulo 4 = rotation around that axis paramtype2 == "leveled" + ^ Only valid for "nodebox" with type = "leveled". + The level of the top face of the nodebox is stored in param2. + The other faces are defined by 'fixed = {}' like 'type = "fixed"' nodeboxes. + The nodebox height is param2 / 64 nodes. + The maximum accepted value of param2 is 127. paramtype2 == "degrotate" ^ The rotation of this node is stored in param2. Plants are rotated this way. Values range 0 - 179. The value stored in param2 is multiplied by two to diff --git a/src/mapnode.cpp b/src/mapnode.cpp index d835daba2..ff5ad5557 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -249,18 +249,11 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, int facedir = n.getFaceDir(nodemgr); u8 axisdir = facedir>>2; facedir&=0x03; - for(std::vector::const_iterator - i = fixed.begin(); - i != fixed.end(); ++i) - { - aabb3f box = *i; + for (aabb3f box : fixed) { + if (nodebox.type == NODEBOX_LEVELED) + box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS; - if (nodebox.type == NODEBOX_LEVELED) { - box.MaxEdge.Y = -BS/2 + BS*((float)1/LEVELED_MAX) * n.getLevel(nodemgr); - } - - switch (axisdir) - { + switch (axisdir) { case 0: if(facedir == 1) { diff --git a/src/mapnode.h b/src/mapnode.h index 9c56a7e17..23248c45d 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -103,8 +103,8 @@ enum Rotation { #define LIQUID_INFINITY_MASK 0x80 //0b10000000 -// mask for param2, now as for liquid -#define LEVELED_MASK 0x3F +// mask for leveled nodebox param2 +#define LEVELED_MASK 0x7F #define LEVELED_MAX LEVELED_MASK From e8286e8894cf132eeda4bac9924e5fd8c74a7eb2 Mon Sep 17 00:00:00 2001 From: DTA7 Date: Thu, 21 Sep 2017 21:52:52 +0200 Subject: [PATCH 070/183] Set placer to nil instead of a non-functional one in item_OnPlace (#6449) * Set placer to nil instead of a non-functional one This requires nil checks in core.rotate_node and core.rotate_and_place. --- builtin/common/misc_helpers.lua | 14 ++++++++------ src/script/cpp_api/s_item.cpp | 7 ++++++- src/script/lua_api/l_env.cpp | 5 ++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 1e282f886..51abed1be 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -349,7 +349,7 @@ if INIT == "game" then itemstack, pointed_thing) return end - local fdir = core.dir_to_facedir(placer:get_look_dir()) + local fdir = placer and core.dir_to_facedir(placer:get_look_dir()) or 0 local wield_name = itemstack:get_name() local above = pointed_thing.above @@ -369,9 +369,9 @@ if INIT == "game" then iswall = false end - if core.is_protected(pos, placer:get_player_name()) then - core.record_protection_violation(pos, - placer:get_player_name()) + local name = placer and placer:get_player_name() or "" + if core.is_protected(pos, name) then + core.record_protection_violation(pos, name) return end @@ -432,9 +432,11 @@ if INIT == "game" then end core.rotate_node = function(itemstack, placer, pointed_thing) + local name = placer and placer:get_player_name() or "" + local invert_wall = placer and placer:get_player_control().sneak or false core.rotate_and_place(itemstack, placer, pointed_thing, - is_creative(placer:get_player_name()), - {invert_wall = placer:get_player_control().sneak}) + is_creative(name), + {invert_wall = invert_wall}) return itemstack end end diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index 032018f2f..e13da1c86 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -69,7 +69,12 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item, // Call function LuaItemStack::create(L, item); - objectrefGetOrCreate(L, placer); + + if (!placer) + lua_pushnil(L); + else + objectrefGetOrCreate(L, placer); + pushPointedThing(pointed); PCALL_RES(lua_pcall(L, 3, 1, error_handler)); if (!lua_isnil(L, -1)) { diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 393c4ed5f..46745facd 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -291,9 +291,8 @@ int ModApiEnvMod::l_place_node(lua_State *L) pointed.type = POINTEDTHING_NODE; pointed.node_abovesurface = pos; pointed.node_undersurface = pos + v3s16(0,-1,0); - // Place it with a NULL placer (appears in Lua as a non-functional - // ObjectRef) - bool success = scriptIfaceItem->item_OnPlace(item, NULL, pointed); + // Place it with a NULL placer (appears in Lua as nil) + bool success = scriptIfaceItem->item_OnPlace(item, nullptr, pointed); lua_pushboolean(L, success); return 1; } From 01b9da870b8501395125f3181cb5893da39831bb Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 23 Sep 2017 17:05:55 +0200 Subject: [PATCH 071/183] Fix blocks written by vmanip not being marked as modified This bug can be triggered by e.g. calling minetest.place_schematic() and stopping the server immediately afterwards. --- src/map.cpp | 1 + src/mapblock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index 3b02ac02f..0c57d6a8a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2737,6 +2737,7 @@ void MMVManip::blitBackAll(std::map *modified_blocks, continue; block->copyFrom(*this); + block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP); if(modified_blocks) (*modified_blocks)[p] = block; diff --git a/src/mapblock.h b/src/mapblock.h index 8816dc817..1ccaccaef 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -122,7 +122,8 @@ public: #define MOD_REASON_STATIC_DATA_REMOVED (1 << 16) #define MOD_REASON_STATIC_DATA_CHANGED (1 << 17) #define MOD_REASON_EXPIRE_DAYNIGHTDIFF (1 << 18) -#define MOD_REASON_UNKNOWN (1 << 19) +#define MOD_REASON_VMANIP (1 << 19) +#define MOD_REASON_UNKNOWN (1 << 20) //// //// MapBlock itself From 3a9d500396b327d04439bb5f629d8d713c228e89 Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 23 Sep 2017 08:38:22 +0100 Subject: [PATCH 072/183] Positional sound: Limit volume when closer than 1 node Change OpenAL distance model from AL_INVERSE_DISTANCE to AL_INVERSE_DISTANCE_CLAMPED to avoid excessive volume when very close to the sound location, for example MTG doors, and MTG fire sounds which are combined at an average position and often located in air nodes. Because AL_REFERENCE_DISTANCE has been reduced to 1 node (the distance under which gain is clamped), multiply volume by the same factor to keep sound gains the same as before, since the gain is calculated as: gain = (AL_REFERENCE_DISTANCE / distance) --- src/sound_openal.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index a425af827..20633404e 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -36,6 +36,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc., #include #include #endif +#include #include #include #include "log.h" @@ -331,7 +332,7 @@ public: return; } - alDistanceModel(AL_INVERSE_DISTANCE); + alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); infostream<<"Audio: Initialized: OpenAL "<source_id, AL_POSITION, 0, 0, 0); alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); - volume = MYMAX(0.0, volume); + volume = std::max(0.0f, volume); alSourcef(sound->source_id, AL_GAIN, volume); alSourcePlay(sound->source_id); warn_if_error(alGetError(), "createPlayingSound"); @@ -428,9 +429,14 @@ public: alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false); alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z); alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); - alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0); + // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference + // distance to clamp gain at <1 node distance, to avoid excessive + // volume when closer + alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f); alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); - volume = MYMAX(0.0, volume); + // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from + // the previous value of 30 to the new value of 10 + volume = std::max(0.0f, volume * 3.0f); alSourcef(sound->source_id, AL_GAIN, volume); alSourcePlay(sound->source_id); warn_if_error(alGetError(), "createPlayingSoundAt"); From bd8d6f8f2f23fb4b4b4ab274bc6356aa141a31ef Mon Sep 17 00:00:00 2001 From: Paramat Date: Wed, 27 Sep 2017 22:03:41 +0100 Subject: [PATCH 073/183] Fix recent commit: std::max -> std::fmax for floats (#6469) Fixes commit a455297d297c0819a7eff89e51e5f01a5ac731c3 header was already present in commit. --- src/sound_openal.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index 20633404e..1bad04b9c 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -408,7 +408,7 @@ public: alSource3f(sound->source_id, AL_POSITION, 0, 0, 0); alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); - volume = std::max(0.0f, volume); + volume = std::fmax(0.0f, volume); alSourcef(sound->source_id, AL_GAIN, volume); alSourcePlay(sound->source_id); warn_if_error(alGetError(), "createPlayingSound"); @@ -436,7 +436,7 @@ public: alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from // the previous value of 30 to the new value of 10 - volume = std::max(0.0f, volume * 3.0f); + volume = std::fmax(0.0f, volume * 3.0f); alSourcef(sound->source_id, AL_GAIN, volume); alSourcePlay(sound->source_id); warn_if_error(alGetError(), "createPlayingSoundAt"); From 9927076b474f95e7b8fe10ac536f38cec3b716c9 Mon Sep 17 00:00:00 2001 From: raymoo Date: Thu, 28 Sep 2017 09:40:47 -0700 Subject: [PATCH 074/183] Document orientation parameter of set_attach (#6473) --- doc/lua_api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 8e750c5a4..c1799d79e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3182,7 +3182,7 @@ This is basically a reference to a C++ `ServerActiveObject` * `set_attach(parent, bone, position, rotation)` * `bone`: string * `position`: `{x=num, y=num, z=num}` (relative) - * `rotation`: `{x=num, y=num, z=num}` + * `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees * `get_attach()`: returns parent, bone, position, rotation or nil if it isn't attached * `set_detach()` * `set_bone_position(bone, position, rotation)` From ab72100a2cf76a64218bbdf8a4dc9f3eae25a72f Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 23 Sep 2017 16:29:48 +0200 Subject: [PATCH 075/183] Localplayer: Fix disable_jump effect and getStandingNodePos() Leave the old move code untouched. --- src/localplayer.cpp | 58 ++++++++++++++++++++++++++++++++------------- src/localplayer.h | 1 + 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 652cebd77..9000888bf 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -220,6 +220,11 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector *collision_info) { + if (!collision_info || collision_info->empty()) { + // Node below the feet, update each ClientEnvironment::step() + m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0); + } + // Temporary option for old move code if (!physics_override_new_move) { old_move(dtime, env, pos_max_d, collision_info); @@ -241,8 +246,9 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool noclip = m_client->checkLocalPrivilege("noclip") && g_settings->getBool("noclip"); - bool free_move = noclip && fly_allowed && g_settings->getBool("free_move"); - if (free_move) { + bool free_move = g_settings->getBool("free_move") && fly_allowed; + + if (noclip && free_move) { position += m_speed * dtime; setPosition(position); return; @@ -340,10 +346,35 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, pos_max_d, m_collisionbox, player_stepheight, dtime, &position, &m_speed, accel_f); - bool could_sneak = control.sneak && - !(fly_allowed && g_settings->getBool("free_move")) && - !in_liquid && !is_climbing && - physics_override_sneak; + bool could_sneak = control.sneak && !free_move && !in_liquid && + !is_climbing && physics_override_sneak; + + // Add new collisions to the vector + if (collision_info && !free_move) { + v3f diff = intToFloat(m_standing_node, BS) - position; + f32 distance = diff.getLength(); + // Force update each ClientEnvironment::step() + bool is_first = collision_info->empty(); + + for (const auto &colinfo : result.collisions) { + collision_info->push_back(colinfo); + + if (colinfo.type != COLLISION_NODE || + colinfo.new_speed.Y != 0 || + (could_sneak && m_sneak_node_exists)) + continue; + + diff = intToFloat(colinfo.node_p, BS) - position; + + // Find nearest colliding node + f32 len = diff.getLength(); + if (is_first || len < distance) { + m_standing_node = colinfo.node_p; + distance = len; + } + } + } + /* If the player's feet touch the topside of any node, this is set to true. @@ -373,6 +404,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, const v3f old_pos = position; const v3f old_speed = m_speed; f32 y_diff = bmax.Y - position.Y; + m_standing_node = m_sneak_node; // (BS * 0.6f) is the basic stepheight while standing on ground if (y_diff < BS * 0.6f) { @@ -400,7 +432,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if (m_speed.Y == 0 || m_sneak_ladder_detected) sneak_can_jump = true; - if (collision_info != NULL && + if (collision_info && m_speed.Y - old_speed.Y > BS) { // Collide with sneak node, report fall damage CollisionInfo sn_info; @@ -429,14 +461,6 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, Report collisions */ - // Dont report if flying - if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) { - for(size_t i=0; ipush_back(info); - } - } - if(!result.standing_on_object && !touching_ground_was && touching_ground) { MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); m_client->event()->put(e); @@ -459,7 +483,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, /* Check properties of the node on which the player is standing */ - const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos())); + const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node)); // Determine if jumping is possible m_can_jump = (touching_ground && !in_liquid && !is_climbing) || sneak_can_jump; @@ -711,7 +735,7 @@ v3s16 LocalPlayer::getStandingNodePos() { if(m_sneak_node_exists) return m_sneak_node; - return floatToInt(getPosition() - v3f(0, BS, 0), BS); + return m_standing_node; } v3s16 LocalPlayer::getFootstepNodePos() diff --git a/src/localplayer.h b/src/localplayer.h index 32714ff32..c371b345c 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -146,6 +146,7 @@ private: bool updateSneakNode(Map *map, const v3f &position, const v3f &sneak_max); v3f m_position; + v3s16 m_standing_node; v3s16 m_sneak_node = v3s16(32767, 32767, 32767); // Stores the top bounding box of m_sneak_node From 017815161b8583c07f0636dcfa05d654acbdfacd Mon Sep 17 00:00:00 2001 From: raymoo Date: Sat, 30 Sep 2017 06:23:52 -0700 Subject: [PATCH 076/183] Fix attached particle spawners far from spawn (#6479) * Fix attached particle spawners far from spawn When far from spawn, attached particle spawners did not spawn particles. --- src/particles.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/particles.cpp b/src/particles.cpp index e89e182e6..0caa4f796 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -326,6 +326,11 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) v3f ppos = m_player->getPosition() / BS; v3f pos = random_v3f(m_minpos, m_maxpos); + // Need to apply this first or the following check + // will be wrong for attached spawners + if (is_attached) + pos += attached_pos; + if (pos.getDistanceFrom(ppos) <= radius) { v3f vel = random_v3f(m_minvel, m_maxvel); v3f acc = random_v3f(m_minacc, m_maxacc); @@ -333,7 +338,6 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) if (is_attached) { // Apply attachment yaw and position pos.rotateXZBy(attached_yaw); - pos += attached_pos; vel.rotateXZBy(attached_yaw); acc.rotateXZBy(attached_yaw); } @@ -387,6 +391,11 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) v3f ppos = m_player->getPosition() / BS; v3f pos = random_v3f(m_minpos, m_maxpos); + // Need to apply this first or the following check + // will be wrong for attached spawners + if (is_attached) + pos += attached_pos; + if (pos.getDistanceFrom(ppos) <= radius) { v3f vel = random_v3f(m_minvel, m_maxvel); v3f acc = random_v3f(m_minacc, m_maxacc); @@ -394,7 +403,6 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) if (is_attached) { // Apply attachment yaw and position pos.rotateXZBy(attached_yaw); - pos += attached_pos; vel.rotateXZBy(attached_yaw); acc.rotateXZBy(attached_yaw); } From 6b0fb94d60f7ddd7345d9bf526733c125ea95397 Mon Sep 17 00:00:00 2001 From: paramat Date: Tue, 19 Sep 2017 16:39:30 +0100 Subject: [PATCH 077/183] CAO footstep sounds: Reduce gain to balance volume --- src/content_cao.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index d15c53e7a..e90bb90ca 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -1184,16 +1184,17 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) float moved = lastpos.getDistanceFrom(pos_translator.vect_show); m_step_distance_counter += moved; - if(m_step_distance_counter > 1.5*BS) - { - m_step_distance_counter = 0; - if(!m_is_local_player && m_prop.makes_footstep_sound) - { + if (m_step_distance_counter > 1.5f * BS) { + m_step_distance_counter = 0.0f; + if (!m_is_local_player && m_prop.makes_footstep_sound) { INodeDefManager *ndef = m_client->ndef(); - v3s16 p = floatToInt(getPosition() + v3f(0, - (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS); + v3s16 p = floatToInt(getPosition() + + v3f(0.0f, (m_prop.collisionbox.MinEdge.Y - 0.5f) * BS, 0.0f), BS); MapNode n = m_env->getMap().getNodeNoEx(p); SimpleSoundSpec spec = ndef->get(n).sound_footstep; + // Reduce footstep gain, as non-local-player footsteps are + // somehow louder. + spec.gain *= 0.6f; m_client->sound()->playSoundAt(spec, false, getPosition()); } } From 200e9cc4a25dd6161ace40c6a222bac2810ea104 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 2 Oct 2017 20:40:59 +0200 Subject: [PATCH 078/183] ParticleSpawner::step cleanup and rotation fix (#6486) * Particles: Move spawner code to a separate fucntion --- src/particles.cpp | 178 +++++++++++++++++----------------------------- src/particles.h | 10 ++- 2 files changed, 74 insertions(+), 114 deletions(-) diff --git a/src/particles.cpp b/src/particles.cpp index 0caa4f796..69bc31358 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -290,6 +290,59 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, ParticleSpawner::~ParticleSpawner() {} +void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, + bool is_attached, const v3f &attached_pos, float attached_yaw) +{ + v3f ppos = m_player->getPosition() / BS; + v3f pos = random_v3f(m_minpos, m_maxpos); + + // Need to apply this first or the following check + // will be wrong for attached spawners + if (is_attached) { + pos.rotateXZBy(attached_yaw); + pos += attached_pos; + } + + if (pos.getDistanceFrom(ppos) > radius) + return; + + v3f vel = random_v3f(m_minvel, m_maxvel); + v3f acc = random_v3f(m_minacc, m_maxacc); + + if (is_attached) { + // Apply attachment yaw + vel.rotateXZBy(attached_yaw); + acc.rotateXZBy(attached_yaw); + } + + float exptime = rand() / (float)RAND_MAX + * (m_maxexptime - m_minexptime) + + m_minexptime; + float size = rand() / (float)RAND_MAX + * (m_maxsize - m_minsize) + + m_minsize; + + m_particlemanager->addParticle(new Particle( + m_gamedef, + m_smgr, + m_player, + env, + pos, + vel, + acc, + exptime, + size, + m_collisiondetection, + m_collision_removal, + m_vertical, + m_texture, + v2f(0.0, 0.0), + v2f(1.0, 1.0), + m_animation, + m_glow + )); +} + void ParticleSpawner::step(float dtime, ClientEnvironment* env) { m_time += dtime; @@ -311,130 +364,33 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) } } - if (m_spawntime != 0) // Spawner exists for a predefined timespan - { - for(std::vector::iterator i = m_spawntimes.begin(); - i != m_spawntimes.end();) - { - if ((*i) <= m_time && m_amount > 0) - { + if (m_spawntime != 0) { + // Spawner exists for a predefined timespan + for (std::vector::iterator i = m_spawntimes.begin(); + i != m_spawntimes.end();) { + if ((*i) <= m_time && m_amount > 0) { m_amount--; // Pretend to, but don't actually spawn a particle if it is // attached to an unloaded object or distant from player. - if (!unloaded) { - v3f ppos = m_player->getPosition() / BS; - v3f pos = random_v3f(m_minpos, m_maxpos); + if (!unloaded) + spawnParticle(env, radius, is_attached, attached_pos, attached_yaw); - // Need to apply this first or the following check - // will be wrong for attached spawners - if (is_attached) - pos += attached_pos; - - if (pos.getDistanceFrom(ppos) <= radius) { - v3f vel = random_v3f(m_minvel, m_maxvel); - v3f acc = random_v3f(m_minacc, m_maxacc); - - if (is_attached) { - // Apply attachment yaw and position - pos.rotateXZBy(attached_yaw); - vel.rotateXZBy(attached_yaw); - acc.rotateXZBy(attached_yaw); - } - - float exptime = rand()/(float)RAND_MAX - *(m_maxexptime-m_minexptime) - +m_minexptime; - float size = rand()/(float)RAND_MAX - *(m_maxsize-m_minsize) - +m_minsize; - - Particle* toadd = new Particle( - m_gamedef, - m_smgr, - m_player, - env, - pos, - vel, - acc, - exptime, - size, - m_collisiondetection, - m_collision_removal, - m_vertical, - m_texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0), - m_animation, - m_glow); - m_particlemanager->addParticle(toadd); - } - } i = m_spawntimes.erase(i); - } - else - { + } else { ++i; } } - } - else // Spawner exists for an infinity timespan, spawn on a per-second base - { + } else { + // Spawner exists for an infinity timespan, spawn on a per-second base + // Skip this step if attached to an unloaded object if (unloaded) return; - for (int i = 0; i <= m_amount; i++) - { - if (rand()/(float)RAND_MAX < dtime) - { - // Do not spawn particle if distant from player - v3f ppos = m_player->getPosition() / BS; - v3f pos = random_v3f(m_minpos, m_maxpos); - // Need to apply this first or the following check - // will be wrong for attached spawners - if (is_attached) - pos += attached_pos; - - if (pos.getDistanceFrom(ppos) <= radius) { - v3f vel = random_v3f(m_minvel, m_maxvel); - v3f acc = random_v3f(m_minacc, m_maxacc); - - if (is_attached) { - // Apply attachment yaw and position - pos.rotateXZBy(attached_yaw); - vel.rotateXZBy(attached_yaw); - acc.rotateXZBy(attached_yaw); - } - - float exptime = rand()/(float)RAND_MAX - *(m_maxexptime-m_minexptime) - +m_minexptime; - float size = rand()/(float)RAND_MAX - *(m_maxsize-m_minsize) - +m_minsize; - - Particle* toadd = new Particle( - m_gamedef, - m_smgr, - m_player, - env, - pos, - vel, - acc, - exptime, - size, - m_collisiondetection, - m_collision_removal, - m_vertical, - m_texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0), - m_animation, - m_glow); - m_particlemanager->addParticle(toadd); - } - } + for (int i = 0; i <= m_amount; i++) { + if (rand() / (float)RAND_MAX < dtime) + spawnParticle(env, radius, is_attached, attached_pos, attached_yaw); } } } diff --git a/src/particles.h b/src/particles.h index eaec1f0fa..cb16d1c07 100644 --- a/src/particles.h +++ b/src/particles.h @@ -117,7 +117,7 @@ private: class ParticleSpawner { - public: +public: ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player, @@ -144,8 +144,12 @@ class ParticleSpawner bool get_expired () { return (m_amount <= 0) && m_spawntime != 0; } - private: - ParticleManager* m_particlemanager; +private: + void spawnParticle(ClientEnvironment *env, float radius, + bool is_attached, const v3f &attached_pos, + float attached_yaw); + + ParticleManager *m_particlemanager; float m_time; IGameDef *m_gamedef; scene::ISceneManager *m_smgr; From 0034abb56075b841da441b1c8ad2e7779f42eac9 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 7 Oct 2017 15:11:07 +0200 Subject: [PATCH 079/183] Unkown nodes: Provide position on interact (#6505) * Unkown nodes: Provide position on interact --- src/script/cpp_api/s_item.cpp | 11 +++++++---- src/script/cpp_api/s_item.h | 2 +- src/script/cpp_api/s_node.cpp | 16 ++++++++-------- src/script/cpp_api/s_nodemeta.cpp | 12 ++++++------ 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index e13da1c86..0654ca3dc 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -211,7 +211,8 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user, // function onto the stack // If core.registered_items[name] doesn't exist, core.nodedef_default // is tried instead so unknown items can still be manipulated to some degree -bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname) +bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname, + const v3s16 *p) { lua_State* L = getStack(); @@ -222,10 +223,12 @@ bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname) lua_getfield(L, -1, name); lua_remove(L, -2); // Remove registered_items // Should be a table - if(lua_type(L, -1) != LUA_TTABLE) - { + if (lua_type(L, -1) != LUA_TTABLE) { // Report error and clean up - errorstream << "Item \"" << name << "\" not defined" << std::endl; + errorstream << "Item \"" << name << "\" not defined"; + if (p) + errorstream << " at position " << PP(*p); + errorstream << std::endl; lua_pop(L, 1); // Try core.nodedef_default instead diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h index 7350a71c5..b4b02b0c5 100644 --- a/src/script/cpp_api/s_item.h +++ b/src/script/cpp_api/s_item.h @@ -53,7 +53,7 @@ protected: friend class LuaItemStack; friend class ModApiItemMod; - bool getItemCallback(const char *name, const char *callbackname); + bool getItemCallback(const char *name, const char *callbackname, const v3s16 *p = nullptr); void pushPointedThing(const PointedThing& pointed); }; diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index 1ae8f58a5..91e153c18 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -107,7 +107,7 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node, INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack - if (!getItemCallback(ndef->get(node).name.c_str(), "on_punch")) + if (!getItemCallback(ndef->get(node).name.c_str(), "on_punch", &p)) return false; // Call function @@ -130,7 +130,7 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node, INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack - if (!getItemCallback(ndef->get(node).name.c_str(), "on_dig")) + if (!getItemCallback(ndef->get(node).name.c_str(), "on_dig", &p)) return false; // Call function @@ -151,7 +151,7 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node) INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack - if (!getItemCallback(ndef->get(node).name.c_str(), "on_construct")) + if (!getItemCallback(ndef->get(node).name.c_str(), "on_construct", &p)) return; // Call function @@ -169,7 +169,7 @@ void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node) INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack - if (!getItemCallback(ndef->get(node).name.c_str(), "on_destruct")) + if (!getItemCallback(ndef->get(node).name.c_str(), "on_destruct", &p)) return; // Call function @@ -187,7 +187,7 @@ bool ScriptApiNode::node_on_flood(v3s16 p, MapNode node, MapNode newnode) INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack - if (!getItemCallback(ndef->get(node).name.c_str(), "on_flood")) + if (!getItemCallback(ndef->get(node).name.c_str(), "on_flood", &p)) return false; // Call function @@ -208,7 +208,7 @@ void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node) INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack - if (!getItemCallback(ndef->get(node).name.c_str(), "after_destruct")) + if (!getItemCallback(ndef->get(node).name.c_str(), "after_destruct", &p)) return; // Call function @@ -227,7 +227,7 @@ bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime) INodeDefManager *ndef = getServer()->ndef(); // Push callback function on stack - if (!getItemCallback(ndef->get(node).name.c_str(), "on_timer")) + if (!getItemCallback(ndef->get(node).name.c_str(), "on_timer", &p)) return false; // Call function @@ -255,7 +255,7 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p, return; // Push callback function on stack - if (!getItemCallback(ndef->get(node).name.c_str(), "on_receive_fields")) + if (!getItemCallback(ndef->get(node).name.c_str(), "on_receive_fields", &p)) return; // Call function diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp index d050c0bc9..2f726dfa2 100644 --- a/src/script/cpp_api/s_nodemeta.cpp +++ b/src/script/cpp_api/s_nodemeta.cpp @@ -45,7 +45,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p, // Push callback function on stack std::string nodename = ndef->get(node).name; - if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_move")) + if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_move", &p)) return count; // function(pos, from_list, from_index, to_list, to_index, count, player) @@ -83,7 +83,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p, // Push callback function on stack std::string nodename = ndef->get(node).name; - if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_put")) + if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_put", &p)) return stack.count; // Call function(pos, listname, index, stack, player) @@ -119,7 +119,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p, // Push callback function on stack std::string nodename = ndef->get(node).name; - if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_take")) + if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_take", &p)) return stack.count; // Call function(pos, listname, index, count, player) @@ -156,7 +156,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p, // Push callback function on stack std::string nodename = ndef->get(node).name; - if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_move")) + if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_move", &p)) return; // function(pos, from_list, from_index, to_list, to_index, count, player) @@ -189,7 +189,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p, // Push callback function on stack std::string nodename = ndef->get(node).name; - if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_put")) + if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_put", &p)) return; // Call function(pos, listname, index, stack, player) @@ -220,7 +220,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p, // Push callback function on stack std::string nodename = ndef->get(node).name; - if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_take")) + if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_take", &p)) return; // Call function(pos, listname, index, stack, player) From c56c3d8d6f5d050acea30a70c058fd442cc41389 Mon Sep 17 00:00:00 2001 From: Rob Blanckaert Date: Sat, 7 Oct 2017 09:11:48 -0400 Subject: [PATCH 080/183] Add setting for near plane distance. (#6395) * Allow setting the near plane * - Add near_plane limit of 0.5 to prevent x-ray. - Add more details to near_plane setting. --- builtin/settingtypes.txt | 6 ++++++ src/camera.cpp | 2 ++ src/defaultsettings.cpp | 1 + 3 files changed, 9 insertions(+) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 0da66750a..ddb0afacd 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -476,6 +476,12 @@ pause_fps_max (FPS in pause menu) int 20 # View distance in nodes. viewing_range (Viewing range) int 100 20 4000 +# Camera near plane distance in nodes, between 0 and 0.5 +# Most users will not need to change this. +# Increasing can reduce artifacting on weaker GPUs. +# 0.1 = Default, 0.25 = Good value for weaker tablets. +near_plane (Near plane) float 0.1 0 0.5 + # Width component of the initial window size. screenW (Screen width) int 800 diff --git a/src/camera.cpp b/src/camera.cpp index 52a42a3a9..6957508cc 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -487,7 +487,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, void Camera::updateViewingRange() { f32 viewing_range = g_settings->getFloat("viewing_range"); + f32 near_plane = g_settings->getFloat("near_plane"); m_draw_control.wanted_range = viewing_range; + m_cameranode->setNearValue(rangelim(near_plane, 0.0f, 0.5f) * BS); if (m_draw_control.range_all) { m_cameranode->setFarValue(100000.0); return; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 3378e8b4a..181a12b5e 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -125,6 +125,7 @@ void set_default_settings(Settings *settings) settings->setDefault("fps_max", "60"); settings->setDefault("pause_fps_max", "20"); settings->setDefault("viewing_range", "100"); + settings->setDefault("near_plane", "0.1"); settings->setDefault("screenW", "800"); settings->setDefault("screenH", "600"); settings->setDefault("autosave_screensize", "true"); From d215198fe8dd9f19e8dc815bdf13989520318758 Mon Sep 17 00:00:00 2001 From: adrido Date: Sat, 7 Oct 2017 15:13:13 +0200 Subject: [PATCH 081/183] Replace deprecated WINAPI GetVersionInfoEx (#6496) * Replace deprecated WINAPI GetVersionInfoEx --- src/CMakeLists.txt | 2 +- src/porting.cpp | 36 +++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7c1a4eee0..b1650f376 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -268,7 +268,7 @@ if(WIN32) else() # Probably MinGW = GCC set(PLATFORM_LIBS "") endif() - set(PLATFORM_LIBS ws2_32.lib shlwapi.lib ${PLATFORM_LIBS}) + set(PLATFORM_LIBS ws2_32.lib version.lib shlwapi.lib ${PLATFORM_LIBS}) # Zlib stuff set(ZLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../../zlib/zlib-1.2.5" diff --git a/src/porting.cpp b/src/porting.cpp index 10b6fc940..0a9de2a59 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include + #include #endif #if !defined(_WIN32) #include @@ -181,20 +182,26 @@ bool detectMSVCBuildDir(const std::string &path) std::string get_sysinfo() { #ifdef _WIN32 - OSVERSIONINFO osvi; - std::ostringstream oss; - std::string tmp; - ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osvi); - tmp = osvi.szCSDVersion; - std::replace(tmp.begin(), tmp.end(), ' ', '_'); - oss << "Windows/" << osvi.dwMajorVersion << "." - << osvi.dwMinorVersion; - if (osvi.szCSDVersion[0]) - oss << "-" << tmp; - oss << " "; + std::ostringstream oss; + LPSTR filePath = new char[MAX_PATH]; + UINT blockSize; + VS_FIXEDFILEINFO *fixedFileInfo; + + GetSystemDirectoryA(filePath, MAX_PATH); + PathAppendA(filePath, "kernel32.dll"); + + DWORD dwVersionSize = GetFileVersionInfoSizeA(filePath, NULL); + LPBYTE lpVersionInfo = new BYTE[dwVersionSize]; + + GetFileVersionInfoA(filePath, 0, dwVersionSize, lpVersionInfo); + VerQueryValueA(lpVersionInfo, "\\", (LPVOID *)&fixedFileInfo, &blockSize); + + oss << "Windows/" + << HIWORD(fixedFileInfo->dwProductVersionMS) << '.' // Major + << LOWORD(fixedFileInfo->dwProductVersionMS) << '.' // Minor + << HIWORD(fixedFileInfo->dwProductVersionLS) << ' '; // Build + #ifdef _WIN64 oss << "x86_64"; #else @@ -205,6 +212,9 @@ std::string get_sysinfo() oss << "x86"; #endif + delete[] lpVersionInfo; + delete[] filePath; + return oss.str(); #else struct utsname osinfo; From 9dc1f2d638ddde253fbfac26c856b5da4ea1495f Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Tue, 10 Oct 2017 00:47:37 +0200 Subject: [PATCH 082/183] NetworkPacket::putRawPacket: resize m_data to datasize + memcpy In some cases NetworkPacket was created using default constructor and m_data is not properly sized. This fixed out of bounds memory copy Also use memcpy instead of std::vector affectation to enhance packet creation --- src/network/networkpacket.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp index f7a6499dd..8e06ae104 100644 --- a/src/network/networkpacket.cpp +++ b/src/network/networkpacket.cpp @@ -58,9 +58,11 @@ void NetworkPacket::putRawPacket(u8 *data, u32 datasize, u16 peer_id) m_datasize = datasize - 2; m_peer_id = peer_id; + m_data.resize(m_datasize); + // split command and datas m_command = readU16(&data[0]); - m_data = std::vector(&data[2], &data[2 + m_datasize]); + memcpy(&m_data[0], &data[2], m_datasize); } const char* NetworkPacket::getString(u32 from_offset) From 0129c9a9dd149f4e5efaed1879c1c3058cbc4d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Tue, 10 Oct 2017 12:27:08 +0200 Subject: [PATCH 083/183] Thread: fix a crash on Windows due to data race condition on Thread::m_start_finished_mutex (#6515) --- src/threading/thread.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp index 1909da61d..e3dde24cd 100644 --- a/src/threading/thread.cpp +++ b/src/threading/thread.cpp @@ -103,8 +103,8 @@ Thread::~Thread() kill(); // Make sure start finished mutex is unlocked before it's destroyed - m_start_finished_mutex.try_lock(); - m_start_finished_mutex.unlock(); + if (m_start_finished_mutex.try_lock()) + m_start_finished_mutex.unlock(); } @@ -267,6 +267,10 @@ DWORD WINAPI Thread::threadProc(LPVOID param) thr->m_retval = thr->run(); thr->m_running = false; + // Unlock m_start_finished_mutex to prevent data race condition on Windows. + // On Windows with VS2017 build TerminateThread is called and this mutex is not + // released. We try to unlock it from caller thread and it's refused by system. + thr->m_start_finished_mutex.unlock(); g_logger.deregisterThread(); // 0 is returned here to avoid an unnecessary ifdef clause From cc48c95ca7abe6949e637ca537a3e75a2de2fac4 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 14 Oct 2017 18:28:56 +0100 Subject: [PATCH 084/183] Profiler: Fix var args not being passed to callback register function Fixes #6517 --- builtin/profiler/instrumentation.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/profiler/instrumentation.lua b/builtin/profiler/instrumentation.lua index be3a460e5..7c21859d3 100644 --- a/builtin/profiler/instrumentation.lua +++ b/builtin/profiler/instrumentation.lua @@ -133,7 +133,7 @@ local function instrument_register(func, func_name) return func(instrument { func = callback, func_name = register_name - }), ... + }, ...) end end From 0041bcc73e04b9e24c0148c0a735d271d2d1b893 Mon Sep 17 00:00:00 2001 From: "Esteban I. RM" Date: Sun, 15 Oct 2017 02:52:05 -0300 Subject: [PATCH 085/183] Don't try to craft a non-existent item --- src/craftdef.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 286d1eada..210fe9f03 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -923,8 +923,19 @@ public: << " against " << def->dump() << std::endl;*/ if (def->check(input, gamedef)) { + // Check if the crafted node/item exists + CraftOutput out = def->getOutput(input, gamedef); + ItemStack is; + is.deSerialize(out.item, gamedef->idef()); + if (!is.isKnown(gamedef->idef())) { + infostream << "trying to craft non-existent " + << out.item << ", ignoring recipe" << std::endl; + continue; + } + // Get output, then decrement input (if requested) - output = def->getOutput(input, gamedef); + output = out; + if (decrementInput) def->decrementInput(input, output_replacement, gamedef); /*errorstream << "Check RETURNS TRUE" << std::endl;*/ From 2f969196b86a9ffd4ef3bce9b17fcfb49ec89cd2 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 19 Oct 2017 21:39:45 -0700 Subject: [PATCH 086/183] Correct `prot_vers` in lua_api.txt. We should avoid providing incorrect struct members in documentation since people will be coding based on them. --- doc/lua_api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c1799d79e..3d461735b 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2205,7 +2205,7 @@ Helper functions max_jitter = 0.5, -- maximum packet time jitter avg_jitter = 0.03, -- average packet time jitter connection_uptime = 200, -- seconds since client connected - prot_vers = 31, -- protocol version used by client + protocol_version = 32, -- protocol version used by client -- following information is available on debug build only!!! -- DO NOT USE IN MODS --ser_vers = 26, -- serialization version used by client From 7b8288d605260a92693a93a85134555a8c975bb9 Mon Sep 17 00:00:00 2001 From: raymoo Date: Sat, 28 Oct 2017 01:30:50 -0700 Subject: [PATCH 087/183] Fix default item callbacks to work with nil users (#5819) * Fix default item callbacks to work with nil users * item.lua: Handle node drops for invalid players The if-condition for the dropping loop is the same as `inv`, which means that the 2nd possible definition of `give_item` is never used. Remove redundant `local _, dropped_item` --- builtin/game/chatcommands.lua | 4 +- builtin/game/item.lua | 158 +++++++++++++++++----------- doc/lua_api.txt | 3 + games/minimal/mods/default/init.lua | 4 +- 4 files changed, 102 insertions(+), 67 deletions(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 3dfc29ffa..e788a2a54 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -653,8 +653,8 @@ core.register_chatcommand("pulverize", { core.rollback_punch_callbacks = {} core.register_on_punchnode(function(pos, node, puncher) - local name = puncher:get_player_name() - if core.rollback_punch_callbacks[name] then + local name = puncher and puncher:get_player_name() + if name and core.rollback_punch_callbacks[name] then core.rollback_punch_callbacks[name](pos, node, puncher) core.rollback_punch_callbacks[name] = nil end diff --git a/builtin/game/item.lua b/builtin/game/item.lua index 1a8bce903..09e3ea999 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -215,6 +215,8 @@ function core.get_node_drops(node, toolname) end if item.tools ~= nil then good_tool = false + end + if item.tools ~= nil and toolname then for _, tool in ipairs(item.tools) do if tool:sub(1, 1) == '~' then good_tool = toolname:find(tool:sub(2)) ~= nil @@ -225,7 +227,7 @@ function core.get_node_drops(node, toolname) break end end - end + end if good_rarity and good_tool then got_count = got_count + 1 for _, add_item in ipairs(item.items) do @@ -245,6 +247,20 @@ function core.get_node_drops(node, toolname) return got_items end +local function user_name(user) + return user and user:get_player_name() or "" +end + +local function is_protected(pos, name) + return core.is_protected(pos, name) and + not minetest.check_player_privs(name, "protection_bypass") +end + +-- Returns a logging function. For empty names, does not log. +local function make_log(name) + return name ~= "" and core.log or function() end +end + function core.item_place_node(itemstack, placer, pointed_thing, param2) local def = itemstack:get_definition() if def.type ~= "node" or pointed_thing.type ~= "node" then @@ -255,10 +271,11 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) local oldnode_under = core.get_node_or_nil(under) local above = pointed_thing.above local oldnode_above = core.get_node_or_nil(above) - local playername = placer:get_player_name() + local playername = user_name(placer) + local log = make_log(playername) if not oldnode_under or not oldnode_above then - core.log("info", playername .. " tried to place" + log("info", playername .. " tried to place" .. " node in unloaded position " .. core.pos_to_string(above)) return itemstack, false end @@ -269,7 +286,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) olddef_above = olddef_above or core.nodedef_default if not olddef_above.buildable_to and not olddef_under.buildable_to then - core.log("info", playername .. " tried to place" + log("info", playername .. " tried to place" .. " node in invalid position " .. core.pos_to_string(above) .. ", replacing " .. oldnode_above.name) return itemstack, false @@ -280,13 +297,12 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) -- If node under is buildable_to, place into it instead (eg. snow) if olddef_under.buildable_to then - core.log("info", "node under is buildable to") + log("info", "node under is buildable to") place_to = {x = under.x, y = under.y, z = under.z} end - if core.is_protected(place_to, playername) and - not minetest.check_player_privs(placer, "protection_bypass") then - core.log("action", playername + if is_protected(place_to, playername) then + log("action", playername .. " tried to place " .. def.name .. " at protected position " .. core.pos_to_string(place_to)) @@ -294,7 +310,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) return itemstack end - core.log("action", playername .. " places node " + log("action", playername .. " places node " .. def.name .. " at " .. core.pos_to_string(place_to)) local oldnode = core.get_node(place_to) @@ -314,7 +330,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) -- Calculate the direction for furnaces and chests and stuff elseif (def.paramtype2 == "facedir" or def.paramtype2 == "colorfacedir") and not param2 then - local placer_pos = placer:getpos() + local placer_pos = placer and placer:getpos() if placer_pos then local dir = { x = above.x - placer_pos.x, @@ -322,7 +338,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) z = above.z - placer_pos.z } newnode.param2 = core.dir_to_facedir(dir) - core.log("action", "facedir: " .. newnode.param2) + log("action", "facedir: " .. newnode.param2) end end @@ -348,7 +364,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) -- Check if the node is attached and if it can be placed there if core.get_item_group(def.name, "attached_node") ~= 0 and not builtin_shared.check_attached_node(place_to, newnode) then - core.log("action", "attached node " .. def.name .. + log("action", "attached node " .. def.name .. " can not be placed at " .. core.pos_to_string(place_to)) return itemstack, false end @@ -419,28 +435,27 @@ function core.item_secondary_use(itemstack, placer) end function core.item_drop(itemstack, dropper, pos) - if dropper and dropper:is_player() then - local v = dropper:get_look_dir() - local p = {x=pos.x, y=pos.y+1.2, z=pos.z} - local cs = itemstack:get_count() + local dropper_is_player = dropper and dropper:is_player() + local p = table.copy(pos) + local cnt = itemstack:get_count() + if dropper_is_player then + p.y = p.y + 1.2 if dropper:get_player_control().sneak then - cs = 1 + cnt = 1 end - local item = itemstack:take_item(cs) - local obj = core.add_item(p, item) - if obj then - v.x = v.x*2 - v.y = v.y*2 + 2 - v.z = v.z*2 - obj:setvelocity(v) + end + local item = itemstack:take_item(cnt) + local obj = core.add_item(p, item) + if obj then + if dropper_is_player then + local dir = dropper:get_look_dir() + dir.x = dir.x * 2.9 + dir.y = dir.y * 2.9 + 2 + dir.z = dir.z * 2.9 + obj:set_velocity(dir) obj:get_luaentity().dropped_by = dropper:get_player_name() - return itemstack - end - - else - if core.add_item(pos, itemstack) then - return itemstack end + return itemstack end -- If we reach this, adding the object to the -- environment failed @@ -461,7 +476,8 @@ function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed itemstack:add_item(replace_with_item) else local inv = user:get_inventory() - if inv:room_for_item("main", {name=replace_with_item}) then + -- Check if inv is null, since non-players don't have one + if inv and inv:room_for_item("main", {name=replace_with_item}) then inv:add_item("main", replace_with_item) else local pos = user:getpos() @@ -476,7 +492,9 @@ end function core.item_eat(hp_change, replace_with_item) return function(itemstack, user, pointed_thing) -- closure - return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing) + if user then + return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing) + end end end @@ -493,63 +511,75 @@ end function core.handle_node_drops(pos, drops, digger) -- Add dropped items to object's inventory - if digger:get_inventory() then - local _, dropped_item - for _, dropped_item in ipairs(drops) do - local left = digger:get_inventory():add_item("main", dropped_item) - if not left:is_empty() then - local p = { - x = pos.x + math.random()/2-0.25, - y = pos.y + math.random()/2-0.25, - z = pos.z + math.random()/2-0.25, - } - core.add_item(p, left) - end + local inv = digger and digger:get_inventory() + local give_item + if inv then + give_item = function(item) + return inv:add_item("main", item) + end + else + give_item = function(item) + return item + end + end + + for _, dropped_item in pairs(drops) do + local left = give_item(dropped_item) + if not left:is_empty() then + local p = { + x = pos.x + math.random()/2-0.25, + y = pos.y + math.random()/2-0.25, + z = pos.z + math.random()/2-0.25, + } + core.add_item(p, left) end end end function core.node_dig(pos, node, digger) + local diggername = user_name(digger) + local log = make_log(diggername) local def = core.registered_nodes[node.name] if def and (not def.diggable or (def.can_dig and not def.can_dig(pos, digger))) then - core.log("info", digger:get_player_name() .. " tried to dig " + log("info", diggername .. " tried to dig " .. node.name .. " which is not diggable " .. core.pos_to_string(pos)) return end - if core.is_protected(pos, digger:get_player_name()) and - not minetest.check_player_privs(digger, "protection_bypass") then - core.log("action", digger:get_player_name() + if is_protected(pos, diggername) then + log("action", diggername .. " tried to dig " .. node.name .. " at protected position " .. core.pos_to_string(pos)) - core.record_protection_violation(pos, digger:get_player_name()) + core.record_protection_violation(pos, diggername) return end - core.log('action', digger:get_player_name() .. " digs " + log('action', diggername .. " digs " .. node.name .. " at " .. core.pos_to_string(pos)) - local wielded = digger:get_wielded_item() - local drops = core.get_node_drops(node, wielded:get_name()) + local wielded = digger and digger:get_wielded_item() + local drops = core.get_node_drops(node, wielded and wielded:get_name()) - local wdef = wielded:get_definition() - local tp = wielded:get_tool_capabilities() - local dp = core.get_dig_params(def and def.groups, tp) - if wdef and wdef.after_use then - wielded = wdef.after_use(wielded, digger, node, dp) or wielded - else - -- Wear out tool - if not core.settings:get_bool("creative_mode") then - wielded:add_wear(dp.wear) - if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then - core.sound_play(wdef.sound.breaks, {pos = pos, gain = 0.5}) + if wielded then + local wdef = wielded:get_definition() + local tp = wielded:get_tool_capabilities() + local dp = core.get_dig_params(def and def.groups, tp) + if wdef and wdef.after_use then + wielded = wdef.after_use(wielded, digger, node, dp) or wielded + else + -- Wear out tool + if not core.settings:get_bool("creative_mode") then + wielded:add_wear(dp.wear) + if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then + core.sound_play(wdef.sound.breaks, {pos = pos, gain = 0.5}) + end end end + digger:set_wielded_item(wielded) end - digger:set_wielded_item(wielded) -- Handle drops core.handle_node_drops(pos, drops, digger) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 3d461735b..d6b8fc5bb 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2287,6 +2287,7 @@ Call these functions only at load time! * `minetest.register_on_placenode(func(pos, newnode, placer, oldnode, itemstack, pointed_thing))` * Called when a node has been placed * If return `true` no item is taken from `itemstack` + * `placer` may be any valid ObjectRef or nil. * **Not recommended**; use `on_construct` or `after_place_node` in node definition whenever possible * `minetest.register_on_dignode(func(pos, oldnode, digger))` @@ -2992,6 +2993,7 @@ These functions return the leftover itemstack. * Returns true, if player `name` shouldn't be abled to dig at `pos` or do other actions, defineable by mods, due to some mod-defined ownership-like concept. Returns false or nil, if the player is allowed to do such actions. + * `name` will be "" for non-players or unknown players. * This function should be overridden by protection mods and should be used to check if a player can interact at a position. * This function should call the old version of itself if the position is not @@ -4276,6 +4278,7 @@ Definition tables ^ Called after constructing node when node was placed using minetest.item_place_node / minetest.place_node ^ If return true no item is taken from itemstack + ^ `placer` may be any valid ObjectRef or nil ^ default: nil ]] after_dig_node = func(pos, oldnode, oldmetadata, digger), --[[ ^ oldmetadata is in table format diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index fcdf93547..bfd938211 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1270,7 +1270,9 @@ minetest.register_node("default:chest_locked", { sounds = default.node_sound_wood_defaults(), after_place_node = function(pos, placer) local meta = minetest.get_meta(pos) - meta:set_string("owner", placer:get_player_name() or "") + local pname = + placer and placer:get_player_name() or "" + meta:set_string("owner", pname) meta:set_string("infotext", "Locked Chest (owned by ".. meta:get_string("owner")..")") end, From 73baeb82ef686991dfbfc1a8b915cbe8e657e33e Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Sat, 28 Oct 2017 01:33:47 -0700 Subject: [PATCH 088/183] Avoid filtering low-res textures for animated meshes (incl. players) (#6562) --- src/content_cao.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index e90bb90ca..994f0492a 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -1355,6 +1355,13 @@ void GenericCAO::updateTextures(std::string mod) material.setFlag(video::EMF_LIGHTING, false); material.setFlag(video::EMF_BILINEAR_FILTER, false); + // don't filter low-res textures, makes them look blurry + // player models have a res of 64 + const core::dimension2d &size = texture->getOriginalSize(); + const u32 res = std::min(size.Height, size.Width); + use_trilinear_filter &= res > 64; + use_bilinear_filter &= res > 64; + m_animated_meshnode->getMaterial(i) .setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter); m_animated_meshnode->getMaterial(i) From 416c4535c7e302191903c2a5c174de4f41a62e1c Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 29 Oct 2017 18:31:50 +0000 Subject: [PATCH 089/183] Fix day_night_ratio_do_override not being initialised server-side Causes get_day_night_ratio() to return unpredictable results. --- src/remoteplayer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remoteplayer.h b/src/remoteplayer.h index ee0d625b6..cbb9386ce 100644 --- a/src/remoteplayer.h +++ b/src/remoteplayer.h @@ -156,7 +156,7 @@ private: float m_chat_message_allowance; u16 m_message_rate_overhead; - bool m_day_night_ratio_do_override; + bool m_day_night_ratio_do_override = false; float m_day_night_ratio; std::string hud_hotbar_image; std::string hud_hotbar_selected_image; From 4d9bf75d3ab6a181b3113c3231d2e41245795a60 Mon Sep 17 00:00:00 2001 From: Rob Blanckaert Date: Mon, 30 Oct 2017 00:18:18 -0700 Subject: [PATCH 090/183] Add sha1 to lua utils. (#6563) --- doc/client_lua_api.md | 3 +++ doc/lua_api.txt | 3 +++ src/script/lua_api/l_util.cpp | 31 +++++++++++++++++++++++++++++++ src/script/lua_api/l_util.h | 3 +++ 4 files changed, 40 insertions(+) diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index ab72bbccf..8ef3bfb2e 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -628,6 +628,9 @@ Minetest namespace reference version entirely. To check for the presence of engine features, test whether the functions exported by the wanted features exist. For example: `if minetest.nodeupdate then ... end`. +* `minetest.sha1(data, [raw])`: returns the sha1 hash of data + * `data`: string of data to hash + * `raw`: return raw bytes instead of hex digits, default: false ### Logging * `minetest.debug(...)` diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d6b8fc5bb..13881ef70 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2234,6 +2234,9 @@ Helper functions version entirely. To check for the presence of engine features, test whether the functions exported by the wanted features exist. For example: `if minetest.nodeupdate then ... end`. +* `minetest.sha1(data, [raw])`: returns the sha1 hash of data + * `data`: string of data to hash + * `raw`: return raw bytes instead of hex digits, default: false ### Logging * `minetest.debug(...)` diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index c4a988e07..901105fe0 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -36,6 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/base64.h" #include "config.h" #include "version.h" +#include "util/hex.h" +#include "util/sha1.h" #include @@ -422,6 +424,32 @@ int ModApiUtil::l_get_version(lua_State *L) return 1; } +int ModApiUtil::l_sha1(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + size_t size; + const char *data = luaL_checklstring(L, 1, &size); + bool hex = !lua_isboolean(L, 2) || !lua_toboolean(L, 2); + + // Compute actual checksum of data + std::string data_sha1; + { + SHA1 ctx; + ctx.addBytes(data, size); + unsigned char *data_tmpdigest = ctx.getDigest(); + data_sha1.assign((char*) data_tmpdigest, 20); + free(data_tmpdigest); + } + + if (hex) { + std::string sha1_hex = hex_encode(data_sha1); + lua_pushstring(L, sha1_hex.c_str()); + } else { + lua_pushlstring(L, data_sha1.data(), data_sha1.size()); + } + + return 1; +} void ModApiUtil::Initialize(lua_State *L, int top) { @@ -454,6 +482,7 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(decode_base64); API_FCT(get_version); + API_FCT(sha1); LuaSettings::create(L, g_settings, g_settings_path); lua_setfield(L, top, "settings"); @@ -479,6 +508,7 @@ void ModApiUtil::InitializeClient(lua_State *L, int top) API_FCT(decode_base64); API_FCT(get_version); + API_FCT(sha1); } void ModApiUtil::InitializeAsync(lua_State *L, int top) @@ -504,6 +534,7 @@ void ModApiUtil::InitializeAsync(lua_State *L, int top) API_FCT(decode_base64); API_FCT(get_version); + API_FCT(sha1); LuaSettings::create(L, g_settings, g_settings_path); lua_setfield(L, top, "settings"); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index b75d9db29..16f4edea8 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -93,6 +93,9 @@ private: // get_version() static int l_get_version(lua_State *L); + // sha1(string, raw) + static int l_sha1(lua_State *L); + public: static void Initialize(lua_State *L, int top); static void InitializeAsync(lua_State *L, int top); From 6808a3d144c6c8f4aa4f54196fe6cedbda1f0805 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 4 Nov 2017 22:19:27 +0100 Subject: [PATCH 091/183] httpfetch: Enable gzip support --- src/httpfetch.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index 3b3f5d331..f7d20100d 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -248,6 +248,7 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_, curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1); + curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip"); std::string bind_address = g_settings->get("bind_address"); if (!bind_address.empty()) { From 0cfe3a810aee9311e8df811b28ae86948f924ae0 Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Sat, 4 Nov 2017 18:05:47 -0700 Subject: [PATCH 092/183] Do not scale texture unless necessary. This avoids scaling textures to 'texture_min_size' unless it is actually required (because either auto-scaling or bi/trilinear filtering is enabled) --- src/client/tile.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 99495132b..cfdff1bb2 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1805,7 +1805,8 @@ bool TextureSource::generateImagePart(std::string part_of_name, * mix high- and low-res textures, or for mods with least-common-denominator * textures that don't have the resources to offer high-res alternatives. */ - s32 scaleto = g_settings->getS32("texture_min_size"); + const bool filter = m_setting_trilinear_filter || m_setting_bilinear_filter; + const s32 scaleto = filter ? g_settings->getS32("texture_min_size") : 1; if (scaleto > 1) { const core::dimension2d dim = baseimg->getDimension(); From a65a46b8891255011e0f0b19490fcb7cf306f076 Mon Sep 17 00:00:00 2001 From: Ezhh Date: Sun, 5 Nov 2017 10:15:32 +0000 Subject: [PATCH 093/183] Fix Settings tab formspec alignment (#6585) --- builtin/mainmenu/tab_settings.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index 5a8cc19b8..bb1318fd9 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -191,7 +191,7 @@ local function formspec(tabview, name, tabdata) .. getSettingIndex.NodeHighlighting() .. "]" .. "dropdown[0.25,3.6;3.3;dd_leaves_style;" .. dd_options.leaves[1] .. ";" .. getSettingIndex.Leaves() .. "]" .. - "box[3.75,0;3.75,4.45;#999999]" .. + "box[3.75,0;3.75,4.5;#999999]" .. "label[3.85,0.1;" .. fgettext("Texturing:") .. "]" .. "dropdown[3.85,0.55;3.85;dd_filters;" .. dd_options.filters[1] .. ";" .. getSettingIndex.Filter() .. "]" .. @@ -203,7 +203,7 @@ local function formspec(tabview, name, tabdata) "label[3.85,3.45;" .. fgettext("Screen:") .. "]" .. "checkbox[3.85,3.6;cb_autosave_screensize;" .. fgettext("Autosave screen size") .. ";" .. dump(core.settings:get_bool("autosave_screensize")) .. "]" .. - "box[7.75,0;4,4.4;#999999]" .. + "box[7.75,0;4,4.5;#999999]" .. "checkbox[8,0;cb_shaders;" .. fgettext("Shaders") .. ";" .. dump(core.settings:get_bool("enable_shaders")) .. "]" From 313ca53b368abf376d3207b32fb9c8fd4291965f Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Sat, 3 Jun 2017 09:51:48 +0700 Subject: [PATCH 094/183] Fix issue Minetest crash when custom font path is not exist We try to use default fallback for both mono and main font when custom font path is not exist. This way, if Minetest is not corrupted, we could avoid crash. --- src/fontengine.cpp | 68 ++++++++++++++++++++++++++++++++++++---------- src/settings.cpp | 43 +++++++++++++++++++++++++++++ src/settings.h | 4 +++ 3 files changed, 100 insertions(+), 15 deletions(-) diff --git a/src/fontengine.cpp b/src/fontengine.cpp index da327c3f6..8eaf53c9f 100644 --- a/src/fontengine.cpp +++ b/src/fontengine.cpp @@ -341,32 +341,70 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode) font_path.c_str(), size, true, true, font_shadow, font_shadow_alpha); - if (font != NULL) { + if (font) { m_font_cache[mode][basesize] = font; return; } - // try fallback font - errorstream << "FontEngine: failed to load: " << font_path << ", trying to fall back " - "to fallback font" << std::endl; + if (font_config_prefix == "mono_") { + const std::string &mono_font_path = m_settings->getDefault("mono_font_path"); - font_path = g_settings->get(font_config_prefix + "fallback_font_path"); + if (font_path != mono_font_path) { + // try original mono font + errorstream << "FontEngine: failed to load custom mono " + "font: " << font_path << ", trying to fall back to " + "original mono font" << std::endl; - font = gui::CGUITTFont::createTTFont(m_env, - font_path.c_str(), size, true, true, font_shadow, - font_shadow_alpha); + font = gui::CGUITTFont::createTTFont(m_env, + mono_font_path.c_str(), size, true, true, + font_shadow, font_shadow_alpha); - if (font != NULL) { - m_font_cache[mode][basesize] = font; - return; + if (font) { + m_font_cache[mode][basesize] = font; + return; + } + } + } else { + // try fallback font + errorstream << "FontEngine: failed to load: " << font_path << + ", trying to fall back to fallback font" << std::endl; + + font_path = g_settings->get(font_config_prefix + "fallback_font_path"); + + font = gui::CGUITTFont::createTTFont(m_env, + font_path.c_str(), size, true, true, font_shadow, + font_shadow_alpha); + + if (font) { + m_font_cache[mode][basesize] = font; + return; + } + + const std::string &fallback_font_path = m_settings->getDefault("fallback_font_path"); + + if (font_path != fallback_font_path) { + // try original fallback font + errorstream << "FontEngine: failed to load custom fallback " + "font: " << font_path << ", trying to fall back to " + "original fallback font" << std::endl; + + font = gui::CGUITTFont::createTTFont(m_env, + fallback_font_path.c_str(), size, true, true, + font_shadow, font_shadow_alpha); + + if (font) { + m_font_cache[mode][basesize] = font; + return; + } + } } // give up errorstream << "FontEngine: failed to load freetype font: " << font_path << std::endl; - errorstream << "minetest can not continue without a valid font. Please correct " - "the 'font_path' setting or install the font file in the proper " - "location" << std::endl; + errorstream << "minetest can not continue without a valid font. " + "Please correct the 'font_path' setting or install the font " + "file in the proper location" << std::endl; abort(); } #endif @@ -468,7 +506,7 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode) } } - if (font != NULL) { + if (font) { font->grab(); m_font_cache[mode][basesize] = font; } diff --git a/src/settings.cpp b/src/settings.cpp index b4083264e..61f35e911 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -362,6 +362,18 @@ const SettingsEntry &Settings::getEntry(const std::string &name) const } +const SettingsEntry &Settings::getEntryDefault(const std::string &name) const +{ + MutexAutoLock lock(m_mutex); + + SettingEntries::const_iterator n; + if ((n = m_defaults.find(name)) == m_defaults.end()) { + throw SettingNotFoundException("Setting [" + name + "] not found."); + } + return n->second; +} + + Settings *Settings::getGroup(const std::string &name) const { const SettingsEntry &entry = getEntry(name); @@ -380,6 +392,15 @@ const std::string &Settings::get(const std::string &name) const } +const std::string &Settings::getDefault(const std::string &name) const +{ + const SettingsEntry &entry = getEntryDefault(name); + if (entry.is_group) + throw SettingNotFoundException("Setting [" + name + "] is a group."); + return entry.value; +} + + bool Settings::getBool(const std::string &name) const { return is_yes(get(name)); @@ -568,6 +589,17 @@ bool Settings::getEntryNoEx(const std::string &name, SettingsEntry &val) const } +bool Settings::getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const +{ + try { + val = getEntryDefault(name); + return true; + } catch (SettingNotFoundException &e) { + return false; + } +} + + bool Settings::getGroupNoEx(const std::string &name, Settings *&val) const { try { @@ -590,6 +622,17 @@ bool Settings::getNoEx(const std::string &name, std::string &val) const } +bool Settings::getDefaultNoEx(const std::string &name, std::string &val) const +{ + try { + val = getDefault(name); + return true; + } catch (SettingNotFoundException &e) { + return false; + } +} + + bool Settings::getFlag(const std::string &name) const { try { diff --git a/src/settings.h b/src/settings.h index 8c4f6e559..a19c15a76 100644 --- a/src/settings.h +++ b/src/settings.h @@ -135,8 +135,10 @@ public: ***********/ const SettingsEntry &getEntry(const std::string &name) const; + const SettingsEntry &getEntryDefault(const std::string &name) const; Settings *getGroup(const std::string &name) const; const std::string &get(const std::string &name) const; + const std::string &getDefault(const std::string &name) const; bool getBool(const std::string &name) const; u16 getU16(const std::string &name) const; s16 getS16(const std::string &name) const; @@ -165,8 +167,10 @@ public: ***************************************/ bool getEntryNoEx(const std::string &name, SettingsEntry &val) const; + bool getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const; bool getGroupNoEx(const std::string &name, Settings *&val) const; bool getNoEx(const std::string &name, std::string &val) const; + bool getDefaultNoEx(const std::string &name, std::string &val) const; bool getFlag(const std::string &name) const; bool getU16NoEx(const std::string &name, u16 &val) const; bool getS16NoEx(const std::string &name, s16 &val) const; From b816c631963a429658473b030ab6e7641b587562 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 7 Nov 2017 11:46:06 +0100 Subject: [PATCH 095/183] Add minetest.safe_write_file() to script API --- doc/lua_api.txt | 4 ++++ src/script/lua_api/l_util.cpp | 18 ++++++++++++++++++ src/script/lua_api/l_util.h | 3 +++ 3 files changed, 25 insertions(+) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 13881ef70..ab5caca0d 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2223,6 +2223,10 @@ Helper functions * nil: return all entries, * true: return only subdirectory names, or * false: return only file names. +* `minetest.safe_file_write(path, content)`: returns boolean indicating success + * Replaces contents of file at path with new contents in a safe (atomic) way. + Use this instead of below code when writing e.g. database files: + `local f = io.open(path, "wb"); f:write(content); f:close()` * `minetest.get_version()`: returns a table containing components of the engine version. Components: * `project`: Name of the project, eg, "Minetest" diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 901105fe0..aaccf17ee 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -356,6 +356,23 @@ int ModApiUtil::l_get_dir_list(lua_State *L) return 1; } +// safe_file_write(path, content) +int ModApiUtil::l_safe_file_write(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *path = luaL_checkstring(L, 1); + size_t size; + const char *content = luaL_checklstring(L, 2, &size); + + CHECK_SECURE_PATH(L, path, true); + + bool ret = fs::safeWriteToFile(path, std::string(content, size)); + lua_pushboolean(L, ret); + + return 1; +} + +// request_insecure_environment() int ModApiUtil::l_request_insecure_environment(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -475,6 +492,7 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(mkdir); API_FCT(get_dir_list); + API_FCT(safe_file_write); API_FCT(request_insecure_environment); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index 16f4edea8..872e43625 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -81,6 +81,9 @@ private: // get_dir_list(path, is_dir) static int l_get_dir_list(lua_State *L); + // safe_file_write(path, content) + static int l_safe_file_write(lua_State *L); + // request_insecure_environment() static int l_request_insecure_environment(lua_State *L); From 0fe3e7574de0b82b080932e446caed1f2cb3b85c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 7 Nov 2017 11:47:28 +0100 Subject: [PATCH 096/183] Make use of safe file writing in auth handler (fixes #6576) --- builtin/game/auth.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index 8cb4ebf57..74eb6ae88 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -67,16 +67,15 @@ local function save_auth_file() assert(type(stuff.privileges) == "table") assert(stuff.last_login == nil or type(stuff.last_login) == "number") end - local file, errmsg = io.open(core.auth_file_path, 'w+b') - if not file then - error(core.auth_file_path.." could not be opened for writing: "..errmsg) - end + local content = "" for name, stuff in pairs(core.auth_table) do local priv_string = core.privs_to_string(stuff.privileges) local parts = {name, stuff.password, priv_string, stuff.last_login or ""} - file:write(table.concat(parts, ":").."\n") + content = content .. table.concat(parts, ":") .. "\n" + end + if not core.safe_file_write(core.auth_file_path, content) then + error(core.auth_file_path.." could not be written to") end - io.close(file) end read_auth_file() From 6f803b9c896e6fa26992ed9c588265e392f80445 Mon Sep 17 00:00:00 2001 From: Ezhh Date: Sun, 12 Nov 2017 00:45:22 +0000 Subject: [PATCH 097/183] Remove incorrect entry from settingtypes --- builtin/settingtypes.txt | 4 ---- minetest.conf.example | 5 ----- 2 files changed, 9 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index ddb0afacd..0d382df70 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -166,10 +166,6 @@ keymap_cmd (Command key) key / # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_cmd_local (Command key) key . -# Key for opening the chat console. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keyman_console (Console key) key KEY_F10 - # Key for toggling unlimited view range. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_rangeselect (Range select key) key KEY_KEY_R diff --git a/minetest.conf.example b/minetest.conf.example index 79f7f68f9..52f8eef71 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -161,11 +161,6 @@ # type: key # keymap_cmd_local = . -# Key for opening the chat console. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -# type: key -# keyman_console = KEY_F10 - # Key for toggling unlimited view range. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 # type: key From 1cf32b22bec56500f799662e4b33cc350739be72 Mon Sep 17 00:00:00 2001 From: paramat Date: Sun, 12 Nov 2017 19:10:08 +0000 Subject: [PATCH 098/183] Lua_api.txt: Add documentation of required mapgen aliases --- doc/lua_api.txt | 74 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ab5caca0d..d2b70a667 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -211,7 +211,8 @@ when registering it. The `:` prefix can also be used for maintaining backwards compatibility. -### Aliases +Aliases +------- Aliases can be added by using `minetest.register_alias(name, convert_to)` or `minetest.register_alias_force(name, convert_to)`. @@ -232,6 +233,75 @@ you have an item called `epiclylongmodname:stuff`, you could do and be able to use `/giveme stuff`. +Mapgen aliases +-------------- +In a game, a certain number of these must be set to tell core mapgens which +of the game's nodes are to be used by the core mapgens. For example: + + minetest.register_alias("mapgen_stone", "default:stone") + +### Aliases needed for all mapgens except Mapgen v6 + +Base terrain: + +"mapgen_stone" +"mapgen_water_source" +"mapgen_river_water_source" + +Caves: + +"mapgen_lava_source" + +Dungeons: + +Only needed for registered biomes where 'node_stone' is stone: +"mapgen_cobble" +"mapgen_stair_cobble" +"mapgen_mossycobble" +Only needed for registered biomes where 'node_stone' is desert stone: +"mapgen_desert_stone" +"mapgen_stair_desert_stone" +Only needed for registered biomes where 'node_stone' is sandstone: +"mapgen_sandstone" +"mapgen_sandstonebrick" +"mapgen_stair_sandstone_block" + +### Aliases needed for Mapgen v6 + +Terrain and biomes: + +"mapgen_stone" +"mapgen_water_source" +"mapgen_lava_source" +"mapgen_dirt" +"mapgen_dirt_with_grass" +"mapgen_sand" +"mapgen_gravel" +"mapgen_desert_stone" +"mapgen_desert_sand" +"mapgen_dirt_with_snow" +"mapgen_snowblock" +"mapgen_snow" +"mapgen_ice" + +Flora: + +"mapgen_tree" +"mapgen_leaves" +"mapgen_apple" +"mapgen_jungletree" +"mapgen_jungleleaves" +"mapgen_junglegrass" +"mapgen_pine_tree" +"mapgen_pine_needles" + +Dungeons: + +"mapgen_cobble" +"mapgen_stair_cobble" +"mapgen_mossycobble" +"mapgen_stair_desert_stone" + Textures -------- Mods should generally prefix their textures with `modname_`, e.g. given @@ -2260,6 +2330,8 @@ Call these functions only at load time! * `minetest.register_craftitem(name, item definition)` * `minetest.unregister_item(name)` * `minetest.register_alias(name, convert_to)` + * Also use this to set the 'mapgen aliases' needed in a game for the core + * mapgens. See 'Mapgen aliases' section above. * `minetest.register_alias_force(name, convert_to)` * `minetest.register_craft(recipe)` * Check recipe table syntax for different types below. From 222fab3d20e6f830dcdaa71b7f5845eb2b152108 Mon Sep 17 00:00:00 2001 From: ezhh Date: Mon, 13 Nov 2017 01:42:57 +0000 Subject: [PATCH 099/183] Improve Settings tab button alignments --- builtin/mainmenu/tab_settings.lua | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index bb1318fd9..52bc8ead1 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -176,7 +176,7 @@ end local function formspec(tabview, name, tabdata) local tab_string = - "box[0,0;3.5,4.5;#999999]" .. + "box[0,0;3.75,4.5;#999999]" .. "checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") .. ";" .. dump(core.settings:get_bool("smooth_lighting")) .. "]" .. "checkbox[0.25,0.5;cb_particles;" .. fgettext("Particles") .. ";" @@ -187,38 +187,38 @@ local function formspec(tabview, name, tabdata) .. dump(core.settings:get_bool("opaque_water")) .. "]" .. "checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";" .. dump(core.settings:get_bool("connected_glass")) .. "]" .. - "dropdown[0.25,2.8;3.3;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";" + "dropdown[0.25,2.8;3.5;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";" .. getSettingIndex.NodeHighlighting() .. "]" .. - "dropdown[0.25,3.6;3.3;dd_leaves_style;" .. dd_options.leaves[1] .. ";" + "dropdown[0.25,3.6;3.5;dd_leaves_style;" .. dd_options.leaves[1] .. ";" .. getSettingIndex.Leaves() .. "]" .. - "box[3.75,0;3.75,4.5;#999999]" .. - "label[3.85,0.1;" .. fgettext("Texturing:") .. "]" .. - "dropdown[3.85,0.55;3.85;dd_filters;" .. dd_options.filters[1] .. ";" + "box[4,0;3.75,4.5;#999999]" .. + "label[4.25,0.1;" .. fgettext("Texturing:") .. "]" .. + "dropdown[4.25,0.55;3.5;dd_filters;" .. dd_options.filters[1] .. ";" .. getSettingIndex.Filter() .. "]" .. - "dropdown[3.85,1.35;3.85;dd_mipmap;" .. dd_options.mipmap[1] .. ";" + "dropdown[4.25,1.35;3.5;dd_mipmap;" .. dd_options.mipmap[1] .. ";" .. getSettingIndex.Mipmap() .. "]" .. - "label[3.85,2.15;" .. fgettext("Antialiasing:") .. "]" .. - "dropdown[3.85,2.6;3.85;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";" + "label[4.25,2.15;" .. fgettext("Antialiasing:") .. "]" .. + "dropdown[4.25,2.6;3.5;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";" .. getSettingIndex.Antialiasing() .. "]" .. - "label[3.85,3.45;" .. fgettext("Screen:") .. "]" .. - "checkbox[3.85,3.6;cb_autosave_screensize;" .. fgettext("Autosave screen size") .. ";" + "label[4.25,3.45;" .. fgettext("Screen:") .. "]" .. + "checkbox[4.25,3.6;cb_autosave_screensize;" .. fgettext("Autosave screen size") .. ";" .. dump(core.settings:get_bool("autosave_screensize")) .. "]" .. - "box[7.75,0;4,4.5;#999999]" .. - "checkbox[8,0;cb_shaders;" .. fgettext("Shaders") .. ";" + "box[8,0;3.75,4.5;#999999]" .. + "checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders") .. ";" .. dump(core.settings:get_bool("enable_shaders")) .. "]" if PLATFORM == "Android" then tab_string = tab_string .. - "button[8,4.75;3.75,0.5;btn_reset_singleplayer;" + "button[8,4.75;4.1,1;btn_reset_singleplayer;" .. fgettext("Reset singleplayer world") .. "]" else tab_string = tab_string .. - "button[8,4.85;3.75,0.5;btn_change_keys;" + "button[8,4.75;4,1;btn_change_keys;" .. fgettext("Change keys") .. "]" end tab_string = tab_string .. - "button[0,4.85;3.75,0.5;btn_advanced_settings;" + "button[0,4.75;4,1;btn_advanced_settings;" .. fgettext("Advanced Settings") .. "]" @@ -231,19 +231,19 @@ local function formspec(tabview, name, tabdata) if core.settings:get_bool("enable_shaders") then tab_string = tab_string .. - "checkbox[8,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";" + "checkbox[8.25,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";" .. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" .. - "checkbox[8,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" + "checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" .. dump(core.settings:get_bool("tone_mapping")) .. "]" .. - "checkbox[8,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";" + "checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";" .. dump(core.settings:get_bool("generate_normalmaps")) .. "]" .. - "checkbox[8,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";" + "checkbox[8.25,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";" .. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" .. - "checkbox[8,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";" + "checkbox[8.25,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";" .. dump(core.settings:get_bool("enable_waving_water")) .. "]" .. - "checkbox[8,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" + "checkbox[8.25,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. - "checkbox[8,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" + "checkbox[8.25,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" .. dump(core.settings:get_bool("enable_waving_plants")) .. "]" else tab_string = tab_string .. From a90c314c80b87abbaa7668a55bc0d1194dd0920f Mon Sep 17 00:00:00 2001 From: paramat Date: Wed, 15 Nov 2017 23:52:31 +0000 Subject: [PATCH 100/183] Biome dust node: Only place on 'walkable' cubic non-liquid drawtypes No longer decide placement on 'buildable_to' parameter. Dust nodes only look acceptable placed on cubic nodes. Modders may not want to make their plantlike decorations 'buildable_to'. --- src/mapgen.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 1aa3be302..f4165f5cd 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -809,7 +809,16 @@ void MapgenBasic::dustTopNodes() } content_t c = vm->m_data[vi].getContent(); - if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) { + NodeDrawType dtype = ndef->get(c).drawtype; + // Only place on walkable cubic non-liquid nodes + // Dust check needed due to vertical overgeneration + if ((dtype == NDT_NORMAL || + dtype == NDT_ALLFACES_OPTIONAL || + dtype == NDT_GLASSLIKE_FRAMED_OPTIONAL || + dtype == NDT_GLASSLIKE || + dtype == NDT_GLASSLIKE_FRAMED || + dtype == NDT_ALLFACES) && + ndef->get(c).walkable && c != biome->c_dust) { vm->m_area.add_y(em, vi, 1); vm->m_data[vi] = MapNode(biome->c_dust); } From 8fba3c93d5c76a25e2a173061022fb3cab0672f5 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Tue, 21 Nov 2017 20:21:52 +0100 Subject: [PATCH 101/183] core.rotate_node: Run callbacks like with any regular placed node (#6648) --- builtin/common/misc_helpers.lua | 48 ++++++++------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 51abed1be..0686c18da 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -345,41 +345,20 @@ if INIT == "game" then end local undef = core.registered_nodes[unode.name] if undef and undef.on_rightclick then - undef.on_rightclick(pointed_thing.under, unode, placer, + return undef.on_rightclick(pointed_thing.under, unode, placer, itemstack, pointed_thing) - return end local fdir = placer and core.dir_to_facedir(placer:get_look_dir()) or 0 - local wield_name = itemstack:get_name() local above = pointed_thing.above local under = pointed_thing.under local iswall = (above.y == under.y) local isceiling = not iswall and (above.y < under.y) - local anode = core.get_node_or_nil(above) - if not anode then - return - end - local pos = pointed_thing.above - local node = anode if undef and undef.buildable_to then - pos = pointed_thing.under - node = unode iswall = false end - local name = placer and placer:get_player_name() or "" - if core.is_protected(pos, name) then - core.record_protection_violation(pos, name) - return - end - - local ndef = core.registered_nodes[node.name] - if not ndef or not ndef.buildable_to then - return - end - if orient_flags.force_floor then iswall = false isceiling = false @@ -393,31 +372,26 @@ if INIT == "game" then iswall = not iswall end + local param2 = fdir if iswall then - core.set_node(pos, {name = wield_name, - param2 = dirs1[fdir + 1]}) + param2 = dirs1[fdir + 1] elseif isceiling then if orient_flags.force_facedir then - core.set_node(pos, {name = wield_name, - param2 = 20}) + cparam2 = 20 else - core.set_node(pos, {name = wield_name, - param2 = dirs2[fdir + 1]}) + param2 = dirs2[fdir + 1] end else -- place right side up if orient_flags.force_facedir then - core.set_node(pos, {name = wield_name, - param2 = 0}) - else - core.set_node(pos, {name = wield_name, - param2 = fdir}) + param2 = 0 end end - if not infinitestacks then - itemstack:take_item() - return itemstack - end + local old_itemstack = ItemStack(itemstack) + local new_itemstack, removed = core.item_place_node( + itemstack, placer, pointed_thing, param2 + ) + return infinitestacks and old_itemstack or new_itemstack end From f72ac42ca05d87571b6a5ca84f588c4f5566aa16 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 22 Nov 2017 19:25:26 +0100 Subject: [PATCH 102/183] Inventory: Restrict access from too far away --- src/network/serverpackethandler.cpp | 67 +++++++++++++++++++---------- src/server.h | 1 + 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 452abdea5..fc64c142e 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -943,6 +943,18 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) (ma->to_inv.type == InventoryLocation::PLAYER) && (ma->to_inv.name == player->getName()); + InventoryLocation *remote = from_inv_is_current_player ? + &ma->to_inv : &ma->from_inv; + + // Check for out-of-range interaction + if (remote->type == InventoryLocation::NODEMETA) { + v3f node_pos = intToFloat(remote->p, BS); + v3f player_pos = player->getPlayerSAO()->getBasePosition(); + f32 d = player_pos.getDistanceFrom(node_pos); + if (!checkInteractDistance(player, d, "inventory")) + return; + } + /* Disable moving items out of craftpreview */ @@ -1257,6 +1269,37 @@ void Server::handleCommand_Respawn(NetworkPacket* pkt) // the previous addition has been successfully removed } +bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std::string what) +{ + PlayerSAO *playersao = player->getPlayerSAO(); + const InventoryList *hlist = playersao->getInventory()->getList("hand"); + const ItemDefinition &playeritem_def = + playersao->getWieldedItem().getDefinition(m_itemdef); + const ItemDefinition &hand_def = + hlist ? hlist->getItem(0).getDefinition(m_itemdef) : m_itemdef->get(""); + + float max_d = BS * playeritem_def.range; + float max_d_hand = BS * hand_def.range; + + if (max_d < 0 && max_d_hand >= 0) + max_d = max_d_hand; + else if (max_d < 0) + max_d = BS * 4.0f; + + // cube diagonal: sqrt(3) = 1.732 + if (d > max_d * 1.732) { + actionstream << "Player " << player->getName() + << " tried to access " << what + << " from too far: " + << "d=" << d <<", max_d=" << max_d + << ". ignoring." << std::endl; + // Call callbacks + m_script->on_cheat(playersao, "interacted_too_far"); + return false; + } + return true; +} + void Server::handleCommand_Interact(NetworkPacket* pkt) { /* @@ -1380,33 +1423,13 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) */ static const bool enable_anticheat = !g_settings->getBool("disable_anticheat"); if ((action == 0 || action == 2 || action == 3 || action == 4) && - (enable_anticheat && !isSingleplayer())) { + enable_anticheat && !isSingleplayer()) { float d = player_pos.getDistanceFrom(pointed_pos_under); - const ItemDefinition &playeritem_def = - playersao->getWieldedItem().getDefinition(m_itemdef); - float max_d = BS * playeritem_def.range; - InventoryList *hlist = playersao->getInventory()->getList("hand"); - const ItemDefinition &hand_def = - hlist ? (hlist->getItem(0).getDefinition(m_itemdef)) : (m_itemdef->get("")); - float max_d_hand = BS * hand_def.range; - if (max_d < 0 && max_d_hand >= 0) - max_d = max_d_hand; - else if (max_d < 0) - max_d = BS * 4.0; - // cube diagonal: sqrt(3) = 1.73 - if (d > max_d * 1.73) { - actionstream << "Player " << player->getName() - << " tried to access " << pointed.dump() - << " from too far: " - << "d=" << d <<", max_d=" << max_d - << ". ignoring." << std::endl; + if (!checkInteractDistance(player, d, pointed.dump())) { // Re-send block to revert change on client-side RemoteClient *client = getClient(pkt->getPeerId()); v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); client->SetBlockNotSent(blockpos); - // Call callbacks - m_script->on_cheat(playersao, "interacted_too_far"); - // Do nothing else return; } } diff --git a/src/server.h b/src/server.h index e3a4291d9..398b6275f 100644 --- a/src/server.h +++ b/src/server.h @@ -481,6 +481,7 @@ private: void RespawnPlayer(u16 peer_id); void DeleteClient(u16 peer_id, ClientDeletionReason reason); void UpdateCrafting(RemotePlayer *player); + bool checkInteractDistance(RemotePlayer *player, const f32 d, const std::string what); void handleChatInterfaceEvent(ChatEvent *evt); From 3f1094475fd42bddc77c7e34265eb54bc2420cd0 Mon Sep 17 00:00:00 2001 From: Ezhh Date: Sat, 25 Nov 2017 23:18:50 +0000 Subject: [PATCH 103/183] Improve documentation for player:set_attribute() --- doc/lua_api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d2b70a667..a32eb34e3 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3331,7 +3331,7 @@ This is basically a reference to a C++ `ServerActiveObject` * `11`: bubbles bar is not shown * `set_attribute(attribute, value)`: * Sets an extra attribute with value on player. - * `value` must be a string. + * `value` must be a string, or a number which will be converted to a string. * If `value` is `nil`, remove attribute from player. * `get_attribute(attribute)`: * Returns value (a string) for extra attribute. From 7cc1a36b3c0ce3da66881b02fce7248543d20163 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 23 Nov 2017 23:35:52 +0100 Subject: [PATCH 104/183] Hint at problematic code when logging deprecated calls --- src/script/common/c_internal.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index b349f9dd1..3fa044172 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -178,9 +178,15 @@ void log_deprecated(lua_State *L, const std::string &message) } if (do_log) { - warningstream << message << std::endl; - // L can be NULL if we get called by log_deprecated(const std::string &msg) - // from scripting_game.cpp. + warningstream << message; + if (L) { // L can be NULL if we get called from scripting_game.cpp + lua_Debug ar; + FATAL_ERROR_IF(!lua_getstack(L, 2, &ar), "lua_getstack() failed"); + FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed"); + warningstream << " (at " << ar.short_src << ":" << ar.currentline << ")"; + } + warningstream << std::endl; + if (L) { if (do_error) script_error(L, LUA_ERRRUN, NULL, NULL); From 7d9dbbbf3c9102fab35ee4c24a9215d198c3dba0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 27 Nov 2017 18:00:30 +0100 Subject: [PATCH 105/183] Update documentation regarding authentication handler and related functions Properly document it instead of referencing the builtin handler as authoritative "example" code. Also adds definition of get_auth_handler() which was missing previously. --- doc/lua_api.txt | 85 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index a32eb34e3..b35c816a1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2460,8 +2460,9 @@ Call these functions only at load time! * `definition`: `{ description = "description text", give_to_singleplayer = boolean}` the default of `give_to_singleplayer` is true * To allow players with `basic_privs` to grant, see `basic_privs` minetest.conf setting. -* `minetest.register_authentication_handler(handler)` - * See `minetest.builtin_auth_handler` in `builtin.lua` for reference +* `minetest.register_authentication_handler(authentication handler definition)` + * Registers an auth handler that overrides the builtin one + * This function can be called by a single mod once only. ### Setting-related * `minetest.settings`: Settings object containing all of the settings from the @@ -2470,37 +2471,44 @@ Call these functions only at load time! parses it as a position (in the format `(1,2,3)`). Returns a position or nil. ### Authentication -* `minetest.notify_authentication_modified(name)` - * Should be called by the authentication handler if privileges changes. - * To report everybody, set `name=nil`. -* `minetest.check_password_entry(name, entry, password)` - * Returns true if the "db entry" for a player with name matches given - * password, false otherwise. - * The "db entry" is the usually player-individual value that is derived - * from the player's chosen password and stored on the server in order to allow - * authentication whenever the player desires to log in. - * Only use this function for making it possible to log in via the password from - * via protocols like IRC, other uses for inside the game are frowned upon. -* `minetest.get_password_hash(name, raw_password)` - * Convert a name-password pair to a password hash that Minetest can use. - * The returned value alone is not a good basis for password checks based - * on comparing the password hash in the database with the password hash - * from the function, with an externally provided password, as the hash - * in the db might use the new SRP verifier format. - * For this purpose, use `minetest.check_password_entry` instead. * `minetest.string_to_privs(str)`: returns `{priv1=true,...}` * `minetest.privs_to_string(privs)`: returns `"priv1,priv2,..."` * Convert between two privilege representations -* `minetest.set_player_password(name, password_hash)` -* `minetest.set_player_privs(name, {priv1=true,...})` * `minetest.get_player_privs(name) -> {priv1=true,...}` -* `minetest.auth_reload()` * `minetest.check_player_privs(player_or_name, ...)`: returns `bool, missing_privs` * A quickhand for checking privileges. - * `player_or_name`: Either a Player object or the name of a player. - * `...` is either a list of strings, e.g. `"priva", "privb"` or - a table, e.g. `{ priva = true, privb = true }`. -* `minetest.get_player_ip(name)`: returns an IP address string + * `player_or_name`: Either a Player object or the name of a player. + * `...` is either a list of strings, e.g. `"priva", "privb"` or + a table, e.g. `{ priva = true, privb = true }`. + +* `minetest.check_password_entry(name, entry, password)` + * Returns true if the "password entry" for a player with name matches given + password, false otherwise. + * The "password entry" is the password representation generated by the engine + as returned as part of a `get_auth()` call on the auth handler. + * Only use this function for making it possible to log in via password from + external protocols such as IRC, other uses are frowned upon. +* `minetest.get_password_hash(name, raw_password)` + * Convert a name-password pair to a password hash that Minetest can use. + * The returned value alone is not a good basis for password checks based + on comparing the password hash in the database with the password hash + from the function, with an externally provided password, as the hash + in the db might use the new SRP verifier format. + * For this purpose, use `minetest.check_password_entry` instead. +* `minetest.get_player_ip(name)`: returns an IP address string for the player `name` + * The player needs to be online for this to be successful. + +* `minetest.get_auth_handler()`: Return the currently active auth handler + * See the `Authentication handler definition` + * Use this to e.g. get the authentication data for a player: + `local auth_data = minetest.get_auth_handler().get_auth(playername)` +* `minetest.notify_authentication_modified(name)` + * Must be called by the authentication handler for privilege changes. + * `name`: string; if omitted, all auth data should be considered modified +* `minetest.set_player_password(name, password_hash)`: Set password hash of player `name` +* `minetest.set_player_privs(name, {priv1=true,...})`: Set privileges of player `name` +* `minetest.auth_reload()` + * See `reload()` in authentication handler definition `minetest.set_player_password`, `minetest_set_player_privs`, `minetest_get_player_privs` and `minetest.auth_reload` call the authetification handler. @@ -4792,3 +4800,26 @@ The Biome API is still in an experimental phase and subject to change. -- ^ HTTP status code data = "response" } + +### Authentication handler definition + + { + get_auth = func(name), + -- ^ Get authentication data for existing player `name` (`nil` if player doesn't exist) + -- ^ returns following structure `{password=, privileges=, last_login=}` + create_auth = func(name, password), + -- ^ Create new auth data for player `name` + -- ^ Note that `password` is not plain-text but an arbitrary representation decided by the engine + set_password = func(name, password), + -- ^ Set password of player `name` to `password` + Auth data should be created if not present + set_privileges = func(name, privileges), + -- ^ Set privileges of player `name` + -- ^ `privileges` is in table form, auth data should be created if not present + reload = func(), + -- ^ Reload authentication data from the storage location + -- ^ Returns boolean indicating success + record_login = func(name), + -- ^ Called when player joins, used for keeping track of last_login + } + From 48ebbf0fc642908e26b58fd17f379920dbb1b387 Mon Sep 17 00:00:00 2001 From: raymoo Date: Sun, 3 Dec 2017 01:28:35 -0800 Subject: [PATCH 106/183] Shut down mapgen threads before other shutdown tasks (#6689) Solves some issues with ModStorage functionality in mapgen threads that occurred when mapgen threads continued to run after the main server thread had stopped. Also shuts down mapgen threads before shutdown callbacks are called. --- src/server.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index 83022e95b..f8e6846c3 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -327,17 +327,14 @@ Server::Server( Server::~Server() { - infostream<<"Server destructing"<on_shutdown(); - + infostream << "Server: Saving players" << std::endl; m_env->saveLoadedPlayers(); @@ -353,6 +350,20 @@ Server::~Server() } m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN, kick_msg, reconnect); + } + + // Do this before stopping the server in case mapgen callbacks need to access + // server-controlled resources (like ModStorages). Also do them before + // shutdown callbacks since they may modify state that is finalized in a + // callback. + m_emerge->stopThreads(); + + { + MutexAutoLock envlock(m_env_mutex); + + // Execute script shutdown hooks + infostream << "Executing shutdown hooks" << std::endl; + m_script->on_shutdown(); infostream << "Server: Saving environment metadata" << std::endl; m_env->saveMeta(); @@ -362,10 +373,6 @@ Server::~Server() stop(); delete m_thread; - // stop all emerge threads before deleting players that may have - // requested blocks to be emerged - m_emerge->stopThreads(); - // Delete things in the reverse order of creation delete m_emerge; delete m_env; @@ -377,7 +384,7 @@ Server::~Server() delete m_craftdef; // Deinitialize scripting - infostream<<"Server: Deinitializing scripting"< Date: Wed, 6 Dec 2017 16:32:05 +0000 Subject: [PATCH 107/183] Ensure no item stack is being held before crafting (#4779) --- src/guiFormSpecMenu.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 19fd9f1f0..c4b072be1 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -3681,18 +3681,24 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) a->from_i = m_selected_item->i; m_invmgr->inventoryAction(a); } else if (craft_amount > 0) { - m_selected_content_guess = ItemStack(); // Clear - - // Send IACTION_CRAFT - assert(s.isValid()); - assert(inv_s); + + // if there are no items selected or the selected item + // belongs to craftresult list, proceed with crafting + if (m_selected_item == NULL || + !m_selected_item->isValid() || m_selected_item->listname == "craftresult") { + + m_selected_content_guess = ItemStack(); // Clear + + assert(inv_s); - infostream << "Handing IACTION_CRAFT to manager" << std::endl; - ICraftAction *a = new ICraftAction(); - a->count = craft_amount; - a->craft_inv = s.inventoryloc; - m_invmgr->inventoryAction(a); + // Send IACTION_CRAFT + infostream << "Handing IACTION_CRAFT to manager" << std::endl; + ICraftAction *a = new ICraftAction(); + a->count = craft_amount; + a->craft_inv = s.inventoryloc; + m_invmgr->inventoryAction(a); + } } // If m_selected_amount has been decreased to zero, deselect From f9738c1909309ff311f604371d7d73abde753e1d Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 13 Aug 2017 09:49:29 +0200 Subject: [PATCH 108/183] Damage: Remove damage ignore timer --- src/client.cpp | 26 +++++++++----------------- src/client.h | 1 - src/network/clientpackethandler.cpp | 4 ---- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index a5228132d..abc84b7cf 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -73,7 +73,6 @@ Client::Client( m_connection_reinit_timer(0.1), m_avg_rtt_timer(0.0), m_playerpos_send_timer(0.0), - m_ignore_damage_timer(0.0), m_tsrc(tsrc), m_shsrc(shsrc), m_itemdef(itemdef), @@ -275,14 +274,9 @@ void Client::step(float dtime) DSTACK(FUNCTION_NAME); // Limit a bit - if(dtime > 2.0) + if (dtime > 2.0) dtime = 2.0; - if(m_ignore_damage_timer > dtime) - m_ignore_damage_timer -= dtime; - else - m_ignore_damage_timer = 0.0; - m_animation_time += dtime; if(m_animation_time > 60.0) m_animation_time -= 60.0; @@ -429,18 +423,16 @@ void Client::step(float dtime) ClientEnvEvent envEvent = m_env.getClientEnvEvent(); if (envEvent.type == CEE_PLAYER_DAMAGE) { - if (m_ignore_damage_timer <= 0) { - u8 damage = envEvent.player_damage.amount; + u8 damage = envEvent.player_damage.amount; - if (envEvent.player_damage.send_to_server) - sendDamage(damage); + if (envEvent.player_damage.send_to_server) + sendDamage(damage); - // Add to ClientEvent queue - ClientEvent event; - event.type = CE_PLAYER_DAMAGE; - event.player_damage.amount = damage; - m_client_event_queue.push(event); - } + // Add to ClientEvent queue + ClientEvent event; + event.type = CE_PLAYER_DAMAGE; + event.player_damage.amount = damage; + m_client_event_queue.push(event); } // Protocol v29 or greater obsoleted this event else if (envEvent.type == CEE_PLAYER_BREATH && m_proto_ver < 29) { diff --git a/src/client.h b/src/client.h index b4145c76f..644549d7f 100644 --- a/src/client.h +++ b/src/client.h @@ -574,7 +574,6 @@ private: float m_connection_reinit_timer; float m_avg_rtt_timer; float m_playerpos_send_timer; - float m_ignore_damage_timer; // Used after server moves player IntervalLimiter m_map_timer_and_unload_interval; IWritableTextureSource *m_tsrc; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 8935ed90c..0d4b792de 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -580,10 +580,6 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt) event.player_force_move.pitch = pitch; event.player_force_move.yaw = yaw; m_client_event_queue.push(event); - - // Ignore damage for a few seconds, so that the player doesn't - // get damage from falling on ground - m_ignore_damage_timer = 3.0; } void Client::handleCommand_DeathScreen(NetworkPacket* pkt) From 8a849e893a4eedb2dd9db8e26f2dab23ac4f3c55 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Tue, 12 Dec 2017 19:19:04 +0100 Subject: [PATCH 109/183] Builtin: Fix handle_node_drops crash with nil digger --- builtin/game/item.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/game/item.lua b/builtin/game/item.lua index 09e3ea999..d4ce6002f 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -519,7 +519,8 @@ function core.handle_node_drops(pos, drops, digger) end else give_item = function(item) - return item + -- itemstring to ItemStack for left:is_empty() + return ItemStack(item) end end From b3167d4e5748ffd14efc5981ba8349c01808b78a Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Thu, 21 Dec 2017 22:58:06 +0300 Subject: [PATCH 110/183] Fix wrong scrolling (#6809) --- src/intlGUIEditBox.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/intlGUIEditBox.cpp b/src/intlGUIEditBox.cpp index 37687e1e4..ad59ef13a 100644 --- a/src/intlGUIEditBox.cpp +++ b/src/intlGUIEditBox.cpp @@ -1419,13 +1419,10 @@ void intlGUIEditBox::calculateScrollPos() } // vertical scroll position - if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos) - VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos; - - else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos) - VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos; - else - VScrollPos = 0; + if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y) + VScrollPos += CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y; // scrolling downwards + else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y) + VScrollPos += CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y; // scrolling upwards // todo: adjust scrollbar } From bb219e1059b711ba9663ed42304c8c22cacf86ab Mon Sep 17 00:00:00 2001 From: nOOb3167 Date: Fri, 22 Dec 2017 11:33:46 +0100 Subject: [PATCH 111/183] Fix undefined behaviour on getting pointer to data in empty vector `&vector[0]` is undefined if vector.empty(), causing build failure on MSVC --- src/network/networkpacket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp index 8e06ae104..c4fcf9600 100644 --- a/src/network/networkpacket.cpp +++ b/src/network/networkpacket.cpp @@ -62,7 +62,7 @@ void NetworkPacket::putRawPacket(u8 *data, u32 datasize, u16 peer_id) // split command and datas m_command = readU16(&data[0]); - memcpy(&m_data[0], &data[2], m_datasize); + memcpy(m_data.data(), &data[2], m_datasize); } const char* NetworkPacket::getString(u32 from_offset) From 669806725637a7e97ae884b7983da6e19fcaa6f5 Mon Sep 17 00:00:00 2001 From: number Zero Date: Mon, 25 Dec 2017 00:54:43 +0300 Subject: [PATCH 112/183] Fix dancing text --- src/intlGUIEditBox.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/intlGUIEditBox.cpp b/src/intlGUIEditBox.cpp index ad59ef13a..74b9f634c 100644 --- a/src/intlGUIEditBox.cpp +++ b/src/intlGUIEditBox.cpp @@ -1418,6 +1418,9 @@ void intlGUIEditBox::calculateScrollPos() // todo: adjust scrollbar } + if (!WordWrap && !MultiLine) + return; + // vertical scroll position if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y) VScrollPos += CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y; // scrolling downwards From df0a8574dcef24e5a12a366cb10f7ac5d69a8b79 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Fri, 22 Dec 2017 10:00:57 +0000 Subject: [PATCH 113/183] Fix rounding error in g/set_node caused by truncation to float --- src/irr_v3d.h | 1 + src/script/common/c_converter.cpp | 46 ++++++++++++++++++++++++++++--- src/util/numeric.h | 11 ++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/irr_v3d.h b/src/irr_v3d.h index fa6af3661..3a8d1fd30 100644 --- a/src/irr_v3d.h +++ b/src/irr_v3d.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include typedef core::vector3df v3f; +typedef core::vector3d v3d; typedef core::vector3d v3s16; typedef core::vector3d v3u16; typedef core::vector3d v3s32; diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index fc516d56a..d9b926e3e 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -196,6 +196,44 @@ v3f check_v3f(lua_State *L, int index) return pos; } +v3d read_v3d(lua_State *L, int index) +{ + v3d pos; + CHECK_POS_TAB(index); + lua_getfield(L, index, "x"); + pos.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + pos.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "z"); + pos.Z = lua_tonumber(L, -1); + lua_pop(L, 1); + return pos; +} + +v3d check_v3d(lua_State *L, int index) +{ + v3d pos; + CHECK_POS_TAB(index); + lua_getfield(L, index, "x"); + CHECK_POS_COORD("x"); + pos.X = lua_tonumber(L, -1); + CHECK_FLOAT_RANGE(pos.X, "x") + lua_pop(L, 1); + lua_getfield(L, index, "y"); + CHECK_POS_COORD("y"); + pos.Y = lua_tonumber(L, -1); + CHECK_FLOAT_RANGE(pos.Y, "y") + lua_pop(L, 1); + lua_getfield(L, index, "z"); + CHECK_POS_COORD("z"); + pos.Z = lua_tonumber(L, -1); + CHECK_FLOAT_RANGE(pos.Z, "z") + lua_pop(L, 1); + return pos; +} + void push_ARGB8(lua_State *L, video::SColor color) { lua_newtable(L); @@ -234,15 +272,15 @@ void push_v3s16(lua_State *L, v3s16 p) v3s16 read_v3s16(lua_State *L, int index) { // Correct rounding at <0 - v3f pf = read_v3f(L, index); - return floatToInt(pf, 1.0); + v3d pf = read_v3d(L, index); + return doubleToInt(pf, 1.0); } v3s16 check_v3s16(lua_State *L, int index) { // Correct rounding at <0 - v3f pf = check_v3f(L, index); - return floatToInt(pf, 1.0); + v3d pf = check_v3d(L, index); + return doubleToInt(pf, 1.0); } bool read_color(lua_State *L, int index, video::SColor *color) diff --git a/src/util/numeric.h b/src/util/numeric.h index 4a27f657d..573f03788 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -254,6 +254,17 @@ inline v3s16 floatToInt(v3f p, f32 d) (p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d); } +/* + Returns integer position of node in given double precision position + */ +inline v3s16 doubleToInt(v3d p, double d) +{ + return v3s16( + (p.X + (p.X > 0 ? d / 2 : -d / 2)) / d, + (p.Y + (p.Y > 0 ? d / 2 : -d / 2)) / d, + (p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d); +} + /* Returns floating point position of node in given integer position */ From b2099d42776510a78940fd2a6ea87e2d63f77216 Mon Sep 17 00:00:00 2001 From: you Date: Thu, 4 Jan 2018 14:25:20 +0100 Subject: [PATCH 114/183] Fix Wstringop-overflow warning from util/srp.cpp (#6855) * Fix Wstringop-overflow warning from util/srp.cpp --- src/util/srp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/srp.cpp b/src/util/srp.cpp index 430ba1137..f27f4f3f9 100644 --- a/src/util/srp.cpp +++ b/src/util/srp.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -417,7 +418,7 @@ static SRP_Result H_nn( } static SRP_Result H_ns(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *n, - size_t len_n, const unsigned char *bytes, size_t len_bytes) + size_t len_n, const unsigned char *bytes, uint32_t len_bytes) { unsigned char buff[SHA512_DIGEST_LENGTH]; size_t nbytes = len_n + len_bytes; From 6b5e2618fbb91cbc7e68ef0fe2f6452cc6de3d5f Mon Sep 17 00:00:00 2001 From: Pedro Gimeno Date: Mon, 8 Jan 2018 21:32:15 +0100 Subject: [PATCH 115/183] Fix buffer parameter not working in LuaPerlinNoiseMap::l_getMapSlice() --- src/script/lua_api/l_noise.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index e3e76191f..90b8864d2 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -316,7 +316,7 @@ int LuaPerlinNoiseMap::l_getMapSlice(lua_State *L) Noise *n = o->noise; if (use_buffer) - lua_pushvalue(L, 3); + lua_pushvalue(L, 4); else lua_newtable(L); From 127b1fa6f8c014172f0f8e11de19ace0a6882c24 Mon Sep 17 00:00:00 2001 From: Pedro Gimeno <4267396+pgimeno@users.noreply.github.com> Date: Tue, 9 Jan 2018 19:07:14 +0100 Subject: [PATCH 116/183] Fix off-by-one in log output line length (#6896) --- src/log.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/log.cpp b/src/log.cpp index 589cfd909..0dec7dd70 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -347,13 +347,10 @@ void StringBuffer::push_back(char c) flush(std::string(buffer, buffer_index)); buffer_index = 0; } else { - int index = buffer_index; - buffer[index++] = c; - if (index >= BUFFER_LENGTH) { + buffer[buffer_index++] = c; + if (buffer_index >= BUFFER_LENGTH) { flush(std::string(buffer, buffer_index)); buffer_index = 0; - } else { - buffer_index = index; } } } From 842eccee19cdef2c870fc7ccc136b0d634982c3a Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 28 Jan 2018 10:21:21 +0100 Subject: [PATCH 117/183] Apply physics overrides correctly during anticheat calculations (#6970) --- src/content_sao.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index d60a30840..b5e43bf1b 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1401,26 +1401,29 @@ bool PlayerSAO::checkMovementCheat() too, and much more lightweight. */ - float player_max_speed = 0; + float player_max_walk = 0; // horizontal movement + float player_max_jump = 0; // vertical upwards movement - if (m_privs.count("fast") != 0) { - // Fast speed - player_max_speed = m_player->movement_speed_fast * m_physics_override_speed; - } else { - // Normal speed - player_max_speed = m_player->movement_speed_walk * m_physics_override_speed; - } - // Tolerance. The lag pool does this a bit. - //player_max_speed *= 2.5; + if (m_privs.count("fast") != 0) + player_max_walk = m_player->movement_speed_fast; // Fast speed + else + player_max_walk = m_player->movement_speed_walk; // Normal speed + player_max_walk *= m_physics_override_speed; + player_max_jump = m_player->movement_speed_jump * m_physics_override_jump; + // FIXME: Bouncy nodes cause practically unbound increase in Y speed, + // until this can be verified correctly, tolerate higher jumping speeds + player_max_jump *= 2.0; v3f diff = (m_base_position - m_last_good_position); float d_vert = diff.Y; diff.Y = 0; float d_horiz = diff.getLength(); - float required_time = d_horiz / player_max_speed; + float required_time = d_horiz / player_max_walk; - if (d_vert > 0 && d_vert / player_max_speed > required_time) - required_time = d_vert / player_max_speed; // Moving upwards + // FIXME: Checking downwards movement is not easily possible currently, + // the server could calculate speed differences to examine the gravity + if (d_vert > 0) + required_time = MYMAX(required_time, d_vert / player_max_jump); if (m_move_pool.grab(required_time)) { m_last_good_position = m_base_position; From 9d3fa874be3509477243272cb23d892879348194 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 2 Feb 2018 23:34:09 +0100 Subject: [PATCH 118/183] Refine movement anticheat again (#7004) * Account for walking speed in vertical dir * Avoid undefined behaviour due to division-by-zero --- src/content_sao.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index b5e43bf1b..1234e3915 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1414,6 +1414,12 @@ bool PlayerSAO::checkMovementCheat() // until this can be verified correctly, tolerate higher jumping speeds player_max_jump *= 2.0; + // Don't divide by zero! + if (player_max_walk < 0.0001f) + player_max_walk = 0.0001f; + if (player_max_jump < 0.0001f) + player_max_jump = 0.0001f; + v3f diff = (m_base_position - m_last_good_position); float d_vert = diff.Y; diff.Y = 0; @@ -1422,8 +1428,11 @@ bool PlayerSAO::checkMovementCheat() // FIXME: Checking downwards movement is not easily possible currently, // the server could calculate speed differences to examine the gravity - if (d_vert > 0) - required_time = MYMAX(required_time, d_vert / player_max_jump); + if (d_vert > 0) { + // In certain cases (water, ladders) walking speed is applied vertically + float s = MYMAX(player_max_jump, player_max_walk); + required_time = MYMAX(required_time, d_vert / s); + } if (m_move_pool.grab(required_time)) { m_last_good_position = m_base_position; From f72490950a178ecf667181eb8c2b2243ba400e8d Mon Sep 17 00:00:00 2001 From: mazocomp <33579456+mazocomp@users.noreply.github.com> Date: Fri, 20 Apr 2018 20:55:23 +0300 Subject: [PATCH 119/183] Fix C++11 feature detection for undefined _MSC_VER (#7255) --- src/cguittfont/irrUString.h | 2 +- src/util/cpp11.h | 2 +- src/util/cpp11_container.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cguittfont/irrUString.h b/src/cguittfont/irrUString.h index eb7abe5a1..5b10e2367 100644 --- a/src/cguittfont/irrUString.h +++ b/src/cguittfont/irrUString.h @@ -31,7 +31,7 @@ #ifndef __IRR_USTRING_H_INCLUDED__ #define __IRR_USTRING_H_INCLUDED__ -#if (__cplusplus > 199711L) || (_MSC_VER >= 1600) || defined(__GXX_EXPERIMENTAL_CXX0X__) +#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1600) || defined(__GXX_EXPERIMENTAL_CXX0X__) # define USTRING_CPP0X # if defined(__GXX_EXPERIMENTAL_CXX0X__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5))) # define USTRING_CPP0X_NEWLITERALS diff --git a/src/util/cpp11.h b/src/util/cpp11.h index 14913cb86..17903f2e3 100644 --- a/src/util/cpp11.h +++ b/src/util/cpp11.h @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef MT_CPP11_HEADER #define MT_CPP11_HEADER -#if __cplusplus < 201103L || _MSC_VER < 1600 +#if __cplusplus < 201103L || (defined(_MSC_VER) && _MSC_VER < 1600) #define USE_CPP11_FAKE_KEYWORD #endif diff --git a/src/util/cpp11_container.h b/src/util/cpp11_container.h index 0194385fc..a09154071 100644 --- a/src/util/cpp11_container.h +++ b/src/util/cpp11_container.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define USE_UNORDERED_CONTAINERS #endif -#if _MSC_VER >= 1600 +#if defined(_MSC_VER) && _MSC_VER >= 1600 #define USE_UNORDERED_CONTAINERS #endif From 0268c9d7c9925daeeb2ac847aca090de635ba43c Mon Sep 17 00:00:00 2001 From: mazocomp <33579456+mazocomp@users.noreply.github.com> Date: Sat, 21 Apr 2018 14:07:25 +0300 Subject: [PATCH 120/183] "static constexpr v3s16 light_dirs[8]" fails to compile, sync it with master! (#7261) --- src/content_mapblock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index e6dd8e83e..7e93c1341 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // Corresponding offsets are listed in g_27dirs #define FRAMED_NEIGHBOR_COUNT 18 -static constexpr v3s16 light_dirs[8] = { +static const v3s16 light_dirs[8] = { v3s16(-1, -1, -1), v3s16(-1, -1, 1), v3s16(-1, 1, -1), From 880a25c92182bd10e8a80930909cf505e851ea65 Mon Sep 17 00:00:00 2001 From: you Date: Mon, 5 Feb 2018 15:17:10 +0100 Subject: [PATCH 121/183] Add minetest.is_player (#7013) * Add minetest.is_player * First use for is_player --- builtin/game/misc.lua | 19 ++++++++++++++----- doc/lua_api.txt | 1 + 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index bfe407b9d..d8f7a638d 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -5,12 +5,11 @@ -- function core.check_player_privs(name, ...) - local arg_type = type(name) - if (arg_type == "userdata" or arg_type == "table") and - name.get_player_name then -- If it quacks like a Player... + if core.is_player(name) then name = name:get_player_name() - elseif arg_type ~= "string" then - error("Invalid core.check_player_privs argument type: " .. arg_type, 2) + elseif type(name) ~= "string" then + error("core.check_player_privs expects a player or playername as " .. + "argument.", 2) end local requested_privs = {...} @@ -70,6 +69,16 @@ function core.get_connected_players() return temp_table end + +function core.is_player(player) + -- a table being a player is also supported because it quacks sufficiently + -- like a player if it has the is_player function + local t = type(player) + return (t == "userdata" or t == "table") and + type(player.is_player) == "function" and player:is_player() +end + + function minetest.player_exists(name) return minetest.get_auth_handler().get_auth(name) ~= nil end diff --git a/doc/lua_api.txt b/doc/lua_api.txt index b35c816a1..73572ac78 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3013,6 +3013,7 @@ These functions return the leftover itemstack. ### Misc. * `minetest.get_connected_players()`: returns list of `ObjectRefs` +* `minetest.is_player(o)`: boolean, whether `o` is a player * `minetest.player_exists(name)`: boolean, whether player exists (regardless of online status) * `minetest.hud_replace_builtin(name, hud_definition)` * Replaces definition of a builtin hud element From bb28afcfc337b73bff2b46ff1a1b8844e035c96e Mon Sep 17 00:00:00 2001 From: red-001 Date: Sun, 21 Jan 2018 12:28:47 +0000 Subject: [PATCH 122/183] Move `setlocale` from Lua to C++. --- builtin/init.lua | 2 -- src/script/cpp_api/s_base.cpp | 3 +++ src/script/cpp_api/s_security.cpp | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/init.lua b/builtin/init.lua index 356e119fb..73ab5cfd0 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -21,7 +21,6 @@ if core.print then core.print = nil -- don't pollute our namespace end math.randomseed(os.time()) -os.setlocale("C", "numeric") minetest = core -- Load other files @@ -47,7 +46,6 @@ elseif INIT == "mainmenu" then elseif INIT == "async" then dofile(asyncpath .. "init.lua") elseif INIT == "client" then - os.setlocale = nil dofile(clientpath .. "init.lua") else error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT))) diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 2537d895f..c75b1c2f8 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -119,6 +119,9 @@ ScriptApiBase::ScriptApiBase() : m_environment = NULL; m_guiengine = NULL; + + // Make sure Lua uses the right locale + setlocale(LC_NUMERIC, "C"); } ScriptApiBase::~ScriptApiBase() diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 5ad7947d5..66a761f4c 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -249,9 +249,8 @@ void ScriptApiSecurity::initializeSecurityClient() static const char *os_whitelist[] = { "clock", "date", - "difftime", - "time", - "setlocale", + "difftime", + "time" }; static const char *debug_whitelist[] = { "getinfo", From 77250300676bc08cff4e687df0b6ac86ea35a6e4 Mon Sep 17 00:00:00 2001 From: you Date: Thu, 8 Feb 2018 19:17:06 +0100 Subject: [PATCH 123/183] Allow dumping userdata (#7012) --- builtin/common/misc_helpers.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 0686c18da..ab2da14ea 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -120,7 +120,12 @@ end -- The dumped and level arguments are internal-only. function dump(o, indent, nested, level) - if type(o) ~= "table" then + local t = type(o) + if not level and t == "userdata" then + -- when userdata (e.g. player) is passed directly, print its metatable: + return "userdata metatable: " .. dump(getmetatable(o)) + end + if t ~= "table" then return basic_dump(o) end -- Contains table -> true/nil of currently nested tables From 529f00a2403cdd185b1eb5902de8947d03bb3413 Mon Sep 17 00:00:00 2001 From: paramat Date: Tue, 6 Feb 2018 00:55:35 +0000 Subject: [PATCH 124/183] Falling.lua: Delete falling node entities on contact with 'ignore' Prevents falling node entities entering the ignore at a world edge and resting on unloaded nodes 16 nodes below, unreachable, undiggable and still being processed by 'on step' because they don't revert to nodes. --- builtin/game/falling.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 1ac4f7081..991962cc3 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -60,8 +60,13 @@ core.register_entity(":__builtin:falling_node", { local pos = self.object:getpos() -- Position of bottom center point local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z} - -- Avoid bugs caused by an unloaded node below + -- 'bcn' is nil for unloaded nodes local bcn = core.get_node_or_nil(bcp) + -- Delete on contact with ignore at world edges + if bcn and bcn.name == "ignore" then + self.object:remove() + return + end local bcd = bcn and core.registered_nodes[bcn.name] if bcn and (not bcd or bcd.walkable or From 6d346a817be6882b9779e6564f9c902f55cfc951 Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 10 Feb 2018 04:55:40 +0000 Subject: [PATCH 125/183] Item entity: Delete in 'ignore' nodes --- builtin/game/item_entity.lua | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index c0e36be2d..caa759828 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -174,19 +174,18 @@ core.register_entity(":__builtin:item", { local p = self.object:getpos() p.y = p.y - 0.5 local node = core.get_node_or_nil(p) - local in_unloaded = (node == nil) - if in_unloaded then - -- Don't infinetly fall into unloaded map - self.object:setvelocity({x = 0, y = 0, z = 0}) - self.object:setacceleration({x = 0, y = 0, z = 0}) - self.physical_state = false - self.object:set_properties({physical = false}) + -- Delete in 'ignore' nodes + if node and node.name == "ignore" then + self.itemstring = "" + self.object:remove() return end - local nn = node.name - -- If node is not registered or node is walkably solid and resting on nodebox + + -- If node is nil (unloaded area), or node is not registered, or node is + -- walkably solid and item is resting on nodebox local v = self.object:getvelocity() - if not core.registered_nodes[nn] or core.registered_nodes[nn].walkable and v.y == 0 then + if not node or not core.registered_nodes[node.name] or + core.registered_nodes[node.name].walkable and v.y == 0 then if self.physical_state then local own_stack = ItemStack(self.object:get_luaentity().itemstring) -- Merge with close entities of the same item From 2a4fbbbff8f555df9984fbff054fe91b1a471d8f Mon Sep 17 00:00:00 2001 From: you Date: Sun, 18 Feb 2018 10:40:14 +0100 Subject: [PATCH 126/183] Fix "Ignoring CONTENT_IGNORE redefinition" warning (#4393) minetest.override_item still passes to core --- builtin/game/register.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index ec6f28097..25af24eb7 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -116,6 +116,8 @@ function core.register_item(name, itemdef) end itemdef.name = name + local is_overriding = core.registered_items[name] + -- Apply defaults and add to registered_* table if itemdef.type == "node" then -- Use the nodebox as selection box if it's not set manually @@ -177,7 +179,13 @@ function core.register_item(name, itemdef) --core.log("Registering item: " .. itemdef.name) core.registered_items[itemdef.name] = itemdef core.registered_aliases[itemdef.name] = nil - register_item_raw(itemdef) + + -- Used to allow builtin to register ignore to registered_items + if name ~= "ignore" then + register_item_raw(itemdef) + elseif is_overriding then + core.log("warning", "Attempted redefinition of \"ignore\"") + end end function core.unregister_item(name) From 858c41b8428fa78039f66b829c0cf8fc92a83212 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 18 Feb 2018 10:40:45 +0100 Subject: [PATCH 127/183] Check argument types inside MetaDataRef Lua API (#7045) --- src/script/lua_api/l_metadata.cpp | 12 ++++++------ src/script/lua_api/l_nodemeta.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp index 5f4e984cb..16cbcc05e 100644 --- a/src/script/lua_api/l_metadata.cpp +++ b/src/script/lua_api/l_metadata.cpp @@ -95,7 +95,7 @@ int MetaDataRef::l_get_int(lua_State *L) MAP_LOCK_REQUIRED; MetaDataRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); + std::string name = luaL_checkstring(L, 2); Metadata *meta = ref->getmeta(false); if (meta == NULL) { @@ -114,8 +114,8 @@ int MetaDataRef::l_set_int(lua_State *L) MAP_LOCK_REQUIRED; MetaDataRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); - int a = lua_tointeger(L, 3); + std::string name = luaL_checkstring(L, 2); + int a = luaL_checkint(L, 3); std::string str = itos(a); Metadata *meta = ref->getmeta(true); @@ -133,7 +133,7 @@ int MetaDataRef::l_get_float(lua_State *L) MAP_LOCK_REQUIRED; MetaDataRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); + std::string name = luaL_checkstring(L, 2); Metadata *meta = ref->getmeta(false); if (meta == NULL) { @@ -152,8 +152,8 @@ int MetaDataRef::l_set_float(lua_State *L) MAP_LOCK_REQUIRED; MetaDataRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); - float a = lua_tonumber(L, 3); + std::string name = luaL_checkstring(L, 2); + float a = luaL_checknumber(L, 3); std::string str = ftos(a); Metadata *meta = ref->getmeta(true); diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 5dfa6d52e..0af5eea54 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -158,7 +158,7 @@ bool NodeMetaRef::handleFromTable(lua_State *L, int table, Metadata *_meta) lua_pushnil(L); while (lua_next(L, inventorytable) != 0) { // key at index -2 and value at index -1 - std::string name = lua_tostring(L, -2); + std::string name = luaL_checkstring(L, -2); read_inventory_list(L, -1, inv, name.c_str(), getServer(L)); lua_pop(L, 1); // Remove value, keep key for next iteration } From f8cc92c190f01c9fc9debddb6f9090ac7b754af8 Mon Sep 17 00:00:00 2001 From: dopik <33575686+dopik@users.noreply.github.com> Date: Mon, 19 Feb 2018 08:41:44 +0100 Subject: [PATCH 128/183] /shutdown can't do countdown when using reconnect and/or shutdown message (#7055) Delay was converted from the param string and not the delay value, thus never using the actual given delay value when used in combination with other string values in the param, in this case reconnect and the shutdown messsage. --- builtin/game/chatcommands.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index e788a2a54..3bd8f2f9c 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -814,7 +814,7 @@ core.register_chatcommand("shutdown", { message = message or "" if delay ~= "" then - delay = tonumber(param) or 0 + delay = tonumber(delay) or 0 else delay = 0 core.log("action", name .. " shuts down server") From c2e39b9363f6a7df81a63c20912c8c469a480a0c Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Sat, 30 Sep 2017 00:50:42 +0700 Subject: [PATCH 129/183] Delete world dialog: Move buttons to avoid double click deletion Move confirmation delete button to never overlap initial delete button, to avoid world deletion by accidental double click. --- builtin/mainmenu/dlg_delete_world.lua | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/builtin/mainmenu/dlg_delete_world.lua b/builtin/mainmenu/dlg_delete_world.lua index 1e5af1feb..df1091033 100644 --- a/builtin/mainmenu/dlg_delete_world.lua +++ b/builtin/mainmenu/dlg_delete_world.lua @@ -17,39 +17,36 @@ local function delete_world_formspec(dialogdata) - local retval = - "size[11.5,4.5,true]" .. - "label[2,2;" .. + "size[10,2.5,true]" .. + "label[0.5,0.5;" .. fgettext("Delete World \"$1\"?", dialogdata.delete_name) .. "]" .. - "button[3.25,3.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" .. - "button[5.75,3.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]" + "button[0.5,1.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" .. + "button[7.0,1.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]" return retval end local function delete_world_buttonhandler(this, fields) if fields["world_delete_confirm"] then - if this.data.delete_index > 0 and - this.data.delete_index <= #menudata.worldlist:get_raw_list() then + this.data.delete_index <= #menudata.worldlist:get_raw_list() then core.delete_world(this.data.delete_index) menudata.worldlist:refresh() end this:delete() return true end - + if fields["world_delete_cancel"] then this:delete() return true end - + return false end -function create_delete_world_dlg(name_to_del,index_to_del) - +function create_delete_world_dlg(name_to_del, index_to_del) assert(name_to_del ~= nil and type(name_to_del) == "string" and name_to_del ~= "") assert(index_to_del ~= nil and type(index_to_del) == "number") @@ -59,6 +56,6 @@ function create_delete_world_dlg(name_to_del,index_to_del) nil) retval.data.delete_name = name_to_del retval.data.delete_index = index_to_del - + return retval end From c683e050d45285f38f822f04eeb65ad6c604c119 Mon Sep 17 00:00:00 2001 From: paramat Date: Wed, 21 Feb 2018 14:34:06 +0000 Subject: [PATCH 130/183] Find nodes in area (under air): Raise volume limit and document it --- doc/lua_api.txt | 2 ++ src/script/lua_api/l_env.cpp | 20 ++++++-------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 73572ac78..04ae33654 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2578,9 +2578,11 @@ and `minetest.auth_reload` call the authetification handler. * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * First return value: Table with all node positions * Second return value: Table with the count of each node with the node name as index + * Area volume is limited to 4,096,000 nodes * `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a list of positions * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * Return value: Table with all node positions with a node air above + * Area volume is limited to 4,096,000 nodes * `minetest.get_perlin(noiseparams)` * `minetest.get_perlin(seeddiff, octaves, persistence, scale)` * Return world-specific perlin noise (`int(worldseed)+seeddiff`) diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 46745facd..4a7885da7 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -655,14 +655,10 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) sortBoxVerticies(minp, maxp); v3s16 cube = maxp - minp + 1; - - /* Limit for too large areas, assume default values - * and give tolerances of 1 node on each side - * (chunksize * MAP_BLOCKSIZE + 2)^3 = 551368 - */ - if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 551368) { + // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000 + if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) { luaL_error(L, "find_nodes_in_area(): area volume" - " exceeds allowed value of 551368"); + " exceeds allowed value of 4096000"); return 0; } @@ -723,14 +719,10 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) sortBoxVerticies(minp, maxp); v3s16 cube = maxp - minp + 1; - - /* Limit for too large areas, assume default values - * and give tolerances of 1 node on each side - * (chunksize * MAP_BLOCKSIZE + 2)^3 = 551368 - */ - if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 551368) { + // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000 + if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) { luaL_error(L, "find_nodes_in_area_under_air(): area volume" - " exceeds allowed value of 551368"); + " exceeds allowed value of 4096000"); return 0; } From 8aaf526730a621ae6d8d2839730437b605d892e7 Mon Sep 17 00:00:00 2001 From: paramat Date: Tue, 20 Feb 2018 19:32:24 +0000 Subject: [PATCH 131/183] SAO limits: Allow SAOs to exist outside the set 'mapgen limit' --- src/content_sao.cpp | 14 -------------- src/map.cpp | 5 ----- src/map.h | 2 -- src/mapgen.cpp | 26 ++++---------------------- src/mapgen.h | 5 ----- src/serverenvironment.cpp | 3 +-- 6 files changed, 5 insertions(+), 50 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 1234e3915..c22e341a9 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -406,20 +406,6 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) m_env->getScriptIface()->luaentity_Step(m_id, dtime); } - // Remove LuaEntity beyond terrain edges - { - ServerMap *map = dynamic_cast(&m_env->getMap()); - assert(map); - if (!m_pending_deactivation && - map->saoPositionOverLimit(m_base_position)) { - infostream << "Remove SAO " << m_id << "(" << m_init_name - << "), outside of limits" << std::endl; - m_pending_deactivation = true; - m_pending_removal = true; - return; - } - } - if(send_recommended == false) return; diff --git a/src/map.cpp b/src/map.cpp index 0c57d6a8a..128971442 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1378,11 +1378,6 @@ s16 ServerMap::getWaterLevel() return getMapgenParams()->water_level; } -bool ServerMap::saoPositionOverLimit(const v3f &p) -{ - return getMapgenParams()->saoPosOverLimit(p); -} - bool ServerMap::blockpos_over_mapgen_limit(v3s16 p) { const s16 mapgen_limit_bp = rangelim( diff --git a/src/map.h b/src/map.h index 4b6e08f96..5123a7ec1 100644 --- a/src/map.h +++ b/src/map.h @@ -377,8 +377,6 @@ public: */ ServerMapSector *createSector(v2s16 p); - bool saoPositionOverLimit(const v3f &p); - /* Blocks are generated by using these and makeBlock(). */ diff --git a/src/mapgen.cpp b/src/mapgen.cpp index f4165f5cd..cb0100722 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -1058,13 +1058,11 @@ void MapgenParams::writeParams(Settings *settings) const bparams->writeParams(settings); } -// Calculate edges of outermost generated mapchunks (less than -// 'mapgen_limit'), and corresponding exact limits for SAO entities. + +// Calculate exact edges of the outermost mapchunks that are within the +// set 'mapgen_limit'. void MapgenParams::calcMapgenEdges() { - if (m_mapgen_edges_calculated) - return; - // Central chunk offset, in blocks s16 ccoff_b = -chunksize / 2; // Chunksize, in nodes @@ -1089,31 +1087,15 @@ void MapgenParams::calcMapgenEdges() // Mapgen edges, in nodes mapgen_edge_min = ccmin - numcmin * csize_n; mapgen_edge_max = ccmax + numcmax * csize_n; - // SAO position limits, in Irrlicht units - m_sao_limit_min = mapgen_edge_min * BS - 3.0f; - m_sao_limit_max = mapgen_edge_max * BS + 3.0f; m_mapgen_edges_calculated = true; } -bool MapgenParams::saoPosOverLimit(const v3f &p) +s32 MapgenParams::getSpawnRangeMax() { if (!m_mapgen_edges_calculated) calcMapgenEdges(); - return p.X < m_sao_limit_min || - p.X > m_sao_limit_max || - p.Y < m_sao_limit_min || - p.Y > m_sao_limit_max || - p.Z < m_sao_limit_min || - p.Z > m_sao_limit_max; -} - - -s32 MapgenParams::getSpawnRangeMax() -{ - calcMapgenEdges(); - return MYMIN(-mapgen_edge_min, mapgen_edge_max); } diff --git a/src/mapgen.h b/src/mapgen.h index 9bfdb22de..d1845d598 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -145,8 +145,6 @@ struct MapgenParams { mapgen_edge_min(-MAX_MAP_GENERATION_LIMIT), mapgen_edge_max(MAX_MAP_GENERATION_LIMIT), - m_sao_limit_min(-MAX_MAP_GENERATION_LIMIT * BS), - m_sao_limit_max(MAX_MAP_GENERATION_LIMIT * BS), m_mapgen_edges_calculated(false) { } @@ -156,14 +154,11 @@ struct MapgenParams { virtual void readParams(const Settings *settings); virtual void writeParams(Settings *settings) const; - bool saoPosOverLimit(const v3f &p); s32 getSpawnRangeMax(); private: void calcMapgenEdges(); - float m_sao_limit_min; - float m_sao_limit_max; bool m_mapgen_edges_calculated; }; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index e4fcf626d..8a66d4dfa 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -579,8 +579,7 @@ PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player, // If the player exists, ensure that they respawn inside legal bounds // This fixes an assert crash when the player can't be added // to the environment - ServerMap &map = getServerMap(); - if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) { + if (objectpos_over_limit(playersao->getBasePosition())) { actionstream << "Respawn position for player \"" << player->getName() << "\" outside limits, resetting" << std::endl; playersao->setBasePosition(m_server->findSpawnPos()); From 79fde0dd52bbdeb705576a285c0da2519f948eb7 Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 24 Feb 2018 15:57:34 +0000 Subject: [PATCH 132/183] CollisionMoveSimple: Collide with 'ignore' nodes --- src/collision.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/collision.cpp b/src/collision.cpp index 4c3bd016d..8faf05f54 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -283,7 +283,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, bool is_position_valid; MapNode n = map->getNodeNoEx(p, &is_position_valid); - if (is_position_valid) { + if (is_position_valid && n.getContent() != CONTENT_IGNORE) { // Object collides into walkable nodes any_position_valid = true; @@ -333,7 +333,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, false, n_bouncy_value, p, box)); } } else { - // Collide with unloaded nodes + // Collide with unloaded nodes (position invalid) and loaded + // CONTENT_IGNORE nodes (position valid) aabb3f box = getNodeBox(p, BS); cinfo.push_back(NearbyCollisionInfo(true, false, 0, p, box)); } @@ -341,6 +342,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Do not move if world has not loaded yet, since custom node boxes // are not available for collision detection. + // This also intentionally occurs in the case of the object being positioned + // solely on loaded CONTENT_IGNORE nodes, no matter where they come from. if (!any_position_valid) { *speed_f = v3f(0, 0, 0); return result; From c40f535df83ab8d10b0f9cc93c2531ac79036dd0 Mon Sep 17 00:00:00 2001 From: red-001 Date: Sun, 25 Feb 2018 22:52:05 +0000 Subject: [PATCH 133/183] Fix liquid post effect colour behaviour in third person view --- src/camera.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index 6957508cc..aa1baf957 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -386,8 +386,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, // *100.0 helps in large map coordinates m_cameranode->setTarget(my_cp-intToFloat(m_camera_offset, BS) + 100 * m_camera_direction); - // update the camera position in front-view mode to render blocks behind player - if (m_camera_mode == CAMERA_MODE_THIRD_FRONT) + // update the camera position in third-person mode to render blocks behind player + // and correctly apply liquid post FX. + if (m_camera_mode != CAMERA_MODE_FIRST) m_camera_position = my_cp; // Get FOV From a6b9acb7af7ac7b8dd184ee33ced425b616b9744 Mon Sep 17 00:00:00 2001 From: paramat Date: Fri, 2 Mar 2018 13:39:39 +0000 Subject: [PATCH 134/183] Generate Notifier: Clear events once after all 'on generated' functions --- src/emerge.cpp | 6 ++++++ src/mapgen.cpp | 10 ++++++---- src/mapgen.h | 4 ++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/emerge.cpp b/src/emerge.cpp index d24971e44..f7f6ff603 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -570,6 +570,12 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata, m_server->setAsyncFatalError("Lua: finishGen" + std::string(e.what())); } + /* + Clear generate notifier events + */ + Mapgen *mg = m_emerge->getCurrentMapgen(); + mg->gennotify.clearEvents(); + EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); /* diff --git a/src/mapgen.cpp b/src/mapgen.cpp index cb0100722..6fe2906bb 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -982,8 +982,7 @@ bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id) void GenerateNotifier::getEvents( - std::map > &event_map, - bool peek_events) + std::map > &event_map) { std::list::iterator it; @@ -995,9 +994,12 @@ void GenerateNotifier::getEvents( event_map[name].push_back(gn.pos); } +} - if (!peek_events) - m_notify_events.clear(); + +void GenerateNotifier::clearEvents() +{ + m_notify_events.clear(); } diff --git a/src/mapgen.h b/src/mapgen.h index d1845d598..4e0d75b39 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -101,8 +101,8 @@ public: void setNotifyOnDecoIds(std::set *notify_on_deco_ids); bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0); - void getEvents(std::map > &event_map, - bool peek_events=false); + void getEvents(std::map > &event_map); + void clearEvents(); private: u32 m_notify_on; From 1d7fbd035df7df2a9000dbd3607a6eef1235641b Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 17 Mar 2018 22:05:21 +0000 Subject: [PATCH 135/183] Minetest ASCII art: Move from actionstream to rawstream --- src/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index f8e6846c3..363065995 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -415,7 +415,7 @@ void Server::start(Address bind_addr) m_thread->start(); // ASCII art for the win! - actionstream + rawstream <<" .__ __ __ "< Date: Tue, 27 Mar 2018 01:43:59 +1000 Subject: [PATCH 136/183] macOS: don't require X11 libraries during compilation (#7149) The xxf86vm needs to be removed from Apple builds to avoid CMake Error XXF86VM_LIBRARY is NOTFOUND --- src/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b1650f376..6663b3c4c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -325,10 +325,12 @@ else() endif(HAVE_LIBRT) endif(APPLE) + if(NOT APPLE) # This way Xxf86vm is found on OpenBSD too - find_library(XXF86VM_LIBRARY Xxf86vm) - mark_as_advanced(XXF86VM_LIBRARY) - set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY}) + find_library(XXF86VM_LIBRARY Xxf86vm) + mark_as_advanced(XXF86VM_LIBRARY) + set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY}) + endif(NOT APPLE) # Prefer local iconv if installed find_library(ICONV_LIBRARY iconv) From fe41725e5027e90787f18088be82839760cccbb5 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 29 Mar 2018 21:44:13 +0200 Subject: [PATCH 137/183] core.rotate_node: Do not trigger after_place_node (#6900) --- builtin/common/misc_helpers.lua | 6 +++--- builtin/game/item.lua | 5 +++-- doc/lua_api.txt | 36 +++++++++++++++++++-------------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index ab2da14ea..aad5f2d21 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -341,7 +341,7 @@ if INIT == "game" then local dirs2 = {20, 23, 22, 21} function core.rotate_and_place(itemstack, placer, pointed_thing, - infinitestacks, orient_flags) + infinitestacks, orient_flags, prevent_after_place) orient_flags = orient_flags or {} local unode = core.get_node_or_nil(pointed_thing.under) @@ -394,7 +394,7 @@ if INIT == "game" then local old_itemstack = ItemStack(itemstack) local new_itemstack, removed = core.item_place_node( - itemstack, placer, pointed_thing, param2 + itemstack, placer, pointed_thing, param2, prevent_after_place ) return infinitestacks and old_itemstack or new_itemstack end @@ -415,7 +415,7 @@ if INIT == "game" then local invert_wall = placer and placer:get_player_control().sneak or false core.rotate_and_place(itemstack, placer, pointed_thing, is_creative(name), - {invert_wall = invert_wall}) + {invert_wall = invert_wall}, true) return itemstack end end diff --git a/builtin/game/item.lua b/builtin/game/item.lua index d4ce6002f..ea9681ae2 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -261,7 +261,8 @@ local function make_log(name) return name ~= "" and core.log or function() end end -function core.item_place_node(itemstack, placer, pointed_thing, param2) +function core.item_place_node(itemstack, placer, pointed_thing, param2, + prevent_after_place) local def = itemstack:get_definition() if def.type ~= "node" or pointed_thing.type ~= "node" then return itemstack, false @@ -375,7 +376,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) local take_item = true -- Run callback - if def.after_place_node then + if def.after_place_node and not prevent_after_place then -- Deepcopy place_to and pointed_thing because callback can modify it local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} local pointed_thing_copy = copy_pointed_thing(pointed_thing) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 04ae33654..bb6491627 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2857,9 +2857,11 @@ and `minetest.auth_reload` call the authetification handler. ### Defaults for the `on_*` item definition functions These functions return the leftover itemstack. -* `minetest.item_place_node(itemstack, placer, pointed_thing, param2)` +* `minetest.item_place_node(itemstack, placer, pointed_thing[, param2, prevent_after_place])` * Place item as a node * `param2` overrides `facedir` and wallmounted `param2` + * `prevent_after_place`: if set to `true`, `after_place_node` is not called + for the newly placed node to prevent a callback and placement loop * returns `itemstack, success` * `minetest.item_place_object(itemstack, placer, pointed_thing)` * Place item as-is @@ -3100,25 +3102,29 @@ These functions return the leftover itemstack. * `minetest.record_protection_violation(pos, name)` * This function calls functions registered with `minetest.register_on_protection_violation`. -* `minetest.rotate_and_place(itemstack, placer, pointed_thing, infinitestacks, orient_flags)` +* `minetest.rotate_and_place(itemstack, placer, pointed_thing[, infinitestacks, + orient_flags, prevent_after_place])` * Attempt to predict the desired orientation of the facedir-capable node - defined by `itemstack`, and place it accordingly (on-wall, on the floor, or - hanging from the ceiling). Stacks are handled normally if the `infinitestacks` - field is false or omitted (else, the itemstack is not changed). `orient_flags` - is an optional table containing extra tweaks to the placement code: - * `invert_wall`: if `true`, place wall-orientation on the ground and ground- - orientation on the wall. + defined by `itemstack`, and place it accordingly (on-wall, on the floor, + or hanging from the ceiling). + * `infinitestacks`: if `true`, the itemstack is not changed. Otherwise the + stacks are handled normally. + * `orient_flags`: Optional table containing extra tweaks to the placement code: + * `invert_wall`: if `true`, place wall-orientation on the ground and + ground-orientation on the wall. * `force_wall` : if `true`, always place the node in wall orientation. * `force_ceiling`: if `true`, always place on the ceiling. * `force_floor`: if `true`, always place the node on the floor. - * `force_facedir`: if `true`, forcefully reset the facedir to north when placing on - the floor or ceiling - * The first four options are mutually-exclusive; the last in the list takes - precedence over the first. + * `force_facedir`: if `true`, forcefully reset the facedir to north + when placing on the floor or ceiling. + * The first four options are mutually-exclusive; the last in the list + takes precedence over the first. + * `prevent_after_place` is directly passed to `minetest.item_place_node` + * Returns the new itemstack after placement * `minetest.rotate_node(itemstack, placer, pointed_thing)` - * calls `rotate_and_place()` with infinitestacks set according to the state of - the creative mode setting, and checks for "sneak" to set the `invert_wall` - parameter. + * calls `rotate_and_place()` with `infinitestacks` set according to the state + of the creative mode setting, checks for "sneak" to set the `invert_wall` + parameter and `prevent_after_place` set to `true`. * `minetest.forceload_block(pos[, transient])` * forceloads the position `pos`. From 396daf1be1c581da70eb40f7273fb7fad894de53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Wed, 4 Apr 2018 10:56:46 +0200 Subject: [PATCH 138/183] Huge LBM lookup performance improvement on mapblock loading (#7195) * Huge LBM lookup performance improvement on mapblock loading --- src/serverenvironment.cpp | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 8a66d4dfa..be1ddd7f0 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -262,16 +262,25 @@ void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) MapNode n; content_t c; lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp); - 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->getNodeNoEx(pos); - c = n.getContent(); - for (LBMManager::lbm_lookup_map::const_iterator iit = it; - iit != m_lbm_lookup.end(); ++iit) { - const std::vector *lbm_list = - iit->second.lookup(c); + for (; it != m_lbm_lookup.end(); ++it) { + // Cache previous version to speedup lookup which has a very high performance + // penalty on each call + content_t previous_c{}; + std::vector *lbm_list = nullptr; + + 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->getNodeNoEx(pos); + c = n.getContent(); + + // If content_t are not matching perform an LBM lookup + if (previous_c != c) { + lbm_list = (std::vector *) + it->second.lookup(c); + previous_c = c; + } + if (!lbm_list) continue; for (std::vector::const_iterator iit = @@ -279,7 +288,7 @@ void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) (*iit)->trigger(env, pos + pos_of_block, n); } } - } + } } /* From 875972ffa6cfbf4621bc71f11f4f30ee2ae47851 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 9 Apr 2018 14:58:35 +0200 Subject: [PATCH 139/183] upright_sprite: Fix texture position for players Fixes #6471 --- src/content_cao.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 994f0492a..994ff5bb5 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -835,6 +835,11 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, video::S3DVertex( dx, dy, 0, 0,0,0, c, 0,0), video::S3DVertex(-dx, dy, 0, 0,0,0, c, 1,0), }; + if (m_is_player) { + // Move minimal Y position to 0 (feet position) + for (video::S3DVertex &vertex : vertices) + vertex.Pos.Y += dy; + } u16 indices[] = {0,1,2,2,3,0}; buf->append(vertices, 4, indices, 6); // Set material @@ -854,6 +859,11 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, video::S3DVertex(-dx, dy, 0, 0,0,0, c, 0,0), video::S3DVertex( dx, dy, 0, 0,0,0, c, 1,0), }; + if (m_is_player) { + // Move minimal Y position to 0 (feet position) + for (video::S3DVertex &vertex : vertices) + vertex.Pos.Y += dy; + } u16 indices[] = {0,1,2,2,3,0}; buf->append(vertices, 4, indices, 6); // Set material From 1d06a8ef6c2e5a66c4f109b7ef9cb76c31e3ce4f Mon Sep 17 00:00:00 2001 From: minduser00 Date: Tue, 27 Mar 2018 11:49:47 +0000 Subject: [PATCH 140/183] Fix for translating empty strings Fix for incorrect translation of empty strings In the key change menu, when a button key not have name an empty string is passed to gettext. The empty string is reserved for gettext to return de header of the .po file an this is shoved in the button --- src/gettext.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gettext.h b/src/gettext.h index 885d7ca2d..170d75dee 100644 --- a/src/gettext.h +++ b/src/gettext.h @@ -51,12 +51,13 @@ extern wchar_t *utf8_to_wide_c(const char *str); // The returned string is allocated using new inline const wchar_t *wgettext(const char *str) { - return utf8_to_wide_c(gettext(str)); + // We must check here that is not an empty string to avoid trying to translate it + return str[0] ? utf8_to_wide_c(gettext(str)) : L""; } inline std::string strgettext(const std::string &text) { - return gettext(text.c_str()); + return text.empty() ? "" : gettext(text.c_str()); } #endif From a6cfe73cb057001f214bd8c6cebc3d699dba16b7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 9 Apr 2018 16:25:57 +0200 Subject: [PATCH 141/183] Fix segfault caused by wrong wgettext() --- src/gettext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gettext.h b/src/gettext.h index 170d75dee..9aa6b3a27 100644 --- a/src/gettext.h +++ b/src/gettext.h @@ -52,7 +52,7 @@ extern wchar_t *utf8_to_wide_c(const char *str); inline const wchar_t *wgettext(const char *str) { // We must check here that is not an empty string to avoid trying to translate it - return str[0] ? utf8_to_wide_c(gettext(str)) : L""; + return str[0] ? utf8_to_wide_c(gettext(str)) : utf8_to_wide_c(""); } inline std::string strgettext(const std::string &text) From 0414322d23f1a4393e909dd075771691603838f3 Mon Sep 17 00:00:00 2001 From: Paramat Date: Tue, 17 Apr 2018 22:25:59 +0100 Subject: [PATCH 142/183] Cavegen: Fix variable typo that broke mgvalleys large cave distribution (#7249) Fix elusive 5 year old bug that caused mgvalleys large caves to be flat and limited to mapchunk borders. Error was fixed 2 years ago in 'CavesV6' but not in 'CavesRandomWalk'. --- src/cavegen.cpp | 2 +- src/cavegen.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cavegen.cpp b/src/cavegen.cpp index dbed79951..07fb629e2 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -332,7 +332,7 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, route_y_min = 0; // Allow half a diameter + 7 over stone surface - route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7; + route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7; // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y - 1); diff --git a/src/cavegen.h b/src/cavegen.h index a1140594e..1cb8dd90d 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -134,7 +134,6 @@ public: bool large_cave_is_flat; bool flooded; - s16 max_stone_y; v3s16 node_min; v3s16 node_max; From 14d20f58275e15fa9e81a82f3db475b0d5c22a2d Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 19 Apr 2018 18:36:10 +0200 Subject: [PATCH 143/183] Builtin auth handler: Speed up file writing (#7252) --- builtin/game/auth.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index 74eb6ae88..19af8db73 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -67,13 +67,13 @@ local function save_auth_file() assert(type(stuff.privileges) == "table") assert(stuff.last_login == nil or type(stuff.last_login) == "number") end - local content = "" + local content = {} for name, stuff in pairs(core.auth_table) do local priv_string = core.privs_to_string(stuff.privileges) local parts = {name, stuff.password, priv_string, stuff.last_login or ""} - content = content .. table.concat(parts, ":") .. "\n" + content[#content + 1] = table.concat(parts, ":") end - if not core.safe_file_write(core.auth_file_path, content) then + if not core.safe_file_write(core.auth_file_path, table.concat(content, "\n")) then error(core.auth_file_path.." could not be written to") end end From e660b05523326bdd1ee4c6d5ae4c4002711e4582 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 13 May 2018 10:15:35 +0200 Subject: [PATCH 144/183] Fix C++03 compiling due to C++11 initialization issues in backport --- src/client/joystick_controller.h | 2 + src/localplayer.cpp | 12 +++-- src/localplayer.h | 87 ++++++++++++++++---------------- 3 files changed, 54 insertions(+), 47 deletions(-) diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h index 2c0e7b90a..ea1db684a 100644 --- a/src/client/joystick_controller.h +++ b/src/client/joystick_controller.h @@ -60,6 +60,8 @@ struct JoystickButtonCmb : public JoystickCombination { this->key = key; } + virtual ~JoystickButtonCmb() {} + virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const; u32 filter_mask; diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 9000888bf..ea2da15da 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., LocalPlayer::LocalPlayer(Client *client, const char *name): Player(name, client->idef()), - parent(0), + parent(NULL), hp(PLAYER_MAX_HP), isAttached(false), touching_ground(false), @@ -53,8 +53,8 @@ LocalPlayer::LocalPlayer(Client *client, const char *name): overridePosition(v3f(0,0,0)), last_position(v3f(0,0,0)), last_speed(v3f(0,0,0)), - last_pitch(0), - last_yaw(0), + last_pitch(0.0f), + last_yaw(0.0f), last_keyPressed(0), last_camera_fov(0), last_wanted_range(0), @@ -67,6 +67,12 @@ LocalPlayer::LocalPlayer(Client *client, const char *name): hurt_tilt_timer(0.0f), hurt_tilt_strength(0.0f), m_position(0,0,0), + m_sneak_node(32767, 32767, 32767), + m_sneak_node_bb_top(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), + m_sneak_node_exists(false), + m_sneak_ladder_detected(false), + m_sneak_node_bb_ymax(0.0f), + m_need_to_get_new_sneak_node(true), m_old_node_below(32767,32767,32767), m_old_node_below_type("air"), m_can_jump(false), diff --git a/src/localplayer.h b/src/localplayer.h index c371b345c..7249be74b 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -45,29 +45,29 @@ public: LocalPlayer(Client *client, const char *name); virtual ~LocalPlayer(); - ClientActiveObject *parent = nullptr; + ClientActiveObject *parent; // Initialize hp to 0, so that no hearts will be shown if server // doesn't support health points - u16 hp = 0; - bool isAttached = false; - bool touching_ground = false; + u16 hp; + bool isAttached; + bool touching_ground; // This oscillates so that the player jumps a bit above the surface - bool in_liquid = false; + bool in_liquid; // This is more stable and defines the maximum speed of the player - bool in_liquid_stable = false; + bool in_liquid_stable; // Gets the viscosity of water to calculate friction - u8 liquid_viscosity = 0; - bool is_climbing = false; - bool swimming_vertical = false; + u8 liquid_viscosity; + bool is_climbing; + bool swimming_vertical; - float physics_override_speed = 1.0f; - float physics_override_jump = 1.0f; - float physics_override_gravity = 1.0f; - bool physics_override_sneak = true; - bool physics_override_sneak_glitch = false; + float physics_override_speed; + float physics_override_jump; + float physics_override_gravity; + bool physics_override_sneak; + bool physics_override_sneak_glitch; // Temporary option for old move code - bool physics_override_new_move = true; + bool physics_override_new_move; v3f overridePosition; @@ -86,26 +86,26 @@ public: // Used to check if anything changed and prevent sending packets if not v3f last_position; v3f last_speed; - float last_pitch = 0.0f; - float last_yaw = 0.0f; - unsigned int last_keyPressed = 0; - u8 last_camera_fov = 0; - u8 last_wanted_range = 0; + float last_pitch; + float last_yaw; + unsigned int last_keyPressed; + u8 last_camera_fov; + u8 last_wanted_range; - float camera_impact = 0.0f; + float camera_impact; - bool makes_footstep_sound = true; + bool makes_footstep_sound; - int last_animation = NO_ANIM; + int last_animation; float last_animation_speed; - std::string hotbar_image = ""; - std::string hotbar_selected_image = ""; + std::string hotbar_image; + std::string hotbar_selected_image; - video::SColor light_color = video::SColor(255, 255, 255, 255); + video::SColor light_color; - float hurt_tilt_timer = 0.0f; - float hurt_tilt_strength = 0.0f; + float hurt_tilt_timer; + float hurt_tilt_strength; GenericCAO *getCAO() const { return m_cao; } @@ -148,35 +148,34 @@ private: v3f m_position; v3s16 m_standing_node; - v3s16 m_sneak_node = v3s16(32767, 32767, 32767); + v3s16 m_sneak_node; // Stores the top bounding box of m_sneak_node - aabb3f m_sneak_node_bb_top = aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + aabb3f m_sneak_node_bb_top; // Whether the player is allowed to sneak - bool m_sneak_node_exists = false; + bool m_sneak_node_exists; // Whether a "sneak ladder" structure is detected at the players pos // see detectSneakLadder() in the .cpp for more info (always false if disabled) - bool m_sneak_ladder_detected = false; + bool m_sneak_ladder_detected; // ***** Variables for temporary option of the old move code ***** // Stores the max player uplift by m_sneak_node - f32 m_sneak_node_bb_ymax = 0.0f; + f32 m_sneak_node_bb_ymax; // Whether recalculation of m_sneak_node and its top bbox is needed - bool m_need_to_get_new_sneak_node = true; + bool m_need_to_get_new_sneak_node; // Node below player, used to determine whether it has been removed, // and its old type - v3s16 m_old_node_below = v3s16(32767, 32767, 32767); - std::string m_old_node_below_type = "air"; + v3s16 m_old_node_below; + std::string m_old_node_below_type; // ***** End of variables for temporary option ***** - bool m_can_jump = false; - u16 m_breath = PLAYER_MAX_BREATH; - f32 m_yaw = 0.0f; - f32 m_pitch = 0.0f; - bool camera_barely_in_ceiling = false; - aabb3f m_collisionbox = aabb3f(-BS * 0.30f, 0.0f, -BS * 0.30f, BS * 0.30f, - BS * 1.75f, BS * 0.30f); + bool m_can_jump; + u16 m_breath; + f32 m_yaw; + f32 m_pitch; + bool camera_barely_in_ceiling; + aabb3f m_collisionbox; - GenericCAO *m_cao = nullptr; + GenericCAO *m_cao; Client *m_client; }; From 0088fa6db4111c20b48c7f0ff894fee779fe221b Mon Sep 17 00:00:00 2001 From: mazocomp <33579456+mazocomp@users.noreply.github.com> Date: Sat, 21 Apr 2018 22:33:38 +0300 Subject: [PATCH 145/183] Fix i386 bit build at OpenBSD (#7259) --- src/unittest/test_serialization.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/unittest/test_serialization.cpp b/src/unittest/test_serialization.cpp index 4cbc999ea..cfcfec0cd 100644 --- a/src/unittest/test_serialization.cpp +++ b/src/unittest/test_serialization.cpp @@ -295,7 +295,7 @@ void TestSerialization::testStreamRead() UASSERT(readU8(is) == 0x11); UASSERT(readU16(is) == 0x2233); UASSERT(readU32(is) == 0x44556677); - UASSERT(readU64(is) == 0x8899AABBCCDDEEFF); + UASSERT(readU64(is) == 0x8899AABBCCDDEEFFLL); UASSERT(readS8(is) == -128); UASSERT(readS16(is) == 30000); @@ -336,7 +336,7 @@ void TestSerialization::testStreamWrite() writeU8(os, 0x11); writeU16(os, 0x2233); writeU32(os, 0x44556677); - writeU64(os, 0x8899AABBCCDDEEFF); + writeU64(os, 0x8899AABBCCDDEEFFLL); writeS8(os, -128); writeS16(os, 30000); @@ -382,7 +382,7 @@ void TestSerialization::testVecPut() putU8(&buf, 0x11); putU16(&buf, 0x2233); putU32(&buf, 0x44556677); - putU64(&buf, 0x8899AABBCCDDEEFF); + putU64(&buf, 0x8899AABBCCDDEEFFLL); putS8(&buf, -128); putS16(&buf, 30000); @@ -464,7 +464,7 @@ void TestSerialization::testBufReader() UASSERT(buf.getU8() == 0x11); UASSERT(buf.getU16() == 0x2233); UASSERT(buf.getU32() == 0x44556677); - UASSERT(buf.getU64() == 0x8899AABBCCDDEEFF); + UASSERT(buf.getU64() == 0x8899AABBCCDDEEFFLL); UASSERT(buf.getS8() == -128); UASSERT(buf.getS16() == 30000); UASSERT(buf.getS32() == -6); @@ -576,7 +576,7 @@ void TestSerialization::testBufReader() UASSERT(u8_data == 0x11); UASSERT(u16_data == 0x2233); UASSERT(u32_data == 0x44556677); - UASSERT(u64_data == 0x8899AABBCCDDEEFF); + UASSERT(u64_data == 0x8899AABBCCDDEEFFLL); UASSERT(s8_data == -128); UASSERT(s16_data == 30000); UASSERT(s32_data == -6); From ebfdb21624d07cb3bb99d1c5e288aa9587aef453 Mon Sep 17 00:00:00 2001 From: Paramat Date: Wed, 25 Apr 2018 00:44:49 +0100 Subject: [PATCH 146/183] Dungeons: Mostly fix missing stair nodes --- src/dungeongen.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp index 6cef3f88d..f8f3df56f 100644 --- a/src/dungeongen.cpp +++ b/src/dungeongen.cpp @@ -431,8 +431,10 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_wall), 0); - makeHole(p); - makeHole(p - dir); + makeFill(p, dp.holesize, VMANIP_FLAG_DUNGEON_UNTOUCHABLE, + MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE); + makeFill(p - dir, dp.holesize, VMANIP_FLAG_DUNGEON_UNTOUCHABLE, + MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE); // TODO: fix stairs code so it works 100% // (quite difficult) @@ -451,16 +453,21 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1); for (u16 st = 0; st < stair_width; st++) { - u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z); - if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) && - vm->m_data[vi].getContent() == dp.c_wall) - vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); - - vi = vm->m_area.index(ps.X, ps.Y, ps.Z); - if (vm->m_area.contains(ps) && - vm->m_data[vi].getContent() == dp.c_wall) - vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); - + if (make_stairs == -1) { + u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z); + if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) && + vm->m_data[vi].getContent() == dp.c_wall) { + vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE; + vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); + } + } else if (make_stairs == 1) { + u32 vi = vm->m_area.index(ps.X, ps.Y - 1, ps.Z); + if (vm->m_area.contains(ps + v3s16(0, -1, 0)) && + vm->m_data[vi].getContent() == dp.c_wall) { + vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE; + vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); + } + } ps += swv; } } From e2815d27f16f20c503c34b9a0ab4f917dcab639b Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 8 May 2018 19:06:55 +0100 Subject: [PATCH 147/183] Fix luajit include not being found --- cmake/Modules/FindLuaJIT.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/FindLuaJIT.cmake b/cmake/Modules/FindLuaJIT.cmake index e4335d834..cd6e7bdd8 100644 --- a/cmake/Modules/FindLuaJIT.cmake +++ b/cmake/Modules/FindLuaJIT.cmake @@ -9,7 +9,7 @@ FIND_PATH(LUA_INCLUDE_DIR luajit.h HINTS $ENV{LUA_DIR} - PATH_SUFFIXES include/luajit-2.0 include/luajit-5_1-2.0 include + PATH_SUFFIXES include/luajit-2.1 include/luajit-2.0 include/luajit-5_1-2.1 include/luajit-5_1-2.0 include PATHS ~/Library/Frameworks /Library/Frameworks From 695d02e6bda939f7b00af402273b39a8fd75a203 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 14 May 2018 07:42:20 +0200 Subject: [PATCH 148/183] More C++03 fixes --- src/client/clientlauncher.h | 2 +- src/client/joystick_controller.h | 2 ++ src/clientmap.cpp | 25 ++++++++++++++----------- src/itemstackmetadata.cpp | 9 +++++---- src/reflowscan.cpp | 4 ++-- src/remoteplayer.cpp | 1 + src/remoteplayer.h | 2 +- src/script/cpp_api/s_item.h | 2 +- src/script/lua_api/l_env.cpp | 4 ++-- src/script/lua_api/l_object.cpp | 8 +++++--- src/serverenvironment.cpp | 2 +- src/serverobject.h | 4 ++-- 12 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index 3d82b9cdc..4ff77bc03 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -81,7 +81,7 @@ protected: scene::ISceneManager *smgr; SubgameSpec gamespec; WorldSpec worldspec; - bool simple_singleplayer_mode = false; + bool simple_singleplayer_mode; // These are set up based on the menu and other things // TODO: Are these required since there's already playername, password, etc diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h index ea1db684a..744315011 100644 --- a/src/client/joystick_controller.h +++ b/src/client/joystick_controller.h @@ -79,6 +79,8 @@ struct JoystickAxisCmb : public JoystickCombination { this->key = key; } + virtual ~JoystickAxisCmb() {} + virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const; u16 axis_to_compare; diff --git a/src/clientmap.cpp b/src/clientmap.cpp index d00443c62..21937ac81 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -314,14 +314,15 @@ struct MeshBufListList // Append to the correct layer std::vector &list = lists[layer]; const video::SMaterial &m = buf->getMaterial(); - for (MeshBufList &l : list) { + for (std::vector::iterator it = list.begin(); it != list.end(); + ++it) { // comparing a full material is quite expensive so we don't do it if // not even first texture is equal - if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture) + if ((*it).m.TextureLayer[0].Texture != m.TextureLayer[0].Texture) continue; - if (l.m == m) { - l.bufs.push_back(buf); + if ((*it).m == m) { + (*it).bufs.push_back(buf); return; } } @@ -356,7 +357,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) Measuring time is very useful for long delays when the machine is swapping a lot. */ - std::time_t time1 = time(0); + time_t time1 = time(0); /* Get animation parameters @@ -477,11 +478,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) std::vector &lists = drawbufs.lists[layer]; int timecheck_counter = 0; - for (MeshBufList &list : lists) { + for (std::vector::iterator it = lists.begin(); it != lists.end(); + ++it) { timecheck_counter++; if (timecheck_counter > 50) { timecheck_counter = 0; - std::time_t time2 = time(0); + time_t time2 = time(0); if (time2 > time1 + 4) { infostream << "ClientMap::renderMap(): " "Rendering takes ages, returning." @@ -490,11 +492,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } } - driver->setMaterial(list.m); + driver->setMaterial((*it).m); - for (scene::IMeshBuffer *buf : list.bufs) { - driver->drawMeshBuffer(buf); - vertex_count += buf->getVertexCount(); + for (std::vector::iterator it2 = (*it).bufs.begin(); + it2 != (*it).bufs.end(); ++it2) { + driver->drawMeshBuffer(*it2); + vertex_count += (*it2)->getVertexCount(); meshbuffer_count++; } } diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp index f63671425..9847cb6f9 100644 --- a/src/itemstackmetadata.cpp +++ b/src/itemstackmetadata.cpp @@ -13,10 +13,11 @@ void ItemStackMetadata::serialize(std::ostream &os) const { std::ostringstream os2; os2 << DESERIALIZE_START; - for (const auto &stringvar : m_stringvars) { - if (!stringvar.first.empty() || !stringvar.second.empty()) - os2 << stringvar.first << DESERIALIZE_KV_DELIM - << stringvar.second << DESERIALIZE_PAIR_DELIM; + for (StringMap::const_iterator it = m_stringvars.begin(); it != m_stringvars.end(); + ++it) { + if (!(*it).first.empty() || !(*it).second.empty()) + os2 << (*it).first << DESERIALIZE_KV_DELIM + << (*it).second << DESERIALIZE_PAIR_DELIM; } os << serializeJsonStringIfNeeded(os2.str()); } diff --git a/src/reflowscan.cpp b/src/reflowscan.cpp index eec371022..ba7898d52 100644 --- a/src/reflowscan.cpp +++ b/src/reflowscan.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., ReflowScan::ReflowScan(Map *map, INodeDefManager *ndef) : m_map(map), m_ndef(ndef), - m_liquid_queue(nullptr) + m_liquid_queue(NULL) { } @@ -42,7 +42,7 @@ void ReflowScan::scan(MapBlock *block, UniqueQueue *liquid_queue) // scanned block. Blocks are only added to the lookup if they are really // needed. The lookup is indexed manually to use the same index in a // bit-array (of uint32 type) which stores for unloaded blocks that they - // were already fetched from Map but were actually nullptr. + // were already fetched from Map but were actually NULL. memset(m_lookup, 0, sizeof(m_lookup)); int block_idx = 1 + (1 * 9) + (1 * 3); m_lookup[block_idx] = block; diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 540132978..df9542630 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -43,6 +43,7 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef): m_last_chat_message_sent(time(NULL)), m_chat_message_allowance(5.0f), m_message_rate_overhead(0), + m_day_night_ratio_do_override(false), hud_hotbar_image(""), hud_hotbar_selected_image("") { diff --git a/src/remoteplayer.h b/src/remoteplayer.h index cbb9386ce..ee0d625b6 100644 --- a/src/remoteplayer.h +++ b/src/remoteplayer.h @@ -156,7 +156,7 @@ private: float m_chat_message_allowance; u16 m_message_rate_overhead; - bool m_day_night_ratio_do_override = false; + bool m_day_night_ratio_do_override; float m_day_night_ratio; std::string hud_hotbar_image; std::string hud_hotbar_selected_image; diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h index b4b02b0c5..daff15bf1 100644 --- a/src/script/cpp_api/s_item.h +++ b/src/script/cpp_api/s_item.h @@ -53,7 +53,7 @@ protected: friend class LuaItemStack; friend class ModApiItemMod; - bool getItemCallback(const char *name, const char *callbackname, const v3s16 *p = nullptr); + bool getItemCallback(const char *name, const char *callbackname, const v3s16 *p = NULL); void pushPointedThing(const PointedThing& pointed); }; diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 4a7885da7..630f6cc64 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -292,7 +292,7 @@ int ModApiEnvMod::l_place_node(lua_State *L) pointed.node_abovesurface = pos; pointed.node_undersurface = pos + v3s16(0,-1,0); // Place it with a NULL placer (appears in Lua as nil) - bool success = scriptIfaceItem->item_OnPlace(item, nullptr, pointed); + bool success = scriptIfaceItem->item_OnPlace(item, NULL, pointed); lua_pushboolean(L, success); return 1; } @@ -676,7 +676,7 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) ndef->getIds(lua_tostring(L, 3), filter); } - std::unordered_map individual_count; + UNORDERED_MAP individual_count; lua_newtable(L); u64 i = 0; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 0195dc399..8905f2d0c 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_item.h" #include "common/c_converter.h" #include "common/c_content.h" +#include "util/cpp11_container.h" #include "log.h" #include "tool.h" #include "serverobject.h" @@ -137,10 +138,11 @@ int ObjectRef::l_remove(lua_State *L) if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) return 0; - const std::unordered_set &child_ids = co->getAttachmentChildIds(); - for (int child_id : child_ids) { + const UNORDERED_SET &child_ids = co->getAttachmentChildIds(); + for (UNORDERED_SET::const_iterator it = child_ids.begin(); it != child_ids.end(); + ++it) { // Child can be NULL if it was deleted earlier - if (ServerActiveObject *child = env->getActiveObject(child_id)) + if (ServerActiveObject *child = env->getActiveObject(*it)) child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index be1ddd7f0..da4aaf006 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -266,7 +266,7 @@ void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) // Cache previous version to speedup lookup which has a very high performance // penalty on each call content_t previous_c{}; - std::vector *lbm_list = nullptr; + std::vector *lbm_list = NULL; for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) diff --git a/src/serverobject.h b/src/serverobject.h index 31af5d6a7..d6072e1a3 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -213,7 +213,7 @@ public: - This is usually set to true by the step() method when the object wants to be deleted but can be set by anything else too. */ - bool m_pending_removal = false; + bool m_pending_removal; /* Same purpose as m_pending_removal but for deactivation. @@ -222,7 +222,7 @@ public: If this is set alongside with m_pending_removal, removal takes priority. */ - bool m_pending_deactivation = false; + bool m_pending_deactivation; /* A getter that unifies the above to answer the question: From 263400b3d89c9499cb4be706461760bb5ccd9f5d Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Fri, 18 May 2018 11:10:53 +0200 Subject: [PATCH 149/183] C++03 oldify in various source files --- src/content_cao.cpp | 8 ++++---- src/localplayer.cpp | 14 ++++++++------ src/mapnode.cpp | 6 +++++- src/server.cpp | 6 ++++-- src/serverenvironment.cpp | 26 +++++++++++++++----------- src/sound_openal.cpp | 4 ++-- 6 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 994ff5bb5..7f1293ac3 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -837,8 +837,8 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, }; if (m_is_player) { // Move minimal Y position to 0 (feet position) - for (video::S3DVertex &vertex : vertices) - vertex.Pos.Y += dy; + for (size_t i = 0; i < 4; i++) + vertices[i].Pos.Y += dy; } u16 indices[] = {0,1,2,2,3,0}; buf->append(vertices, 4, indices, 6); @@ -861,8 +861,8 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, }; if (m_is_player) { // Move minimal Y position to 0 (feet position) - for (video::S3DVertex &vertex : vertices) - vertex.Pos.Y += dy; + for (size_t i = 0; i < 4; i++) + vertices[i].Pos.Y += dy; } u16 indices[] = {0,1,2,2,3,0}; buf->append(vertices, 4, indices, 6); diff --git a/src/localplayer.cpp b/src/localplayer.cpp index ea2da15da..05195c91f 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -362,20 +362,22 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, // Force update each ClientEnvironment::step() bool is_first = collision_info->empty(); - for (const auto &colinfo : result.collisions) { - collision_info->push_back(colinfo); + for (std::vector::const_iterator + colinfo = result.collisions.begin(); + colinfo != result.collisions.end(); ++colinfo) { + collision_info->push_back(*colinfo); - if (colinfo.type != COLLISION_NODE || - colinfo.new_speed.Y != 0 || + if (colinfo->type != COLLISION_NODE || + colinfo->new_speed.Y != 0 || (could_sneak && m_sneak_node_exists)) continue; - diff = intToFloat(colinfo.node_p, BS) - position; + diff = intToFloat(colinfo->node_p, BS) - position; // Find nearest colliding node f32 len = diff.getLength(); if (is_first || len < distance) { - m_standing_node = colinfo.node_p; + m_standing_node = colinfo->node_p; distance = len; } } diff --git a/src/mapnode.cpp b/src/mapnode.cpp index ff5ad5557..aaea3831e 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -249,7 +249,11 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, int facedir = n.getFaceDir(nodemgr); u8 axisdir = facedir>>2; facedir&=0x03; - for (aabb3f box : fixed) { + for (std::vector::const_iterator + i = fixed.begin(); + i != fixed.end(); ++i) { + aabb3f box = *i; + if (nodebox.type == NODEBOX_LEVELED) box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS; diff --git a/src/server.cpp b/src/server.cpp index 363065995..71a349b08 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1660,8 +1660,10 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message) Send(&pkt); } else { - for (u16 id : m_clients.getClientIDs()) - SendChatMessage(id, message); + std::vector clients = m_clients.getClientIDs(); + for (std::vector::iterator it = clients.begin(); + it != clients.end(); ++it) + SendChatMessage(*it, message); } } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index da4aaf006..8c3c34854 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -265,7 +265,7 @@ void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) for (; it != m_lbm_lookup.end(); ++it) { // Cache previous version to speedup lookup which has a very high performance // penalty on each call - content_t previous_c{}; + content_t previous_c = CONTENT_IGNORE; std::vector *lbm_list = NULL; for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) @@ -1026,9 +1026,10 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode) infostream << "ServerEnvironment::clearObjects(): " << "Removing all active objects" << std::endl; std::vector objects_to_remove; - for (auto &it : m_active_objects) { - u16 id = it.first; - ServerActiveObject* obj = it.second; + for (ActiveObjectMap::iterator it = m_active_objects.begin(); + it != m_active_objects.end(); ++it) { + u16 id = it->first; + ServerActiveObject* obj = it->second; if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) continue; @@ -1054,8 +1055,9 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode) } // Remove references from m_active_objects - for (u16 i : objects_to_remove) { - m_active_objects.erase(i); + for (std::vector::iterator it = objects_to_remove.begin(); + it != objects_to_remove.end(); ++it) { + m_active_objects.erase(*it); } // Get list of loaded blocks @@ -1822,8 +1824,9 @@ void ServerEnvironment::removeRemovedObjects() objects_to_remove.push_back(id); } // Remove references from m_active_objects - for (u16 i : objects_to_remove) { - m_active_objects.erase(i); + for (std::vector::iterator it = objects_to_remove.begin(); + it != objects_to_remove.end(); ++it) { + m_active_objects.erase(*it); } } @@ -2093,8 +2096,9 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) } // Remove references from m_active_objects - for (u16 i : objects_to_remove) { - m_active_objects.erase(i); + for (std::vector::iterator it = objects_to_remove.begin(); + it != objects_to_remove.end(); ++it) { + m_active_objects.erase(*it); } } @@ -2128,7 +2132,7 @@ bool ServerEnvironment::saveStaticToBlock( ServerActiveObject *obj, const StaticObject &s_obj, u32 mod_reason) { - MapBlock *block = nullptr; + MapBlock *block = NULL; try { block = m_map->emergeBlock(blockpos); } catch (InvalidPositionException &e) { diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index 1bad04b9c..e56de3178 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -408,7 +408,7 @@ public: alSource3f(sound->source_id, AL_POSITION, 0, 0, 0); alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); - volume = std::fmax(0.0f, volume); + volume = MYMAX(0.0f, volume); alSourcef(sound->source_id, AL_GAIN, volume); alSourcePlay(sound->source_id); warn_if_error(alGetError(), "createPlayingSound"); @@ -436,7 +436,7 @@ public: alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from // the previous value of 30 to the new value of 10 - volume = std::fmax(0.0f, volume * 3.0f); + volume = MYMAX(0.0f, volume * 3.0f); alSourcef(sound->source_id, AL_GAIN, volume); alSourcePlay(sound->source_id); warn_if_error(alGetError(), "createPlayingSoundAt"); From 24abdf45b98be8d7120d722be8279d268a16f65a Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 21 May 2018 21:00:56 +0200 Subject: [PATCH 150/183] Fix windows toolchain due to missing packages, upgrade to more recent toolchain --- util/travis/before_install.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/util/travis/before_install.sh b/util/travis/before_install.sh index a4328fa46..af69f78ca 100755 --- a/util/travis/before_install.sh +++ b/util/travis/before_install.sh @@ -1,4 +1,5 @@ #!/bin/bash -e + echo "Preparing for $TRAVIS_COMMIT_RANGE" if [[ "$LINT" == "1" ]]; then @@ -37,13 +38,17 @@ if [[ $PLATFORM == "Unix" ]]; then #brew upgrade postgresql fi elif [[ $PLATFORM == "Win32" ]]; then - wget http://minetest.kitsunemimi.pw/mingw_w64_i686_ubuntu12.04_4.9.1.7z -O mingw.7z + sudo apt-get update + sudo apt-get install p7zip-full + wget http://minetest.kitsunemimi.pw/mingw-w64-i686_7.1.1_ubuntu14.04.7z -O mingw.7z sed -e "s|%PREFIX%|i686-w64-mingw32|" \ -e "s|%ROOTPATH%|/usr/i686-w64-mingw32|" \ < util/travis/toolchain_mingw.cmake.in > util/buildbot/toolchain_mingw.cmake sudo 7z x -y -o/usr mingw.7z elif [[ $PLATFORM == "Win64" ]]; then - wget http://minetest.kitsunemimi.pw/mingw_w64_x86_64_ubuntu12.04_4.9.1.7z -O mingw.7z + sudo apt-get update + sudo apt-get install p7zip-full + wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_7.1.1_ubuntu14.04.7z -O mingw.7z sed -e "s|%PREFIX%|x86_64-w64-mingw32|" \ -e "s|%ROOTPATH%|/usr/x86_64-w64-mingw32|" \ < util/travis/toolchain_mingw.cmake.in > util/buildbot/toolchain_mingw64.cmake From 04fe8cc0a2563f9ea7566323679e7b4de177c9ec Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 21 May 2018 21:04:18 +0200 Subject: [PATCH 151/183] Update macosx build from master --- util/travis/before_install.sh | 4 +-- util/travis/common.sh | 47 ++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/util/travis/before_install.sh b/util/travis/before_install.sh index af69f78ca..b87668cce 100755 --- a/util/travis/before_install.sh +++ b/util/travis/before_install.sh @@ -33,9 +33,7 @@ if [[ $PLATFORM == "Unix" ]]; then fi else - brew update - brew install freetype gettext hiredis irrlicht jpeg leveldb libogg libvorbis luajit - #brew upgrade postgresql + install_macosx_deps fi elif [[ $PLATFORM == "Win32" ]]; then sudo apt-get update diff --git a/util/travis/common.sh b/util/travis/common.sh index 35ceec08d..949540967 100644 --- a/util/travis/common.sh +++ b/util/travis/common.sh @@ -1,9 +1,54 @@ #!/bin/bash -e +set_linux_compiler_env() { + if [[ "${COMPILER}" == "gcc-5.1" ]]; then + export CC=gcc-5.1 + export CXX=g++-5.1 + elif [[ "${COMPILER}" == "gcc-6" ]]; then + export CC=gcc-6 + export CXX=g++-6 + elif [[ "${COMPILER}" == "gcc-7" ]]; then + export CC=gcc-7 + export CXX=g++-7 + elif [[ "${COMPILER}" == "clang-3.6" ]]; then + export CC=clang-3.6 + export CXX=clang++-3.6 + elif [[ "${COMPILER}" == "clang-5.0" ]]; then + export CC=clang-5.0 + export CXX=clang++-5.0 + fi +} + +# Linux build only +install_linux_deps() { + sudo apt-get update + sudo apt-get install libirrlicht-dev cmake libbz2-dev libpng12-dev \ + libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev \ + libhiredis-dev libogg-dev libgmp-dev libvorbis-dev libopenal-dev \ + gettext libpq-dev libleveldb-dev +} + +# Mac OSX build only +install_macosx_deps() { + brew update + brew install freetype gettext hiredis irrlicht leveldb libogg libvorbis luajit + if brew ls | grep -q jpeg; then + brew upgrade jpeg + else + brew install jpeg + fi + #brew upgrade postgresql +} + # Relative to git-repository root: TRIGGER_COMPILE_PATHS="src/.*\.(c|cpp|h)|CMakeLists.txt|cmake/Modules/|util/travis/|util/buildbot/" needs_compile() { - git diff --name-only $TRAVIS_COMMIT_RANGE | egrep -q "^($TRIGGER_COMPILE_PATHS)" + RANGE="$TRAVIS_COMMIT_RANGE" + if [[ "$(git diff --name-only $RANGE -- 2>/dev/null)" == "" ]]; then + RANGE="$TRAVIS_COMMIT^...$TRAVIS_COMMIT" + echo "Fixed range: $RANGE" + fi + git diff --name-only $RANGE -- | egrep -q "^($TRIGGER_COMPILE_PATHS)" } From aac35a6e7c9c492ca1f8112a6ea55e06e36ef0e4 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 21 May 2018 23:14:54 +0200 Subject: [PATCH 152/183] Backport buildbot win32 scripts too --- util/buildbot/buildwin32.sh | 17 ++++++++++------- util/buildbot/buildwin64.sh | 17 ++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh index d807003d2..1478bc926 100755 --- a/util/buildbot/buildwin32.sh +++ b/util/buildbot/buildwin32.sh @@ -16,13 +16,13 @@ toolchain_file=$dir/toolchain_mingw.cmake irrlicht_version=1.8.4 ogg_version=1.3.2 vorbis_version=1.3.5 -curl_version=7.50.3 +curl_version=7.54.0 gettext_version=0.19.8.1 -freetype_version=2.7 -sqlite3_version=3.14.2 -luajit_version=2.1.0-beta2 -leveldb_version=1.18 -zlib_version=1.2.8 +freetype_version=2.8 +sqlite3_version=3.19.2 +luajit_version=2.1.0-beta3 +leveldb_version=1.19 +zlib_version=1.2.11 mkdir -p $packagedir mkdir -p $libdir @@ -149,6 +149,9 @@ cmake .. \ -DLEVELDB_LIBRARY=$libdir/leveldb/lib/libleveldb.dll.a \ -DLEVELDB_DLL=$libdir/leveldb/bin/libleveldb.dll -make package -j2 +make -j2 +[ "x$NO_PACKAGE" = "x" ] && make package + +exit 0 # EOF diff --git a/util/buildbot/buildwin64.sh b/util/buildbot/buildwin64.sh index 3d6965f1e..81fbf43fa 100755 --- a/util/buildbot/buildwin64.sh +++ b/util/buildbot/buildwin64.sh @@ -16,13 +16,13 @@ toolchain_file=$dir/toolchain_mingw64.cmake irrlicht_version=1.8.4 ogg_version=1.3.2 vorbis_version=1.3.5 -curl_version=7.50.3 +curl_version=7.54.0 gettext_version=0.19.8.1 -freetype_version=2.7 -sqlite3_version=3.14.2 -luajit_version=2.1.0-beta2 -leveldb_version=1.18 -zlib_version=1.2.8 +freetype_version=2.8 +sqlite3_version=3.19.2 +luajit_version=2.1.0-beta3 +leveldb_version=1.19 +zlib_version=1.2.11 mkdir -p $packagedir mkdir -p $libdir @@ -150,6 +150,9 @@ cmake .. \ -DLEVELDB_LIBRARY=$libdir/leveldb/lib/libleveldb.dll.a \ -DLEVELDB_DLL=$libdir/leveldb/bin/libleveldb.dll -make package -j2 +make -j2 +[ "x$NO_PACKAGE" = "x" ] && make package + +exit 0 # EOF From ecbb9310de9cde6bf22dacbb1e95da326daafa51 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 3 Jun 2018 17:35:20 +0200 Subject: [PATCH 153/183] Bump version to 0.4.17 --- CMakeLists.txt | 2 +- build/android/build.gradle | 2 +- doc/lua_api.txt | 2 +- doc/menu_lua_api.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a0dcb688..c64ac897b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(PROJECT_NAME_CAPITALIZED "Minetest") # Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 16) +set(VERSION_PATCH 17) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases diff --git a/build/android/build.gradle b/build/android/build.gradle index 1263b2232..f92fb3bbf 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -14,7 +14,7 @@ android { buildToolsVersion "25.0.3" defaultConfig { - versionCode 17 + versionCode 18 versionName "${System.env.VERSION_STR}.${versionCode}" minSdkVersion 9 targetSdkVersion 9 diff --git a/doc/lua_api.txt b/doc/lua_api.txt index bb6491627..728fd0a1c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Modding API Reference 0.4.16 +Minetest Lua Modding API Reference 0.4.17 ========================================= * More information at * Developer Wiki: diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index 074bc962d..eb0d2ed6c 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Mainmenu API Reference 0.4.16 +Minetest Lua Mainmenu API Reference 0.4.17 ======================================== Introduction From b5350e27ad7bc54a3a97db988119db83c67f96b3 Mon Sep 17 00:00:00 2001 From: stujones11 Date: Wed, 15 Nov 2017 19:52:19 +0000 Subject: [PATCH 154/183] Android: Update build system for ndk-r15x Add workarounds for ndk-r16. --- build/android/Makefile | 73 +++++++------------ build/android/build.gradle | 4 +- build/android/jni/Application.mk | 10 +-- build/android/jni/Deps.mk | 8 ++ build/android/jni/Irrlicht.mk | 9 +++ .../patches/irrlicht-native_activity.patch | 12 +++ 6 files changed, 63 insertions(+), 53 deletions(-) create mode 100644 build/android/jni/Deps.mk create mode 100644 build/android/jni/Irrlicht.mk create mode 100644 build/android/patches/irrlicht-native_activity.patch diff --git a/build/android/Makefile b/build/android/Makefile index d9a82da4d..df1b01ae9 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -6,7 +6,8 @@ OS := $(shell uname) # GPROF = 1 # build for build platform -APP_PLATFORM = android-9 +API = 14 +APP_PLATFORM = android-$(API) ANDR_ROOT = $(shell pwd) PROJ_ROOT = $(shell realpath $(ANDR_ROOT)/../..) @@ -30,7 +31,7 @@ TARGET_HOST = arm-linux TARGET_ABI = armeabi-v7a TARGET_LIBDIR = armeabi-v7a TARGET_TOOLCHAIN = arm-linux-androideabi- -TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3 +TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3 -D__ANDROID_API__=$(API) TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON) TARGET_ARCH = armv7 CROSS_PREFIX = arm-linux-androideabi- @@ -131,7 +132,6 @@ SQLITE3_URL = https://www.sqlite.org/2017/$(SQLITE3_FOLDER).zip ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') ANDROID_NDK = $(shell grep '^ndk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') -NDK_MODULE_PATH = $(ANDROID_NDK)/toolchains #use interim target variable to switch leveldb on or off ifeq ($(HAVE_LEVELDB),1) @@ -217,14 +217,10 @@ $(OPENAL_LIB): $(OPENAL_TIMESTAMP) if [ $$REFRESH -ne 0 ] ; then \ echo "changed timestamp for openal detected building..."; \ cd ${OPENAL_DIR}; \ + export APP_PLATFORM=${APP_PLATFORM}; \ + export TARGET_ABI=${TARGET_ABI}; \ ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ - NDK_MODULE_PATH=${NDK_MODULE_PATH} APP_ABI=${TARGET_ABI} \ - TARGET_ARCH_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \ - PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ - PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ - TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ - TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ - TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ + NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ touch ${OPENAL_TIMESTAMP}; \ touch ${OPENAL_TIMESTAMP_INT}; \ else \ @@ -248,7 +244,6 @@ ogg_download : git clone ${OGG_URL_GIT}|| exit 1; \ cd libvorbis-libogg-android ; \ patch -p1 < ${ANDR_ROOT}/patches/libvorbis-libogg-fpu.patch || exit 1; \ - sed -i 's-:-?-' jni/Application.mk; \ fi ogg : $(OGG_LIB) @@ -265,14 +260,10 @@ $(OGG_LIB): $(OGG_TIMESTAMP) if [ $$REFRESH -ne 0 ] ; then \ echo "changed timestamp for ogg detected building..."; \ cd ${OGG_DIR}; \ + export APP_PLATFORM=${APP_PLATFORM}; \ + export TARGET_ABI=${TARGET_ABI}; \ ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ - NDK_MODULE_PATH=${NDK_MODULE_PATH} \ - APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \ - PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ - PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ - TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ - TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ - TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ + NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ touch ${OGG_TIMESTAMP}; \ touch ${OGG_TIMESTAMP_INT}; \ else \ @@ -317,7 +308,7 @@ $(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) $(GMP_LIB) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=android-9 \ + --platform=${APP_PLATFORM} \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ CC=${CROSS_PREFIX}gcc ./Configure enable-gmp -DL_ENDIAN -I${GMP_DIR} -L${GMP_DIR}/usr/lib android-${TARGET_ARCH};\ @@ -368,7 +359,7 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=android-9 \ + --platform=${APP_PLATFORM} \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ @@ -420,14 +411,10 @@ $(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP) mkdir -p ${FREETYPE_DIR}; \ echo "changed timestamp for freetype detected building..."; \ cd ${FREETYPE_DIR}/Android/jni; \ + export APP_PLATFORM=${APP_PLATFORM}; \ + export TARGET_ABI=${TARGET_ABI}; \ ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ - NDK_MODULE_PATH=${NDK_MODULE_PATH} \ - APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI} \ - PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ - PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ - TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ - TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ - TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ + NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ touch ${FREETYPE_TIMESTAMP}; \ touch ${FREETYPE_TIMESTAMP_INT}; \ else \ @@ -478,9 +465,10 @@ $(ICONV_LIB) : $(ICONV_TIMESTAMP) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=android-9 \ + --platform=${APP_PLATFORM} \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ + export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ export CC=${CROSS_PREFIX}gcc; \ export CXX=${CROSS_PREFIX}g++; \ export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ @@ -513,6 +501,7 @@ irrlicht_download : patch -p1 < ${ANDR_ROOT}/patches/irrlicht-touchcount.patch || exit 1; \ patch -p1 < ${ANDR_ROOT}/patches/irrlicht-back_button.patch || exit 1; \ patch -p1 < ${ANDR_ROOT}/patches/irrlicht-texturehack.patch || exit 1; \ + patch -p1 < ${ANDR_ROOT}/patches/irrlicht-native_activity.patch || exit 1; \ fi $(IRRLICHT_TIMESTAMP) : irrlicht_download @@ -538,14 +527,10 @@ $(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB) mkdir -p ${IRRLICHT_DIR}; \ echo "changed timestamp for irrlicht detected building..."; \ cd deps/irrlicht/source/Irrlicht/Android; \ + export APP_PLATFORM=${APP_PLATFORM}; \ + export TARGET_ABI=${TARGET_ABI}; \ ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ - NDK_MODULE_PATH=${NDK_MODULE_PATH} \ - APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \ - PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ - PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ - TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ - TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ - TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ + NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Irrlicht.mk || exit 1; \ touch ${IRRLICHT_TIMESTAMP}; \ touch ${IRRLICHT_TIMESTAMP_INT}; \ else \ @@ -593,7 +578,7 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=android-9 \ + --platform=${APP_PLATFORM} \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ @@ -653,7 +638,7 @@ $(GMP_LIB): $(GMP_TIMESTAMP) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-gmp; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=android-9 \ + --platform=${APP_PLATFORM} \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ @@ -767,15 +752,11 @@ clean_assets : apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET) \ $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \ $(ANDR_ROOT)/jni/src/android_version_githash.h sqlite3_download - + @${ANDROID_NDK}/ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} \ - GPROF=${GPROF} APP_ABI=${TARGET_ABI} HAVE_LEVELDB=${HAVE_LEVELDB} \ - APP_PLATFORM=${APP_PLATFORM} \ - PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ - PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ - TARGET_LIBDIR=${TARGET_LIBDIR} \ - TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ - TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ - TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ + + @export TARGET_LIBDIR=${TARGET_LIBDIR}; \ + export HAVE_LEVELDB=${HAVE_LEVELDB}; \ + export APP_PLATFORM=${APP_PLATFORM}; \ + export TARGET_ABI=${TARGET_ABI}; \ + ${ANDROID_NDK}/ndk-build || exit 1; \ if [ ! -e ${APP_ROOT}/jniLibs ]; then \ ln -s ${ANDR_ROOT}/libs ${APP_ROOT}/jniLibs || exit 1; \ fi; \ diff --git a/build/android/build.gradle b/build/android/build.gradle index f92fb3bbf..51751732e 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -16,8 +16,8 @@ android { defaultConfig { versionCode 18 versionName "${System.env.VERSION_STR}.${versionCode}" - minSdkVersion 9 - targetSdkVersion 9 + minSdkVersion 14 + targetSdkVersion 14 applicationId "net.minetest.minetest" manifestPlaceholders = [ package: "net.minetest.minetest", project: project.name ] } diff --git a/build/android/jni/Application.mk b/build/android/jni/Application.mk index 53467059a..dfe7afb3d 100644 --- a/build/android/jni/Application.mk +++ b/build/android/jni/Application.mk @@ -1,9 +1,9 @@ -# NDK_TOOLCHAIN_VERSION := clang3.8 - -APP_PLATFORM := android-9 -APP_MODULES := minetest +APP_PLATFORM := ${APP_PLATFORM} +APP_ABI := ${TARGET_ABI} APP_STL := gnustl_static +NDK_TOOLCHAIN_VERSION := 4.9 +APP_DEPRECATED_HEADERS := true +APP_MODULES := minetest APP_CPPFLAGS += -fexceptions APP_GNUSTL_FORCE_CPP_FEATURES := rtti - diff --git a/build/android/jni/Deps.mk b/build/android/jni/Deps.mk new file mode 100644 index 000000000..36af338b2 --- /dev/null +++ b/build/android/jni/Deps.mk @@ -0,0 +1,8 @@ +APP_PLATFORM := ${APP_PLATFORM} +APP_ABI := ${TARGET_ABI} +APP_STL := gnustl_static +NDK_TOOLCHAIN_VERSION := 4.9 +APP_DEPRECATED_HEADERS := true + +APP_CLAFGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3 +APP_CPPFLAGS += -fexceptions diff --git a/build/android/jni/Irrlicht.mk b/build/android/jni/Irrlicht.mk new file mode 100644 index 000000000..a48c2902b --- /dev/null +++ b/build/android/jni/Irrlicht.mk @@ -0,0 +1,9 @@ +APP_PLATFORM := ${APP_PLATFORM} +APP_ABI := ${TARGET_ABI} +APP_STL := gnustl_static +NDK_TOOLCHAIN_VERSION := 4.9 +APP_DEPRECATED_HEADERS := true +APP_MODULES := Irrlicht + +APP_CLAFGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3 +APP_CPPFLAGS += -fexceptions diff --git a/build/android/patches/irrlicht-native_activity.patch b/build/android/patches/irrlicht-native_activity.patch new file mode 100644 index 000000000..5e9699e4f --- /dev/null +++ b/build/android/patches/irrlicht-native_activity.patch @@ -0,0 +1,12 @@ +--- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2017-11-15 18:19:58.467279274 +0000 ++++ irrlicht/source/Irrlicht/CEGLManager.cpp 2017-11-15 18:19:54.175279087 +0000 +@@ -8,6 +8,9 @@ + + #include "irrString.h" + #include "os.h" ++#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_) ++#include ++#endif + + namespace irr + { From 0ef9c53a8c20872ce69f1c539167497a04efe529 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 10 Jun 2018 16:59:34 +0200 Subject: [PATCH 155/183] Fix many Android build issues It remains one issue with MT itself and rtti --- build/android/Makefile | 8 +++++--- build/android/jni/Application.mk | 5 +---- build/android/jni/Deps.mk | 5 ++--- build/android/jni/Irrlicht.mk | 3 +-- build/android/patches/irrlicht-native_activity.patch | 11 ++++++----- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/build/android/Makefile b/build/android/Makefile index df1b01ae9..5fe0321b7 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -83,7 +83,7 @@ OGG_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so VORBIS_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so OGG_TIMESTAMP = $(OGG_DIR)timestamp OGG_TIMESTAMP_INT = $(ANDR_ROOT)/deps/ogg_timestamp -OGG_URL_GIT = https://github.com/vincentjames501/libvorbis-libogg-android +OGG_URL_GIT = https://gitlab.com/minetest/libvorbis-libogg-android IRRLICHT_REVISION = 5145 IRRLICHT_DIR = $(ANDR_ROOT)/deps/irrlicht/ @@ -262,8 +262,10 @@ $(OGG_LIB): $(OGG_TIMESTAMP) cd ${OGG_DIR}; \ export APP_PLATFORM=${APP_PLATFORM}; \ export TARGET_ABI=${TARGET_ABI}; \ - ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ - NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ + ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ + --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ + --platform=${APP_PLATFORM} \ + --install-dir=$${TOOLCHAIN}; \ touch ${OGG_TIMESTAMP}; \ touch ${OGG_TIMESTAMP_INT}; \ else \ diff --git a/build/android/jni/Application.mk b/build/android/jni/Application.mk index dfe7afb3d..0d5e5100e 100644 --- a/build/android/jni/Application.mk +++ b/build/android/jni/Application.mk @@ -1,9 +1,6 @@ APP_PLATFORM := ${APP_PLATFORM} APP_ABI := ${TARGET_ABI} -APP_STL := gnustl_static -NDK_TOOLCHAIN_VERSION := 4.9 -APP_DEPRECATED_HEADERS := true +APP_STL := c++_static APP_MODULES := minetest APP_CPPFLAGS += -fexceptions -APP_GNUSTL_FORCE_CPP_FEATURES := rtti diff --git a/build/android/jni/Deps.mk b/build/android/jni/Deps.mk index 36af338b2..6a58c5428 100644 --- a/build/android/jni/Deps.mk +++ b/build/android/jni/Deps.mk @@ -1,8 +1,7 @@ APP_PLATFORM := ${APP_PLATFORM} APP_ABI := ${TARGET_ABI} -APP_STL := gnustl_static -NDK_TOOLCHAIN_VERSION := 4.9 +APP_STL := c++_static APP_DEPRECATED_HEADERS := true -APP_CLAFGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3 +APP_CFLAGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3 APP_CPPFLAGS += -fexceptions diff --git a/build/android/jni/Irrlicht.mk b/build/android/jni/Irrlicht.mk index a48c2902b..0bc56a2ab 100644 --- a/build/android/jni/Irrlicht.mk +++ b/build/android/jni/Irrlicht.mk @@ -1,7 +1,6 @@ APP_PLATFORM := ${APP_PLATFORM} APP_ABI := ${TARGET_ABI} -APP_STL := gnustl_static -NDK_TOOLCHAIN_VERSION := 4.9 +APP_STL := c++_static APP_DEPRECATED_HEADERS := true APP_MODULES := Irrlicht diff --git a/build/android/patches/irrlicht-native_activity.patch b/build/android/patches/irrlicht-native_activity.patch index 5e9699e4f..83c837886 100644 --- a/build/android/patches/irrlicht-native_activity.patch +++ b/build/android/patches/irrlicht-native_activity.patch @@ -1,12 +1,13 @@ ---- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2017-11-15 18:19:58.467279274 +0000 -+++ irrlicht/source/Irrlicht/CEGLManager.cpp 2017-11-15 18:19:54.175279087 +0000 -@@ -8,6 +8,9 @@ - +--- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2018-06-10 16:58:11.357709173 +0200 ++++ irrlicht/source/Irrlicht/CEGLManager.cpp 2018-06-10 16:58:25.100709843 +0200 +@@ -9,6 +9,10 @@ #include "irrString.h" #include "os.h" + +#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_) +#include +#endif - ++ namespace irr { + namespace video From 9bbe99b4f8697ebf10956cd7a412c78ae28b157d Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 10 Jun 2018 17:07:32 +0200 Subject: [PATCH 156/183] Android: fix RTTI issue --- build/android/jni/Application.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/android/jni/Application.mk b/build/android/jni/Application.mk index 0d5e5100e..f6e2f6b22 100644 --- a/build/android/jni/Application.mk +++ b/build/android/jni/Application.mk @@ -3,4 +3,4 @@ APP_ABI := ${TARGET_ABI} APP_STL := c++_static APP_MODULES := minetest -APP_CPPFLAGS += -fexceptions +APP_CPPFLAGS += -fexceptions -frtti From fb4bfc60deeab6c54e9c51f7f7a0ccf595e42b77 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 10 Jun 2018 17:11:57 +0200 Subject: [PATCH 157/183] Android: fix another build typo --- build/android/jni/Irrlicht.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/android/jni/Irrlicht.mk b/build/android/jni/Irrlicht.mk index 0bc56a2ab..b6a48acb8 100644 --- a/build/android/jni/Irrlicht.mk +++ b/build/android/jni/Irrlicht.mk @@ -4,5 +4,5 @@ APP_STL := c++_static APP_DEPRECATED_HEADERS := true APP_MODULES := Irrlicht -APP_CLAFGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3 +APP_CFLAGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3 APP_CPPFLAGS += -fexceptions From 119aa5c91977ce5381e399e21eabf390e5cb5f5e Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 2 Jul 2017 22:26:25 +0200 Subject: [PATCH 158/183] Fix crash due to missing pointer validation Based on commit 014a1a0 --- src/game.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/game.cpp b/src/game.cpp index facc68aea..5ad93b95a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -170,7 +170,8 @@ struct LocalFormspecHandler : public TextDest } // Don't disable this part when modding is disabled, it's used in builtin - m_client->getScript()->on_formspec_input(m_formname, fields); + if (m_client && m_client->getScript()) + m_client->getScript()->on_formspec_input(m_formname, fields); } Client *m_client; From 2515207606683d60a6657c7ea18189381dae157c Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 14 May 2018 21:03:48 +0200 Subject: [PATCH 159/183] Fix crash in log_deprecated when triggered from no function Based on commit a1598e1b --- src/script/common/c_internal.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index 3fa044172..3e28d9cf6 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -181,7 +181,9 @@ void log_deprecated(lua_State *L, const std::string &message) warningstream << message; if (L) { // L can be NULL if we get called from scripting_game.cpp lua_Debug ar; - FATAL_ERROR_IF(!lua_getstack(L, 2, &ar), "lua_getstack() failed"); + + if (!lua_getstack(L, 2, &ar)) + FATAL_ERROR_IF(!lua_getstack(L, 1, &ar), "lua_getstack() failed"); FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed"); warningstream << " (at " << ar.short_src << ":" << ar.currentline << ")"; } From 24a2fd4dc4918038fb587cfebe00f02fef03a347 Mon Sep 17 00:00:00 2001 From: number Zero Date: Sat, 9 Jun 2018 17:31:35 +0300 Subject: [PATCH 160/183] Fix narrow/utf8 difference in incoming/outcoming messages --- src/script/lua_api/l_server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index a0e475dec..bb1d75221 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -74,7 +74,7 @@ int ModApiServer::l_chat_send_all(lua_State *L) // Get server from registry Server *server = getServer(L); // Send - server->notifyPlayers(narrow_to_wide(text)); + server->notifyPlayers(utf8_to_wide(text)); return 0; } @@ -88,7 +88,7 @@ int ModApiServer::l_chat_send_player(lua_State *L) // Get server from registry Server *server = getServer(L); // Send - server->notifyPlayer(name, narrow_to_wide(text)); + server->notifyPlayer(name, utf8_to_wide(text)); return 0; } From 6dc7177a5de51f1329c1be04e7f07be64d5cc76c Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Fri, 8 Jun 2018 22:06:08 +0200 Subject: [PATCH 161/183] Bump version to 0.4.17.1 --- CMakeLists.txt | 3 ++- src/cmake_config.h.in | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c64ac897b..95f1f28d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,12 +14,13 @@ set(PROJECT_NAME_CAPITALIZED "Minetest") set(VERSION_MAJOR 0) set(VERSION_MINOR 4) set(VERSION_PATCH 17) +set(VERSION_TWEAK 1) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases set(DEVELOPMENT_BUILD FALSE) -set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") +set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK}") if(VERSION_EXTRA) set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA}) elseif(DEVELOPMENT_BUILD) diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index 4b731020a..9a57d3de8 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -8,6 +8,7 @@ #define VERSION_MAJOR @VERSION_MAJOR@ #define VERSION_MINOR @VERSION_MINOR@ #define VERSION_PATCH @VERSION_PATCH@ +#define VERSION_TWEAK @VERSION_TWEAK@ #define VERSION_EXTRA "@VERSION_EXTRA@" #define VERSION_STRING "@VERSION_STRING@" #define PRODUCT_VERSION_STRING "@VERSION_MAJOR@.@VERSION_MINOR@" From f5c9c760e3e817676b41cf7907ebad84efdff560 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 10 Jun 2018 17:46:47 +0200 Subject: [PATCH 162/183] Android: use c++_shared library instead of c++_static MT doesn't launch without that --- build/android/jni/Application.mk | 2 +- build/android/jni/Deps.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/android/jni/Application.mk b/build/android/jni/Application.mk index f6e2f6b22..d3d8b352d 100644 --- a/build/android/jni/Application.mk +++ b/build/android/jni/Application.mk @@ -1,6 +1,6 @@ APP_PLATFORM := ${APP_PLATFORM} APP_ABI := ${TARGET_ABI} -APP_STL := c++_static +APP_STL := c++_shared APP_MODULES := minetest APP_CPPFLAGS += -fexceptions -frtti diff --git a/build/android/jni/Deps.mk b/build/android/jni/Deps.mk index 6a58c5428..a4b6dde12 100644 --- a/build/android/jni/Deps.mk +++ b/build/android/jni/Deps.mk @@ -1,6 +1,6 @@ APP_PLATFORM := ${APP_PLATFORM} APP_ABI := ${TARGET_ABI} -APP_STL := c++_static +APP_STL := c++_shared APP_DEPRECATED_HEADERS := true APP_CFLAGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3 From 9dfe9be1f390dd91baacfbb2641cd7b516b33749 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 10 Jun 2018 18:28:41 +0200 Subject: [PATCH 163/183] Bump android version for next release --- build/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/android/build.gradle b/build/android/build.gradle index 51751732e..d978fa848 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -14,7 +14,7 @@ android { buildToolsVersion "25.0.3" defaultConfig { - versionCode 18 + versionCode 19 versionName "${System.env.VERSION_STR}.${versionCode}" minSdkVersion 14 targetSdkVersion 14 From 289c8dfde5fe19eabde93a5dc245a945fe885d13 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 11 Jun 2018 19:13:56 +0200 Subject: [PATCH 164/183] Android: fix clean_assets target --- build/android/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/android/Makefile b/build/android/Makefile index 5fe0321b7..af7981636 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -749,7 +749,7 @@ assets : $(ASSETS_TIMESTAMP) fi clean_assets : - @$(RM) -r assets + @$(RM) -r ${APP_ROOT}/assets apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET) \ $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \ From 4a48cd57e8f95f33dabd6804f7f28944ea2ff36a Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 11 Jun 2018 19:14:10 +0200 Subject: [PATCH 165/183] Fix android tools version used to build MT --- build/android/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/android/build.gradle b/build/android/build.gradle index d978fa848..e2939a42d 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -3,15 +3,15 @@ buildscript { mavenCentral() } dependencies { - classpath "com.android.tools.build:gradle:1.5.0" + classpath "com.android.tools.build:gradle:3.1.3" } } apply plugin: "com.android.application" android { - compileSdkVersion 25 - buildToolsVersion "25.0.3" + compileSdkVersion 26 + buildToolsVersion "26.0.3" defaultConfig { versionCode 19 From ea86ecf015cd82d2daa4c0f4a82096402290a5a6 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 11 Jun 2018 20:37:17 +0200 Subject: [PATCH 166/183] Android: gradle 3.1.3 is not available on mavenCentral Also update wrapper --- build/android/build.gradle | 2 +- build/android/gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/android/build.gradle b/build/android/build.gradle index e2939a42d..c67160446 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath "com.android.tools.build:gradle:3.1.3" + classpath "com.android.tools.build:gradle:2.3.0" } } diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties index 980438b75..ca812af6c 100644 --- a/build/android/gradle/wrapper/gradle-wrapper.properties +++ b/build/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip From e66d5e500c187eaf06297348c498f3ba8d7ef738 Mon Sep 17 00:00:00 2001 From: red-001 Date: Wed, 27 Jun 2018 16:06:37 +0100 Subject: [PATCH 167/183] Fix small memory leaks in client. (#7492) --- src/client.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index abc84b7cf..1cbb6953d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -230,6 +230,8 @@ Client::~Client() m_shutdown = true; m_con.Disconnect(); + deleteAuthData(); + m_mesh_update_thread.stop(); m_mesh_update_thread.wait(); while (!m_mesh_update_thread.m_queue_out.empty()) { @@ -257,6 +259,7 @@ Client::~Client() } delete m_minimap; + delete m_media_downloader; } void Client::connect(Address address, bool is_local_server) From 85ce23b165839041a238af69ef8ea9a3345505be Mon Sep 17 00:00:00 2001 From: red-001 Date: Tue, 26 Jun 2018 09:02:26 +0100 Subject: [PATCH 168/183] Fix buffer overrun in SRP (#7484) The old code got a pointer to the array instead of the first element, this resulted in a buffer overflow when the function was used more than once. --- src/util/srp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/srp.cpp b/src/util/srp.cpp index f27f4f3f9..af68d6f54 100644 --- a/src/util/srp.cpp +++ b/src/util/srp.cpp @@ -612,7 +612,7 @@ SRP_Result srp_create_salted_verification_key( SRP_HashAlgorithm alg, if (fill_buff() != SRP_OK) goto error_and_exit; *bytes_s = (unsigned char *)srp_alloc(size_to_fill); if (!*bytes_s) goto error_and_exit; - memcpy(*bytes_s, &g_rand_buff + g_rand_idx, size_to_fill); + memcpy(*bytes_s, &g_rand_buff[g_rand_idx], size_to_fill); g_rand_idx += size_to_fill; } From 971dea7efd3b9328f0a6f1acbfc2f81d47be7417 Mon Sep 17 00:00:00 2001 From: red-001 Date: Fri, 22 Jun 2018 20:04:41 +0100 Subject: [PATCH 169/183] Fix crash caused by Lua error during startup (#7473) --- src/emerge.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/emerge.cpp b/src/emerge.cpp index f7f6ff603..2f0dc8750 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -146,7 +146,10 @@ EmergeManager::~EmergeManager() } delete thread; - delete m_mapgens[i]; + + // Mapgen init might not be finished if there is an error during startup. + if (m_mapgens.size() > i) + delete m_mapgens[i]; } delete biomemgr; From 2e85254e91d623c68949205f681c11c88228824b Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 26 Jun 2018 01:12:09 +0200 Subject: [PATCH 170/183] Fix MurmurHash implementation to really be unaligned (#7482) --- src/util/numeric.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index e6a9cb75e..9127527de 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -60,13 +60,13 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed) const int r = 47; u64 h = seed ^ (len * m); - const u64 *data = (const u64 *)key; - const u64 *end = data + (len / 8); + const u8 *data = (const u8 *)key; + const u8 *end = data + (len / 8) * 8; while (data != end) { u64 k; memcpy(&k, data, sizeof(u64)); - data++; + data += sizeof(u64); k *= m; k ^= k >> r; From 8427ae529a68b4ed6a009beace17e6cdf9b47aea Mon Sep 17 00:00:00 2001 From: stujones11 Date: Sat, 23 Jun 2018 21:38:19 +0100 Subject: [PATCH 171/183] Android: Use correct temporary path (#7463) --- src/filesys.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/filesys.cpp b/src/filesys.cpp index bd8b94aff..f60e9218b 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -27,6 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "config.h" #include "porting.h" +#ifdef __ANDROID__ +#include "settings.h" // For g_settings +#endif namespace fs { @@ -374,7 +377,7 @@ std::string TempPath() configuration hardcodes mkstemp("/tmp/lua_XXXXXX"). */ #ifdef __ANDROID__ - return DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM "tmp"; + return g_settings->get("TMPFolder"); #else return DIR_DELIM "tmp"; #endif From 5e7faf3f50c9ebb2771f9a84c3e0d755da261d9e Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Thu, 28 Jun 2018 17:46:01 +0200 Subject: [PATCH 172/183] Update cURL (7.60.0) & SQLite3 (3.24.0) --- build/android/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/android/Makefile b/build/android/Makefile index af7981636..1626c0663 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -100,7 +100,7 @@ OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp OPENSSL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/openssl_timestamp OPENSSL_URL = https://www.openssl.org/source/openssl-$(OPENSSL_VERSION).tar.gz -CURL_VERSION = 7.54.0 +CURL_VERSION = 7.60.0 CURL_DIR = $(ANDR_ROOT)/deps/curl-$(CURL_VERSION) CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a CURL_TIMESTAMP = $(CURL_DIR)/timestamp @@ -127,8 +127,8 @@ ICONV_TIMESTAMP = $(ICONV_DIR)timestamp ICONV_TIMESTAMP_INT = $(ANDR_ROOT)/deps/iconv_timestamp ICONV_URL_HTTP = https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$(ICONV_VERSION).tar.gz -SQLITE3_FOLDER = sqlite-amalgamation-3180000 -SQLITE3_URL = https://www.sqlite.org/2017/$(SQLITE3_FOLDER).zip +SQLITE3_FOLDER = sqlite-amalgamation-3240000 +SQLITE3_URL = https://www.sqlite.org/2018/$(SQLITE3_FOLDER).zip ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') ANDROID_NDK = $(shell grep '^ndk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') From dd5f03731ad545f52da24825a2ee870f899f6adc Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Thu, 28 Jun 2018 18:11:21 +0200 Subject: [PATCH 173/183] Bump android version to 20 --- build/android/build.gradle | 2 +- build/android/settings.gradle | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build/android/build.gradle b/build/android/build.gradle index c67160446..823175894 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -14,7 +14,7 @@ android { buildToolsVersion "26.0.3" defaultConfig { - versionCode 19 + versionCode 20 versionName "${System.env.VERSION_STR}.${versionCode}" minSdkVersion 14 targetSdkVersion 14 diff --git a/build/android/settings.gradle b/build/android/settings.gradle index a6e60c439..b0cee5dad 100644 --- a/build/android/settings.gradle +++ b/build/android/settings.gradle @@ -1,2 +1 @@ rootProject.name = "Minetest" - From 86e29ae5860f197b87d85e755475a445cb65b04c Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Tue, 4 Dec 2018 19:11:03 +0100 Subject: [PATCH 174/183] Update android version code (rebuild) --- build/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/android/build.gradle b/build/android/build.gradle index 823175894..8d1899307 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -14,7 +14,7 @@ android { buildToolsVersion "26.0.3" defaultConfig { - versionCode 20 + versionCode 21 versionName "${System.env.VERSION_STR}.${versionCode}" minSdkVersion 14 targetSdkVersion 14 From a873a3f4f8e4fbaf0c2c55292f790c746000556f Mon Sep 17 00:00:00 2001 From: stujones11 Date: Fri, 26 Jan 2018 19:08:54 +0000 Subject: [PATCH 175/183] Include alpha channel reference in MaterialTypeParam --- src/client/tile.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/tile.h b/src/client/tile.h index 66ca8be1d..46a5b4791 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -251,6 +251,7 @@ struct TileLayer case TILE_MATERIAL_BASIC: case TILE_MATERIAL_WAVING_LEAVES: case TILE_MATERIAL_WAVING_PLANTS: + material.MaterialTypeParam = 0.5; material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; break; case TILE_MATERIAL_ALPHA: From ce4497224f6239a8e6c4aadfa7f593520602fa03 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 22 Dec 2018 07:46:41 +0000 Subject: [PATCH 176/183] Android: Fix memory leak when displaying images in the mainmenu (#8011) --- src/guiEngine.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index 2d1bd6d44..8661b1ca2 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -87,22 +87,30 @@ MenuTextureSource::~MenuTextureSource() /******************************************************************************/ video::ITexture* MenuTextureSource::getTexture(const std::string &name, u32 *id) { - if(id) + if (id) *id = 0; - if(name.empty()) + + if (name.empty()) return NULL; + m_to_delete.insert(name); #ifdef __ANDROID__ - video::IImage *image = m_driver->createImageFromFile(name.c_str()); - if (image) { - image = Align2Npot2(image, m_driver); - video::ITexture* retval = m_driver->addTexture(name.c_str(), image); - image->drop(); + video::ITexture *retval = m_driver->findTexture(name.c_str()); + if (retval) return retval; - } -#endif + + video::IImage *image = m_driver->createImageFromFile(name.c_str()); + if (!image) + return NULL; + + image = Align2Npot2(image, m_driver); + retval = m_driver->addTexture(name.c_str(), image); + image->drop(); + return retval; +#else return m_driver->getTexture(name.c_str()); +#endif } /******************************************************************************/ From 9d64805ec14613c34abce32a855d3c07c5b966ec Mon Sep 17 00:00:00 2001 From: Maksim Date: Mon, 3 Dec 2018 00:39:35 +0100 Subject: [PATCH 177/183] Update Android java code (#7820) Targets SDK 26 as required by the playstore. Fixes screen auto-rotation closing game. Hides on-screen navigation bar if present. Update gradlew. Fix display aspect on 18+/:9 displays (like a Samsung Galaxy S9). Remove small app icons, not required. Fix xml in unpacking activity. Support Android permission: On Android 6.0+ you need to manually give write permission (as required by google). Background during unpacking (just a demo for now). Material Design: no more Android 2 interface. Immersive mode (Android 4.4+ - hide NavBar for fullscreen mode). --- .gitignore | 3 + build/android/build.gradle | 91 ++- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 49896 -> 54731 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- build/android/gradlew | 110 +-- build/android/gradlew.bat | 14 +- build/android/src/debug/AndroidManifest.xml | 3 +- build/android/src/main/AndroidManifest.xml | 67 +- .../net.minetest.minetest/MainActivity.java | 79 ++ .../MinetestAssetCopy.java | 711 ++++++++---------- .../MinetestTextEntry.java | 50 +- .../MtNativeActivity.java | 69 +- .../src/main/res/drawable-hdpi/irr_icon.png | Bin 5490 -> 0 bytes .../src/main/res/drawable-ldpi/irr_icon.png | Bin 2262 -> 0 bytes .../src/main/res/drawable-mdpi/irr_icon.png | Bin 3110 -> 0 bytes .../src/main/res/drawable-xhdpi/irr_icon.png | Bin 7610 -> 0 bytes .../src/main/res/drawable/background.png | Bin 0 -> 83 bytes build/android/src/main/res/drawable/bg.xml | 4 + .../android/src/main/res/layout/assetcopy.xml | 40 +- .../src/main/res/mipmap/ic_launcher.png | Bin 0 -> 5780 bytes .../src/main/res/values-v21/styles.xml | 12 + build/android/src/main/res/values/strings.xml | 6 +- build/android/src/main/res/values/styles.xml | 13 +- 23 files changed, 723 insertions(+), 553 deletions(-) create mode 100644 build/android/src/main/java/net.minetest.minetest/MainActivity.java delete mode 100644 build/android/src/main/res/drawable-hdpi/irr_icon.png delete mode 100644 build/android/src/main/res/drawable-ldpi/irr_icon.png delete mode 100644 build/android/src/main/res/drawable-mdpi/irr_icon.png delete mode 100644 build/android/src/main/res/drawable-xhdpi/irr_icon.png create mode 100644 build/android/src/main/res/drawable/background.png create mode 100644 build/android/src/main/res/drawable/bg.xml create mode 100644 build/android/src/main/res/mipmap/ic_launcher.png create mode 100644 build/android/src/main/res/values-v21/styles.xml diff --git a/.gitignore b/.gitignore index 7b5ecab67..9489d6f3f 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ src/cmake_config_githash.h src/lua/build/ locale/ .directory +.gradle/ *.cbp *.layout *.o @@ -84,6 +85,8 @@ locale/ *.ninja .ninja* *.gch +*.iml +test_config.h cmake-build-debug/ cmake-build-release/ diff --git a/build/android/build.gradle b/build/android/build.gradle index 8d1899307..06c79a08f 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -1,25 +1,39 @@ buildscript { repositories { - mavenCentral() + maven { url 'https://maven.google.com' } + jcenter() } dependencies { - classpath "com.android.tools.build:gradle:2.3.0" + classpath 'com.android.tools.build:gradle:3.1.3' + } +} + +allprojects { + repositories { + maven { url 'https://maven.google.com' } + jcenter() } } apply plugin: "com.android.application" android { - compileSdkVersion 26 - buildToolsVersion "26.0.3" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { versionCode 21 versionName "${System.env.VERSION_STR}.${versionCode}" minSdkVersion 14 - targetSdkVersion 14 + targetSdkVersion 28 applicationId "net.minetest.minetest" - manifestPlaceholders = [ package: "net.minetest.minetest", project: project.name ] + manifestPlaceholders = [package: "net.minetest.minetest", project: project.name] + ndk { + // Specifies the ABI configurations of your native + // libraries Gradle should build and package with your APK. + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', + 'arm64-v8a' + } } lintOptions { @@ -46,3 +60,68 @@ android { } } } + +task cleanAssets(type: Delete) { + delete 'src/main/assets' +} + +task cleanIconv(type: Delete) { + delete 'deps/libiconv' +} + +task cleanIrrlicht(type: Delete) { + delete 'deps/irrlicht' +} + +task cleanLevelDB(type: Delete) { + delete 'deps/leveldb' +} + +task cleanCURL(type: Delete) { + delete 'deps/curl' + delete 'deps/curl-' + curl_version +} + +task cleanOpenSSL(type: Delete) { + delete 'deps/openssl' + delete 'deps/openssl-' + openssl_version + delete 'deps/openssl-' + openssl_version + '.tar.gz' +} + +task cleanOpenAL(type: Delete) { + delete 'deps/openal-soft' +} + +task cleanFreetype(type: Delete) { + delete 'deps/freetype2-android' +} + +task cleanOgg(type: Delete) { + delete 'deps/libvorbis-libogg-android' +} + +task cleanSQLite3(type: Delete) { + delete 'deps/sqlite-amalgamation-' + sqlite3_version + delete 'deps/sqlite-amalgamation-' + sqlite3_version + '.zip' +} + +task cleanGMP(type: Delete) { + delete 'deps/gmp' + delete 'deps/gmp-' + gmp_version +} + +task cleanAll(type: Delete, dependsOn: [clean, cleanAssets, cleanIconv, + cleanFreetype, cleanIrrlicht, cleanLevelDB, cleanSQLite3, cleanCURL, + cleanOpenSSL, cleanOpenAL, cleanOgg, cleanGMP]) { + delete 'deps' + delete 'gen' + delete 'libs' + delete 'obj' + delete 'bin' + delete 'Debug' + delete 'and_env' +} + +dependencies { + implementation 'com.android.support:support-v4:28.0.0' +} diff --git a/build/android/gradle/wrapper/gradle-wrapper.jar b/build/android/gradle/wrapper/gradle-wrapper.jar index 8c0fb64a8698b08ecc4158d828ca593c4928e9dd..6b6ea3ab4ff4f69d55c5fd9c0a6ac70f47d41008 100644 GIT binary patch delta 48672 zcmZ6y18`+gw=JBGZQHhO+qP}%bkfO*la6iMCr&!t`;)vy2ezW3jI->zD_YVBHc zRqd)-W6e3nSSR5S6V(vNstOR0m|$SAuwYQpVXSH*q?jQEt}Kd(&{T(=`0w)&O%N;sQEN=q|2VIUeb&eVGCje?LE-ZNQBC zRoKOS4Mxl+6?A{Pij9ymkLwOm;iV_;O(Y?bq*)pEYxQ;mFoxjEOMbLd?{Kq;>7`_}9EC2Qsi$S4#T2*gAUU@+D-Ui?w?Iz`pO&?tw--|C z?99&Kw#9h~NK#6t(&Cs0*{@D!VePQo7Jo0EYh5&Y?9H0hD*h?Eo@R|{Z4_-yjm?=m z#eZ9&9U}P9iiS=593+{g&C_s6Yb)TNmV|E0I_y)fqd4g{ zac`gpvU4a?ODa&KyUR*csI;Q!>Su(RbZwzinfg84eWWSVT2pLkS{q)^@dJ0D=sR*8 zviln2zo;GC@O3QaX(n3UP&FtHQjB-wpZwqCg=G4THj)b%Xa|0p(yt7`z%%v|e*4yf zmEvXs=!bAbWA&GIYqNLj0rCd-ciih@I_^tE=auJ~a#^YL%e?4{&M?1@zZq49g{fc$ zM$c5l8{;Q+>X~=L!V8a9;J7o1EW&kyrU&1}`_o_K`y~REzHhdd^|Y7mW3uc42M&1cn!4k(=3IOx7=vfcPvMo`%lI50GC_xUr}Tr}j2XOmiHSVU1jwKBWdF z->fF&HQEDCM(!5#YR$@uTVA*|Qn^uPkXCDk*dW;v63om3Ld#271;a}#QcUGyLO?UX zDu&?h>J!P?h#sYP=)y9)gB3FY;^%OPH(jrs=zSSLN?b{F{h%3XmT1N)u72L;l{F~;H6H~$->-$Z z)F9M5#QU!#)_?`Sr)liccZrPzix&Diuo1ME!;taZhu|_t~&jSz&^^mgc0Rt?(JhCjgi>3A}JvNH4=UN zh5B^Mhl24d{7c60ngl$qf#SZ%a^C-8~L!Q#FX zY2BE-d}_8s(s2uXH*xe|MDh|jjtS&Z`ws@vlnyi}f3?J?obrYoD7~uE5YnIdZ?{OJ zWm6U`!+S|vQ?$n{zCTT zC?E&EOh}>!SpI(9V@9wSnz5j$u&PTP%;XLrCK=>?hy9jCEjxM|7)|O{;c>tnN}NN+ z8tjSotW@Mxkj}Y1pa;I9k}4uYlNiyKoHU_K{vyD8{UY}T^B-h|`41o`^L<;x`7fXx zcj5Oy`3JS4|G<(nxgP^NSri-&pz7}8>nG;rX=~-|X=h>P>Eh08;b`XJk*ZB*&SF?Kc)9mM6jAGzC&bsX$%8d-( zHJFw-+@GL~fa5%mYat)&r{j7T5wMio6=`%n0%UjWCG>VX3nBtIN9qPgz=i=g$aP&B z(+^=C`-*ia|Fy6_pF{FKTnMPE#0@p5zKJq?zO@^o>hdBciq^(VAh@zW&hzfZTDBgX z-atf^0R1P=IY#vzsjk%A)i}IRwe`DCS1503rzTTmpYp$}~ji;4o+@*L*bg1H_OL9)KKub3*tRyu1UNPLEcgJpra{ zPy!(VH~Ozn24bd2RoVAH9T*%X5mDbz47RQcs)xxW<~L%Jh87`!fZD*I(4tfl5Pf(v z8IXn}@^3~~UVI`flY>)TmAo4|+62y%1L7(k)04xIzs^x?6xW*rVFpWdWQUYLr$Wp9 z?|FoQfhRU{liyLKN)CazACWOF{1o7ZhGYa1(5r6lsYTEe!7>lI1Ook`1U|JGl)ZZc zY!@>chr&uJ6Y=9{(isD-ZXM;86c$X5ptQ+I)+O_9EJKpATI5$A*UTCPV{|CP(70;L zMCA|Y|9No#yhWQvK?Vb(CIkZ``%gN-0{&0(XXI zV{%?t`s8s+4xZ=!?6iAR60w`V-wJNgcfu?OJst&J1wHo$%=BFMJ|6eIo&sjTSLm2= zWp_G5%(^uPw`?r$^;WwXvvm&6SWaGYLQIBO>|U%Opq+7jvHHw+bZ-4tKe9?*y;eVf zA?vqU!0p8w1L&-qb?AW?B8a}2?c|9*mTZyPTPO%Xq)5^f$;43%bZ?jl+fDfk$Lfb4 zl2nl1s0r?i!bd8w{^Z300$^~Ki}Ww!FV?dPP|cG~)hChxNun^w((z@=Iov$Md5e=z_lzk?t87W75A* z9rm+_eoa=BfR`M+6BeJGlGErUt0GYY$ZYRrX78I$eTJyUg6}!^8(=hyuN1~LR}$0O zX}csD!@Puti*xRUHa862aV>36?mRzCZREH#Jk=R_W*)=rJU6V}(a){qxI9#w)vg2v zepIl(Vhk9UFTZRN^ve5`H^jLzb7O>*u*ks#Q;O8*pZWlBQI4wDc<(v+xYp1CXJf_+Tt{o6(-GP-J$< ztqa&Gl`ZOR&@phC`N{E3t3ZFOQY!V>aivxkbyZn!Pq}TMusLVB=bj!Yjxj?Ls`RIJO&sl=`4si-0-o!>*~ss{FD=2hS7O%2ae(qI?to5!rP4$0 zlv%$g&*=G14~G&SCn>{%KkgDPe$2eWe({R)qwZIA`&-`+OOukSI>M@8v%z=s&?(kfu=GEPp56V2?nn#qU_(Cil%Si07#TS1n!pho1MRS~hF0XMCn60D`1k%R|T-ExUXnbz1B*pwu>)!$@x> z&x|4sPrIUZ%(j@^vUZ6LyFIw>;USpY7CP^!u8d%KwYhTfCFPV6kNQ?~08k8#d7pMLHNkWhXc=Tx_P%5_=7F zfOy&Ayd)e$C-tLmEF9PCa7u(+!)$DnvzmyT`%)EflJMA(#x3)6=p)RPII9P%5lU?2 z@ebyTm^4Ef)aGTpbR$Y^WI#nXdHhYr@Jza`FdQYc^eMYJhiHx_pFYRwrrlnYj2FC* z8d23ZdZ%cZVF~bbmB;uv#CXE^_;R>vz)4~zHEmYyA%ooKn9~*t^2@Idfrn?MuU6dhou4yp5UCKnrTf;M)IB^%d4y~NiBm+ zpb2%RdOz-XB~7h_V=&@Orn!2ayF8_$S&ZEg#@M=4V`ZIiY9c3_MYLYOq8(!?0H6A{ zse*f$&rLWXNBzl3D*nx|itqHub3BG#i8o6&(_Hxmvi~+Yf*+1mXOg9up@CcLw>Y>O zUqI#gykV6C`zd87vVQL&%jCLZzvuT(yQN7F2$6Dql6py(kXLuRW;SElF}^O#nP#YV zxgR<=O8ow8r!#eX@0R>$?ZKS6fXsZKN(u7u4k+r_les7}sMUzfPIs}bR1|kS>2BvV zmG~o!+=pD7=oUoY)Q(Q(gZqq3lEZrq31{9m&f<>KI>R;26$5UkN=1-cKxaSz|5~R| zkIrXXaft)GiKT3lyt27*WdnbEOSfHX7$`YA9oovV|F%|vs*ZNJAN-sNKwYITo)q=- z{RQFdPFL%D=XZl>_HlU^3a59NOFM?-y`IL#17dyY@+Ve>dy0J(do#b1u{Z*R^l9}t ztSo_iS${jN)m-Y{p!8BPxI=Zl)-o?ECmg$4!vn_Cq+^?uIb3CcTwG(J;!qQMRp}i( znw&-KDvFIRFZ1d%2wqAKaCisG!=|Ha&q$J2Z0bf&hEn`{2cF1{gblmJRlO9R?q z=0UCqT`?!;@$S%iB)OM1Ha#7zLvOrRp$sOSIbmMjO|vYrC|C)rjLzf`&0VW%p=7l8 zG9Xm`r5 zTw>z&q-^P!>0z})d{>jnr&T)DM`l`^gZJ^baf~C()}A4i`QEhmCY5aM6-a;cF-0Gv z-sjsm=24}frtdEU@cZoI!6BwD?!wW! zsj%^J_-ikmQeo&H6IN#AGJQ7@uiCGU75}q*0GpB(@f!gd!0{Jfj*SRZ0lWvH0F?!f z*{h_f2%`zy+4*r_Aj6Hr&nI%`Mxz~DLNUmfS~~|EqTCo%mI^0gwJOBz6~d514%7|cP-5}FJq|!CiM1bQ-e{h zu=WC`u}#D{z@@hQ`p74!n7GZH$yoxjb|s8RKHaE;D5leq`K(Egs9F9|ur%gEt`KFy#kKmnln3K*G3SmOpj#`-_Lo(6T#k#4$gfPcr+ix2 zYJ3{(0~KGdngjpP%jMaO;jFw_udw~)In45%))-sjsIVs6Q@EB^8Sofg8HM`mIr0!! z$|9>qGW6f(7Amev494>To_w8lZEDTsAZryPO5C~T70hiTjYr0#Ix`P!9sueT3pxH) z(lFC9pplDs3lw?D(zeL8YzTT&Bq4HDsdwD2fU(Nu|bE6}k&SN26L=zyJPtLkSK+}w!`J+pD!141e z!vxOmkI;cN2j5)k03s8u?VM9UhAsHjQAGp*^LhT`Ju>EVXgTQN`Wt^KPQctK;GHe< zTP!*m8{r6KJnIWIo~=w&jfLbaD-nArjT@m#@)-tiNPd|^aNc4*#iK*yJ_2tX%PwMX zZ1W2|aRIhlV6MmZFTDP?@34E{%m&o206bikqYS<D6M& zZ-g}wq7DdUR8M?GgheM9LzL24J_VZ+)H=fs($3q3w$g@``;U;nHplvmKDuH6T{w-$ z@^d?6!>)CdmY^L8O(-ce5^AdaPd_xk%mK&T7wAc9Lume$9-JlY+OE8Y(LWDZN!XVj ze+|1GK=i&BvqI9#5(D8hso(QqJtrfXUXB*w-_E%rsB~yXq(o3k@XRK~SlaSAys~Z* z+wxFr==Q7rSbdvVnjyv34>|k#S}LD#3KZZnY)%y z1Zu`7K=H=w@R-q#tmcW9llHq}Qwa6TlA<4+xpsF71q#Nfp~rTIV(9$FPp+Yv(53jM z7S&DTO0<7_6Bc`~!J;9T`?RP)YPl4#ZRUAwLq>D;fvxh#da$R+kA2F>j_ zvST!|Z8VD0uIT;r^|9oIH_;fNbXNPK_5_}zmq0Rj@43d8#l`c?OZ2(_0{3Jd+mLUm zQU;=lISNwGOu}J5{K6~->ZbHX9B|wT@FrcY;00rF+_~~jdzb7(J6i|3Oa|nH016su zVit1w5=_X1ry0%aq>K<$<|wX<>91{)zQBIGpQ4*wGtxI}6ed1%tla`U&feJZ#C1~Y zs}ESz{91c|3sEIa1Fy@)C3c(TJ77xOcVSkaAO}#!R9GFyRA>cPD;STo1|2NqF|EBUEM`7b`<>8@d=izDPZ1o?8!Yi&H zK2#J_>Y8D}9!qlKNosbs(+Z7JJaI92K7wxV_hiQ3r6QD*V)8i9=J2jF^6_J^;I|sq zn;pLs4C=wN)Og?YBgG77z?p_x zwPZ7!m^~v-O?s`IE^Th4)X*CH4E}`EX6rGEKv(Z#BBJ2@B_a0 zPVoQ3Vh2aEq8USjfgK`%fpPrrf7@>%1*B>lcwijk1qzo0g1I7o zb~UPk-gbK*E4sT-AzOP`=T)g*07f5f0kjv8fB@rc>hp%aX-9v`;bN(%jHynE4$6oF zcoAts;_y|`<+#S<^Jraqj{j6I{ivf%e42a{K%1b=&_VkR zGOJ|(B4o}JGvd-Jb;Gr#8U~ci<+ePNe( z$s;;hq29x^9(havCCF(Nu-u{L!&%NX$J}L%Att=03UaZM!>&@WT5V%HOB@^9rC40n zBiBQ6bO&l?HrN<3RJ<>ekYrBQDm^65Xjc7}15z{#G5nBYciZqL98L#$0#nxv8?mh| zw{Gq495>TK20b5z9?*;>rD#|LH<}wd=}yZ_vFzuJK=pjmcDiIrH5iI)D06dtG*5+U<6@xIR#M z^e!T}r~eo|5~e3JyRu&Y$!$9I57E0T#gc8ducXX6qyo=O7uwcrVQ0Zex7(3PEM7*+ z`6U-ZLo7uVk^z_k{33X11m@$u&~0(54z&aG@m~0X$!7WRTA>%Z9IJGL$7~$YCGC{c zrX13UShr^1?-baIc^sgPVX1BGUs6NbZa_d2f)^xb+n3r9A6NB3CWyX4B<(Y^2PQL= z;41}P_)^MxSjNtBHcJkGOu55tCJZhA%SZh=^~$rDl><|A)`-nDzRxCCsDX?_EHvydwgpe zDI2D?p_va*9Ru39Z4<&L9K0!|sLe7AaW^l-{taJ!q+eHFG4s`27Q-6ZFjbIGa$f#I z=PUtlXGzNX%LtL*Uu$NkQ>PVa+HDJoY~q&GXCcK-BmX8u02N|gMAFf8G{QDOL{k{E zYMAfIW8(+%#W3~7;DR6<1y>-kNn|iYn6qTNdb~Nn5P)VSgV+|w5_ zM4q(ZQ{c9`B`Q-tT^KSgz^aUN$Bm;hOmNZ}E@8j*fXx4ABM9L$<|Nbk?8&(dy)+=* zlcbBq9WGIUJ%tH^gw%V1zb491rfQ!BSIZ`7&7jTA7tNp?CgA;Q=hl!s)pjS!kUiCr z6bl|8{1QO#hqJB@hs-(Wea{T)d*8vZGH>|vi2SC>jK8wl6?6W{U`p5?u?fy6Gz`JP zDXUqi1Ri)oTKbo6xuaL{_%mmY+;+p}>u;2b6pzOW6L&S96&gmSYoE!~WJ}euLk*T} zxKK$5`G7~wgp~G>`P@WGMUy|*(uJ1xvCBMh&J+)m(07d;n5sOi*U5i2VyFn0GugXQ2t; zN#h!=@avYlp4qJ?10Z}H%aqGKmJbRPd@{eanW`KnOc1I7kB=oLr70JN%BxjWHU2Ul z>%M4y#ny!ctkB#r?+riu5tf_cY)T)!{e(5qCSIC#1KA1XIa!$1XJ4D(Am z(P-&Wy@s|GWN!dzu&RHlwJWYZ$CdO{2;?6G^oz- z`$yMU$COX6$I;~EwTMVCxb8qzsM_S1Z_y52o?Ijytf{fI;Y$>%bOY&bM=0PYSP!bd znzXvD(trY-0%Eek+tZsEy~)~GolEJ%yqNM#n)gD!C(!k5g_nzSqM=%U+C!^ZSeB#)LAk~l4KP#6!vS9 zi#$LRqwH31jKzX}v_|>{P`bHltnmGTh4qYTopF-)F1^q~zhMPyv;e5~jqzEmPkTV9 z_Dw)Ry!Ro5@w(}geY?p>?}=Bvs@c88A2dx3z*{E6zm6rt?hB$q?{OaUWg*RV`+3qH zBoDNms$*r`ifR%9a}&+mkHEbe4Okpu+C;E(xwoAeq##=}r*k$k5Uq$C>PByLSs(YJiy91Kc7VY&JY^$!@Sz3hX!^K*y$~exh19>#IwrGYHhsfyH7e z2!pBo;#<5yjq*SQL@+)LIA2hSyO(KsKe7C``VZ-!iQv4(KDiA3pFoA1yu-%^(1i)a zSjPJ+@-yh})$xZTD>i?gV%SR9Vj~i+C4r-bxvV(yYl9SJZlZFmwHGCkTsFv) zdXqzrM$TY0F8Swl%c=777Q<#h!rv}|i6+nQfKMoq&G)Ug<#*$}kD0-QZ!4tSl`Ft5 zEyp2+gFE$(ZpD5(EGl%v&~yf0KsZVL!@idLvprUQzfkbT`KSX{x3W~~P}RWs;Vyw@ zH=2w3jWre*@(U9^Xume;c>UncyQ5oTIH=vE9|C^sbhL}|WRpH^UkHAy+8k@E+#GM3 z)>m~Hf9z0Luw>^mI8aoINVtML2JV%ME*)j;&`hN4!&fq#Bw_s10)Dy*;7drNeTx>T zIXrsfhL!&d175gtM}r=;BZrkgV)OA*Q}UWalCN_E_4+F1_mU#`k!1OhLSm8@+q*Za zgB7T>=H6!5do@aQ_e>BRs4?7j;{>&RyWf@c4|97HNPZti&UBs_`Km~k2i=7D@QEAL8xL%=MWrG#6%G97 z@#pvPBuF(Sw^q4Uwuny8CT}af)t;`-uka_u(x*bP(Ndk`s4SMaLtY<|B*1%-J;LUz z$Qw)MvgHW5)qlM0wv(lD7W)Uvstg}&LA}*9>gt;s4JpO}Wi;LZvKz4ZvZwp|()8Vf zTUS2bLjH_$#er~N@QigLx{gguMk;YhCat55-_P8)O3NYXiDEPGwuoa01ZxRZG6h@A zrS-)jQ&Q~obJ_ON;?FBv`V;=V`b+OOx<+Kmo{XW{T@VvQ#EutP-|^zfg(@JxK`WOs zQ8d!f&?ktEa^m>`Xph>lEItG!*W9J=P9l3rjeeyuyI)M`Hv0NPGhcf8UKTeAOhH~U z#$^{5tFOj%7oj$y$mtx4cPiu0#af{Q;&8scR(0nujcAHTTKN9F{gy36fz)1_i66Ph zMXWiCbmKHu`@W@$I}u*T{mPELh*S8wuP9fk%S@C6#M)X0Y)^5B@lE`5uqab8eOgZA zp33V?{!El#H?%La!c5t7gXyn?sY+s>x2DG8T3Z-mdkFhgl;gZPb{l*6U9O5~dT5X0 zQ+hEn&VY|IJE)hM;G89gTI$Gzi>B!QFpS?cdv|eSuPD?h5uD2~=A1KjPaCOi#~Mk- zjiYNW9A0h^z;eTBq;;Y!4Fn(%&L1JNWshcH;s-~W(7m(*w|Clx_^4Ig88J9cnw9>_ zz}deL1=sJQycCCu*u8Lk4U}ZjRpk2n*T|48-{^cO4Q}6}AlYqH?qcRu_751re^u_9 zytMsNM|t*|=p{^O-VsC=n8FxquS{rw;iaMB6vSx*D6o9lfY!_Co=%o@1NfaNSJX7n zulo#7z!&{UO{VhFK<$aQcnzNGx@1FwW%|MXvxX5(R%J}`pnI&{1<5GdKVbur@FH3m zR_(b?2l_yPR_Ox4e94L;!+ASL9FNo6=!#j%(hBteOg@*xuEsWNdQ97*=VQ-?hafKi zC8qaO*gp@nc+Ad1vaoPjIj1-vtB<43G^#TVDPvxoiHYFgq_)n)Ep6;a^|Z*Q;~+R1 zD#+e-dP|SbRr6usHueywB}hwqMM&ZLNXQ&5$b1p&gaj49CuvpVz4{3L7~an5{TMC- zM?ayzVdC2?uxalxC(ByCY6-G}dNu$wF84%Chle-8b$xZm_ArN^j$HfkSC6iPV-A8s z%Sax^QB=^Td-nZ~yjWJ;nV$%!;EiTa`kQX@HpeuhCT`2K_gefaM5@+FA1q8`AKC9DlvlSScC5_T!0j0Ub#^4qe^lYwMs)KM64>PqSAPc(6)bpe zF;$mWGfl0OnlDwlk|7V*@rnTVVP41Gfh@wa(e2YYN!v*txsB}SWrYM~c4^tpCAmWYx2asdPJ2;Q)A413z-pOrtNj7mt^q2)q z9p#n%yON7EG?RhTB)8S(6II@Dm--+KT3fkm4G;T!arQrjL-!c?0 zyW`WA=8TXkq$tvg;j$Zv1?EC~JlD8ZM7JG7#ZKf5f$I%WJ_P_QmI3XYkyPFo?{%O3 zctE7j5b_p9Ab0#+zEy^3tO+|Q%I!Bk;A_e~n6qS#Zh#qi2xU+(#9xlF(iB4EJzRBF z+UnfFW89AvP-a3iVn=S7@u*wqEw9}K+Q}}DiHmba@c~ytpVz|y<{6Lx;t|6(19H>g zJT&W0lmof4#2g?>Q#B`*?${Qmf6*|YqzV4CthP?8)f;9MlHTd3rR=RV-Nym7`X@Kt z=n!Ngirsdg+tTIR3L4O>IX3YqphBJAOM-KxwOmdft>YezulG#f{PfIS4A9}?wr-@0 znbI@N8n)`XmXNZ9Hvnc+yAJD(+ak4CH9i_MCjR=B+6$0rbn*?}|3z7s^^0S=bG$`m z=ZphX1NAUn6$7n@1c`yLq z^_|iO!KY)Rt$l-`oK?0AD}^;AC>+mAgE(D2J;roSt_J5X%73eC|CP1LsgOJW&9j=zhF$SdIYwC?5nj)?Rzw1d@7TuFrCh8*WP0Oe(!tJd8xSOY0lns zOoKi0d&=S44orUz+!om8^LyW}?}auc_yQps+_WQ-r0hzt`qiVUYC8 zNbOoQ1vX)jjMDJm$_2^;RcKtrhVte1nqLgj^V3a}U&O&xsBT&Wg((JchQAc##J`$= zHe7<`yA1F~Hi%uL0PlNX&;Y%V?P}RT^akC2Y+~z1O^pwOXS8xZ{rg~Ac|&LKT}@@Q zOUHD(%?trwSw0cKF-*EzlIfbCrq+CzLsF%X=h%PPagGBXlqqzMSDJKcMKj=6p_lqp zxx{L*ApJK!*CG`!{E9WhiFfz{7b}MxY458=>uR;Cq0T4;fL4A%0Alk0k!JB4fW=&r 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 delta 43942 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 diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties index ca812af6c..d7d50b600 100644 --- a/build/android/gradle/wrapper/gradle-wrapper.properties +++ b/build/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Aug 27 20:10:09 CEST 2016 +#Mon Oct 15 00:47:03 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/build/android/gradlew b/build/android/gradlew index 91a7e269e..cccdd3d51 100755 --- a/build/android/gradlew +++ b/build/android/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,47 +6,6 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do fi done SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- +cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" -cd "$SAVED" >&- +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/build/android/gradlew.bat b/build/android/gradlew.bat index 8a0b282aa..f9553162f 100644 --- a/build/android/gradlew.bat +++ b/build/android/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/build/android/src/debug/AndroidManifest.xml b/build/android/src/debug/AndroidManifest.xml index a3815b9f8..ee04d1d03 100644 --- a/build/android/src/debug/AndroidManifest.xml +++ b/build/android/src/debug/AndroidManifest.xml @@ -1,4 +1,5 @@ - + + diff --git a/build/android/src/main/AndroidManifest.xml b/build/android/src/main/AndroidManifest.xml index df218fb33..7f61cda38 100644 --- a/build/android/src/main/AndroidManifest.xml +++ b/build/android/src/main/AndroidManifest.xml @@ -1,34 +1,59 @@ - + package="net.minetest.minetest" + android:installLocation="auto"> + + + - - + + + + - + android:launchMode="singleTask" + android:screenOrientation="sensorLandscape" + android:theme="@style/AppTheme"> - - - - + + + + + + + diff --git a/build/android/src/main/java/net.minetest.minetest/MainActivity.java b/build/android/src/main/java/net.minetest.minetest/MainActivity.java new file mode 100644 index 000000000..1baa71668 --- /dev/null +++ b/build/android/src/main/java/net.minetest.minetest/MainActivity.java @@ -0,0 +1,79 @@ +package net.minetest.minetest; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat;; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MainActivity extends Activity { + + private final static int PERMISSIONS = 1; + private static final String[] REQUIRED_SDK_PERMISSIONS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + checkPermission(); + } else { + next(); + } + } + + protected void checkPermission() { + final List missingPermissions = new ArrayList(); + // check required permission + for (final String permission : REQUIRED_SDK_PERMISSIONS) { + final int result = ContextCompat.checkSelfPermission(this, permission); + if (result != PackageManager.PERMISSION_GRANTED) { + missingPermissions.add(permission); + } + } + if (!missingPermissions.isEmpty()) { + // request permission + final String[] permissions = missingPermissions + .toArray(new String[missingPermissions.size()]); + ActivityCompat.requestPermissions(this, permissions, PERMISSIONS); + } else { + final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length]; + Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED); + onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS, + grantResults); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], + @NonNull int[] grantResults) { + switch (requestCode) { + case PERMISSIONS: + for (int index = 0; index < permissions.length; index++) { + if (grantResults[index] != PackageManager.PERMISSION_GRANTED) { + // permission not granted - toast and exit + Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show(); + finish(); + return; + } + } + // permission were granted - run + next(); + break; + } + } + + public void next() { + Intent intent = new Intent(this, MtNativeActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + } +} diff --git a/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java b/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java index eb92acb63..b570fe61a 100644 --- a/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java +++ b/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java @@ -1,5 +1,17 @@ package net.minetest.minetest; +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.Display; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; + import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; @@ -7,410 +19,355 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.util.Vector; import java.util.Iterator; -import java.lang.Object; +import java.util.Vector; -import android.app.Activity; -import android.content.res.AssetFileDescriptor; +public class MinetestAssetCopy extends Activity { + ProgressBar m_ProgressBar; + TextView m_Filename; + copyAssetTask m_AssetCopy; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Environment; -import android.util.Log; -import android.view.Display; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.graphics.Rect; -import android.graphics.Paint; -import android.text.TextPaint; - -public class MinetestAssetCopy extends Activity -{ @Override - public void onCreate(Bundle savedInstanceState) - { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.assetcopy); - - m_ProgressBar = (ProgressBar) findViewById(R.id.progressBar1); - m_Filename = (TextView) findViewById(R.id.textView1); - + m_ProgressBar = findViewById(R.id.progressBar1); + m_Filename = findViewById(R.id.textView1); Display display = getWindowManager().getDefaultDisplay(); m_ProgressBar.getLayoutParams().width = (int) (display.getWidth() * 0.8); m_ProgressBar.invalidate(); - + /* check if there's already a copy in progress and reuse in case it is*/ - MinetestAssetCopy prevActivity = + MinetestAssetCopy prevActivity = (MinetestAssetCopy) getLastNonConfigurationInstance(); - if(prevActivity!= null) { + if (prevActivity != null) { m_AssetCopy = prevActivity.m_AssetCopy; - } - else { + } else { m_AssetCopy = new copyAssetTask(); m_AssetCopy.execute(); } } - + + @Override + protected void onResume() { + super.onResume(); + makeFullScreen(); + } + + public void makeFullScreen() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + this.getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + ); + } + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + makeFullScreen(); + } + } + /* preserve asset copy background task to prevent restart of copying */ /* this way of doing it is not recommended for latest android version */ /* but the recommended way isn't available on android 2.x */ - public Object onRetainNonConfigurationInstance() - { + public Object onRetainNonConfigurationInstance() { return this; } - - ProgressBar m_ProgressBar; - TextView m_Filename; - - copyAssetTask m_AssetCopy; - - private class copyAssetTask extends AsyncTask - { - private long getFullSize(String filename) - { - long size = 0; - try { - InputStream src = getAssets().open(filename); - byte[] buf = new byte[4096]; - - int len = 0; - while ((len = src.read(buf)) > 0) - { - size += len; - } - } - catch (IOException e) - { - e.printStackTrace(); - } - return size; - } - @Override - protected String doInBackground(String... files) - { - m_foldernames = new Vector(); - m_filenames = new Vector(); - m_tocopy = new Vector(); - m_asset_size_unknown = new Vector(); - String baseDir = - Environment.getExternalStorageDirectory().getAbsolutePath() - + "/"; - - - // prepare temp folder - File TempFolder = new File(baseDir + "Minetest/tmp/"); - - if (!TempFolder.exists()) - { - TempFolder.mkdir(); - } - else { - File[] todel = TempFolder.listFiles(); - - for(int i=0; i < todel.length; i++) - { - Log.v("MinetestAssetCopy","deleting: " + todel[i].getAbsolutePath()); - todel[i].delete(); - } - } - - // add a .nomedia file - try { - OutputStream dst = new FileOutputStream(baseDir + "Minetest/.nomedia"); - dst.close(); - } catch (IOException e) { - Log.e("MinetestAssetCopy","Failed to create .nomedia file"); - e.printStackTrace(); - } - - - // build lists from prepared data - BuildFolderList(); - BuildFileList(); - - // scan filelist - ProcessFileList(); - - // doing work - m_copy_started = true; - m_ProgressBar.setMax(m_tocopy.size()); - - for (int i = 0; i < m_tocopy.size(); i++) - { - try - { - String filename = m_tocopy.get(i); - publishProgress(i); - - boolean asset_size_unknown = false; - long filesize = -1; - - if (m_asset_size_unknown.contains(filename)) - { - File testme = new File(baseDir + "/" + filename); - - if(testme.exists()) - { - filesize = testme.length(); - } - asset_size_unknown = true; - } - - InputStream src; - try - { - src = getAssets().open(filename); - } catch (IOException e) { - Log.e("MinetestAssetCopy","Copying file: " + filename + " FAILED (not in assets)"); - e.printStackTrace(); - continue; - } - - // Transfer bytes from in to out - byte[] buf = new byte[1*1024]; - int len = src.read(buf, 0, 1024); - - /* following handling is crazy but we need to deal with */ - /* compressed assets.Flash chips limited livetime due to */ - /* write operations, we can't allow large files to destroy */ - /* users flash. */ - if (asset_size_unknown) - { - if ( (len > 0) && (len < buf.length) && (len == filesize)) - { - src.close(); - continue; - } - - if (len == buf.length) - { - src.close(); - long size = getFullSize(filename); - if ( size == filesize) - { - continue; - } - src = getAssets().open(filename); - len = src.read(buf, 0, 1024); - } - } - if (len > 0) - { - int total_filesize = 0; - OutputStream dst; - try - { - dst = new FileOutputStream(baseDir + "/" + filename); - } catch (IOException e) { - Log.e("MinetestAssetCopy","Copying file: " + baseDir + - "/" + filename + " FAILED (couldn't open output file)"); - e.printStackTrace(); - src.close(); - continue; - } - dst.write(buf, 0, len); - total_filesize += len; - - while ((len = src.read(buf)) > 0) - { - dst.write(buf, 0, len); - total_filesize += len; - } - - dst.close(); - Log.v("MinetestAssetCopy","Copied file: " + - m_tocopy.get(i) + " (" + total_filesize + - " bytes)"); - } - else if (len < 0) - { - Log.e("MinetestAssetCopy","Copying file: " + - m_tocopy.get(i) + " failed, size < 0"); - } - src.close(); - } - catch (IOException e) - { - Log.e("MinetestAssetCopy","Copying file: " + - m_tocopy.get(i) + " failed"); - e.printStackTrace(); - } - } - return ""; - } - - - /** - * update progress bar - */ - protected void onProgressUpdate(Integer... progress) - { - - if (m_copy_started) - { - boolean shortened = false; - String todisplay = m_tocopy.get(progress[0]); - m_ProgressBar.setProgress(progress[0]); - m_Filename.setText(todisplay); - } - else - { - boolean shortened = false; - String todisplay = m_Foldername; - String full_text = "scanning " + todisplay + " ..."; - m_Filename.setText(full_text); - } - } - - /** - * check al files and folders in filelist - */ - protected void ProcessFileList() - { - String FlashBaseDir = - Environment.getExternalStorageDirectory().getAbsolutePath(); - - Iterator itr = m_filenames.iterator(); - - while (itr.hasNext()) - { - String current_path = (String) itr.next(); - String FlashPath = FlashBaseDir + "/" + current_path; - - if (isAssetFolder(current_path)) - { - /* store information and update gui */ - m_Foldername = current_path; - publishProgress(0); - - /* open file in order to check if it's a folder */ - File current_folder = new File(FlashPath); - if (!current_folder.exists()) - { - if (!current_folder.mkdirs()) - { - Log.e("MinetestAssetCopy","\t failed create folder: " + - FlashPath); - } - else - { - Log.v("MinetestAssetCopy","\t created folder: " + - FlashPath); - } - } - - continue; - } - - /* if it's not a folder it's most likely a file */ - boolean refresh = true; - - File testme = new File(FlashPath); - - long asset_filesize = -1; - long stored_filesize = -1; - - if (testme.exists()) - { - try - { - AssetFileDescriptor fd = getAssets().openFd(current_path); - asset_filesize = fd.getLength(); - fd.close(); - } - catch (IOException e) - { - refresh = true; - m_asset_size_unknown.add(current_path); - Log.e("MinetestAssetCopy","Failed to open asset file \"" + - FlashPath + "\" for size check"); - } - - stored_filesize = testme.length(); - - if (asset_filesize == stored_filesize) - { - refresh = false; - } - - } - - if (refresh) - { - m_tocopy.add(current_path); - } - } - } - - /** - * read list of folders prepared on package build - */ - protected void BuildFolderList() - { - try - { - InputStream is = getAssets().open("index.txt"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - String line = reader.readLine(); - while (line != null) - { - m_foldernames.add(line); - line = reader.readLine(); - } - is.close(); - } catch (IOException e1) - { - Log.e("MinetestAssetCopy","Error on processing index.txt"); - e1.printStackTrace(); - } - } - - /** - * read list of asset files prepared on package build - */ - protected void BuildFileList() - { - long entrycount = 0; - try - { - InputStream is = getAssets().open("filelist.txt"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - String line = reader.readLine(); - while (line != null) - { - m_filenames.add(line); - line = reader.readLine(); - entrycount ++; - } - is.close(); - } - catch (IOException e1) - { - Log.e("MinetestAssetCopy","Error on processing filelist.txt"); - e1.printStackTrace(); - } - } - - protected void onPostExecute (String result) - { - finish(); - } - - protected boolean isAssetFolder(String path) - { - return m_foldernames.contains(path); - } - + private class copyAssetTask extends AsyncTask { boolean m_copy_started = false; String m_Foldername = "media"; Vector m_foldernames; Vector m_filenames; Vector m_tocopy; Vector m_asset_size_unknown; + + private long getFullSize(String filename) { + long size = 0; + try { + InputStream src = getAssets().open(filename); + byte[] buf = new byte[4096]; + + int len = 0; + while ((len = src.read(buf)) > 0) { + size += len; + } + } catch (IOException e) { + e.printStackTrace(); + } + return size; + } + + @Override + protected String doInBackground(String... files) { + m_foldernames = new Vector(); + m_filenames = new Vector(); + m_tocopy = new Vector(); + m_asset_size_unknown = new Vector(); + String baseDir = + Environment.getExternalStorageDirectory().getAbsolutePath() + + "/"; + + + // prepare temp folder + File TempFolder = new File(baseDir + "Minetest/tmp/"); + + if (!TempFolder.exists()) { + TempFolder.mkdir(); + } else { + File[] todel = TempFolder.listFiles(); + + for (int i = 0; i < todel.length; i++) { + Log.v("MinetestAssetCopy", "deleting: " + todel[i].getAbsolutePath()); + todel[i].delete(); + } + } + + // add a .nomedia file + try { + OutputStream dst = new FileOutputStream(baseDir + "Minetest/.nomedia"); + dst.close(); + } catch (IOException e) { + Log.e("MinetestAssetCopy", "Failed to create .nomedia file"); + e.printStackTrace(); + } + + + // build lists from prepared data + BuildFolderList(); + BuildFileList(); + + // scan filelist + ProcessFileList(); + + // doing work + m_copy_started = true; + m_ProgressBar.setMax(m_tocopy.size()); + + for (int i = 0; i < m_tocopy.size(); i++) { + try { + String filename = m_tocopy.get(i); + publishProgress(i); + + boolean asset_size_unknown = false; + long filesize = -1; + + if (m_asset_size_unknown.contains(filename)) { + File testme = new File(baseDir + "/" + filename); + + if (testme.exists()) { + filesize = testme.length(); + } + asset_size_unknown = true; + } + + InputStream src; + try { + src = getAssets().open(filename); + } catch (IOException e) { + Log.e("MinetestAssetCopy", "Copying file: " + filename + " FAILED (not in assets)"); + e.printStackTrace(); + continue; + } + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len = src.read(buf, 0, 1024); + + /* following handling is crazy but we need to deal with */ + /* compressed assets.Flash chips limited livetime due to */ + /* write operations, we can't allow large files to destroy */ + /* users flash. */ + if (asset_size_unknown) { + if ((len > 0) && (len < buf.length) && (len == filesize)) { + src.close(); + continue; + } + + if (len == buf.length) { + src.close(); + long size = getFullSize(filename); + if (size == filesize) { + continue; + } + src = getAssets().open(filename); + len = src.read(buf, 0, 1024); + } + } + if (len > 0) { + int total_filesize = 0; + OutputStream dst; + try { + dst = new FileOutputStream(baseDir + "/" + filename); + } catch (IOException e) { + Log.e("MinetestAssetCopy", "Copying file: " + baseDir + + "/" + filename + " FAILED (couldn't open output file)"); + e.printStackTrace(); + src.close(); + continue; + } + dst.write(buf, 0, len); + total_filesize += len; + + while ((len = src.read(buf)) > 0) { + dst.write(buf, 0, len); + total_filesize += len; + } + + dst.close(); + Log.v("MinetestAssetCopy", "Copied file: " + + m_tocopy.get(i) + " (" + total_filesize + + " bytes)"); + } else if (len < 0) { + Log.e("MinetestAssetCopy", "Copying file: " + + m_tocopy.get(i) + " failed, size < 0"); + } + src.close(); + } catch (IOException e) { + Log.e("MinetestAssetCopy", "Copying file: " + + m_tocopy.get(i) + " failed"); + e.printStackTrace(); + } + } + return ""; + } + + /** + * update progress bar + */ + protected void onProgressUpdate(Integer... progress) { + + if (m_copy_started) { + boolean shortened = false; + String todisplay = m_tocopy.get(progress[0]); + m_ProgressBar.setProgress(progress[0]); + m_Filename.setText(todisplay); + } else { + boolean shortened = false; + String todisplay = m_Foldername; + String full_text = "scanning " + todisplay + " ..."; + m_Filename.setText(full_text); + } + } + + /** + * check all files and folders in filelist + */ + protected void ProcessFileList() { + String FlashBaseDir = + Environment.getExternalStorageDirectory().getAbsolutePath(); + + Iterator itr = m_filenames.iterator(); + + while (itr.hasNext()) { + String current_path = (String) itr.next(); + String FlashPath = FlashBaseDir + "/" + current_path; + + if (isAssetFolder(current_path)) { + /* store information and update gui */ + m_Foldername = current_path; + publishProgress(0); + + /* open file in order to check if it's a folder */ + File current_folder = new File(FlashPath); + if (!current_folder.exists()) { + if (!current_folder.mkdirs()) { + Log.e("MinetestAssetCopy", "\t failed create folder: " + + FlashPath); + } else { + Log.v("MinetestAssetCopy", "\t created folder: " + + FlashPath); + } + } + + continue; + } + + /* if it's not a folder it's most likely a file */ + boolean refresh = true; + + File testme = new File(FlashPath); + + long asset_filesize = -1; + long stored_filesize = -1; + + if (testme.exists()) { + try { + AssetFileDescriptor fd = getAssets().openFd(current_path); + asset_filesize = fd.getLength(); + fd.close(); + } catch (IOException e) { + refresh = true; + m_asset_size_unknown.add(current_path); + Log.e("MinetestAssetCopy", "Failed to open asset file \"" + + FlashPath + "\" for size check"); + } + + stored_filesize = testme.length(); + + if (asset_filesize == stored_filesize) { + refresh = false; + } + + } + + if (refresh) { + m_tocopy.add(current_path); + } + } + } + + /** + * read list of folders prepared on package build + */ + protected void BuildFolderList() { + try { + InputStream is = getAssets().open("index.txt"); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + + String line = reader.readLine(); + while (line != null) { + m_foldernames.add(line); + line = reader.readLine(); + } + is.close(); + } catch (IOException e1) { + Log.e("MinetestAssetCopy", "Error on processing index.txt"); + e1.printStackTrace(); + } + } + + /** + * read list of asset files prepared on package build + */ + protected void BuildFileList() { + long entrycount = 0; + try { + InputStream is = getAssets().open("filelist.txt"); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + + String line = reader.readLine(); + while (line != null) { + m_filenames.add(line); + line = reader.readLine(); + entrycount++; + } + is.close(); + } catch (IOException e1) { + Log.e("MinetestAssetCopy", "Error on processing filelist.txt"); + e1.printStackTrace(); + } + } + + protected void onPostExecute(String result) { + finish(); + } + + protected boolean isAssetFolder(String path) { + return m_foldernames.contains(path); + } } } diff --git a/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java b/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java index 68dc73274..4cd899025 100644 --- a/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java +++ b/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java @@ -6,63 +6,59 @@ import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.text.InputType; -import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnKeyListener; import android.widget.EditText; public class MinetestTextEntry extends Activity { + private final int MultiLineTextInput = 1; + private final int SingleLineTextInput = 2; + private final int SingleLinePasswordInput = 3; public AlertDialog mTextInputDialog; public EditText mTextInputWidget; - - private final int MultiLineTextInput = 1; - private final int SingleLineTextInput = 2; - private final int SingleLinePasswordInput = 3; - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Bundle b = getIntent().getExtras(); String acceptButton = b.getString("EnterButton"); - String hint = b.getString("hint"); - String current = b.getString("current"); - int editType = b.getInt("editType"); - + String hint = b.getString("hint"); + String current = b.getString("current"); + int editType = b.getInt("editType"); + AlertDialog.Builder builder = new AlertDialog.Builder(this); mTextInputWidget = new EditText(this); mTextInputWidget.setHint(hint); mTextInputWidget.setText(current); mTextInputWidget.setMinWidth(300); if (editType == SingleLinePasswordInput) { - mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT | + mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); - } - else { + } else { mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT); } - - + builder.setView(mTextInputWidget); - + if (editType == MultiLineTextInput) { builder.setPositiveButton(acceptButton, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) - { pushResult(mTextInputWidget.getText().toString()); } - }); + public void onClick(DialogInterface dialog, int whichButton) { + pushResult(mTextInputWidget.getText().toString()); + } + }); } - + builder.setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { cancelDialog(); } }); - + mTextInputWidget.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View view, int KeyCode, KeyEvent event) { - if ( KeyCode == KeyEvent.KEYCODE_ENTER){ + if (KeyCode == KeyEvent.KEYCODE_ENTER) { pushResult(mTextInputWidget.getText().toString()); return true; @@ -70,19 +66,19 @@ public class MinetestTextEntry extends Activity { return false; } }); - + mTextInputDialog = builder.create(); mTextInputDialog.show(); } - + public void pushResult(String text) { Intent resultData = new Intent(); resultData.putExtra("text", text); - setResult(Activity.RESULT_OK,resultData); + setResult(Activity.RESULT_OK, resultData); mTextInputDialog.dismiss(); finish(); } - + public void cancelDialog() { setResult(Activity.RESULT_CANCELED); mTextInputDialog.dismiss(); diff --git a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java b/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java index 159521a50..234a503da 100644 --- a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java +++ b/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java @@ -2,22 +2,54 @@ package net.minetest.minetest; import android.app.NativeActivity; import android.content.Intent; +import android.os.Build; import android.os.Bundle; -import android.util.Log; +import android.view.View; import android.view.WindowManager; public class MtNativeActivity extends NativeActivity { + + static { + System.loadLibrary("openal"); + System.loadLibrary("ogg"); + System.loadLibrary("vorbis"); + System.loadLibrary("gmp"); + System.loadLibrary("iconv"); + System.loadLibrary("minetest"); + } + + private int m_MessagReturnCode; + private String m_MessageReturnValue; + + public static native void putMessageBoxResult(String text); + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); m_MessagReturnCode = -1; m_MessageReturnValue = ""; - } @Override - public void onDestroy() { - super.onDestroy(); + protected void onResume() { + super.onResume(); + makeFullScreen(); + } + + public void makeFullScreen() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + this.getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + ); + } + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + makeFullScreen(); + } } public void copyAssets() { @@ -26,7 +58,7 @@ public class MtNativeActivity extends NativeActivity { } public void showDialog(String acceptButton, String hint, String current, - int editType) { + int editType) { Intent intent = new Intent(this, MinetestTextEntry.class); Bundle params = new Bundle(); @@ -37,11 +69,9 @@ public class MtNativeActivity extends NativeActivity { intent.putExtras(params); startActivityForResult(intent, 101); m_MessageReturnValue = ""; - m_MessagReturnCode = -1; + m_MessagReturnCode = -1; } - public static native void putMessageBoxResult(String text); - /* ugly code to workaround putMessageBoxResult not beeing found */ public int getDialogState() { return m_MessagReturnCode; @@ -66,34 +96,15 @@ public class MtNativeActivity extends NativeActivity { @Override protected void onActivityResult(int requestCode, int resultCode, - Intent data) { + Intent data) { if (requestCode == 101) { if (resultCode == RESULT_OK) { String text = data.getStringExtra("text"); m_MessagReturnCode = 0; m_MessageReturnValue = text; - } - else { + } else { m_MessagReturnCode = 1; } } } - - static { - System.loadLibrary("openal"); - System.loadLibrary("ogg"); - System.loadLibrary("vorbis"); - System.loadLibrary("ssl"); - System.loadLibrary("crypto"); - System.loadLibrary("gmp"); - System.loadLibrary("iconv"); - - // We don't have to load libminetest.so ourselves, - // but if we do, we get nicer logcat errors when - // loading fails. - System.loadLibrary("minetest"); - } - - private int m_MessagReturnCode; - private String m_MessageReturnValue; } diff --git a/build/android/src/main/res/drawable-hdpi/irr_icon.png b/build/android/src/main/res/drawable-hdpi/irr_icon.png deleted file mode 100644 index 0b6861a0d3d87b15f65460a33a520afc62b53657..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5490 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 diff --git a/build/android/src/main/res/drawable/background.png b/build/android/src/main/res/drawable/background.png new file mode 100644 index 0000000000000000000000000000000000000000..43bd6089ec30a1ebd68f90296cb8d4633c0dde14 GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;m;-!5Tzhu?;t_A+19G`NT^vIy c;*uqv067a77-dYInSd+?Pgg&ebxsLQ0GX;0bN~PV literal 0 HcmV?d00001 diff --git a/build/android/src/main/res/drawable/bg.xml b/build/android/src/main/res/drawable/bg.xml new file mode 100644 index 000000000..c76ec372d --- /dev/null +++ b/build/android/src/main/res/drawable/bg.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/build/android/src/main/res/layout/assetcopy.xml b/build/android/src/main/res/layout/assetcopy.xml index 1fcfffd65..b3da2f027 100644 --- a/build/android/src/main/res/layout/assetcopy.xml +++ b/build/android/src/main/res/layout/assetcopy.xml @@ -1,24 +1,24 @@ - + - + - + - + diff --git a/build/android/src/main/res/mipmap/ic_launcher.png b/build/android/src/main/res/mipmap/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..88a83782c6a6d1fe866a0eb8ef5355398dc2e1a8 GIT binary patch literal 5780 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 S + + + + + + +