From f21040dd94e6600b3f28c1285ee54d32dacce6ac 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 688a2257..fc756941 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 8e628288..dce45fa3 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 b4908d62..00499ff3 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 feebf08bf8b9b86bef40465ad06a37fc51a1bef5 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 deeba788..d9ebc39c 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 27d709a4fa967f1c6eac9a6354175d4f68c011b4 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 fe3e838b..ba28aa78 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 20c9ecde..3f285fa5 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 6d6c0745..48cb5c55 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 9c85d5cd3e6efc253c888337b79d83c9a1105667 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 0999bd16..774dc663 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 319e4ea3060064f3a25850430b2e8c0e378b332e 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 c175c792..eb7abe5a 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 f97e09ca..9ea7ebde 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 4ab006ae..32e9bc2f 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 6a45a37d..e1d40f4c 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 16e001e9..15281ba6 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 8387453e..3d267756 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 6add66dd3056190206c7a90b88a3adab4403b862 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 00499ff3..de669b47 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 60b5a1cf1a335278e99aa9be3428ed3913324c10 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 d16ecca8..e00f580b 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 9ca62e069408b32ac5d3869195ecd8694b951acb 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 f0ddfce8..999125d6 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 ea6671223da5f054b30f8ae7a5111d67b80235d7 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 28c3d193..0d836510 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 cdab5aaf37bdf52ba9993d4261c03d2bb0e25a21 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 ba28aa78..a1e2b807 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 9c6a24a7c39214a0b80064c1979d31c1d97b854f 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 e00f580b..901f1455 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 c44fd014..734cb5d3 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 cab1702c..87bd551c 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 a1e2b807..89697351 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 9bc8118a..4cd9f36d 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 d9a5cc48..0e19f571 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 c540ab96ceae4e2ea18f7f0c9580953edb4152cc 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 ce24e54a..563275b0 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 c2aa5edeac5ce6bf182d951a47da0eb2c4754664 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 36decb43..89640478 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 9a44981b85a7e7e1643f71c8d601efe1c0b5ea28 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 bdce7c87..8210e0bf 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 0471d1a46d102099f85af2602d9f4e9dbb0c835d 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 2d94817e..91b685fd 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 eb883fa8..eefe3b7b 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 d8f01874..efa53ca0 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 f55a72d1..5744e681 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 399c41b7..2d84ad03 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 0fbaeb041e13eb0ba51d38d8a4cdea8601bc2879 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 dfaa04d3..d008ec8b 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 89697351..8e88fbc8 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 8d4979d9d9aa37e9b24fdb446b8d5518096b7f87 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 9a7bb9d8..53a2608b 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 fcd2bcbefcd4d5dbff81826b4cb479af3ee5ae91 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 eefe3b7b..88e2db8a 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 53a2608b..d7163bf6 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 1386681c308920d6abdb6714b2eb808ef00fa743 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 32e1fbf8..b42d119e 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 c485caff..a697bcb0 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 24fc871a05abcb9ec82662833ad7300b6d48d400 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 d9ebc39c..39fca7d1 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 549c0967..f32d77f2 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 734cb5d3..f9ac78f1 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 87bd551c..b9a6b650 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 5c0f90df..f76124a0 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 0d836510..2bed2a25 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 ff61dd97..8b21a93a 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 472a6b85..a3353340 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 cdf12205fdb34e4f5fcbc017a781673146da564e 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 a3353340..6732e5ac 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 9f5d5792704d0e7da8119a6059ee494a7da2c482 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 a15e4c11..4d13faea 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 a24d2c6cd5b4820d5aa61932d8f92b7850f31ff8 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 5c64b2e7..98d9aee0 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 68625b6a..6027982e 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 586c520f..d2d88588 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 f76124a0..e5ba46d4 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 ec89158960d9a40f82242b31ecedd6fce3f65a8c 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 a0ca7993..0f75ca64 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 6027982e..ef9376df 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 45dc6373..5776e77b 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 db175a48..68dc7327 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 ba7d6216..2bfcef93 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 96c9385a..6871ce46 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 8d597659ccf8f6957060fa3fc55b1a8cd8501964 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 de669b47..a29bce94 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 318555eb5e80904b280b6db310a8bbb0e2d09410 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 ef9376df..f53f7f9f 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 8e88fbc8..7fb99ef3 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 dce45fa3..8235efa8 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 a29bce94..956a1ac4 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 388184ca..dc520e3a 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 37e472df20aa62eac01872320267bfd7658a8610 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 b9a6b650..995c7213 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 86342f4d1a966415e95c403148d1582c5a7c05c4 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 f53f7f9f..bfc24bc8 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 9f2d9fbee592216bf191a4874dc4fef6d0be7142 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 1a0dcb68..c64ac897 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 b3e494cc..436f22b4 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 ae8263c6..8c02ebb8 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 074bc962..eb0d2ed6 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 948561ac..35cad78a 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 1f308bf7ee3173d93e53da4a510e9b9be7e72cb8 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 c64ac897..9f0fda61 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 2c1068ad12f0adf4036eede0a533927cdfe58612 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 9f0fda61..1a0dcb68 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 436f22b4..b3e494cc 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 8c02ebb8..ae8263c6 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 eb0d2ed6..074bc962 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 35cad78a..948561ac 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 d7306ba69ea844e966515386acb2a58a70540a87 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 ff473e02..75d1f7cc 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 d99767ea8b6043e53d04227e4d7399825929071f 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 b3e494cc..ab72bbcc 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 ae8263c6..f3d3b1bc 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 4d16def09ab61bc37d2d96057c757e7c8cbebc28 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 0774433b..b0235225 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 ffd719995cc496958dde51151a2233d952cc439c 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 68481f7c..0bdd4b02 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 ba3339d3..5182cb3f 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 1e8e6a5d..e9ccc3d7 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 a9e53c951c6f4f48e9f0ce4668ee909ff2c80d80 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 f3d3b1bc..2bbf1831 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 5e9880f9bb4167bc7da893d85105f17cddb31882 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 5182cb3f..0da66750 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 c933047d..79f7f68f 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 0a44069f..3378e8b4 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 e9ccc3d7..a0c4e30d 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 2e735e77..e3a4291d 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 383da33a..684b125b 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 32b46d21c324abffcf33b707eea115d2d4351dfa 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 1f7f9862..1aa3be30 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 22283801..9bfdb22d 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 a0c4e30d..83022e95 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 cbdc747d..5e39ce6a 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 f7b094ac9348d24b8aeeee1714ce08897331f505 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 59669fe6..8935ed90 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 a766044a0c392e3f8c8ce0b90650ddb444317572 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 2bbf1831..e893d646 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 b8b6bc5b..e7284b03 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 4836612b9fc972dab28dfe7492eddb208a6544dd 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 b1beb1ab..1ac4f708 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 e36745f9..f6de2c33 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 e893d646..aefcba06 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 75d1f7cc..9bb56017 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 8617f726..ec8f3db7 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 a9fef3b0..51664e8d 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 f5e76a7b..e92197c1 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 91d41c0d..502827a1 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 8f8001e6d21999cf0f66129799df13aa1e56a587 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 5bb80bbb..91ed9130 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 2d210073f2325e7fb351e7a5a2a139ef74e5b95f 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 91ed9130..ba73a58e 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 4e3ee7efb0846043173f72fbd51e41d20d0d352c 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 6cd24ffc..d00443c6 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 822ac11c29567d555d764bfd746028ba9d25b308 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 5e9bc4aa..ae500d5f 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 a6917005..2b79cce5 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 7fcecb2b2422aee7dd6412eaabfc69d08d31b510 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 ae500d5f..3ac099d3 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 2b79cce5..a6917005 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 3a59d9757cd197010f5f64da090962f18edc6d87 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 c3d60224..65829fd6 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 669b0a0a560233f21df7f61e4c0931bf52d4ee7a 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 3ac099d3..44a42948 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 aca94945009a039d5933b58845e1dce9a85e1b24 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 15854fb7..66ca8be1 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 98b34ea9..17162b34 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 66f32c9a..f52ca69e 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 a61050e6cf813e66ab9bad12f61b1ca1a6976c17 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 f52ca69e..e525aa0a 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 9bfbc115f1b5e8909846cf8d53b6567318a5b639 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 b587f7bb..ace0b992 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 9cbefae2..32714ff3 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 a89bf3e480b8bf5b250ba48e7b04d3c6de5f076d 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 b918c993..abd29f3e 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 71eb9b54c6a4e572d081e9bc9f5cff0914c2efcb 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 4ff77bc0..3d82b9cd 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 a2558d3464b8e8992b14ab61d7bc70b9f4a49859 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 c0891c15..c9a94591 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 b56cfde1e1feb1695ba710557f5709d0d35f8c43 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 c9a94591..4c3bd016 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 b4b6decb40b533eba1b8b11978bdc95f83b4fb5a 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 9bb56017..8958826a 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 9429b15fd8cfaaadd514768264bfc5465671cd39 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 aefcba06..edef7559 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 8958826a..68acc81b 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 5e5a350a8349c5f9f5f5b598ab510509224008dc 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 f6de2c33..1a8bce90 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 edef7559..03c82568 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 c5f090e7b4646ff89fecb7371adc2f0cbc68eb10 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 68bd4afe..47539727 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 d2299c87..6e742930 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 5b026bbd..22456f48 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 250e7d6754f08dc165f8a2e051d8de185856bb19 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 22456f48..62ffdde1 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 df5bd8e2e4f6f5b68b7f02d12c9a16c3a51e44c4 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 61d369bc..a1e6790f 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 e2243419..a864d21a 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 940a82ed188574ab45d2e625a9cb0956ffa63e6c 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 68acc81b..facc68ae 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 f4f1766c..1f680ccb 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 a061ea0aa627396552760918e6067809921f9419 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 ace0b992..652cebd7 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 4064048bef3bdadb0cbeeaf6915acb4a9990f0e7 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 a2f031b4..6895349e 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 3a562391fe941e9ed0374c81b4b9d8b3e9d5b63c 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 65829fd6..f6367142 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 1e63e53a3f12859522a5f72a31100a1e08a586a6 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 0bdd4b02..4840dcba 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 fa7ae583..7eb94177 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 9510a9e1..7f95355a 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 03c82568..f0e6931d 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 8e3a110314665c0fc6187a01e6ba055281273dfa 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 be1c52fe..d60a3084 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 62ffdde1..452abdea 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 4d7461c5..2537d895 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 e7284b03..393c4ed5 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 aaab0d98..0195dc39 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 5e39ce6a..e4fcf626 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 7c370fd5..f6c2f17f 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 19124782..b3cb85b8 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 38204980..31af5d6a 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 b6382d8532c39781bf33d64f67ce333475d7033a 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 4840dcba..1e282f88 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 c94cab7fc92aad6423ed69aae85a776ce177556f 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 f0e6931d..8e750c5a 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 d835daba..ff5ad555 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 9c56a7e1..23248c45 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 1219f8ef7b4bc3b67fb7bb09087913e39fa2f598 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 1e282f88..51abed1b 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 032018f2..e13da1c8 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 393c4ed5..46745fac 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 ca3da992d7d7236fd5eb3dcffad11ccfdad7a569 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 3b02ac02..0c57d6a8 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 8816dc81..1ccaccae 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 4402674ae622207def23179a316a3e5d1b51bc62 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 a425af82..20633404 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 50e0121d49b1e7f30a9d4400ceba5fe88bc50469 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 20633404..1bad04b9 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 6f093e257d280cbb9e7a0cac3c853e37ac19f1e4 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 8e750c5a..c1799d79 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 c4fb2c83e9addd38bd8ee05ac4e4d66804244752 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 652cebd7..9000888b 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 32714ff3..c371b345 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 5881ef50b4f4e350e0fe3505a2efba016091c6e4 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 e89e182e..0caa4f79 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 c8dba10a93a641712e66dc9d176cba0bf4c7de65 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 d15c53e7..e90bb90c 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 4979544ff279bcb417817d811e48f80294c8dfef 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 0caa4f79..69bc3135 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 eaec1f0f..cb16d1c0 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 483f0e27ca6a13d1a61df0ae5531778e1051bd7a 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 e13da1c8..0654ca3d 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 7350a71c..b4b02b0c 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 1ae8f58a..91e153c1 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 d050c0bc..2f726dfa 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 f073f1e7e07142b3a520af94083965496190647c 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 0da66750..ddb0afac 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 52a42a3a..6957508c 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 3378e8b4..181a12b5 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 1fac02166fc6bfe8aa563d41e2bf4a931b3d6093 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 7c1a4eee..b1650f37 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 10b6fc94..0a9de2a5 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 678fc644751283157f528162a816418793020aa6 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 f7a6499d..8e06ae10 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 158ff85524238f4c163d105d2aced024753541e2 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 1909da61..e3dde24c 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 2724db2d75e90931568045544fc656d7073801ec 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 be3a460e..7c21859d 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 ab19a90c0bbd9d6af94c9509fd6bbdfae003abf1 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 286d1ead..210fe9f0 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 80d3922685c642e7d67182baec2673ea534eb463 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 c1799d79..3d461735 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 352e17583df160f158c56bd8d5c9cc3074b9964a 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 + 3 files changed, 99 insertions(+), 66 deletions(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 3dfc29ff..e788a2a5 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 1a8bce90..09e3ea99 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 3d461735..d6b8fc5b 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 From e1c5e4ac7ed6967e047374cf7e575b25cd48fc0b 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 e90bb90c..994f0492 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 a18da19c83c0d1f2dbdfbfef63247bcee28bef07 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 ee0d625b..cbb9386c 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 c9e758408d5ef87946b5e95c14db97d6999d3ac7 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 ab72bbcc..8ef3bfb2 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 d6b8fc5b..13881ef7 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 c4a988e0..901105fe 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 b75d9db2..16f4edea 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 8976b7e5d2463df1e470f29d382548fa3d8bda6f 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 3b3f5d33..f7d20100 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 a5eab716ba18a8464f7dbe871ba003c8fa980d68 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 99495132..cfdff1bb 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 86f066a9707f4fdfaaa9f33b3c3075018a58bbba 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 5a8cc19b..bb1318fd 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 bf0a04792cc7ac0a113bb708ccbbe416895670e7 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 da327c3f..8eaf53c9 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 b4083264..61f35e91 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 8c4f6e55..a19c15a7 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 2f2a7755901da916fa42f4327808f683f96df82d 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 13881ef7..ab5caca0 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 901105fe..aaccf17e 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 16f4edea..872e4362 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 40225cfcc8325f2acdec61b8dd4f26ad57856f3c 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 8cb4ebf5..74eb6ae8 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 00108a2c348370c6a6995481417f8210e9e1232f 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 ddb0afac..0d382df7 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 79f7f68f..52f8eef7 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 bc6b1639a5169c30b630efca24ed421c85cb2f30 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 ab5caca0..d2b70a66 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 9850600f812ed19cddb4176028b3021f0a369fa3 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 bb1318fd..52bc8ead 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 b33d7208762b3eb8ab5d90750f978e3772c6a721 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 1aa3be30..f4165f5c 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 045b0629a1b170eed4852ebbbb95f83fc8681847 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 51abed1b..0686c18d 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 b6138113e9a99e7c2f3ec90708abc0c040bfeb73 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 452abdea..fc64c142 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 e3a4291d..398b6275 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 b8193de01019e10640eb8d17f811527b831315d7 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 d2b70a66..a32eb34e 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 bae4de48c6e0d044981536df8c461199bc8baae9 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 b349f9dd..3fa04417 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 cccbb257144318296fb00e1b21684770984cbea9 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 a32eb34e..b35c816a 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 4cc55c58b73e1e81866ee5fbe47d591f932ec0d5 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 83022e95..f8e6846c 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 19fd9f1f..c4b072be 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 2fb3b1e41441bb162d07c63c0f69556850df4a75 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 a5228132..abc84b7c 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 b4145c76..644549d7 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 8935ed90..0d4b792d 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 ead095e283019a5b6fe378c57e68ea876fb17d70 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 09e3ea99..d4ce6002 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 8a7486d025b8010dee7b5d93e6da3544808ff8b9 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 37687e1e..ad59ef13 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 80e8904d0f2fedc2c1818f184a18c8b7cc416770 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 8e06ae10..c4fcf960 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 aa1df7cfbdf0f13409d2d91967253a435b9d72c7 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 ad59ef13..74b9f634 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 a7be7716f8a660a9ecdc7b92d1ba369b6af9817a 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 fa6af366..3a8d1fd3 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 fc516d56..d9b926e3 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 4a27f657..573f0378 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 2e8a5ab17bf811e4962502002e0774fa1e144c8d 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 430ba113..f27f4f3f 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 1cd770e81292cbff39a4d6753fe01ee831738894 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 e3e76191..90b8864d 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 8746cf12cacfd8c07cb594e612ef1dc70e968392 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 589cfd90..0dec7dd7 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 678c9e26488f5269f5d48a6c8384ce7dc77fb0d8 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 d60a3084..b5e43bf1 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 c6f24f4f3182fcae51f61466bac82ff3be0e9e31 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 b5e43bf1..1234e391 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 049851d3bfbdc46fc1554420b8f2679ef496e84c 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 eb7abe5a..5b10e236 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 14913cb8..17903f2e 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 0194385f..a0915407 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 447ae47314c15cadf014d3ae546c8b0485d439c7 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 e6dd8e83..7e93c134 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 5004ad17f04fe185e91e72b8b02137ed3b8733f3 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 bfe407b9..d8f7a638 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 b35c816a..73572ac7 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 c0eeec2b1a0018e22b33cc781c4f3b44e0b2ee6f 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 356e119f..73ab5cfd 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 2537d895..c75b1c2f 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 5ad7947d..66a761f4 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 aca4d66e2fee819aff51bcac3313075579b2e1c5 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 0686c18d..ab2da14e 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 1cf56c6617d6ddea997b347c85bdc05e7635588e 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 1ac4f708..991962cc 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 cd7c7d7e6c29c1013e0e6f98f239ff3c1fc7220a 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 c0e36be2..caa75982 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 b763ea1284e21eba38e686ecba9d3d93b7503341 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 ec6f2809..25af24eb 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 588b02948bd6ec84faf40afc02273f1bb34c3196 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 5f4e984c..16cbcc05 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 5dfa6d52..0af5eea5 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 ff17d76d38d41800dcffa76240751aad68a04e0d 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 e788a2a5..3bd8f2f9 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 2324a0add6c7be7e684c6c9a6657b46e6dc0bacc 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 1e5af1fe..df109103 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 c307b0ae1db306b41ac4e4f3912f72b0e2761168 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 73572ac7..04ae3365 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 46745fac..4a7885da 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 63276e2a8b54ea7b7289c95e03f42f4e2b22a2f1 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 1234e391..c22e341a 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 0c57d6a8..12897144 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 4b6e08f9..5123a7ec 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 f4165f5c..cb010072 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 9bfdb22d..d1845d59 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 e4fcf626..8a66d4df 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 4dafa7f9b410a2c26e420f0dd4a486438ea8e2e0 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 4c3bd016..8faf05f5 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 d16e5a2077b7044cbc47542fff12e1a271e45fb6 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 6957508c..aa1baf95 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 dd47cac6e6cfa69e80cb96845120bbdc7e966e0f 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 d24971e4..f7f6ff60 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 cb010072..6fe2906b 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 d1845d59..4e0d75b3 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 f76a46f1d48f3af69f6ab3ccc3e5de0871b1fc40 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 f8e6846c..36306599 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 b1650f37..6663b3c4 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 7c0ec10625c3ee861ad5de6d2d38c297d2eb1a6a 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 ab2da14e..aad5f2d2 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 d4ce6002..ea9681ae 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 04ae3365..bb649162 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 263deb2a09502bb34a75d8f7bf157ca03cae35b3 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 8a66d4df..be1ddd7f 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 393ebea6019b20ae709ae201618e6340362e5eee 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 994f0492..994ff5bb 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 a6dcede72b681950cf006f27d374069163b76684 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 885d7ca2..170d75de 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 deecdd65439ab7d2d1bde5956266fbccc04fec75 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 170d75de..9aa6b3a2 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 82163b8290f469c068abbec4badced43d9201cc2 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 dbed7995..07fb629e 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 a1140594..1cb8dd90 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 814bb249f12578fd101de38e68b5602def8890c7 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 74eb6ae8..19af8db7 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 2127408133ce96c4960ffdd36303e96bec960d37 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 2c0e7b90..ea1db684 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 9000888b..ea2da15d 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 c371b345..7249be74 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 513889919997c6bbda3665660f5763e82c7bbcef 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 4cbc999e..cfcfec0c 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 97baad8909114d959253d55911fd1bb5ca292501 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 6cef3f88..f8f3df56 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 96d9479f3f06c3c797dde7a3ec5fbaa9f0f54998 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 e4335d83..cd6e7bdd 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 8474e543a0578c3688be9344d9225898547aca1a 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 3d82b9cd..4ff77bc0 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 ea1db684..74431501 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 d00443c6..21937ac8 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 f6367142..9847cb6f 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 eec37102..ba7898d5 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 54013297..df954263 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 cbb9386c..ee0d625b 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 b4b02b0c..daff15bf 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 4a7885da..630f6cc6 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 0195dc39..8905f2d0 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 be1ddd7f..da4aaf00 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 31af5d6a..d6072e1a 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 f6286e636bb80795e248d8839fe9d6236617d164 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 994ff5bb..7f1293ac 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 ea2da15d..05195c91 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 ff5ad555..aaea3831 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 36306599..71a349b0 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 da4aaf00..8c3c3485 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 1bad04b9..e56de317 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 3f56de915256752b468952899b1bcb849a4f1736 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 a4328fa4..af69f78c 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 10f8769495ba70fd81455742755207951838d706 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 af69f78c..b87668cc 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 35ceec08..94954096 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 3461dd52302677fbe089e318e49b4e12dd9705c5 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 d807003d..1478bc92 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 3d6965f1..81fbf43f 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 c1c999595952c29ddc4a14d0b619c8d574d35952 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 1a0dcb68..c64ac897 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 1263b223..f92fb3bb 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 bb649162..728fd0a1 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 074bc962..eb0d2ed6 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 4278f74377196f1051427e9bc045662246eb9f01 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 d9a82da4..df1b01ae 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 f92fb3bb..51751732 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 53467059..dfe7afb3 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 00000000..36af338b --- /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 00000000..a48c2902 --- /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 00000000..5e9699e4 --- /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 4c323809b91e82d11b2947991e35ae4d70d21fda 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 df1b01ae..5fe0321b 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 dfe7afb3..0d5e5100 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 36af338b..6a58c542 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 a48c2902..0bc56a2a 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 5e9699e4..83c83788 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 3787ab50473dc9f18256c822d18832a6b77dabe6 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 0d5e5100..f6e2f6b2 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 03113bb0ab192e1f5ca1a3de88c7a085ecf7a0e3 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 0bc56a2a..b6a48acb 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 6b378a7cc7c48ecbc40062a642b825002298930b 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 facc68ae..5ad93b95 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 31b863a7f2b7617bef0d74a52e56ec0959bf4441 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 3fa04417..3e28d9cf 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 347a09a6125e9db802aeb1a2120eb6dc7d7b73e5 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 a0e475de..bb1d7522 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 91ec0c23d179dafe2ea1826caa260e01207f5bdf 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 c64ac897..95f1f28d 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 4b731020..9a57d3de 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 25b3dc569160a5a195681750234fd9cc9cb96063 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 f6e2f6b2..d3d8b352 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 6a58c542..a4b6dde1 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 1a6a5cfb7804be135546a1b9e414fbb29e946b1d 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 51751732..d978fa84 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 fce827cc158ae88adbdeb64067f09c2cdd029838 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 5fe0321b..af798163 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 2a04ffb1772ded7a25d095d09ec3a978cebc4670 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 d978fa84..e2939a42 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 d8a485604d6e1fd9ebe0e79aeb8c133409841bf1 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 e2939a42..c6716044 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 980438b7..ca812af6 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 ac44679577030669e2a022e410a024164c72834e 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 abc84b7c..1cbb6953 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 95137a2515ac2c57765cc24bc78a220510b102e5 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 f27f4f3f..af68d6f5 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 6691b721fde2395132a02293375fb30029a22eff 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 f7f6ff60..2f0dc875 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 13310f7db7ccd76ee4bc486b2114286fcaadcc49 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 e6a9cb75..9127527d 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 f6f98f795886c8cdd3c38384c109477174a4bc7c 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 bd8b94af..f60e9218 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 987b500560a3274aa5e9011c192fe9a8a22f6109 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 af798163..1626c066 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 b01c455b65c847dc272e9bd351b4e853f4cb56bb 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 c6716044..82317589 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 a6e60c43..b0cee5da 100644 --- a/build/android/settings.gradle +++ b/build/android/settings.gradle @@ -1,2 +1 @@ rootProject.name = "Minetest" - From 6ecdcc1f193b02490bcb5033c74aa3cb0c868088 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 82317589..8d189930 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 96d0ce1dcbebee8fed1208b0e6b0cad226331552 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 66ca8be1..46a5b479 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 9d66835b42fb3822719bba082f4ac759c4bdafe2 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 2d1bd6d4..8661b1ca 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 7c60a58121012d7e86dde00259fa5e0a225e0b42 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 7b5ecab6..9489d6f3 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 8d189930..06c79a08 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 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%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 ca812af6..d7d50b60 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 91a7e269..cccdd3d5 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 8a0b282a..f9553162 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 a3815b9f..ee04d1d0 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 df218fb3..7f61cda3 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 00000000..1baa7166 --- /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 eb92acb6..b570fe61 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 68dc7327..4cd89902 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 159521a5..234a503d 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 00000000..c76ec372 --- /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 1fcfffd6..b3da2f02 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 zcmV;F7HjE=P)9x zFZ;4D8?O<$G&eg7U9k9aniE6iFcsn72=6m;97oz&>7>QT>6y$>_521A4-~Q;9x!qo zmW;`u^|3Q#qs3G@jzwf2AxY;HIY7uPI*7*80g+D%@dGo5UMQE-dfFMY(&DIS0~jj$ z21M{fOfstg5kK7#WUI7@eAN;BD|DU+mBZ(Ls-97H_8wzNRJ7hql|w_T&jSc90~VTkHVg^XVoLZ;0sE=^&h-6CYF#cbM69HLZcx z!_Kt98dXAbMFh=3_&>sK(C(Gg(}~?|3HiE+Kmu}0w6=t->@W`KcGgL|$?2Jmh`CJ= zgmx?5wRrbFF7bL;=?K9y9D$Wj3Gt_$c`~nW-I=^H2Z&tgXkFqBfCD)B^X$wU2!MB~ zEy*Wgq5w}ZGMv(wnNDA_GJ9WPDV(GK`U0%#9s+Qa-Na0V({V&T=%e=@wxZVvy8t#4 zAT-Klj5yKdb5<+i+*~I}S?vi)%RGRc?Mw=BpOxXXkR|WqVHdzk0`MbfeZ@&GM_B33 z4>8GXr86Wnc|gUPeo%W^gNoBWAY0@P=*b|3_z79m#8GmZqg?>Y1$ZhT-enOt)#(9g zGokgaP<+&zBHj?{1Ktok(N)))K4WA$OYH(!Ab@|0)4xd~#g(p*v$JoT#2X@eTVD{A zxabPe!pZ18%r1Z#0+2qR`lct;TP@H+tQNf4%V0HnlL_Uruygc#=aDs5DOPd8BOGz7w<{kUdwfqweoXRB)GzYr? z79w*?AK({j?I%v*14&{xNENsl6W}R4HbvL)FTJ_8%Hbj!sRq~uFaw1LQC%?EjUry0 z?h31C1;F?FD+uz93DCOhHG`&FI)u!01h$&qbxp#qYQ{Yd@#Uv9nGdTq^WmmDLtV`n z_-;=Le7#)_P30Vd{4@k*yu-K32q&)L{f5n^>rL^xT!7AQM_WRC^#x7tPx(B8GS3$- zy`_RXd&;1BS}3T3y)@*D+(8-B8*=pA`n8P^!%gS31D<2 znd|V-RwK#adM7A6y3K%UKUrhx}G0jG7xHWpQnf~ zm3hO5+x+3dy*RVUH${N#c!%#ZrM<#Cm-sl>k)lNB>*P2Pw|EWP#5iz9=oBo*4(Ix3 zJAeX-ReS02i!3}J5Larm9&wU0ENFNR{_|~&MZ}vb0Fg1_v{gnwqlj=GXj9^OQkr|9 z%y|zx-H8|n;=(3ozB`nkFy1C^oYV`d6g?m~kFHz70}DZk(3su>CLQW))&5TUhCaZ{ z*Te&SeTvFH@XaS7HWOgoYtKSndQVN-D)0GF8RNK3Zi~@pP^{}TMTq1bweBz(?M_|}9ny+=!`qWQUxdFl@S@tgt>~|FlE*d_|<}^p(6qC zYrZ}g2(u=8Q}u%?$>GvoM6C}H6eI7Njl+ArjvJFyz-Q(Ti*wj9-e~KsZ zD~2rv&<)U`Q?xT-vC%^+ zVmlPzn~O2<`yK7q;Fz<0-HT9^MW^C}Qbs?SFLUW{GWl|ek1Ib}Yd2x9xIytT6Ny4k zM(MFZkhgUx%&c(gP=Jy*214PUAy9q3zfDD`I`0FC%RH$wLu`b`#lwse|Gzh7@ad^2 zIDJssp#befnS|L9D3)koe@I zDERs7SiSFg2|a0bl^$e}Hmz=u^WFQ(g{~lt^8lVwxO`cQHx4 zp#YmpoZ#pbE%|#Upaz(tr@jEK$lLA7FWt1)o? zNR%F-x&lNkaWvYFHhewC7iXglgpH-11d|28MNCeJH(cAE4|UTR=#kYt5xhKY&R=t} zKa?Cq-NSc=K0-dm)R)}|k};Ml9bkKvE38+ez?`A^yZNXNC|p0-=+_qP^o6R^10I|B zqQl;Lg<5n`f7tm(JVgj8z#U{E#sm-+I#HFn_~q7tOd-AV1i)oI#_`P=ps0F=x)EWK zC&-r?Cr=mEi+o^So+Bj$fy^E-rOq7+HV*0V>n$a|0u5nsjl#;(Y#ecpJFHq947c}| z!OdOe5D+$;z>i;PQ`7r*vOPmdpdYf}wQAP++R`YnfQoE#0`uUyt~!C!*nB zH}o5PmyT*J=&=OAxVNtY-d!1kDqJ66rn&{1d?bmN* z#4gq)x4Bse$rHRms7Aqi>1a54KpQ76d=Ldce4+3C-(3!YMKcCa#9KuG61UJOZ{a)< ziVx_~PDMd2*$2WW4FJ{V_Q}lOJj7(;)w_m3d(yJK-jxANRPyyuFK%^hgw@YbwCOq>;${zq91}@4DiR996(Hz`uV!P(l5BP;3BPJ`t%` zVBIwmAHFyj4ga~VGe5YinIBZ9`brysmFIS3eX% zeQh6#G-R^Q|oUDZ5f>?sg!K_#a%$W&g%TGK)ydjF4j)5S( z4hAwcqCMvy3iIX$w%N+QcP$KFpQD{2SEyWIcT*_bLtN-`b7yZkL`Mz+CXZ=50Z3d& zBw#YQS+2lH@c>qe4@m0=LCzKsxje?Kg$tHgG|IE(?|L}OqPS}Yx zkpJp|4v9BJ_M8?NBQ2u745} zWkZZK5XQ2(1Z*w<5VpG-+%8HuHR(3z^g3HJf-nto;}gUV0OT2~(->8!Z>Xdz)BLQ|L_X`gs$E!PVA4uZxRY z)KIL9q=@fy0Z8Crh=U?C%?+ikh{P%{C_G^LG4s;n-Viavjq>@ByuO6hVaxk+dnweG zXXyF7wCpvgdgJWlk*|ZYW zpGOeR@)v+IS7kc^CKkuQXIqQgeGM*ta1n%yA!1OHr5WV3cizA1oDcEdFHW}B=W*(L zb9XU1(N+$N>a$^lP)3k42zjZj>7|aMjEO?8lZ9BEb9qyW2gQP#^2Om$cLsWa=5*xtX&~+F@%dEwn9THH(%4% z(48&RT+&Rn_=Vc37EX1qw5D_KEXf1Dj?XjVDj|2lKARD52=eC|-dlQN2t~3aJOai~ z98VBW!fFBp1q)%*+zg_sBn2QLjR=5kixeF`H77P_LUzqCZDXv$6>@fXLEdgJ2&~lx zSVi?SaC}ob@iiZ=FM@;=IYq#T;A}{(f2-4o???cW19fpB%$Yk=k9?~NKz^gNgb?^( zZQdh61|vH)3{I^tXjcR*AolXG0xxQ(Q~7*;Us(ToIC`wh;nS_9P+pXda&yg%Kf_Ok zn5yMQ^Lbk$cm6)`lT{cM;P~2H>fQ~Bx0wJKejMH-a+%rYsTw$mgwrr}>)n z7H3*Gkny)Oqe-qTK5xxL==h-EltXHML4{s5TX;3wE28k z9?Ivpb_wx@o_~5{kx?FE65?UXD|KxW-)#gS(H8jyxoPMz*WB#0sQ8JiT@BT!80t#$ zO$oXk5#MD3NLd`~dpqag)zp?u}3E+_tp7ogma0U=0)3XaBj3Q*K^wCl)6F(>Ke^)O7F-rC}bhQjRJI}>r?|4(l|*ZU zc=A}N&??J-uii7>Q0&O_@e`3#kOypmu=TYZ5b|F9G@Sp;i&i-$ohI~PBLP0rd~@yY z76wWnJ}HhO-Vl+|Qeg7=)a!A(cE@Y&7i8qI5nBn6-p~xgQ8SNx+EUZyaBg$^EtB>H zsH~{~QLwff>dzVTb1?T=xz*nzAdyHMIBdV8x;t7Z!2-m$DF6n3GJ?ZGsmBr{AvOp; zdAqPv1yC~2kJNG>UtR*7D%`+?8=lr-#tI_Rm{U{r$M0b6C`PRO3U6Gla-slS(^nZmKPg%%TU_}LN&--PpgOK4BxLSETCmc@BU zk}W{R87|VW2obnO@YmJ|@i9Ril7o+=ES#!)^iW0|KP2o%B!i{XRTkR~opv9Y^G6F99Oh3{d}!?*>BaorNd{nb&blQK(>cQbK z_Ti?LZhn1flhWqoAAUU%%3=nS#6LvXfSZoA|Hj2)v4%1^jBm*EMCl?ASXRx28#{`v zPW-jEat!U}vYhW4NV6|gKd?t7p+Nn2vhAH4>P=)MI5T-%;%~K-5Ta&?JxJnz#&#o- zbkV2ZcIQR3y@hTcQXj0B^bFt(#ZvN#Pe_ayKob9FiokVA0@uyU&UE|hsZ_qJJXPR& zQ+F)->D##`6Mtbt7SyE=*R`9Ia*+qiPOTXW7eKfW!UYi~5oau`CkO~jX|sQXVh6Sx z982)mqzK%;M|Pv^Hchi9ab@zHhTu%4HDdvF~74 zG}>AVXDsNn#s$(=rewST=htV#sWoXZLFW?^5k5#2xs0lS~YwHC* S + + + + + + +