From 7a9fd071c12dc295c09b7dbb7fec8887f4c504a5 Mon Sep 17 00:00:00 2001 From: Maksim Gamarnik Date: Fri, 5 Feb 2016 20:56:56 +0200 Subject: [PATCH 01/32] Update Android dependencies, -O3 optimization, remove old ARMv5 config * Update OpenSSL (thanks @sapier, i use his patch!), Curl and SQLite3. * Remove old arm config. Almost all phones that use ARMv5 have 1 core and 256-512 RAM, it's about 2-5 FPS. * Do -O3 optimization for libs and remove -fexpensive-optimizations for Minetest (-O3 includes this! Read gcc docs). * OpenSSL fix - thanks @sapier, again. --- build/android/Makefile | 28 ++++++++-------------------- build/android/jni/Android.mk | 2 +- build/android/openssl_arch.patch | 14 ++++++++------ 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/build/android/Makefile b/build/android/Makefile index 2fa4aa9a..d27fb8cf 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -31,19 +31,6 @@ VERSION_PATCH := $(shell cat $(ROOT)/../../CMakeLists.txt | \ # Play Store actual version (16/03/15): 11 ANDROID_VERSION_CODE = 13 -################################################################################ -# toolchain config for arm old processors -################################################################################ -#TARGET_HOST = arm-linux -#TARGET_ABI = armeabi -#TARGET_LIBDIR = armeabi -#TARGET_TOOLCHAIN = arm-linux-androideabi- -#TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfp -#TARGET_ARCH = arm -#CROSS_PREFIX = arm-linux-androideabi- -#COMPILER_VERSION = 4.8 -#HAVE_LEVELDB = 1 - ################################################################################ # toolchain config for arm new processors ################################################################################ @@ -51,7 +38,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 +TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3 TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON) TARGET_ARCH = armv7 CROSS_PREFIX = arm-linux-androideabi- @@ -112,7 +99,7 @@ IRRLICHT_TIMESTAMP = $(IRRLICHT_DIR)timestamp IRRLICHT_TIMESTAMP_INT = $(ROOT)/deps/irrlicht_timestamp IRRLICHT_URL_SVN = http://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@$(IRRLICHT_REVISION) -OPENSSL_VERSION = 1.0.1p +OPENSSL_VERSION = 1.0.2g OPENSSL_BASEDIR = openssl-$(OPENSSL_VERSION) OPENSSL_DIR = $(ROOT)/deps/$(OPENSSL_BASEDIR)/ OPENSSL_LIB = $(OPENSSL_DIR)/libssl.so.1.0.0 @@ -120,7 +107,7 @@ OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp OPENSSL_TIMESTAMP_INT = $(ROOT)/deps/openssl_timestamp OPENSSL_URL = http://www.openssl.org/source/openssl-$(OPENSSL_VERSION).tar.gz -CURL_VERSION = 7.45.0 +CURL_VERSION = 7.47.0 CURL_DIR = $(ROOT)/deps/curl-$(CURL_VERSION) CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a CURL_TIMESTAMP = $(CURL_DIR)/timestamp @@ -147,8 +134,8 @@ ICONV_TIMESTAMP = $(ICONV_DIR)timestamp ICONV_TIMESTAMP_INT = $(ROOT)/deps/iconv_timestamp ICONV_URL_HTTP = http://ftp.gnu.org/pub/gnu/libiconv/libiconv-$(ICONV_VERSION).tar.gz -SQLITE3_FOLDER = sqlite-amalgamation-3090200 -SQLITE3_URL = http://www.sqlite.org/2015/$(SQLITE3_FOLDER).zip +SQLITE3_FOLDER = sqlite-amalgamation-3100200 +SQLITE3_URL = http://www.sqlite.org/2016/$(SQLITE3_FOLDER).zip -include $(PATHCFGFILE) @@ -338,7 +325,8 @@ $(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - CC=${CROSS_PREFIX}gcc ./Configure android-${TARGET_ARCH} no-idea no-seed -no-sha0 -DL_ENDIAN;\ + CC=${CROSS_PREFIX}gcc ./Configure enable-gmp -DL_ENDIAN -I${GMP_DIR} -L${GMP_DIR}/usr/lib android-${TARGET_ARCH};\ + CC=${CROSS_PREFIX}gcc ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make depend; \ CC=${CROSS_PREFIX}gcc ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make build_libs; \ touch ${OPENSSL_TIMESTAMP}; \ touch ${OPENSSL_TIMESTAMP_INT}; \ @@ -612,7 +600,7 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB) export CXX=${CROSS_PREFIX}g++; \ export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ export CPPFLAGS="$${CPPFLAGS} -I${OPENSSL_DIR}/include \ - -L${OPENSSL_DIR} ${TARGET_CFLAGS_ADDON}"; \ + ${TARGET_CFLAGS_ADDON}"; \ export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ export LDFLAGS="$${LDFLAGS} -L${OPENSSL_DIR} ${TARGET_LDFLAGS_ADDON}"; \ ./configure --host=${TARGET_HOST} --disable-shared --enable-static --with-ssl; \ diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 0528c560..e1a2a54c 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -80,7 +80,7 @@ LOCAL_CFLAGS := -D_IRR_ANDROID_PLATFORM_ \ ifndef NDEBUG LOCAL_CFLAGS += -g -D_DEBUG -O0 -fno-omit-frame-pointer else -LOCAL_CFLAGS += -fexpensive-optimizations -O3 +LOCAL_CFLAGS += -O3 endif ifdef GPROF diff --git a/build/android/openssl_arch.patch b/build/android/openssl_arch.patch index d9ebbd59..d15e2b13 100644 --- a/build/android/openssl_arch.patch +++ b/build/android/openssl_arch.patch @@ -1,11 +1,13 @@ ---- openssl-1.0.1j/Configure.orig 2014-10-15 14:53:39.000000000 +0200 -+++ openssl-1.0.1j/Configure 2015-01-03 22:41:43.505749921 +0100 -@@ -407,6 +407,8 @@ +--- openssl-1.0.2e.orig/Configure 2015-12-03 15:04:23.000000000 +0100 ++++ openssl-1.0.2e/Configure 2015-12-14 21:01:40.351265968 +0100 +@@ -464,8 +464,10 @@ + # Android: linux-* but without pointers to headers and libs. "android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", "android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - "android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +"android-arm","gcc:-march=armv4 -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", -+"android-mips32","gcc:-march=mips32 -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + "android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + "android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips32","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", #### *BSD [do see comment about ${BSDthreads} above!] - "BSD-generic32","gcc:-DTERMIOS -O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", From 08b9a710bea638a54bb9b9525fbf823a0e7bfc94 Mon Sep 17 00:00:00 2001 From: Jean-Patrick Guerrero Date: Thu, 3 Mar 2016 21:48:18 +0100 Subject: [PATCH 02/32] Update settings tab + some misc. clean-up --- builtin/mainmenu/dlg_settings_advanced.lua | 4 +- builtin/mainmenu/tab_settings.lua | 239 ++++++++++----------- 2 files changed, 114 insertions(+), 129 deletions(-) diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua index cab9f169..f5e80c25 100644 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@ -151,8 +151,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se if setting_type == "float" then local default, min, max = remaining_line:match("^" -- first float is required, the last 2 are optional - .. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "?" - .. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "?" + .. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*" + .. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*" .. "(" .. CHAR_CLASSES.FLOAT .. "*)" .."$") diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index dc2bcff1..d3226e0f 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -17,6 +17,7 @@ -------------------------------------------------------------------------------- +-- Dropdown labels : Leaves local leaves_style_labels = { fgettext("Opaque Leaves"), fgettext("Simple Leaves"), @@ -24,10 +25,22 @@ local leaves_style_labels = { } local leaves_style = { - {leaves_style_labels[1] .. "," .. leaves_style_labels[2] .. "," .. leaves_style_labels[3]}, + table.concat(leaves_style_labels, ","), {"opaque", "simple", "fancy"}, } +-- Dropdown labels : Node highlighting +local node_highlighting_style_labels = { + fgettext("Node Outlining"), + fgettext("Node Highlighting") +} + +local node_highlighting_style = { + table.concat(node_highlighting_style_labels, ","), + {"box", "halo"}, +} + +-- Dropdown labels : Textures filtering local dd_filter_labels = { fgettext("No Filter"), fgettext("Bilinear Filter"), @@ -35,10 +48,11 @@ local dd_filter_labels = { } local filters = { - {dd_filter_labels[1] .. "," .. dd_filter_labels[2] .. "," .. dd_filter_labels[3]}, + table.concat(dd_filter_labels, ","), {"", "bilinear_filter", "trilinear_filter"}, } +-- Dropdown labels : Mip-mapping local dd_mipmap_labels = { fgettext("No Mipmap"), fgettext("Mipmap"), @@ -46,20 +60,11 @@ local dd_mipmap_labels = { } local mipmap = { - {dd_mipmap_labels[1] .. "," .. dd_mipmap_labels[2] .. "," .. dd_mipmap_labels[3]}, + table.concat(dd_mipmap_labels, ","), {"", "mip_map", "anisotropic_filter"}, } -local function getLeavesStyleSettingIndex() - local style = core.setting_get("leaves_style") - if (style == leaves_style[2][3]) then - return 3 - elseif (style == leaves_style[2][2]) then - return 2 - end - return 1 -end - +-- Dropdown labels : Anti-aliasing local dd_antialiasing_labels = { fgettext("None"), fgettext("2x"), @@ -68,34 +73,54 @@ local dd_antialiasing_labels = { } local antialiasing = { - {dd_antialiasing_labels[1] .. "," .. dd_antialiasing_labels[2] .. "," .. - dd_antialiasing_labels[3] .. "," .. dd_antialiasing_labels[4]}, + table.concat(dd_antialiasing_labels, ","), {"0", "2", "4", "8"} } +-- Dropdown index getter : Leaves +local function getLeavesStyleSettingIndex() + local style = core.setting_get("leaves_style") + for idx, name in pairs(leaves_style[2]) do + if style == name then return idx end + end + return 1 +end + +-- Dropdown index getter : Node highlighting +local function getNodeHighlightingSettingIndex() + local style = core.setting_get("node_highlighting") + for idx, name in pairs(node_highlighting_style[2]) do + if style == name then return idx end + end + return 1 +end + +-- Dropdown index getter : Textures filtering local function getFilterSettingIndex() - if (core.setting_get(filters[2][3]) == "true") then + if core.setting_get(filters[2][3]) == "true" then return 3 - end - if (core.setting_get(filters[2][3]) == "false" and core.setting_get(filters[2][2]) == "true") then + elseif core.setting_get(filters[2][3]) == "false" and + core.setting_get(filters[2][2]) == "true" then return 2 end return 1 end +-- Dropdown index getter : Mip-mapping local function getMipmapSettingIndex() - if (core.setting_get(mipmap[2][3]) == "true") then + if core.setting_get(mipmap[2][3]) == "true" then return 3 - end - if (core.setting_get(mipmap[2][3]) == "false" and core.setting_get(mipmap[2][2]) == "true") then + elseif core.setting_get(mipmap[2][3]) == "false" and + core.setting_get(mipmap[2][2]) == "true" then return 2 end return 1 end +-- Dropdown index getter : Anti-aliasing local function getAntialiasingSettingIndex() local antialiasing_setting = core.setting_get("fsaa") - for i = 1, #(antialiasing[2]) do + for i = 1, #antialiasing[2] do if antialiasing_setting == antialiasing[2][i] then return i end @@ -104,7 +129,7 @@ local function getAntialiasingSettingIndex() end local function antialiasing_fname_to_name(fname) - for i = 1, #(dd_antialiasing_labels) do + for i = 1, #dd_antialiasing_labels do if fname == dd_antialiasing_labels[i] then return antialiasing[2][i] end @@ -113,14 +138,10 @@ local function antialiasing_fname_to_name(fname) end local function dlg_confirm_reset_formspec(data) - local retval = - "size[8,3]" .. + return "size[8,3]" .. "label[1,1;" .. fgettext("Are you sure to reset your singleplayer world?") .. "]" .. - "button[1,2;2.6,0.5;dlg_reset_singleplayer_confirm;" .. - fgettext("Yes") .. "]" .. - "button[4,2;2.8,0.5;dlg_reset_singleplayer_cancel;" .. - fgettext("No!!!") .. "]" - return retval + "button[1,2;2.6,0.5;dlg_reset_singleplayer_confirm;" .. fgettext("Yes") .. "]" .. + "button[4,2;2.8,0.5;dlg_reset_singleplayer_cancel;" .. fgettext("No") .. "]" end local function dlg_confirm_reset_btnhandler(this, fields, dialogdata) @@ -129,7 +150,7 @@ local function dlg_confirm_reset_btnhandler(this, fields, dialogdata) local worldlist = core.get_worlds() local found_singleplayerworld = false - for i = 1, #worldlist, 1 do + for i = 1, #worldlist do if worldlist[i].name == "singleplayerworld" then found_singleplayerworld = true gamedata.worldindex = i @@ -141,12 +162,10 @@ local function dlg_confirm_reset_btnhandler(this, fields, dialogdata) end core.create_world("singleplayerworld", 1) - worldlist = core.get_worlds() - found_singleplayerworld = false - for i = 1, #worldlist, 1 do + for i = 1, #worldlist do if worldlist[i].name == "singleplayerworld" then found_singleplayerworld = true gamedata.worldindex = i @@ -170,41 +189,12 @@ local function showconfirm_reset(tabview) new_dlg:show() end -local function gui_scale_to_scrollbar() - local current_value = tonumber(core.setting_get("gui_scaling")) - - if (current_value == nil) or current_value < 0.25 then - return 0 - end - if current_value <= 1.25 then - return ((current_value - 0.25)/ 1.0) * 700 - end - if current_value <= 6 then - return ((current_value -1.25) * 100) + 700 - end - - return 1000 -end - -local function scrollbar_to_gui_scale(value) - value = tonumber(value) - - if (value <= 700) then - return ((value / 700) * 1.0) + 0.25 - end - if (value <= 1000) then - return ((value - 700) / 100) + 1.25 - end - - return 1 -end - local function formspec(tabview, name, tabdata) local tab_string = - "box[0,0;3.5,4.3;#999999]" .. - "checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") - .. ";" .. dump(core.setting_getbool("smooth_lighting")) .. "]" .. - "checkbox[0.25,0.5;cb_particles;" .. fgettext("Enable Particles") .. ";" + "box[0,0;3.5,4.5;#999999]" .. + "checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") .. ";" + .. dump(core.setting_getbool("smooth_lighting")) .. "]" .. + "checkbox[0.25,0.5;cb_particles;" .. fgettext("Particles") .. ";" .. dump(core.setting_getbool("enable_particles")) .. "]" .. "checkbox[0.25,1;cb_3d_clouds;" .. fgettext("3D Clouds") .. ";" .. dump(core.setting_getbool("enable_3d_clouds")) .. "]" .. @@ -212,20 +202,20 @@ local function formspec(tabview, name, tabdata) .. dump(core.setting_getbool("opaque_water")) .. "]" .. "checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";" .. dump(core.setting_getbool("connected_glass")) .. "]" .. - "checkbox[0.25,2.5;cb_node_highlighting;" .. fgettext("Node Highlighting") .. ";" - .. dump(core.setting_getbool("enable_node_highlighting")) .. "]" .. - "dropdown[0.25,3.4;3.3;dd_leaves_style;" .. leaves_style[1][1] .. ";" + "dropdown[0.25,2.8;3.3;dd_node_highlighting;" .. node_highlighting_style[1] .. ";" + .. getNodeHighlightingSettingIndex() .. "]" .. + "dropdown[0.25,3.6;3.3;dd_leaves_style;" .. leaves_style[1] .. ";" .. getLeavesStyleSettingIndex() .. "]" .. "box[3.75,0;3.75,3.45;#999999]" .. "label[3.85,0.1;" .. fgettext("Texturing:") .. "]" .. - "dropdown[3.85,0.55;3.85;dd_filters;" .. filters[1][1] .. ";" + "dropdown[3.85,0.55;3.85;dd_filters;" .. filters[1] .. ";" .. getFilterSettingIndex() .. "]" .. - "dropdown[3.85,1.35;3.85;dd_mipmap;" .. mipmap[1][1] .. ";" + "dropdown[3.85,1.35;3.85;dd_mipmap;" .. mipmap[1] .. ";" .. getMipmapSettingIndex() .. "]" .. "label[3.85,2.15;" .. fgettext("Antialiasing:") .. "]" .. - "dropdown[3.85,2.6;3.85;dd_antialiasing;" .. antialiasing[1][1] .. ";" + "dropdown[3.85,2.6;3.85;dd_antialiasing;" .. antialiasing[1] .. ";" .. getAntialiasingSettingIndex() .. "]" .. - "box[7.75,0;4,4;#999999]" .. + "box[7.75,0;4,4.4;#999999]" .. "checkbox[8,0;cb_shaders;" .. fgettext("Shaders") .. ";" .. dump(core.setting_getbool("enable_shaders")) .. "]" @@ -236,37 +226,40 @@ local function formspec(tabview, name, tabdata) if core.setting_get("touchscreen_threshold") ~= nil then tab_string = tab_string .. - "label[4.3,4.1;" .. fgettext("Touchthreshold (px)") .. "]" .. - "dropdown[3.85,4.55;3.85;dd_touchthreshold;0,10,20,30,40,50;" .. - ((tonumber(core.setting_get("touchscreen_threshold"))/10)+1) .. "]" + "label[4.3,4.1;" .. fgettext("Touchthreshold (px)") .. "]" .. + "dropdown[3.85,4.55;3.85;dd_touchthreshold;0,10,20,30,40,50;" .. + ((tonumber(core.setting_get("touchscreen_threshold")) / 10) + 1) .. "]" end if core.setting_getbool("enable_shaders") then tab_string = tab_string .. - "checkbox[8,0.5;cb_bumpmapping;" .. fgettext("Bumpmapping") .. ";" - .. dump(core.setting_getbool("enable_bumpmapping")) .. "]" .. - "checkbox[8,1.0;cb_generate_normalmaps;" .. fgettext("Generate Normalmaps") .. ";" - .. dump(core.setting_getbool("generate_normalmaps")) .. "]" .. - "checkbox[8,1.5;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";" - .. dump(core.setting_getbool("enable_parallax_occlusion")) .. "]" .. - "checkbox[8,2.0;cb_waving_water;" .. fgettext("Waving Water") .. ";" - .. dump(core.setting_getbool("enable_waving_water")) .. "]" .. - "checkbox[8,2.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" - .. dump(core.setting_getbool("enable_waving_leaves")) .. "]" .. - "checkbox[8,3.0;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" - .. dump(core.setting_getbool("enable_waving_plants")) .. "]" + "checkbox[8,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";" + .. dump(core.setting_getbool("enable_bumpmapping")) .. "]" .. + "checkbox[8,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" + .. dump(core.setting_getbool("tone_mapping")) .. "]" .. + "checkbox[8,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";" + .. dump(core.setting_getbool("generate_normalmaps")) .. "]" .. + "checkbox[8,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";" + .. dump(core.setting_getbool("enable_parallax_occlusion")) .. "]" .. + "checkbox[8,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";" + .. dump(core.setting_getbool("enable_waving_water")) .. "]" .. + "checkbox[8,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" + .. dump(core.setting_getbool("enable_waving_leaves")) .. "]" .. + "checkbox[8,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" + .. dump(core.setting_getbool("enable_waving_plants")) .. "]" else tab_string = tab_string .. - "tablecolumns[color;text]" .. - "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. - "table[8.33,0.7;3.5,4;shaders;" .. - "#888888," .. fgettext("Bumpmapping") .. "," .. - "#888888," .. fgettext("Generate Normalmaps") .. "," .. - "#888888," .. fgettext("Parallax Occlusion") .. "," .. - "#888888," .. fgettext("Waving Water") .. "," .. - "#888888," .. fgettext("Waving Leaves") .. "," .. - "#888888," .. fgettext("Waving Plants") .. "," .. - ";1]" + "tablecolumns[color;text]" .. + "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. + "table[8.33,0.7;3.5,4;shaders;" .. + "#888888," .. fgettext("Bump Mapping") .. "," .. + "#888888," .. fgettext("Tone Mapping") .. "," .. + "#888888," .. fgettext("Normal Mapping") .. "," .. + "#888888," .. fgettext("Parallax Occlusion") .. "," .. + "#888888," .. fgettext("Waving Water") .. "," .. + "#888888," .. fgettext("Waving Leaves") .. "," .. + "#888888," .. fgettext("Waving Plants") .. "," .. + ";1]" end return tab_string @@ -283,7 +276,6 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) --mm_texture.update("singleplayer", current_game()) return true end - if fields["cb_smooth_lighting"] then core.setting_set("smooth_lighting", fields["cb_smooth_lighting"]) return true @@ -304,13 +296,9 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) core.setting_set("connected_glass", fields["cb_connected_glass"]) return true end - if fields["cb_node_highlighting"] then - core.setting_set("enable_node_highlighting", fields["cb_node_highlighting"]) - return true - end if fields["cb_shaders"] then - if (core.setting_get("video_driver") == "direct3d8" - or core.setting_get("video_driver") == "direct3d9") then + if (core.setting_get("video_driver") == "direct3d8" or + core.setting_get("video_driver") == "direct3d9") then core.setting_set("enable_shaders", "false") gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.") else @@ -320,9 +308,15 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) end if fields["cb_bumpmapping"] then core.setting_set("enable_bumpmapping", fields["cb_bumpmapping"]) + return true + end + if fields["cb_tonemapping"] then + core.setting_set("tone_mapping", fields["cb_tonemapping"]) + return true end if fields["cb_generate_normalmaps"] then core.setting_set("generate_normalmaps", fields["cb_generate_normalmaps"]) + return true end if fields["cb_parallax"] then core.setting_set("enable_parallax_occlusion", fields["cb_parallax"]) @@ -339,17 +333,6 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) core.setting_set("enable_waving_plants", fields["cb_waving_plants"]) return true end - - if fields["sb_gui_scaling"] then - local event = core.explode_scrollbar_event(fields["sb_gui_scaling"]) - - if event.type == "CHG" then - local tosave = string.format("%.2f",scrollbar_to_gui_scale(event.value)) - core.setting_set("gui_scaling", tosave) - return true - end - end - if fields["btn_change_keys"] then core.show_keys_menu() return true @@ -366,15 +349,17 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) --Note dropdowns have to be handled LAST! local ddhandled = false - if fields["dd_leaves_style"] == leaves_style_labels[1] then - core.setting_set("leaves_style", leaves_style[2][1]) - ddhandled = true - elseif fields["dd_leaves_style"] == leaves_style_labels[2] then - core.setting_set("leaves_style", leaves_style[2][2]) - ddhandled = true - elseif fields["dd_leaves_style"] == leaves_style_labels[3] then - core.setting_set("leaves_style", leaves_style[2][3]) - ddhandled = true + for i = 1, #leaves_style_labels do + if fields["dd_leaves_style"] == leaves_style_labels[i] then + core.setting_set("leaves_style", leaves_style[2][i]) + ddhandled = true + end + end + for i = 1, #node_highlighting_style_labels do + if fields["dd_node_highlighting"] == node_highlighting_style_labels[i] then + core.setting_set("node_highlighting", node_highlighting_style[2][i]) + ddhandled = true + end end if fields["dd_filters"] == dd_filter_labels[1] then core.setting_set("bilinear_filter", "false") @@ -408,7 +393,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) ddhandled = true end if fields["dd_touchthreshold"] then - core.setting_set("touchscreen_threshold",fields["dd_touchthreshold"]) + core.setting_set("touchscreen_threshold", fields["dd_touchthreshold"]) ddhandled = true end From e4c448b00f6a22372b80f76ff1f1a0116b74059b Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Sat, 5 Mar 2016 13:21:51 +0700 Subject: [PATCH 03/32] Add forgotten valleys mapgen in mapgen name Missing `valleys` in `settingtypes.txt` and `minetest.conf.example`. --- builtin/settingtypes.txt | 2 +- minetest.conf.example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 493cedd6..3c808194 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -800,7 +800,7 @@ liquid_update (Liquid update tick) float 1.0 # Name of map generator to be used when creating a new world. # Creating a world in the main menu will override this. -mg_name (Mapgen name) enum v6 v5,v6,v7,flat,fractal,singlenode +mg_name (Mapgen name) enum v6 v5,v6,v7,flat,valleys,fractal,singlenode # Water surface level of the world. water_level (Water level) int 1 diff --git a/minetest.conf.example b/minetest.conf.example index e4193a77..6b7347cc 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -986,7 +986,7 @@ # Name of map generator to be used when creating a new world. # Creating a world in the main menu will override this. -# type: enum values: v5, v6, v7, flat, fractal, singlenode +# type: enum values: v5, v6, v7, flat, fractal, valleys, singlenode # mg_name = v6 # Water surface level of the world. From 7a9e8367db856f3df0edb3760e6cefacfde2278e Mon Sep 17 00:00:00 2001 From: Rui914 Date: Mon, 7 Mar 2016 00:53:45 +0900 Subject: [PATCH 04/32] Faster insertion into table --- builtin/common/filterlist.lua | 2 +- builtin/common/misc_helpers.lua | 29 +++++++++++++-------------- builtin/common/serialize.lua | 13 ++++++------ builtin/fstk/buttonbar.lua | 7 ++++++- builtin/fstk/tabview.lua | 2 +- builtin/game/auth.lua | 2 +- builtin/game/chatcommands.lua | 6 +++--- builtin/game/misc.lua | 10 ++++----- builtin/game/register.lua | 8 ++++---- builtin/mainmenu/common.lua | 4 ++-- builtin/mainmenu/modmgr.lua | 6 +++--- builtin/mainmenu/tab_mods.lua | 2 +- builtin/mainmenu/tab_texturepacks.lua | 2 +- 13 files changed, 48 insertions(+), 45 deletions(-) diff --git a/builtin/common/filterlist.lua b/builtin/common/filterlist.lua index 21068113..2a62362e 100644 --- a/builtin/common/filterlist.lua +++ b/builtin/common/filterlist.lua @@ -189,7 +189,7 @@ function filterlist.process(self) for k,v in pairs(self.m_raw_list) do if self.m_filtercriteria == nil or self.m_filter_fct(v,self.m_filtercriteria) then - table.insert(self.m_processed_list,v) + self.m_processed_list[#self.m_processed_list + 1] = v end end diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 08a23043..e4653d41 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -2,7 +2,6 @@ -------------------------------------------------------------------------------- -- Localize functions to avoid table lookups (better performance). -local table_insert = table.insert local string_sub, string_find = string.sub, string.find -------------------------------------------------------------------------------- @@ -94,13 +93,13 @@ function dump2(o, name, dumped) -- the form _G["table: 0xFFFFFFF"] keyStr = string.format("_G[%q]", tostring(k)) -- Dump key table - table_insert(t, dump2(k, keyStr, dumped)) + t[#t + 1] = dump2(k, keyStr, dumped) end else keyStr = basic_dump(k) end local vname = string.format("%s[%s]", name, keyStr) - table_insert(t, dump2(v, vname, dumped)) + t[#t + 1] = dump2(v, vname, dumped) end return string.format("%s = {}\n%s", name, table.concat(t)) end @@ -135,7 +134,7 @@ function dump(o, indent, nested, level) local t = {} local dumped_indexes = {} for i, v in ipairs(o) do - table_insert(t, dump(v, indent, nested, level + 1)) + t[#t + 1] = dump(v, indent, nested, level + 1) dumped_indexes[i] = true end for k, v in pairs(o) do @@ -144,7 +143,7 @@ function dump(o, indent, nested, level) k = "["..dump(k, indent, nested, level + 1).."]" end v = dump(v, indent, nested, level + 1) - table_insert(t, k.." = "..v) + t[#t + 1] = k.." = "..v end end nested[o] = nil @@ -177,7 +176,7 @@ function string.split(str, delim, include_empty, max_splits, sep_is_pattern) local s = string_sub(str, pos, np - 1) if include_empty or (s ~= "") then max_splits = max_splits - 1 - table_insert(items, s) + items[#items + 1] = s end pos = npe + 1 until (max_splits == 0) or (pos > (len + 1)) @@ -186,8 +185,8 @@ end -------------------------------------------------------------------------------- function table.indexof(list, val) - for i = 1, #list do - if list[i] == val then + for i, v in ipairs(list) do + if v == val then return i end end @@ -324,7 +323,7 @@ function core.splittext(text,charlimit) local last_line = "" while start ~= nil do if string.len(last_line) + (stop-start) > charlimit then - table_insert(retval, last_line) + retval[#retval + 1] = last_line last_line = "" end @@ -335,7 +334,7 @@ function core.splittext(text,charlimit) last_line = last_line .. string_sub(text, current_idx, stop - 1) if gotnewline then - table_insert(retval, last_line) + retval[#retval + 1] = last_line last_line = "" gotnewline = false end @@ -353,11 +352,11 @@ function core.splittext(text,charlimit) --add last part of text if string.len(last_line) + (string.len(text) - current_idx) > charlimit then - table_insert(retval, last_line) - table_insert(retval, string_sub(text, current_idx)) + retval[#retval + 1] = last_line + retval[#retval + 1] = string_sub(text, current_idx) else last_line = last_line .. " " .. string_sub(text, current_idx) - table_insert(retval, last_line) + retval[#retval + 1] = last_line end return retval @@ -430,14 +429,14 @@ if INIT == "game" then 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}) 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 diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua index 90b8b2ad..b2165648 100644 --- a/builtin/common/serialize.lua +++ b/builtin/common/serialize.lua @@ -104,7 +104,7 @@ function core.serialize(x) local i = local_index local_index = local_index + 1 var = "_["..i.."]" - table.insert(local_defs, var.." = "..val) + local_defs[#local_defs + 1] = var.." = "..val dumped[x] = var return var end @@ -135,16 +135,15 @@ function core.serialize(x) local np = nest_points[x] for i, v in ipairs(x) do if not np or not np[i] then - table.insert(vals, dump_or_ref_val(v)) + vals[#vals + 1] = dump_or_ref_val(v) end idx_dumped[i] = true end for k, v in pairs(x) do if (not np or not np[k]) and not idx_dumped[k] then - table.insert(vals, - "["..dump_or_ref_val(k).."] = " - ..dump_or_ref_val(v)) + vals[#vals + 1] = "["..dump_or_ref_val(k).."] = " + ..dump_or_ref_val(v) end end return "{"..table.concat(vals, ", ").."}" @@ -156,9 +155,9 @@ function core.serialize(x) local function dump_nest_points() for parent, vals in pairs(nest_points) do for k, v in pairs(vals) do - table.insert(local_defs, dump_or_ref_val(parent) + local_defs[#local_defs + 1] = dump_or_ref_val(parent) .."["..dump_or_ref_val(k).."] = " - ..dump_or_ref_val(v)) + ..dump_or_ref_val(v) end end end diff --git a/builtin/fstk/buttonbar.lua b/builtin/fstk/buttonbar.lua index 1ad17562..46558832 100644 --- a/builtin/fstk/buttonbar.lua +++ b/builtin/fstk/buttonbar.lua @@ -145,7 +145,12 @@ local buttonbar_metatable = { if image == nil then image = "" end if tooltip == nil then tooltip = "" end - table.insert(self.buttons,{ name=name, caption=caption, image=image, tooltip=tooltip}) + self.buttons[#self.buttons + 1] = { + name = name, + caption = caption, + image = image, + tooltip = tooltip + } if self.orientation == "horizontal" then if ( (self.btn_size * #self.buttons) + (self.btn_size * 0.05 *2) > self.size.x ) then diff --git a/builtin/fstk/tabview.lua b/builtin/fstk/tabview.lua index c65f6dd7..72551afd 100644 --- a/builtin/fstk/tabview.lua +++ b/builtin/fstk/tabview.lua @@ -46,7 +46,7 @@ local function add_tab(self,tab) tabdata = {}, } - table.insert(self.tablist,newtab) + self.tablist[#self.tablist + 1] = newtab if self.last_tab_index == #self.tablist then self.current_tab = tab.name diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index 423eb313..deb811b1 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -20,7 +20,7 @@ function core.privs_to_string(privs, delim) local list = {} for priv, bool in pairs(privs) do if bool then - table.insert(list, priv) + list[#list + 1] = priv end end return table.concat(list, delim) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 6b4ca0d1..2c7a0b53 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -116,7 +116,7 @@ core.register_chatcommand("help", { local cmds = {} for cmd, def in pairs(core.chatcommands) do if core.check_player_privs(name, def.privs) then - table.insert(cmds, cmd) + cmds[#cmds + 1] = cmd end end table.sort(cmds) @@ -127,7 +127,7 @@ core.register_chatcommand("help", { local cmds = {} for cmd, def in pairs(core.chatcommands) do if core.check_player_privs(name, def.privs) then - table.insert(cmds, format_help_line(cmd, def)) + cmds[#cmds + 1] = format_help_line(cmd, def) end end table.sort(cmds) @@ -135,7 +135,7 @@ core.register_chatcommand("help", { elseif param == "privs" then local privs = {} for priv, def in pairs(core.registered_privileges) do - table.insert(privs, priv .. ": " .. def.description) + privs[#privs + 1] = priv .. ": " .. def.description end table.sort(privs) return true, "Available privileges:\n"..table.concat(privs, "\n") diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index d0e67bd8..4f58710d 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -40,12 +40,12 @@ end) function core.after(after, func, ...) assert(tonumber(time) and type(func) == "function", "Invalid core.after invocation") - table.insert(jobs, { + jobs[#jobs + 1] = { func = func, expire = time + after, arg = {...}, mod_origin = core.get_last_run_mod() - }) + } end function core.check_player_privs(player_or_name, ...) @@ -63,14 +63,14 @@ function core.check_player_privs(player_or_name, ...) -- We were provided with a table like { privA = true, privB = true }. for priv, value in pairs(requested_privs[1]) do if value and not player_privs[priv] then - table.insert(missing_privileges, priv) + missing_privileges[#missing_privileges + 1] = priv end end else -- Only a list, we can process it directly. for key, priv in pairs(requested_privs) do if not player_privs[priv] then - table.insert(missing_privileges, priv) + missing_privileges[#missing_privileges + 1] = priv end end end @@ -96,7 +96,7 @@ function core.get_connected_players() local temp_table = {} for index, value in pairs(player_list) do if value:is_player_connected() then - table.insert(temp_table, value) + temp_table[#temp_table + 1] = value end end return temp_table diff --git a/builtin/game/register.lua b/builtin/game/register.lua index ba5f69d6..398daf05 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -75,7 +75,7 @@ end function core.register_abm(spec) -- Add to core.registered_abms - core.registered_abms[#core.registered_abms+1] = spec + core.registered_abms[#core.registered_abms + 1] = spec spec.mod_origin = core.get_current_modname() or "??" end @@ -391,7 +391,7 @@ end local function make_registration() local t = {} local registerfunc = function(func) - table.insert(t, func) + t[#t + 1] = func core.callback_origins[func] = { mod = core.get_current_modname() or "??", name = debug.getinfo(1, "n").name or "??" @@ -467,9 +467,9 @@ end function core.register_on_player_hpchange(func, modifier) if modifier then - table.insert(core.registered_on_player_hpchanges.modifiers, func) + core.registered_on_player_hpchanges.modifiers[#core.registered_on_player_hpchanges.modifiers + 1] = func else - table.insert(core.registered_on_player_hpchanges.loggers, func) + core.registered_on_player_hpchanges.loggers[#core.registered_on_player_hpchanges.loggers + 1] = func end core.callback_origins[func] = { mod = core.get_current_modname() or "??", diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index f4020aaa..f40c787a 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -67,13 +67,13 @@ function order_favorite_list(list) 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) + res[#res + 1] = 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) + res[#res + 1] = fav end end return res diff --git a/builtin/mainmenu/modmgr.lua b/builtin/mainmenu/modmgr.lua index 41e747b3..f996df7b 100644 --- a/builtin/mainmenu/modmgr.lua +++ b/builtin/mainmenu/modmgr.lua @@ -23,7 +23,7 @@ function get_mods(path,retval,modpack) if name:sub(1, 1) ~= "." then local prefix = path .. DIR_DELIM .. name .. DIR_DELIM local toadd = {} - table.insert(retval, toadd) + retval[#retval + 1] = toadd local mod_conf = Settings(prefix .. "mod.conf"):to_table() if mod_conf.name then @@ -412,7 +412,7 @@ function modmgr.preparemodlist(data) for i=1,#global_mods,1 do global_mods[i].typ = "global_mod" - table.insert(retval,global_mods[i]) + retval[#retval + 1] = global_mods[i] end --read game mods @@ -421,7 +421,7 @@ function modmgr.preparemodlist(data) for i=1,#game_mods,1 do game_mods[i].typ = "game_mod" - table.insert(retval,game_mods[i]) + retval[#retval + 1] = game_mods[i] end if data.worldpath == nil then diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua index 2ddc9b07..af758f8d 100644 --- a/builtin/mainmenu/tab_mods.lua +++ b/builtin/mainmenu/tab_mods.lua @@ -78,7 +78,7 @@ local function get_formspec(tabview, name, tabdata) descriptionfile:close() else descriptionlines = {} - table.insert(descriptionlines,fgettext("No mod description available")) + descriptionlines[#descriptionlines + 1] = fgettext("No mod description available") end retval = retval .. diff --git a/builtin/mainmenu/tab_texturepacks.lua b/builtin/mainmenu/tab_texturepacks.lua index d8b9ba35..d669d568 100644 --- a/builtin/mainmenu/tab_texturepacks.lua +++ b/builtin/mainmenu/tab_texturepacks.lua @@ -20,7 +20,7 @@ local function filter_texture_pack_list(list) local retval = {} for _, item in ipairs(list) do if item ~= "base" then - table.insert(retval, item) + retval[#retval + 1] = item end end From b5bb7f8105523e14bff3ae46e4e174d910c95369 Mon Sep 17 00:00:00 2001 From: Jean-Patrick Guerrero Date: Sat, 5 Mar 2016 11:48:02 +0100 Subject: [PATCH 05/32] Settings Tab: Regroup dropdown datas in tables --- builtin/mainmenu/tab_settings.lua | 242 ++++++++++++++---------------- 1 file changed, 113 insertions(+), 129 deletions(-) diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index d3226e0f..6285968e 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -17,121 +17,105 @@ -------------------------------------------------------------------------------- --- Dropdown labels : Leaves -local leaves_style_labels = { - fgettext("Opaque Leaves"), - fgettext("Simple Leaves"), - fgettext("Fancy Leaves") +local labels = { + leaves = { + fgettext("Opaque Leaves"), + fgettext("Simple Leaves"), + fgettext("Fancy Leaves") + }, + node_highlighting = { + fgettext("Node Outlining"), + fgettext("Node Highlighting") + }, + filters = { + fgettext("No Filter"), + fgettext("Bilinear Filter"), + fgettext("Trilinear Filter") + }, + mipmap = { + fgettext("No Mipmap"), + fgettext("Mipmap"), + fgettext("Mipmap + Aniso. Filter") + }, + antialiasing = { + fgettext("None"), + fgettext("2x"), + fgettext("4x"), + fgettext("8x") + } } -local leaves_style = { - table.concat(leaves_style_labels, ","), - {"opaque", "simple", "fancy"}, +local dd_options = { + leaves = { + table.concat(labels.leaves, ","), + {"opaque", "simple", "fancy"} + }, + node_highlighting = { + table.concat(labels.node_highlighting, ","), + {"box", "halo"} + }, + filters = { + table.concat(labels.filters, ","), + {"", "bilinear_filter", "trilinear_filter"} + }, + mipmap = { + table.concat(labels.mipmap, ","), + {"", "mip_map", "anisotropic_filter"} + }, + antialiasing = { + table.concat(labels.antialiasing, ","), + {"0", "2", "4", "8"} + } } --- Dropdown labels : Node highlighting -local node_highlighting_style_labels = { - fgettext("Node Outlining"), - fgettext("Node Highlighting") -} - -local node_highlighting_style = { - table.concat(node_highlighting_style_labels, ","), - {"box", "halo"}, -} - --- Dropdown labels : Textures filtering -local dd_filter_labels = { - fgettext("No Filter"), - fgettext("Bilinear Filter"), - fgettext("Trilinear Filter") -} - -local filters = { - table.concat(dd_filter_labels, ","), - {"", "bilinear_filter", "trilinear_filter"}, -} - --- Dropdown labels : Mip-mapping -local dd_mipmap_labels = { - fgettext("No Mipmap"), - fgettext("Mipmap"), - fgettext("Mipmap + Aniso. Filter") -} - -local mipmap = { - table.concat(dd_mipmap_labels, ","), - {"", "mip_map", "anisotropic_filter"}, -} - --- Dropdown labels : Anti-aliasing -local dd_antialiasing_labels = { - fgettext("None"), - fgettext("2x"), - fgettext("4x"), - fgettext("8x"), -} - -local antialiasing = { - table.concat(dd_antialiasing_labels, ","), - {"0", "2", "4", "8"} -} - --- Dropdown index getter : Leaves -local function getLeavesStyleSettingIndex() - local style = core.setting_get("leaves_style") - for idx, name in pairs(leaves_style[2]) do - if style == name then return idx end - end - return 1 -end - --- Dropdown index getter : Node highlighting -local function getNodeHighlightingSettingIndex() - local style = core.setting_get("node_highlighting") - for idx, name in pairs(node_highlighting_style[2]) do - if style == name then return idx end - end - return 1 -end - --- Dropdown index getter : Textures filtering -local function getFilterSettingIndex() - if core.setting_get(filters[2][3]) == "true" then - return 3 - elseif core.setting_get(filters[2][3]) == "false" and - core.setting_get(filters[2][2]) == "true" then - return 2 - end - return 1 -end - --- Dropdown index getter : Mip-mapping -local function getMipmapSettingIndex() - if core.setting_get(mipmap[2][3]) == "true" then - return 3 - elseif core.setting_get(mipmap[2][3]) == "false" and - core.setting_get(mipmap[2][2]) == "true" then - return 2 - end - return 1 -end - --- Dropdown index getter : Anti-aliasing -local function getAntialiasingSettingIndex() - local antialiasing_setting = core.setting_get("fsaa") - for i = 1, #antialiasing[2] do - if antialiasing_setting == antialiasing[2][i] then - return i +local getSettingIndex = { + Leaves = function() + local style = core.setting_get("leaves_style") + for idx, name in pairs(dd_options.leaves[2]) do + if style == name then return idx end end + return 1 + end, + NodeHighlighting = function() + local style = core.setting_get("node_highlighting") + for idx, name in pairs(dd_options.node_highlighting[2]) do + if style == name then return idx end + end + return 1 + end, + Filter = function() + if core.setting_get(dd_options.filters[2][3]) == "true" then + return 3 + elseif core.setting_get(dd_options.filters[2][3]) == "false" and + core.setting_get(dd_options.filters[2][2]) == "true" then + return 2 + end + return 1 + end, + Mipmap = function() + if core.setting_get(dd_options.mipmap[2][3]) == "true" then + return 3 + elseif core.setting_get(dd_options.mipmap[2][3]) == "false" and + core.setting_get(dd_options.mipmap[2][2]) == "true" then + return 2 + end + return 1 + end, + Antialiasing = function() + local antialiasing_setting = core.setting_get("fsaa") + for i = 1, #dd_options.antialiasing[2] do + if antialiasing_setting == dd_options.antialiasing[2][i] then + return i + end + end + return 1 end - return 1 -end +} local function antialiasing_fname_to_name(fname) - for i = 1, #dd_antialiasing_labels do - if fname == dd_antialiasing_labels[i] then - return antialiasing[2][i] + for i = 1, #labels.antialiasing do + if fname == labels.antialiasing[i] then + return dd_options.antialiasing[2][i] end end return 0 @@ -202,19 +186,19 @@ local function formspec(tabview, name, tabdata) .. dump(core.setting_getbool("opaque_water")) .. "]" .. "checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";" .. dump(core.setting_getbool("connected_glass")) .. "]" .. - "dropdown[0.25,2.8;3.3;dd_node_highlighting;" .. node_highlighting_style[1] .. ";" - .. getNodeHighlightingSettingIndex() .. "]" .. - "dropdown[0.25,3.6;3.3;dd_leaves_style;" .. leaves_style[1] .. ";" - .. getLeavesStyleSettingIndex() .. "]" .. + "dropdown[0.25,2.8;3.3;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";" + .. getSettingIndex.NodeHighlighting() .. "]" .. + "dropdown[0.25,3.6;3.3;dd_leaves_style;" .. dd_options.leaves[1] .. ";" + .. getSettingIndex.Leaves() .. "]" .. "box[3.75,0;3.75,3.45;#999999]" .. "label[3.85,0.1;" .. fgettext("Texturing:") .. "]" .. - "dropdown[3.85,0.55;3.85;dd_filters;" .. filters[1] .. ";" - .. getFilterSettingIndex() .. "]" .. - "dropdown[3.85,1.35;3.85;dd_mipmap;" .. mipmap[1] .. ";" - .. getMipmapSettingIndex() .. "]" .. + "dropdown[3.85,0.55;3.85;dd_filters;" .. dd_options.filters[1] .. ";" + .. getSettingIndex.Filter() .. "]" .. + "dropdown[3.85,1.35;3.85;dd_mipmap;" .. dd_options.mipmap[1] .. ";" + .. getSettingIndex.Mipmap() .. "]" .. "label[3.85,2.15;" .. fgettext("Antialiasing:") .. "]" .. - "dropdown[3.85,2.6;3.85;dd_antialiasing;" .. antialiasing[1] .. ";" - .. getAntialiasingSettingIndex() .. "]" .. + "dropdown[3.85,2.6;3.85;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";" + .. getSettingIndex.Antialiasing() .. "]" .. "box[7.75,0;4,4.4;#999999]" .. "checkbox[8,0;cb_shaders;" .. fgettext("Shaders") .. ";" .. dump(core.setting_getbool("enable_shaders")) .. "]" @@ -349,40 +333,40 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) --Note dropdowns have to be handled LAST! local ddhandled = false - for i = 1, #leaves_style_labels do - if fields["dd_leaves_style"] == leaves_style_labels[i] then - core.setting_set("leaves_style", leaves_style[2][i]) + for i = 1, #labels.leaves do + if fields["dd_leaves_style"] == labels.leaves[i] then + core.setting_set("leaves_style", dd_options.leaves[2][i]) ddhandled = true end end - for i = 1, #node_highlighting_style_labels do - if fields["dd_node_highlighting"] == node_highlighting_style_labels[i] then - core.setting_set("node_highlighting", node_highlighting_style[2][i]) + for i = 1, #labels.node_highlighting do + if fields["dd_node_highlighting"] == labels.node_highlighting[i] then + core.setting_set("node_highlighting", dd_options.node_highlighting[2][i]) ddhandled = true end end - if fields["dd_filters"] == dd_filter_labels[1] then + if fields["dd_filters"] == labels.filters[1] then core.setting_set("bilinear_filter", "false") core.setting_set("trilinear_filter", "false") ddhandled = true - elseif fields["dd_filters"] == dd_filter_labels[2] then + elseif fields["dd_filters"] == labels.filters[2] then core.setting_set("bilinear_filter", "true") core.setting_set("trilinear_filter", "false") ddhandled = true - elseif fields["dd_filters"] == dd_filter_labels[3] then + elseif fields["dd_filters"] == labels.filters[3] then core.setting_set("bilinear_filter", "false") core.setting_set("trilinear_filter", "true") ddhandled = true end - if fields["dd_mipmap"] == dd_mipmap_labels[1] then + if fields["dd_mipmap"] == labels.mipmap[1] then core.setting_set("mip_map", "false") core.setting_set("anisotropic_filter", "false") ddhandled = true - elseif fields["dd_mipmap"] == dd_mipmap_labels[2] then + elseif fields["dd_mipmap"] == labels.mipmap[2] then core.setting_set("mip_map", "true") core.setting_set("anisotropic_filter", "false") ddhandled = true - elseif fields["dd_mipmap"] == dd_mipmap_labels[3] then + elseif fields["dd_mipmap"] == labels.mipmap[3] then core.setting_set("mip_map", "true") core.setting_set("anisotropic_filter", "true") ddhandled = true From a4ec25f8b7006c475dfc93700fe22fcb2edfcd20 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Sun, 6 Mar 2016 14:41:26 -0500 Subject: [PATCH 06/32] Use LuaErrors in security check macros Throwing a LuaError calls destructors as it propagates up the stack, wheres lua_error just executes a longjmp. --- src/script/cpp_api/s_security.h | 5 ++--- src/script/lua_api/l_util.cpp | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/script/cpp_api/s_security.h b/src/script/cpp_api/s_security.h index 4a4389cf..97bc5c06 100644 --- a/src/script/cpp_api/s_security.h +++ b/src/script/cpp_api/s_security.h @@ -25,9 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CHECK_SECURE_PATH(L, path) \ if (!ScriptApiSecurity::checkPath(L, path)) { \ - lua_pushstring(L, (std::string("Attempt to access external file ") + \ - path + " with mod security on.").c_str()); \ - lua_error(L); \ + throw LuaError(std::string("Attempt to access external file ") + \ + path + " with mod security on."); \ } #define CHECK_SECURE_PATH_OPTIONAL(L, path) \ if (ScriptApiSecurity::isSecure(L)) { \ diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index df46925d..98f70d91 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -77,8 +77,7 @@ int ModApiUtil::l_get_us_time(lua_State *L) #define CHECK_SECURE_SETTING(L, name) \ if (ScriptApiSecurity::isSecure(L) && \ name.compare(0, 7, "secure.") == 0) { \ - lua_pushliteral(L, "Attempt to set secure setting."); \ - lua_error(L); \ + throw LuaError("Attempt to set secure setting."); \ } // setting_set(name, value) From fd7cbba94b1c985f73dcacdc1be6362b474da215 Mon Sep 17 00:00:00 2001 From: est31 Date: Tue, 9 Feb 2016 07:08:31 +0100 Subject: [PATCH 07/32] Add minetest.register_lbm() to run code on block load only --- builtin/game/register.lua | 10 +- doc/lua_api.txt | 16 +++ src/environment.cpp | 250 +++++++++++++++++++++++++++++++++++ src/environment.h | 80 ++++++++++- src/nodedef.cpp | 12 +- src/nodedef.h | 5 +- src/script/cpp_api/s_base.h | 1 + src/script/cpp_api/s_env.cpp | 55 +++++++- src/script/lua_api/l_env.cpp | 40 ++++++ src/script/lua_api/l_env.h | 18 +++ src/server.cpp | 7 +- src/util/string.h | 24 ++-- 12 files changed, 493 insertions(+), 25 deletions(-) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 398daf05..f330491a 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -11,10 +11,11 @@ local register_alias_raw = core.register_alias_raw core.register_alias_raw = nil -- --- Item / entity / ABM registration functions +-- Item / entity / ABM / LBM registration functions -- core.registered_abms = {} +core.registered_lbms = {} core.registered_entities = {} core.registered_items = {} core.registered_nodes = {} @@ -79,6 +80,13 @@ function core.register_abm(spec) spec.mod_origin = core.get_current_modname() or "??" end +function core.register_lbm(spec) + -- Add to core.registered_lbms + check_modname_prefix(spec.name) + core.registered_lbms[#core.registered_lbms + 1] = spec + spec.mod_origin = core.get_current_modname() or "??" +end + function core.register_entity(name, prototype) -- Check name if name == nil then diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f8934b48..0520096e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1793,6 +1793,7 @@ Call these functions only at load time! * `minetest.register_entity(name, prototype table)` * `minetest.register_abm(abm definition)` +* `minetest.register_lbm(lbm definition)` * `minetest.register_node(name, node definition)` * `minetest.register_tool(name, item definition)` * `minetest.register_craftitem(name, item definition)` @@ -3305,6 +3306,21 @@ Definition tables action = func(pos, node, active_object_count, active_object_count_wider), } +### LBM (LoadingBlockModifier) definition (`register_lbm`) + + { + name = "modname:replace_legacy_door", + nodenames = {"default:lava_source"}, + -- ^ List of node names to trigger the LBM on. + -- Also non-registered nodes will work. + -- Groups (as of group:groupname) will work as well. + run_at_every_load = false, + -- ^ Whether to run the LBM's action every time a block gets loaded, + -- and not just for blocks that were saved last time before LBMs were + -- introduced to the world. + action = func(pos, node), + } + ### Item definition (`register_node`, `register_craftitem`, `register_tool`) { diff --git a/src/environment.cpp b/src/environment.cpp index 081079a7..6da376a1 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -48,6 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" +#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" + Environment::Environment(): m_time_of_day_speed(0), m_time_of_day(9000), @@ -270,6 +272,223 @@ ABMWithState::ABMWithState(ActiveBlockModifier *abm_): timer = myrand_range(minval, maxval); } +/* + LBMManager +*/ + +void LBMContentMapping::deleteContents() +{ + for (std::vector::iterator it = lbm_list.begin(); + it != lbm_list.end(); ++it) { + delete *it; + } +} + +void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef) +{ + // Add the lbm_def to the LBMContentMapping. + // Unknown names get added to the global NameIdMapping. + INodeDefManager *nodedef = gamedef->ndef(); + + lbm_list.push_back(lbm_def); + + for (std::set::const_iterator it = lbm_def->trigger_contents.begin(); + it != lbm_def->trigger_contents.end(); ++it) { + std::set c_ids; + bool found = nodedef->getIds(*it, c_ids); + if (!found) { + content_t c_id = gamedef->allocateUnknownNodeId(*it); + if (c_id == CONTENT_IGNORE) { + // Seems it can't be allocated. + warningstream << "Could not internalize node name \"" << *it + << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl; + continue; + } + c_ids.insert(c_id); + } + + for (std::set::const_iterator iit = + c_ids.begin(); iit != c_ids.end(); ++iit) { + content_t c_id = *iit; + map[c_id].push_back(lbm_def); + } + } +} + +const std::vector * + LBMContentMapping::lookup(content_t c) const +{ + container_map::const_iterator it = map.find(c); + if (it == map.end()) + return NULL; + // This first dereferences the iterator, returning + // a std::vector + // reference, then we convert it to a pointer. + return &(it->second); +} + +LBMManager::~LBMManager() +{ + for (std::map::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + delete it->second; + } + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + (it->second).deleteContents(); + } +} + +void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def) +{ + // Precondition, in query mode the map isn't used anymore + FATAL_ERROR_IF(m_query_mode == true, + "attempted to modify LBMManager in query mode"); + + if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) { + throw ModError("Error adding LBM \"" + lbm_def->name + + "\": Does not follow naming conventions: " + "Only chararacters [a-z0-9_:] are allowed."); + } + + m_lbm_defs[lbm_def->name] = lbm_def; +} + +void LBMManager::loadIntroductionTimes(const std::string ×, + IGameDef *gamedef, u32 now) +{ + m_query_mode = true; + + // name -> time map. + // Storing it in a map first instead of + // handling the stuff directly in the loop + // removes all duplicate entries. + // TODO make this std::unordered_map + std::map introduction_times; + + /* + The introduction times string consists of name~time entries, + with each entry terminated by a semicolon. The time is decimal. + */ + + size_t idx = 0; + size_t idx_new; + while ((idx_new = times.find(";", idx)) != std::string::npos) { + std::string entry = times.substr(idx, idx_new - idx); + std::vector components = str_split(entry, '~'); + if (components.size() != 2) + throw SerializationError("Introduction times entry \"" + + entry + "\" requires exactly one '~'!"); + const std::string &name = components[0]; + u32 time = from_string(components[1]); + introduction_times[name] = time; + idx = idx_new + 1; + } + + // Put stuff from introduction_times into m_lbm_lookup + for (std::map::const_iterator it = introduction_times.begin(); + it != introduction_times.end(); ++it) { + const std::string &name = it->first; + u32 time = it->second; + + std::map::iterator def_it = + m_lbm_defs.find(name); + if (def_it == m_lbm_defs.end()) { + // This seems to be an LBM entry for + // an LBM we haven't loaded. Discard it. + continue; + } + LoadingBlockModifierDef *lbm_def = def_it->second; + if (lbm_def->run_at_every_load) { + // This seems to be an LBM entry for + // an LBM that runs at every load. + // Don't add it just yet. + continue; + } + + m_lbm_lookup[time].addLBM(lbm_def, gamedef); + + // Erase the entry so that we know later + // what elements didn't get put into m_lbm_lookup + m_lbm_defs.erase(name); + } + + // Now also add the elements from m_lbm_defs to m_lbm_lookup + // that weren't added in the previous step. + // They are introduced first time to this world, + // or are run at every load (introducement time hardcoded to U32_MAX). + + LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now]; + LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX]; + + for (std::map::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + if (it->second->run_at_every_load) { + lbms_running_always.addLBM(it->second, gamedef); + } else { + lbms_we_introduce_now.addLBM(it->second, gamedef); + } + } + + // Clear the list, so that we don't delete remaining elements + // twice in the destructor + m_lbm_defs.clear(); +} + +std::string LBMManager::createIntroductionTimesString() +{ + // Precondition, we must be in query mode + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + + std::ostringstream oss; + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + u32 time = it->first; + std::vector &lbm_list = it->second.lbm_list; + for (std::vector::iterator iit = lbm_list.begin(); + iit != lbm_list.end(); ++iit) { + // Don't add if the LBM runs at every load, + // then introducement time is hardcoded + // and doesn't need to be stored + if ((*iit)->run_at_every_load) + continue; + oss << (*iit)->name << "~" << time << ";"; + } + } + return oss.str(); +} + +void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) +{ + // Precondition, we need m_lbm_lookup to be initialized + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + v3s16 pos_of_block = block->getPosRelative(); + v3s16 pos; + 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); + if (!lbm_list) + continue; + for (std::vector::const_iterator iit = + lbm_list->begin(); iit != lbm_list->end(); ++iit) { + (*iit)->trigger(env, pos + pos_of_block, n); + } + } + } +} + /* ActiveBlockList */ @@ -505,6 +724,9 @@ void ServerEnvironment::saveMeta() args.setU64("game_time", m_game_time); args.setU64("time_of_day", getTimeOfDay()); args.setU64("last_clear_objects_time", m_last_clear_objects_time); + args.setU64("lbm_introduction_times_version", 1); + args.set("lbm_introduction_times", + m_lbm_mgr.createIntroductionTimesString()); args.writeLines(ss); ss<<"EnvArgsEnd\n"; @@ -555,6 +777,26 @@ void ServerEnvironment::loadMeta() // If missing, do as if clearObjects was never called m_last_clear_objects_time = 0; } + + std::string lbm_introduction_times = ""; + try { + u64 ver = args.getU64("lbm_introduction_times_version"); + if (ver == 1) { + lbm_introduction_times = args.get("lbm_introduction_times"); + } else { + infostream << "ServerEnvironment::loadMeta(): Non-supported" + << " introduction time version " << ver << std::endl; + } + } catch (SettingNotFoundException &e) { + // No problem, this is expected. Just continue with an empty string + } + m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time); + +} + +void ServerEnvironment::loadDefaultMeta() +{ + m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time); } struct ActiveABM @@ -770,6 +1012,9 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) // Activate stored objects activateObjects(block, dtime_s); + /* Handle LoadingBlockModifiers */ + m_lbm_mgr.applyLBMs(this, block, stamp); + // Run node timers std::map elapsed_timers = block->m_node_timers.step((float)dtime_s); @@ -795,6 +1040,11 @@ void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm) m_abms.push_back(ABMWithState(abm)); } +void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) +{ + m_lbm_mgr.addLBMDef(lbm); +} + bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) { INodeDefManager *ndef = m_gamedef->ndef(); diff --git a/src/environment.h b/src/environment.h index e7b818dc..448ed70e 100644 --- a/src/environment.h +++ b/src/environment.h @@ -139,7 +139,7 @@ private: }; /* - Active block modifier interface. + {Active, Loading} block modifier interface. These are fed into ServerEnvironment at initialization time; ServerEnvironment handles deleting them. @@ -177,6 +177,77 @@ struct ABMWithState ABMWithState(ActiveBlockModifier *abm_); }; +struct LoadingBlockModifierDef +{ + // Set of contents to trigger on + std::set trigger_contents; + std::string name; + bool run_at_every_load; + + virtual ~LoadingBlockModifierDef() {} + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; +}; + +struct LBMContentMapping +{ + typedef std::map > container_map; + container_map map; + + std::vector lbm_list; + + // Needs to be separate method (not inside destructor), + // because the LBMContentMapping may be copied and destructed + // many times during operation in the lbm_lookup_map. + void deleteContents(); + void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef); + const std::vector *lookup(content_t c) const; +}; + +class LBMManager +{ +public: + LBMManager(): + m_query_mode(false) + {} + + ~LBMManager(); + + // Don't call this after loadIntroductionTimes() ran. + void addLBMDef(LoadingBlockModifierDef *lbm_def); + + void loadIntroductionTimes(const std::string ×, + IGameDef *gamedef, u32 now); + + // Don't call this before loadIntroductionTimes() ran. + std::string createIntroductionTimesString(); + + // Don't call this before loadIntroductionTimes() ran. + void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp); + + // Warning: do not make this std::unordered_map, order is relevant here + typedef std::map lbm_lookup_map; + +private: + // Once we set this to true, we can only query, + // not modify + bool m_query_mode; + + // For m_query_mode == false: + // The key of the map is the LBM def's name. + // TODO make this std::unordered_map + std::map m_lbm_defs; + + // For m_query_mode == true: + // The key of the map is the LBM def's first introduction time. + lbm_lookup_map m_lbm_lookup; + + // Returns an iterator to the LBMs that were introduced + // after the given time. This is guaranteed to return + // valid values for everything + lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time) + { return m_lbm_lookup.lower_bound(time); } +}; + /* List of active blocks, used by ServerEnvironment */ @@ -254,6 +325,9 @@ public: */ void saveMeta(); void loadMeta(); + // to be called instead of loadMeta if + // env_meta.txt doesn't exist (e.g. new world) + void loadDefaultMeta(); /* External ActiveObject interface @@ -312,11 +386,12 @@ public: void activateBlock(MapBlock *block, u32 additional_dtime=0); /* - ActiveBlockModifiers + {Active,Loading}BlockModifiers ------------------------------------------- */ void addActiveBlockModifier(ActiveBlockModifier *abm); + void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm); /* Other stuff @@ -427,6 +502,7 @@ private: u32 m_last_clear_objects_time; // Active block modifiers std::vector m_abms; + LBMManager m_lbm_mgr; // An interval for generally sending object positions and stuff float m_recommended_send_interval; // Estimate for general maximum lag as determined by server. diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 1dd5aa53..ba9b4abf 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -419,7 +419,7 @@ public: inline virtual const ContentFeatures& get(const MapNode &n) const; virtual bool getId(const std::string &name, content_t &result) const; virtual content_t getId(const std::string &name) const; - virtual void getIds(const std::string &name, std::set &result) const; + virtual bool getIds(const std::string &name, std::set &result) const; virtual const ContentFeatures& get(const std::string &name) const; content_t allocateId(); virtual content_t set(const std::string &name, const ContentFeatures &def); @@ -603,22 +603,23 @@ content_t CNodeDefManager::getId(const std::string &name) const } -void CNodeDefManager::getIds(const std::string &name, +bool CNodeDefManager::getIds(const std::string &name, std::set &result) const { //TimeTaker t("getIds", NULL, PRECISION_MICRO); if (name.substr(0,6) != "group:") { content_t id = CONTENT_IGNORE; - if(getId(name, id)) + bool exists = getId(name, id); + if (exists) result.insert(id); - return; + return exists; } std::string group = name.substr(6); std::map::const_iterator i = m_group_to_items.find(group); if (i == m_group_to_items.end()) - return; + return true; const GroupItems &items = i->second; for (GroupItems::const_iterator j = items.begin(); @@ -627,6 +628,7 @@ void CNodeDefManager::getIds(const std::string &name, result.insert((*j).first); } //printf("getIds: %dus\n", t.stop()); + return true; } diff --git a/src/nodedef.h b/src/nodedef.h index db36021c..bf2c7bc7 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -303,7 +303,8 @@ public: virtual bool getId(const std::string &name, content_t &result) const=0; virtual content_t getId(const std::string &name) const=0; // Allows "group:name" in addition to regular node names - virtual void getIds(const std::string &name, std::set &result) + // returns false if node name not found, true otherwise + virtual bool getIds(const std::string &name, std::set &result) const=0; virtual const ContentFeatures &get(const std::string &name) const=0; @@ -327,7 +328,7 @@ public: // If not found, returns CONTENT_IGNORE virtual content_t getId(const std::string &name) const=0; // Allows "group:name" in addition to regular node names - virtual void getIds(const std::string &name, std::set &result) + virtual bool getIds(const std::string &name, std::set &result) const=0; // If not found, returns the features of CONTENT_UNKNOWN virtual const ContentFeatures &get(const std::string &name) const=0; diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index ead385a4..f52474f0 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -83,6 +83,7 @@ public: protected: friend class LuaABM; + friend class LuaLBM; friend class InvRef; friend class ObjectRef; friend class NodeMetaRef; diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index a1b11bfe..35b7b36f 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -86,13 +86,12 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) setEnv(env); /* - Add ActiveBlockModifiers to environment + Add {Loading,Active}BlockModifiers to environment */ // Get core.registered_abms lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_abms"); - luaL_checktype(L, -1, LUA_TTABLE); int registered_abms = lua_gettop(L); if(lua_istable(L, registered_abms)){ @@ -154,6 +153,58 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) // removes value, keeps key for next iteration lua_pop(L, 1); } + } else { + lua_pop(L, 1); + throw LuaError("core.registered_abms was not a lua table, as expected."); + } + lua_pop(L, 1); + + // Get core.registered_lbms + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_lbms"); + int registered_lbms = lua_gettop(L); + + if (!lua_istable(L, registered_lbms)) { + lua_pop(L, 1); + throw LuaError("core.registered_lbms was not a lua table, as expected."); + } + + lua_pushnil(L); + while (lua_next(L, registered_lbms)) { + // key at index -2 and value at index -1 + int id = lua_tonumber(L, -2); + int current_lbm = lua_gettop(L); + + std::set trigger_contents; + lua_getfield(L, current_lbm, "nodenames"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table)) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + trigger_contents.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if (lua_isstring(L, -1)) { + trigger_contents.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + std::string name; + getstringfield(L, current_lbm, "name", name); + + bool run_at_every_load = getboolfield_default(L, current_lbm, + "run_at_every_load", false); + + LuaLBM *lbm = new LuaLBM(L, id, trigger_contents, name, + run_at_every_load); + + env->addLoadingBlockModifierDef(lbm); + + // removes value, keeps key for next iteration + lua_pop(L, 1); } lua_pop(L, 1); } diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index b445b1eb..f4ddc2af 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -90,6 +90,46 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, lua_pop(L, 1); // Pop error handler } +void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) +{ + GameScripting *scriptIface = env->getScriptIface(); + scriptIface->realityCheck(); + + lua_State *L = scriptIface->getStack(); + sanity_check(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + int error_handler = PUSH_ERROR_HANDLER(L); + + // Get registered_lbms + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_lbms"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_remove(L, -2); // Remove core + + // Get registered_lbms[m_id] + lua_pushnumber(L, m_id); + lua_gettable(L, -2); + FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table"); + lua_remove(L, -2); // Remove registered_lbms + + scriptIface->setOriginFromTable(-1); + + // Call action + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, "action"); + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_remove(L, -2); // Remove registered_lbms[m_id] + push_v3s16(L, p); + pushnode(L, n, env->getGameDef()->ndef()); + + int result = lua_pcall(L, 2, 0, error_handler); + if (result) + scriptIface->scriptError(result, "LuaLBM::trigger"); + + lua_pop(L, 1); // Pop error handler +} + void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param) { ScriptCallbackState *state = (ScriptCallbackState *)param; diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 4f8dfcd3..0e385ffe 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -220,6 +220,24 @@ public: u32 active_object_count, u32 active_object_count_wider); }; +class LuaLBM : public LoadingBlockModifierDef +{ +private: + int m_id; +public: + LuaLBM(lua_State *L, int id, + const std::set &trigger_contents, + const std::string &name, + bool run_at_every_load): + m_id(id) + { + this->run_at_every_load = run_at_every_load; + this->trigger_contents = trigger_contents; + this->name = name; + } + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n); +}; + struct ScriptCallbackState { GameScripting *script; int callback_ref; diff --git a/src/server.cpp b/src/server.cpp index c4ed3225..75ca7448 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -344,10 +344,11 @@ Server::Server( servermap->addEventReceiver(this); // If file exists, load environment metadata - if(fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) - { - infostream<<"Server: Loading environment metadata"<loadMeta(); + } else { + m_env->loadDefaultMeta(); } // Add some test ActiveBlockModifiers to environment diff --git a/src/util/string.h b/src/util/string.h index c8f60b80..cf94b7f5 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -299,15 +299,6 @@ inline s32 mystoi(const std::string &str, s32 min, s32 max) } -/// Returns a 64-bit value represented by the string \p str (decimal). -inline s64 stoi64(const std::string &str) -{ - std::stringstream tmp(str); - s64 t; - tmp >> t; - return t; -} - // MSVC2010 includes it's own versions of these //#if !defined(_MSC_VER) || _MSC_VER < 1600 @@ -346,9 +337,22 @@ inline float mystof(const std::string &str) #define stoi mystoi #define stof mystof +/// Returns a value represented by the string \p val. +template +inline T from_string(const std::string &str) +{ + std::stringstream tmp(str); + T t; + tmp >> t; + return t; +} + +/// Returns a 64-bit signed value represented by the string \p str (decimal). +inline s64 stoi64(const std::string &str) { return from_string(str); } + // TODO: Replace with C++11 std::to_string. -/// Returns A string representing the value \p val. +/// Returns a string representing the value \p val. template inline std::string to_string(T val) { From f486364e1496e951d2606686daca30dc4d1b8ac1 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 7 Mar 2016 19:34:48 +0100 Subject: [PATCH 08/32] s_env.{cpp, h} cleanups * Replace string by-val passing with const reference * Fix code style * Remove redundant `int table` definition and indentation level --- src/script/cpp_api/s_env.cpp | 126 +++++++++++++++++------------------ src/script/cpp_api/s_env.h | 5 +- 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index 35b7b36f..82d0d4f0 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, - u32 blockseed) + u32 blockseed) { SCRIPTAPI_PRECHECKHEADER @@ -44,7 +44,7 @@ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, void ScriptApiEnv::environment_Step(float dtime) { SCRIPTAPI_PRECHECKHEADER - //infostream<<"scriptapi_environment_step"< trigger_contents; - lua_getfield(L, current_abm, "nodenames"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - trigger_contents.insert(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, -1)){ - trigger_contents.insert(lua_tostring(L, -1)); - } - lua_pop(L, 1); - - std::set required_neighbors; - lua_getfield(L, current_abm, "neighbors"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - required_neighbors.insert(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, -1)){ - required_neighbors.insert(lua_tostring(L, -1)); - } - lua_pop(L, 1); - - float trigger_interval = 10.0; - getfloatfield(L, current_abm, "interval", trigger_interval); - - int trigger_chance = 50; - getintfield(L, current_abm, "chance", trigger_chance); - - bool simple_catch_up = true; - getboolfield(L, current_abm, "catch_up", simple_catch_up); - - LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors, - trigger_interval, trigger_chance, simple_catch_up); - - env->addActiveBlockModifier(abm); - - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else { + if (!lua_istable(L, registered_abms)) { lua_pop(L, 1); throw LuaError("core.registered_abms was not a lua table, as expected."); } + lua_pushnil(L); + while (lua_next(L, registered_abms)) { + // key at index -2 and value at index -1 + int id = lua_tonumber(L, -2); + int current_abm = lua_gettop(L); + + std::set trigger_contents; + lua_getfield(L, current_abm, "nodenames"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table)) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + trigger_contents.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if (lua_isstring(L, -1)) { + trigger_contents.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + std::set required_neighbors; + lua_getfield(L, current_abm, "neighbors"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table)) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + required_neighbors.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if (lua_isstring(L, -1)) { + required_neighbors.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + float trigger_interval = 10.0; + getfloatfield(L, current_abm, "interval", trigger_interval); + + int trigger_chance = 50; + getintfield(L, current_abm, "chance", trigger_chance); + + bool simple_catch_up = true; + getboolfield(L, current_abm, "catch_up", simple_catch_up); + + LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors, + trigger_interval, trigger_chance, simple_catch_up); + + env->addActiveBlockModifier(abm); + + // removes value, keeps key for next iteration + lua_pop(L, 1); + } lua_pop(L, 1); // Get core.registered_lbms diff --git a/src/script/cpp_api/s_env.h b/src/script/cpp_api/s_env.h index 0d98e627..e0702456 100644 --- a/src/script/cpp_api/s_env.h +++ b/src/script/cpp_api/s_env.h @@ -26,7 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc., class ServerEnvironment; struct ScriptCallbackState; -class ScriptApiEnv : virtual public ScriptApiBase { +class ScriptApiEnv : virtual public ScriptApiBase +{ public: // Called on environment step void environment_Step(float dtime); @@ -35,7 +36,7 @@ public: void environment_OnGenerated(v3s16 minp, v3s16 maxp, u32 blockseed); // Called on player event - void player_event(ServerActiveObject *player, std::string type); + void player_event(ServerActiveObject *player, const std::string &type); // Called after emerge of a block queued from core.emerge_area() void on_emerge_area_completion(v3s16 blockpos, int action, From 9ae267cae567674f72da185a21ddf8111dee5dc8 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Thu, 29 Oct 2015 23:08:32 -0400 Subject: [PATCH 09/32] Simplify AreaStore ID management --- doc/lua_api.txt | 3 +- src/areastore.cpp | 37 +++++++++++------------- src/areastore.h | 46 +++++++++--------------------- src/script/lua_api/l_areastore.cpp | 17 ++--------- src/unittest/test_areastore.cpp | 28 +++++++----------- 5 files changed, 45 insertions(+), 86 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 0520096e..03b2d560 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2727,8 +2727,7 @@ If you chose the parameter-less constructor, a fast implementation will be autom * `get_area(id, include_borders, include_data)`: returns the area with the id `id`. (optional) Boolean values `include_borders` and `include_data` control what's copied. * `get_areas_for_pos(pos, include_borders, include_data)`: returns all areas that contain the position `pos`. (optional) Boolean values `include_borders` and `include_data` control what's copied. * `get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data)`: returns all areas that contain all nodes inside the area specified by `edge1` and `edge2` (inclusive). If `accept_overlap` is true, also areas are returned that have nodes in common with the specified area. (optional) Boolean values `include_borders` and `include_data` control what's copied. -* `insert_area(edge1, edge2, data)`: inserts an area into the store. Returns the id if successful, nil otherwise. The (inclusive) positions `edge1` and `edge2` describe the area, `data` -is a string stored with the area. +* `insert_area(edge1, edge2, data)`: inserts an area into the store. Returns the new area's ID, or nil if the insertion failed. The (inclusive) positions `edge1` and `edge2` describe the area, `data` is a string stored with the area. * `reserve(count)`: reserves resources for at most `count` many contained areas. Only needed for efficiency, and only some implementations profit. * `remove_area(id)`: removes the area with the given id from the store, returns success. * `set_cache_params(params)`: sets params for the included prefiltering cache. Calling invalidates the cache, so that its elements have to be newly generated. diff --git a/src/areastore.cpp b/src/areastore.cpp index f9362c4a..c2cc4ce9 100644 --- a/src/areastore.cpp +++ b/src/areastore.cpp @@ -49,21 +49,6 @@ u16 AreaStore::size() const return areas_map.size(); } -u32 AreaStore::getFreeId(v3s16 minedge, v3s16 maxedge) -{ - int keep_on = 100; - while (keep_on--) { - m_highest_id++; - // Handle overflows, we dont want to return 0 - if (m_highest_id == AREA_ID_INVALID) - m_highest_id++; - if (areas_map.find(m_highest_id) == areas_map.end()) - return m_highest_id; - } - // search failed - return AREA_ID_INVALID; -} - const Area *AreaStore::getArea(u32 id) const { const Area *res = NULL; @@ -185,11 +170,17 @@ void AreaStore::getAreasForPos(std::vector *result, v3s16 pos) //// -void VectorAreaStore::insertArea(const Area &a) +bool VectorAreaStore::insertArea(Area *a) { - areas_map[a.id] = a; - m_areas.push_back(&(areas_map[a.id])); + a->id = getNextId(); + std::pair::iterator, bool> res = + areas_map.insert(std::make_pair(a->id, *a)); + if (!res.second) + // ID is not unique + return false; + m_areas.push_back(&res.first->second); invalidateCache(); + return true; } void VectorAreaStore::reserve(size_t count) @@ -273,11 +264,15 @@ static inline SpatialIndex::Point get_spatial_point(const v3s16 pos) } -void SpatialAreaStore::insertArea(const Area &a) +bool SpatialAreaStore::insertArea(Area *a) { - areas_map[a.id] = a; - m_tree->insertData(0, NULL, get_spatial_region(a.minedge, a.maxedge), a.id); + a->id = getNextId(); + if (!areas_map.insert(std::make_pair(a->id, *a)).second) + // ID is not unique + return false; + m_tree->insertData(0, NULL, get_spatial_region(a->minedge, a->maxedge), a->id); invalidateCache(); + return true; } bool SpatialAreaStore::removeArea(u32 id) diff --git a/src/areastore.h b/src/areastore.h index 57d96450..de458870 100644 --- a/src/areastore.h +++ b/src/areastore.h @@ -36,34 +36,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #endif -#define AST_EXTREMIFY(min, max, pa, pb) \ - (min).X = MYMIN((pa).X, (pb).X); \ - (min).Y = MYMIN((pa).Y, (pb).Y); \ - (min).Z = MYMIN((pa).Z, (pb).Z); \ - (max).X = MYMAX((pa).X, (pb).X); \ - (max).Y = MYMAX((pa).Y, (pb).Y); \ - (max).Z = MYMAX((pa).Z, (pb).Z); - -#define AREA_ID_INVALID 0 struct Area { - Area(const v3s16 &minedge, const v3s16 &maxedge) - { - this->minedge = minedge; - this->maxedge = maxedge; - } - Area() {} - - void extremifyEdges() + Area(const v3s16 &mine, const v3s16 &maxe) { - v3s16 nminedge; - v3s16 nmaxedge; - - AST_EXTREMIFY(nminedge, nmaxedge, minedge, maxedge) - - maxedge = nmaxedge; - minedge = nminedge; + minedge = mine; + maxedge = maxe; + sortBoxVerticies(minedge, maxedge); } u32 id; @@ -76,13 +56,16 @@ std::vector get_areastore_typenames(); class AreaStore { protected: - // TODO change to unordered_map when we can - std::map areas_map; void invalidateCache(); virtual void getAreasForPosImpl(std::vector *result, v3s16 pos) = 0; + u32 getNextId() { return m_next_id++; } + + // TODO change to unordered_map when we can + std::map areas_map; bool cache_enabled; // don't write to this from subclasses, only read. public: - virtual void insertArea(const Area &a) = 0; + // Updates the area's ID + virtual bool insertArea(Area *a) = 0; virtual void reserve(size_t count) {}; virtual bool removeArea(u32 id) = 0; void getAreasForPos(std::vector *result, v3s16 pos); @@ -103,13 +86,12 @@ public: cache_enabled(true), m_cacheblock_radius(64), m_res_cache(1000, &cacheMiss, this), - m_highest_id(0) + m_next_id(0) { } void setCacheParams(bool enabled, u8 block_radius, size_t limit); - u32 getFreeId(v3s16 minedge, v3s16 maxedge); const Area *getArea(u32 id) const; u16 size() const; #if 0 @@ -120,7 +102,7 @@ private: static void cacheMiss(void *data, const v3s16 &mpos, std::vector *dest); u8 m_cacheblock_radius; // if you modify this, call invalidateCache() LRUCache > m_res_cache; - u32 m_highest_id; + u32 m_next_id; }; @@ -129,7 +111,7 @@ class VectorAreaStore : public AreaStore { protected: virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); public: - virtual void insertArea(const Area &a); + virtual bool insertArea(Area *a); virtual void reserve(size_t count); virtual bool removeArea(u32 id); virtual void getAreasInArea(std::vector *result, @@ -146,7 +128,7 @@ protected: virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); public: SpatialAreaStore(); - virtual void insertArea(const Area &a); + virtual bool insertArea(Area *a); virtual bool removeArea(u32 id); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap); diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp index 4148780a..ff6abbd9 100644 --- a/src/script/lua_api/l_areastore.cpp +++ b/src/script/lua_api/l_areastore.cpp @@ -159,26 +159,15 @@ int LuaAreaStore::l_insert_area(lua_State *L) LuaAreaStore *o = checkobject(L, 1); AreaStore *ast = o->as; - Area a; - - a.minedge = check_v3s16(L, 2); - a.maxedge = check_v3s16(L, 3); - - a.extremifyEdges(); - a.id = ast->getFreeId(a.minedge, a.maxedge); - - if (a.id == AREA_ID_INVALID) { - // couldn't get free id - lua_pushnil(L); - return 1; - } + Area a(check_v3s16(L, 2), check_v3s16(L, 3)); size_t d_len; const char *data = luaL_checklstring(L, 4, &d_len); a.data = std::string(data, d_len); - ast->insertArea(a); + if (!ast->insertArea(&a)) + return 0; lua_pushnumber(L, a.id); return 1; diff --git a/src/unittest/test_areastore.cpp b/src/unittest/test_areastore.cpp index a0dcada9..9d70d0b7 100644 --- a/src/unittest/test_areastore.cpp +++ b/src/unittest/test_areastore.cpp @@ -62,18 +62,15 @@ void TestAreaStore::testSpatialStore() void TestAreaStore::genericStoreTest(AreaStore *store) { Area a(v3s16(-10, -3, 5), v3s16(0, 29, 7)); - a.id = 1; Area b(v3s16(-5, -2, 5), v3s16(0, 28, 6)); - b.id = 2; Area c(v3s16(-7, -3, 6), v3s16(-1, 27, 7)); - c.id = 3; std::vector res; UASSERTEQ(size_t, store->size(), 0); store->reserve(2); // sic - store->insertArea(a); - store->insertArea(b); - store->insertArea(c); + store->insertArea(&a); + store->insertArea(&b); + store->insertArea(&c); UASSERTEQ(size_t, store->size(), 3); store->getAreasForPos(&res, v3s16(-1, 0, 6)); @@ -81,20 +78,18 @@ void TestAreaStore::genericStoreTest(AreaStore *store) res.clear(); store->getAreasForPos(&res, v3s16(0, 0, 7)); UASSERTEQ(size_t, res.size(), 1); - UASSERTEQ(u32, res[0]->id, 1); res.clear(); - store->removeArea(1); + store->removeArea(a.id); store->getAreasForPos(&res, v3s16(0, 0, 7)); UASSERTEQ(size_t, res.size(), 0); res.clear(); - store->insertArea(a); + store->insertArea(&a); store->getAreasForPos(&res, v3s16(0, 0, 7)); UASSERTEQ(size_t, res.size(), 1); - UASSERTEQ(u32, res[0]->id, 1); res.clear(); store->getAreasInArea(&res, v3s16(-10, -3, 5), v3s16(0, 29, 7), false); @@ -109,21 +104,20 @@ void TestAreaStore::genericStoreTest(AreaStore *store) UASSERTEQ(size_t, res.size(), 3); res.clear(); - store->removeArea(1); - store->removeArea(2); - store->removeArea(3); + store->removeArea(a.id); + store->removeArea(b.id); + store->removeArea(c.id); Area d(v3s16(-100, -300, -200), v3s16(-50, -200, -100)); - d.id = 4; d.data = "Hi!"; - store->insertArea(d); + store->insertArea(&d); store->getAreasForPos(&res, v3s16(-75, -250, -150)); UASSERTEQ(size_t, res.size(), 1); - UASSERTEQ(u32, res[0]->id, 4); UASSERTEQ(u16, res[0]->data.size(), 3); UASSERT(strncmp(res[0]->data.c_str(), "Hi!", 3) == 0); res.clear(); - store->removeArea(4); + store->removeArea(d.id); } + From a7e129ab688a06f12cc8be884d6623be3598bd02 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Thu, 29 Oct 2015 23:17:44 -0400 Subject: [PATCH 10/32] Make AreaStore cache setting private --- src/areastore.cpp | 6 +++--- src/areastore.h | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/areastore.cpp b/src/areastore.cpp index c2cc4ce9..b1b5c0db 100644 --- a/src/areastore.cpp +++ b/src/areastore.cpp @@ -112,14 +112,14 @@ void AreaStore::serialize(std::ostream &os) const void AreaStore::invalidateCache() { - if (cache_enabled) { + if (m_cache_enabled) { m_res_cache.invalidate(); } } void AreaStore::setCacheParams(bool enabled, u8 block_radius, size_t limit) { - cache_enabled = enabled; + m_cache_enabled = enabled; m_cacheblock_radius = MYMAX(block_radius, 16); m_res_cache.setLimit(MYMAX(limit, 20)); invalidateCache(); @@ -148,7 +148,7 @@ void AreaStore::cacheMiss(void *data, const v3s16 &mpos, std::vector *de void AreaStore::getAreasForPos(std::vector *result, v3s16 pos) { - if (cache_enabled) { + if (m_cache_enabled) { v3s16 mblock = getContainerPos(pos, m_cacheblock_radius); const std::vector *pre_list = m_res_cache.lookupCache(mblock); diff --git a/src/areastore.h b/src/areastore.h index de458870..493b4fc2 100644 --- a/src/areastore.h +++ b/src/areastore.h @@ -62,7 +62,6 @@ protected: // TODO change to unordered_map when we can std::map areas_map; - bool cache_enabled; // don't write to this from subclasses, only read. public: // Updates the area's ID virtual bool insertArea(Area *a) = 0; @@ -83,10 +82,10 @@ public: {} AreaStore() : - cache_enabled(true), m_cacheblock_radius(64), m_res_cache(1000, &cacheMiss, this), - m_next_id(0) + m_next_id(0), + m_cache_enabled(true) { } @@ -103,7 +102,7 @@ private: u8 m_cacheblock_radius; // if you modify this, call invalidateCache() LRUCache > m_res_cache; u32 m_next_id; - + bool m_cache_enabled; }; From 9070fd90d58af1461b163dfca8630b794d408047 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Thu, 29 Oct 2015 23:26:03 -0400 Subject: [PATCH 11/32] Move AreaStore to util --- build/android/jni/Android.mk | 2 +- src/CMakeLists.txt | 1 - src/script/lua_api/l_areastore.cpp | 2 +- src/script/lua_api/l_areastore.h | 13 ++++++------- src/script/lua_api/l_util.cpp | 1 - src/unittest/test_areastore.cpp | 2 +- src/util/CMakeLists.txt | 1 + src/{ => util}/areastore.cpp | 2 +- src/{ => util}/areastore.h | 6 +++--- 9 files changed, 14 insertions(+), 16 deletions(-) rename src/{ => util}/areastore.cpp (99%) rename src/{ => util}/areastore.h (98%) diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index e1a2a54c..92738e6c 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -112,7 +112,6 @@ LOCAL_C_INCLUDES := \ deps/sqlite/ LOCAL_SRC_FILES := \ - jni/src/areastore.cpp \ jni/src/ban.cpp \ jni/src/camera.cpp \ jni/src/cavegen.cpp \ @@ -221,6 +220,7 @@ LOCAL_SRC_FILES := \ jni/src/version.cpp \ jni/src/voxel.cpp \ jni/src/voxelalgorithms.cpp \ + jni/src/util/areastore.cpp \ jni/src/util/auth.cpp \ jni/src/util/base64.cpp \ jni/src/util/directiontables.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0b84ff85..feca199c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -348,7 +348,6 @@ add_subdirectory(unittest) add_subdirectory(util) set(common_SRCS - areastore.cpp ban.cpp cavegen.cpp chat.cpp diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp index ff6abbd9..8b8b157d 100644 --- a/src/script/lua_api/l_areastore.cpp +++ b/src/script/lua_api/l_areastore.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "cpp_api/s_security.h" #include "irr_v3d.h" -#include "areastore.h" +#include "util/areastore.h" #include "filesys.h" #ifndef ANDROID #include "cmake_config.h" diff --git a/src/script/lua_api/l_areastore.h b/src/script/lua_api/l_areastore.h index 543a2aa3..6321de6d 100644 --- a/src/script/lua_api/l_areastore.h +++ b/src/script/lua_api/l_areastore.h @@ -17,15 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef L_AREASTORE_H_ -#define L_AREASTORE_H_ +#ifndef L_AREA_STORE_H_ +#define L_AREA_STORE_H_ #include "lua_api/l_base.h" -#include "areastore.h" -/* - AreaStore - */ + +class AreaStore; + class LuaAreaStore : public ModApiBase { private: @@ -66,4 +65,4 @@ public: static void Register(lua_State *L); }; -#endif /* L_AREASTORE_H_ */ +#endif // L_AREA_STORE_H_ diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 98f70d91..f990dacf 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" #include "json/json.h" #include "cpp_api/s_security.h" -#include "areastore.h" #include "porting.h" #include "debug.h" #include "log.h" diff --git a/src/unittest/test_areastore.cpp b/src/unittest/test_areastore.cpp index 9d70d0b7..cac9e0b5 100644 --- a/src/unittest/test_areastore.cpp +++ b/src/unittest/test_areastore.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "test.h" -#include "areastore.h" +#include "util/areastore.h" class TestAreaStore : public TestBase { public: diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 33900a43..0e7cbad0 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,4 +1,5 @@ set(UTIL_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp diff --git a/src/areastore.cpp b/src/util/areastore.cpp similarity index 99% rename from src/areastore.cpp rename to src/util/areastore.cpp index b1b5c0db..b0076faa 100644 --- a/src/areastore.cpp +++ b/src/util/areastore.cpp @@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "areastore.h" +#include "util/areastore.h" #include "util/serialize.h" #include "util/container.h" diff --git a/src/areastore.h b/src/util/areastore.h similarity index 98% rename from src/areastore.h rename to src/util/areastore.h index 493b4fc2..c36cbc38 100644 --- a/src/areastore.h +++ b/src/util/areastore.h @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef AREASTORE_H_ -#define AREASTORE_H_ +#ifndef AREA_STORE_H_ +#define AREA_STORE_H_ #include "irr_v3d.h" #include "noise.h" // for PcgRandom @@ -174,4 +174,4 @@ private: #endif -#endif /* AREASTORE_H_ */ +#endif // AREA_STORE_H_ From 239f5ed868d0d4cd91bac99951252127d362e30c Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Thu, 29 Oct 2015 23:38:36 -0400 Subject: [PATCH 12/32] Move AreaStore container selection logic into getOptimalImplementation --- src/script/lua_api/l_areastore.cpp | 9 +-------- src/util/areastore.cpp | 10 ++++++++++ src/util/areastore.h | 3 ++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp index 8b8b157d..8e9b2c7d 100644 --- a/src/script/lua_api/l_areastore.cpp +++ b/src/script/lua_api/l_areastore.cpp @@ -25,9 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include "util/areastore.h" #include "filesys.h" -#ifndef ANDROID - #include "cmake_config.h" -#endif #include static inline void get_data_and_border_flags(lua_State *L, u8 start_i, @@ -294,11 +291,7 @@ int LuaAreaStore::l_from_file(lua_State *L) LuaAreaStore::LuaAreaStore() { -#if USE_SPATIAL - this->as = new SpatialAreaStore(); -#else - this->as = new VectorAreaStore(); -#endif + this->as = AreaStore::getOptimalImplementation(); } LuaAreaStore::LuaAreaStore(const std::string &type) diff --git a/src/util/areastore.cpp b/src/util/areastore.cpp index b0076faa..cf972586 100644 --- a/src/util/areastore.cpp +++ b/src/util/areastore.cpp @@ -44,6 +44,16 @@ with this program; if not, write to the Free Software Foundation, Inc., AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Y) && \ AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Z)) + +AreaStore *AreaStore::getOptimalImplementation() +{ +#if USE_SPATIAL + return new SpatialAreaStore(); +#else + return new VectorAreaStore(); +#endif +} + u16 AreaStore::size() const { return areas_map.size(); diff --git a/src/util/areastore.h b/src/util/areastore.h index c36cbc38..dee1f8ba 100644 --- a/src/util/areastore.h +++ b/src/util/areastore.h @@ -52,7 +52,6 @@ struct Area { std::string data; }; -std::vector get_areastore_typenames(); class AreaStore { protected: @@ -93,6 +92,8 @@ public: const Area *getArea(u32 id) const; u16 size() const; + + static AreaStore *getOptimalImplementation(); #if 0 bool deserialize(std::istream &is); void serialize(std::ostream &is) const; From d2d654fcbf8e10558d025333821b5c208d542b80 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Fri, 30 Oct 2015 00:04:10 -0400 Subject: [PATCH 13/32] Clean up VectorAreaStore --- src/util/areastore.cpp | 48 +++++++++++++++++------------------------- src/util/areastore.h | 8 ++++--- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/util/areastore.cpp b/src/util/areastore.cpp index cf972586..56849238 100644 --- a/src/util/areastore.cpp +++ b/src/util/areastore.cpp @@ -183,7 +183,7 @@ void AreaStore::getAreasForPos(std::vector *result, v3s16 pos) bool VectorAreaStore::insertArea(Area *a) { a->id = getNextId(); - std::pair::iterator, bool> res = + std::pair res = areas_map.insert(std::make_pair(a->id, *a)); if (!res.second) // ID is not unique @@ -193,35 +193,27 @@ bool VectorAreaStore::insertArea(Area *a) return true; } -void VectorAreaStore::reserve(size_t count) -{ - m_areas.reserve(count); -} - bool VectorAreaStore::removeArea(u32 id) { - std::map::iterator itr = areas_map.find(id); - if (itr != areas_map.end()) { - size_t msiz = m_areas.size(); - for (size_t i = 0; i < msiz; i++) { - Area * b = m_areas[i]; - if (b->id == id) { - areas_map.erase(itr); - m_areas.erase(m_areas.begin() + i); - invalidateCache(); - return true; - } + AreaMap::iterator it = areas_map.find(id); + if (it == areas_map.end()) + return false; + Area *a = &it->second; + for (std::vector::iterator v_it = m_areas.begin(); + v_it != m_areas.end(); ++v_it) { + if (*v_it == a) { + m_areas.erase(v_it); + break; } - // we should never get here, it means we did find it in map, - // but not in the vector } - return false; + areas_map.erase(it); + invalidateCache(); + return true; } void VectorAreaStore::getAreasForPosImpl(std::vector *result, v3s16 pos) { - size_t msiz = m_areas.size(); - for (size_t i = 0; i < msiz; i++) { + for (size_t i = 0; i < m_areas.size(); ++i) { Area *b = m_areas[i]; if (AST_CONTAINS_PT(b, pos)) { result->push_back(b); @@ -232,9 +224,8 @@ void VectorAreaStore::getAreasForPosImpl(std::vector *result, v3s16 pos) void VectorAreaStore::getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap) { - size_t msiz = m_areas.size(); - for (size_t i = 0; i < msiz; i++) { - Area * b = m_areas[i]; + for (size_t i = 0; i < m_areas.size(); ++i) { + Area *b = m_areas[i]; if (accept_overlap ? AST_AREAS_OVERLAP(minedge, maxedge, b) : AST_CONTAINS_AREA(minedge, maxedge, b)) { result->push_back(b); @@ -243,11 +234,10 @@ void VectorAreaStore::getAreasInArea(std::vector *result, } #if 0 -bool VectorAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const +bool SimpleAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const { - size_t msiz = m_areas.size(); - for (size_t i = 0; i < msiz; i++) { - if (callback(args, m_areas[i])) { + for (size_t i = 0; i < m_areas.size(); ++i) { + if (callback(m_areas[i], arg)) { return true; } } diff --git a/src/util/areastore.h b/src/util/areastore.h index dee1f8ba..da787639 100644 --- a/src/util/areastore.h +++ b/src/util/areastore.h @@ -59,8 +59,10 @@ protected: virtual void getAreasForPosImpl(std::vector *result, v3s16 pos) = 0; u32 getNextId() { return m_next_id++; } - // TODO change to unordered_map when we can - std::map areas_map; + // Note: This can't be an unordered_map, since all + // references would be invalidated on rehash. + typedef std::map AreaMap; + AreaMap areas_map; public: // Updates the area's ID virtual bool insertArea(Area *a) = 0; @@ -111,8 +113,8 @@ class VectorAreaStore : public AreaStore { protected: virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); public: + virtual void reserve(size_t count) { m_areas.reserve(count); } virtual bool insertArea(Area *a); - virtual void reserve(size_t count); virtual bool removeArea(u32 id); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap); From b9e0e8d3374815a52fd678f414b80f8cfb51952a Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Fri, 30 Oct 2015 00:52:45 -0400 Subject: [PATCH 14/32] Sort AreaStore header --- src/util/areastore.cpp | 19 ++----- src/util/areastore.h | 126 +++++++++++++++++++++-------------------- 2 files changed, 70 insertions(+), 75 deletions(-) diff --git a/src/util/areastore.cpp b/src/util/areastore.cpp index 56849238..fdd4d7b7 100644 --- a/src/util/areastore.cpp +++ b/src/util/areastore.cpp @@ -54,19 +54,12 @@ AreaStore *AreaStore::getOptimalImplementation() #endif } -u16 AreaStore::size() const -{ - return areas_map.size(); -} - const Area *AreaStore::getArea(u32 id) const { - const Area *res = NULL; - std::map::const_iterator itr = areas_map.find(id); - if (itr != areas_map.end()) { - res = &itr->second; - } - return res; + AreaMap::const_iterator it = areas_map.find(id); + if (it == areas_map.end()) + return NULL; + return &it->second; } #if 0 @@ -234,7 +227,7 @@ void VectorAreaStore::getAreasInArea(std::vector *result, } #if 0 -bool SimpleAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const +bool SimpleAreaStore::forEach(ForEachCallback callback, void *arg) const { for (size_t i = 0; i < m_areas.size(); ++i) { if (callback(m_areas[i], arg)) { @@ -308,7 +301,7 @@ void SpatialAreaStore::getAreasInArea(std::vector *result, } #if 0 -bool SpatialAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const +bool SpatialAreaStore::forEach(ForEachCallback callback, void *arg) const { // TODO ?? (this is only needed for serialisation, but libspatial has its own serialisation) return false; diff --git a/src/util/areastore.h b/src/util/areastore.h index da787639..20e9bdfc 100644 --- a/src/util/areastore.h +++ b/src/util/areastore.h @@ -39,21 +39,55 @@ with this program; if not, write to the Free Software Foundation, Inc., struct Area { Area() {} - Area(const v3s16 &mine, const v3s16 &maxe) + Area(const v3s16 &mine, const v3s16 &maxe) : + minedge(mine), maxedge(maxe) { - minedge = mine; - maxedge = maxe; sortBoxVerticies(minedge, maxedge); } u32 id; - v3s16 minedge; - v3s16 maxedge; + v3s16 minedge, maxedge; std::string data; }; class AreaStore { +public: + AreaStore() : + m_cache_enabled(true), + m_cacheblock_radius(64), + m_res_cache(1000, &cacheMiss, this), + m_next_id(0) + {} + + virtual ~AreaStore() {} + + static AreaStore *getOptimalImplementation(); + + virtual void reserve(size_t count) {}; + size_t size() const { return areas_map.size(); } + + // Updates the area's ID + virtual bool insertArea(Area *a) = 0; + virtual bool removeArea(u32 id) = 0; + void getAreasForPos(std::vector *result, v3s16 pos); + virtual void getAreasInArea(std::vector *result, + v3s16 minedge, v3s16 maxedge, bool accept_overlap) = 0; + void setCacheParams(bool enabled, u8 block_radius, size_t limit); + + const Area *getArea(u32 id) const; + +#if 0 + typedef bool (*ForEachCallback)(const Area *a, void *arg); + // Calls a passed function for every stored area, until the + // callback returns true. If that happens, it returns true, + // if the search is exhausted, it returns false. + virtual bool forEach(ForEachCallback, void *arg=NULL) const = 0; + + void serialize(std::ostream &is) const; + bool deserialize(std::istream &is); +#endif + protected: void invalidateCache(); virtual void getAreasForPosImpl(std::vector *result, v3s16 pos) = 0; @@ -63,98 +97,64 @@ protected: // references would be invalidated on rehash. typedef std::map AreaMap; AreaMap areas_map; -public: - // Updates the area's ID - virtual bool insertArea(Area *a) = 0; - virtual void reserve(size_t count) {}; - virtual bool removeArea(u32 id) = 0; - void getAreasForPos(std::vector *result, v3s16 pos); - virtual void getAreasInArea(std::vector *result, - v3s16 minedge, v3s16 maxedge, bool accept_overlap) = 0; -#if 0 - // calls a passed function for every stored area, until the - // callback returns true. If that happens, it returns true, - // if the search is exhausted, it returns false - virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const = 0; -#endif - - virtual ~AreaStore() - {} - - AreaStore() : - m_cacheblock_radius(64), - m_res_cache(1000, &cacheMiss, this), - m_next_id(0), - m_cache_enabled(true) - { - } - - void setCacheParams(bool enabled, u8 block_radius, size_t limit); - - const Area *getArea(u32 id) const; - u16 size() const; - - static AreaStore *getOptimalImplementation(); -#if 0 - bool deserialize(std::istream &is); - void serialize(std::ostream &is) const; -#endif private: static void cacheMiss(void *data, const v3s16 &mpos, std::vector *dest); + + bool m_cache_enabled; u8 m_cacheblock_radius; // if you modify this, call invalidateCache() LRUCache > m_res_cache; + u32 m_next_id; - bool m_cache_enabled; }; class VectorAreaStore : public AreaStore { -protected: - virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); public: virtual void reserve(size_t count) { m_areas.reserve(count); } virtual bool insertArea(Area *a); virtual bool removeArea(u32 id); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap); - // virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const; + //virtual bool forEach(ForEachCallback, void *arg) const; + +protected: + virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); + private: std::vector m_areas; }; + #if USE_SPATIAL class SpatialAreaStore : public AreaStore { -protected: - virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); public: SpatialAreaStore(); + virtual ~SpatialAreaStore(); + virtual bool insertArea(Area *a); virtual bool removeArea(u32 id); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap); - // virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const; + //virtual bool forEach(ForEachCallback, void *arg) const; + +protected: + virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); - virtual ~SpatialAreaStore(); private: SpatialIndex::ISpatialIndex *m_tree; SpatialIndex::IStorageManager *m_storagemanager; class VectorResultVisitor : public SpatialIndex::IVisitor { - private: - SpatialAreaStore *m_store; - std::vector *m_result; public: - VectorResultVisitor(std::vector *result, SpatialAreaStore *store) - { - m_store = store; - m_result = result; - } + VectorResultVisitor(std::vector *result, SpatialAreaStore *store) : + m_store(store), + m_result(result) + {} + ~VectorResultVisitor() {} - virtual void visitNode(const SpatialIndex::INode &in) - { - } + virtual void visitNode(const SpatialIndex::INode &in) {} virtual void visitData(const SpatialIndex::IData &in) { @@ -171,10 +171,12 @@ private: visitData(*(v[i])); } - ~VectorResultVisitor() {} + private: + SpatialAreaStore *m_store; + std::vector *m_result; }; }; -#endif +#endif // USE_SPATIAL #endif // AREA_STORE_H_ From 7dead21e9804f3a718e9926fd93cd159c063cac9 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Fri, 30 Oct 2015 01:25:44 -0400 Subject: [PATCH 15/32] Add basic AreaStore method documentation --- src/util/areastore.h | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/util/areastore.h b/src/util/areastore.h index 20e9bdfc..5b4e9a71 100644 --- a/src/util/areastore.h +++ b/src/util/areastore.h @@ -67,21 +67,36 @@ public: virtual void reserve(size_t count) {}; size_t size() const { return areas_map.size(); } - // Updates the area's ID + /// Add an area to the store. + /// Updates the area's ID. virtual bool insertArea(Area *a) = 0; + + /// Removes an area from the store by ID. + /// @return Whether the area was in the store and removed. virtual bool removeArea(u32 id) = 0; + + /// Finds areas that the passed position is contained in. + /// Stores output in passed vector. void getAreasForPos(std::vector *result, v3s16 pos); + + /// Finds areas that are completely contained inside the area defined + /// by the passed edges. If @p accept_overlap is true this finds any + /// areas that intersect with the passed area at any point. virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap) = 0; + + /// Sets cache parameters. void setCacheParams(bool enabled, u8 block_radius, size_t limit); + /// Returns a pointer to the area coresponding to the passed ID, + /// or NULL if it doesn't exist. const Area *getArea(u32 id) const; #if 0 typedef bool (*ForEachCallback)(const Area *a, void *arg); - // Calls a passed function for every stored area, until the - // callback returns true. If that happens, it returns true, - // if the search is exhausted, it returns false. + /// Calls a passed function for every stored area, until the + /// callback returns true. If that happens, it returns true, + /// if the search is exhausted, it returns false. virtual bool forEach(ForEachCallback, void *arg=NULL) const = 0; void serialize(std::ostream &is) const; @@ -89,8 +104,15 @@ public: #endif protected: + /// Invalidates the getAreasForPos cache. + /// Call after adding or removing an area. void invalidateCache(); + + /// Implementation of getAreasForPos. + /// getAreasForPos calls this if the cache is disabled. virtual void getAreasForPosImpl(std::vector *result, v3s16 pos) = 0; + + /// Returns the next area ID and increments it. u32 getNextId() { return m_next_id++; } // Note: This can't be an unordered_map, since all @@ -99,10 +121,13 @@ protected: AreaMap areas_map; private: + /// Called by the cache when a value isn't found in the cache. static void cacheMiss(void *data, const v3s16 &mpos, std::vector *dest); bool m_cache_enabled; - u8 m_cacheblock_radius; // if you modify this, call invalidateCache() + /// Range, in nodes, of the getAreasForPos cache. + /// If you modify this, call invalidateCache() + u8 m_cacheblock_radius; LRUCache > m_res_cache; u32 m_next_id; From ec56801e71d2cd410d11f3f19eb2fcaeefd89d7c Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Fri, 30 Oct 2015 10:26:14 -0400 Subject: [PATCH 16/32] Fix SpatialAreaStore not freeing removed areas --- src/util/areastore.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/areastore.cpp b/src/util/areastore.cpp index fdd4d7b7..357ce37f 100644 --- a/src/util/areastore.cpp +++ b/src/util/areastore.cpp @@ -275,6 +275,7 @@ bool SpatialAreaStore::removeArea(u32 id) Area *a = &itr->second; bool result = m_tree->deleteData(get_spatial_region(a->minedge, a->maxedge), id); + areas_map.erase(itr); invalidateCache(); return result; } else { From e666e6c1091474cf2651d68f3da15d3695095e47 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Fri, 30 Oct 2015 19:27:48 -0400 Subject: [PATCH 17/32] Implement AreaStore serialization --- doc/lua_api.txt | 4 ++ src/script/lua_api/l_areastore.cpp | 37 +++++++----- src/script/lua_api/l_areastore.h | 4 +- src/unittest/test_areastore.cpp | 39 ++++++++++++ src/util/areastore.cpp | 97 ++++++++++-------------------- src/util/areastore.h | 18 +++--- 6 files changed, 104 insertions(+), 95 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 03b2d560..50fa2527 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2737,6 +2737,10 @@ If you chose the parameter-less constructor, a fast implementation will be autom block_radius = number, -- the radius (in nodes) of the areas the cache generates prefiltered lists for, minimum 16, default 64 limit = number, -- the cache's size, minimum 20, default 1000 } +* `to_string()`: Experimental. Returns area store serialized as a (binary) string. +* `to_file(filename)`: Experimental. Like `to_string()`, but writes the data to a file. +* `from_string(str)`: Experimental. Deserializes string and loads it into the AreaStore. Returns success and, optionally, an error message. +* `from_file(filename)`: Experimental. Like `from_string()`, but reads the data from a file. ### `ItemStack` An `ItemStack` is a stack of items. diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp index 8e9b2c7d..261baf6c 100644 --- a/src/script/lua_api/l_areastore.cpp +++ b/src/script/lua_api/l_areastore.cpp @@ -70,6 +70,22 @@ static inline void push_areas(lua_State *L, const std::vector &areas, } } +// Deserializes value and handles errors +static int deserialization_helper(lua_State *L, AreaStore *as, + std::istream &is) +{ + try { + as->deserialize(is); + } catch (const SerializationError &e) { + lua_pushboolean(L, false); + lua_pushstring(L, e.what()); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + // garbage collector int LuaAreaStore::gc_object(lua_State *L) { @@ -217,17 +233,15 @@ int LuaAreaStore::l_set_cache_params(lua_State *L) return 0; } -#if 0 // to_string() int LuaAreaStore::l_to_string(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaAreaStore *o = checkobject(L, 1); - AreaStore *ast = o->as; std::ostringstream os(std::ios_base::binary); - ast->serialize(os); + o->as->serialize(os); std::string str = os.str(); lua_pushlstring(L, str.c_str(), str.length()); @@ -258,16 +272,12 @@ int LuaAreaStore::l_from_string(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaAreaStore *o = checkobject(L, 1); - AreaStore *ast = o->as; size_t len; const char *str = luaL_checklstring(L, 2, &len); std::istringstream is(std::string(str, len), std::ios::binary); - bool success = ast->deserialize(is); - - lua_pushboolean(L, success); - return 1; + return deserialization_helper(L, o->as, is); } // from_file(filename) @@ -276,18 +286,13 @@ int LuaAreaStore::l_from_file(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaAreaStore *o = checkobject(L, 1); - AreaStore *ast = o->as; const char *filename = luaL_checkstring(L, 2); CHECK_SECURE_PATH_OPTIONAL(L, filename); std::ifstream is(filename, std::ios::binary); - bool success = ast->deserialize(is); - - lua_pushboolean(L, success); - return 1; + return deserialization_helper(L, o->as, is); } -#endif LuaAreaStore::LuaAreaStore() { @@ -377,9 +382,9 @@ const luaL_reg LuaAreaStore::methods[] = { luamethod(LuaAreaStore, reserve), luamethod(LuaAreaStore, remove_area), luamethod(LuaAreaStore, set_cache_params), - /* luamethod(LuaAreaStore, to_string), + luamethod(LuaAreaStore, to_string), luamethod(LuaAreaStore, to_file), luamethod(LuaAreaStore, from_string), - luamethod(LuaAreaStore, from_file),*/ + luamethod(LuaAreaStore, from_file), {0,0} }; diff --git a/src/script/lua_api/l_areastore.h b/src/script/lua_api/l_areastore.h index 6321de6d..4bd94ceb 100644 --- a/src/script/lua_api/l_areastore.h +++ b/src/script/lua_api/l_areastore.h @@ -43,11 +43,11 @@ private: static int l_set_cache_params(lua_State *L); - /* static int l_to_string(lua_State *L); + static int l_to_string(lua_State *L); static int l_to_file(lua_State *L); static int l_from_string(lua_State *L); - static int l_from_file(lua_State *L); */ + static int l_from_file(lua_State *L); public: AreaStore *as; diff --git a/src/unittest/test_areastore.cpp b/src/unittest/test_areastore.cpp index cac9e0b5..62d446f5 100644 --- a/src/unittest/test_areastore.cpp +++ b/src/unittest/test_areastore.cpp @@ -31,6 +31,7 @@ public: void genericStoreTest(AreaStore *store); void testVectorStore(); void testSpatialStore(); + void testSerialization(); }; static TestAreaStore g_test_instance; @@ -41,6 +42,7 @@ void TestAreaStore::runTests(IGameDef *gamedef) #if USE_SPATIAL TEST(testSpatialStore); #endif + TEST(testSerialization); } //////////////////////////////////////////////////////////////////////////////// @@ -121,3 +123,40 @@ void TestAreaStore::genericStoreTest(AreaStore *store) store->removeArea(d.id); } +void TestAreaStore::testSerialization() +{ + VectorAreaStore store; + + Area a(v3s16(-1, 0, 1), v3s16(0, 1, 2)); + a.data = "Area A"; + store.insertArea(&a); + + Area b(v3s16(123, 456, 789), v3s16(32000, 100, 10)); + b.data = "Area B"; + store.insertArea(&b); + + std::ostringstream os; + store.serialize(os); + std::string str = os.str(); + + std::string str_wanted("\x00" // Version + "\x00\x02" // Count + "\xFF\xFF\x00\x00\x00\x01" // Area A min edge + "\x00\x00\x00\x01\x00\x02" // Area A max edge + "\x00\x06" // Area A data length + "Area A" // Area A data + "\x00\x7B\x00\x64\x00\x0A" // Area B min edge (last two swapped with max edge for sorting) + "\x7D\x00\x01\xC8\x03\x15" // Area B max edge (^) + "\x00\x06" // Area B data length + "Area B", // Area B data + 1 + 2 + + 6 + 6 + 2 + 6 + + 6 + 6 + 2 + 6); + UASSERTEQ(std::string, str, str_wanted); + + std::istringstream is(str); + store.deserialize(is); + + UASSERTEQ(size_t, store.size(), 4); // deserialize() doesn't clear the store +} + diff --git a/src/util/areastore.cpp b/src/util/areastore.cpp index 357ce37f..17addb3a 100644 --- a/src/util/areastore.cpp +++ b/src/util/areastore.cpp @@ -62,56 +62,41 @@ const Area *AreaStore::getArea(u32 id) const return &it->second; } -#if 0 -Currently, serialisation is commented out. This is because of multiple reasons: -1. Why do we store the areastore into a file, why not into the database? -2. We don't use libspatial's serialisation, but we should, or perhaps not, because - it would remove the ability to switch. Perhaps write migration routines? -3. Various things need fixing, e.g. the size is serialized as - c++ implementation defined size_t -bool AreaStore::deserialize(std::istream &is) -{ - u8 ver = readU8(is); - if (ver != 1) - return false; - u16 count_areas = readU16(is); - for (u16 i = 0; i < count_areas; i++) { - // deserialize an area - Area a; - a.id = readU32(is); - a.minedge = readV3S16(is); - a.maxedge = readV3S16(is); - a.datalen = readU16(is); - a.data = new char[a.datalen]; - is.read((char *) a.data, a.datalen); - insertArea(a); - } - return true; -} - - -static bool serialize_area(void *ostr, Area *a) -{ - std::ostream &os = *((std::ostream *) ostr); - writeU32(os, a->id); - writeV3S16(os, a->minedge); - writeV3S16(os, a->maxedge); - writeU16(os, a->datalen); - os.write(a->data, a->datalen); - - return false; -} - - void AreaStore::serialize(std::ostream &os) const { - // write initial data - writeU8(os, 1); // serialisation version - writeU16(os, areas_map.size()); //DANGER: not platform independent - forEach(&serialize_area, &os); + writeU8(os, 0); // Serialisation version + + // TODO: Compression? + writeU16(os, areas_map.size()); + for (AreaMap::const_iterator it = areas_map.begin(); + it != areas_map.end(); ++it) { + const Area &a = it->second; + writeV3S16(os, a.minedge); + writeV3S16(os, a.maxedge); + writeU16(os, a.data.size()); + os.write(a.data.data(), a.data.size()); + } } -#endif +void AreaStore::deserialize(std::istream &is) +{ + u8 ver = readU8(is); + if (ver != 0) + throw SerializationError("Unknown AreaStore " + "serialization version!"); + + u16 num_areas = readU16(is); + for (u32 i = 0; i < num_areas; ++i) { + Area a; + a.minedge = readV3S16(is); + a.maxedge = readV3S16(is); + u16 data_len = readU16(is); + char *data = new char[data_len]; + is.read(data, data_len); + a.data = std::string(data, data_len); + insertArea(&a); + } +} void AreaStore::invalidateCache() { @@ -226,18 +211,6 @@ void VectorAreaStore::getAreasInArea(std::vector *result, } } -#if 0 -bool SimpleAreaStore::forEach(ForEachCallback callback, void *arg) const -{ - for (size_t i = 0; i < m_areas.size(); ++i) { - if (callback(m_areas[i], arg)) { - return true; - } - } - return false; -} -#endif - #if USE_SPATIAL static inline SpatialIndex::Region get_spatial_region(const v3s16 minedge, @@ -301,14 +274,6 @@ void SpatialAreaStore::getAreasInArea(std::vector *result, } } -#if 0 -bool SpatialAreaStore::forEach(ForEachCallback callback, void *arg) const -{ - // TODO ?? (this is only needed for serialisation, but libspatial has its own serialisation) - return false; -} -#endif - SpatialAreaStore::~SpatialAreaStore() { delete m_tree; diff --git a/src/util/areastore.h b/src/util/areastore.h index 5b4e9a71..ab6bd76a 100644 --- a/src/util/areastore.h +++ b/src/util/areastore.h @@ -92,16 +92,14 @@ public: /// or NULL if it doesn't exist. const Area *getArea(u32 id) const; -#if 0 - typedef bool (*ForEachCallback)(const Area *a, void *arg); - /// Calls a passed function for every stored area, until the - /// callback returns true. If that happens, it returns true, - /// if the search is exhausted, it returns false. - virtual bool forEach(ForEachCallback, void *arg=NULL) const = 0; - + /// Serializes the store's areas to a binary ostream. void serialize(std::ostream &is) const; - bool deserialize(std::istream &is); -#endif + + /// Deserializes the Areas from a binary istream. + /// This does not currently clear the AreaStore before adding the + /// areas, making it possible to deserialize multiple serialized + /// AreaStores. + void deserialize(std::istream &is); protected: /// Invalidates the getAreasForPos cache. @@ -141,7 +139,6 @@ public: virtual bool removeArea(u32 id); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap); - //virtual bool forEach(ForEachCallback, void *arg) const; protected: virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); @@ -162,7 +159,6 @@ public: virtual bool removeArea(u32 id); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap); - //virtual bool forEach(ForEachCallback, void *arg) const; protected: virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); From 3e9b7d3da78c904e3c4a6586ed8ce4972cbda88c Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Fri, 30 Oct 2015 20:38:22 -0400 Subject: [PATCH 18/32] Add AreaStore custom ID API --- builtin/game/features.lua | 1 + doc/lua_api.txt | 2 +- src/script/lua_api/l_areastore.cpp | 5 ++++- src/util/areastore.cpp | 6 ++++-- src/util/areastore.h | 7 ++++--- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index a5f17e54..2aad458d 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -8,6 +8,7 @@ core.features = { use_texture_alpha = true, no_legacy_abms = true, texture_names_parens = true, + area_store_custom_ids = true, } function core.has_feature(arg) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 50fa2527..65af5157 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2727,7 +2727,7 @@ If you chose the parameter-less constructor, a fast implementation will be autom * `get_area(id, include_borders, include_data)`: returns the area with the id `id`. (optional) Boolean values `include_borders` and `include_data` control what's copied. * `get_areas_for_pos(pos, include_borders, include_data)`: returns all areas that contain the position `pos`. (optional) Boolean values `include_borders` and `include_data` control what's copied. * `get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data)`: returns all areas that contain all nodes inside the area specified by `edge1` and `edge2` (inclusive). If `accept_overlap` is true, also areas are returned that have nodes in common with the specified area. (optional) Boolean values `include_borders` and `include_data` control what's copied. -* `insert_area(edge1, edge2, data)`: inserts an area into the store. Returns the new area's ID, or nil if the insertion failed. The (inclusive) positions `edge1` and `edge2` describe the area, `data` is a string stored with the area. +* `insert_area(edge1, edge2, data, [id])`: inserts an area into the store. Returns the new area's ID, or nil if the insertion failed. The (inclusive) positions `edge1` and `edge2` describe the area. `data` is a string stored with the area. If passed, `id` will be used as the internal area ID, it must be a unique number between 0 and 2^32-2. If you use the `id` parameter you must always use it, or insertions are likely to fail due to conflicts. * `reserve(count)`: reserves resources for at most `count` many contained areas. Only needed for efficiency, and only some implementations profit. * `remove_area(id)`: removes the area with the given id from the store, returns success. * `set_cache_params(params)`: sets params for the included prefiltering cache. Calling invalidates the cache, so that its elements have to be newly generated. diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp index 261baf6c..20e7875c 100644 --- a/src/script/lua_api/l_areastore.cpp +++ b/src/script/lua_api/l_areastore.cpp @@ -164,7 +164,7 @@ int LuaAreaStore::l_get_areas_in_area(lua_State *L) return 1; } -// insert_area(edge1, edge2, data) +// insert_area(edge1, edge2, data, id) int LuaAreaStore::l_insert_area(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -179,6 +179,9 @@ int LuaAreaStore::l_insert_area(lua_State *L) a.data = std::string(data, d_len); + if (lua_isnumber(L, 5)) + a.id = lua_tonumber(L, 5); + if (!ast->insertArea(&a)) return 0; diff --git a/src/util/areastore.cpp b/src/util/areastore.cpp index 17addb3a..58f08a8c 100644 --- a/src/util/areastore.cpp +++ b/src/util/areastore.cpp @@ -160,7 +160,8 @@ void AreaStore::getAreasForPos(std::vector *result, v3s16 pos) bool VectorAreaStore::insertArea(Area *a) { - a->id = getNextId(); + if (a->id == U32_MAX) + a->id = getNextId(); std::pair res = areas_map.insert(std::make_pair(a->id, *a)); if (!res.second) @@ -232,7 +233,8 @@ static inline SpatialIndex::Point get_spatial_point(const v3s16 pos) bool SpatialAreaStore::insertArea(Area *a) { - a->id = getNextId(); + if (a->id == U32_MAX) + a->id = getNextId(); if (!areas_map.insert(std::make_pair(a->id, *a)).second) // ID is not unique return false; diff --git a/src/util/areastore.h b/src/util/areastore.h index ab6bd76a..bebecfd7 100644 --- a/src/util/areastore.h +++ b/src/util/areastore.h @@ -38,9 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc., struct Area { - Area() {} + Area() : id(U32_MAX) {} Area(const v3s16 &mine, const v3s16 &maxe) : - minedge(mine), maxedge(maxe) + id(U32_MAX), minedge(mine), maxedge(maxe) { sortBoxVerticies(minedge, maxedge); } @@ -68,7 +68,8 @@ public: size_t size() const { return areas_map.size(); } /// Add an area to the store. - /// Updates the area's ID. + /// Updates the area's ID if it hasn't already been set. + /// @return Whether the area insertion was successful. virtual bool insertArea(Area *a) = 0; /// Removes an area from the store by ID. From 9cf7a3ef96ff81380b10fb32d60e28d375b01484 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Sun, 6 Mar 2016 12:02:21 -0800 Subject: [PATCH 19/32] Add consistent monotonic day counter - get_day_count() I've written several experimental bits of code that revolve around the need for a consistent calendar, but implementing one is extremely hard in mods due to time changes and mods overriding core.get_timeofday(), which will conflict. The second part of the problem is that doing this from a mod requires constant maintenance of a settings file. An implementation in core is trivial, however, and solves all of these problems at virtually no cost: No extra branches in server steps, and a single branch when minetest.set_time_of_day(), which is entirely reasonable. We store the day_count value in env_meta.txt. The use case is obvious: This change allows mods to create an actual virtual calendar, or properly account for seasonal changes, etc.. We add a "/days" chatcommand that displays the current day count. No permissions are needed. It can only retrieve the day count, not modify it. --- builtin/game/chatcommands.lua | 7 +++++++ doc/lua_api.txt | 2 ++ src/environment.cpp | 20 +++++++++++++++++++- src/environment.h | 5 +++++ src/script/lua_api/l_env.cpp | 10 ++++++++++ src/script/lua_api/l_env.h | 3 +++ 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 2c7a0b53..0b197664 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -785,6 +785,13 @@ core.register_chatcommand("time", { end, }) +core.register_chatcommand("days", { + description = "Display day count", + func = function(name, param) + return true, "Current day is " .. core.get_day_count() + end +}) + core.register_chatcommand("shutdown", { description = "shutdown server", privs = {server=true}, diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 65af5157..e2ab65dd 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1998,6 +1998,8 @@ and `minetest.auth_reload` call the authetification handler. * `val` is between `0` and `1`; `0` for midnight, `0.5` for midday * `minetest.get_timeofday()` * `minetest.get_gametime()`: returns the time, in seconds, since the world was created +* `minetest.get_day_count()`: returns number days elapsed since world was created, + * accounting for time changes. * `minetest.find_node_near(pos, radius, nodenames)`: returns pos or `nil` * `radius`: using a maximum metric * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` diff --git a/src/environment.cpp b/src/environment.cpp index 6da376a1..0d00ed17 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -208,6 +208,8 @@ void Environment::setDayNightRatioOverride(bool enable, u32 value) void Environment::setTimeOfDay(u32 time) { MutexAutoLock lock(this->m_time_lock); + if (m_time_of_day > time) + m_day_count++; m_time_of_day = time; m_time_of_day_f = (float)time / 24000.0; } @@ -238,8 +240,10 @@ void Environment::stepTimeOfDay(float dtime) bool sync_f = false; if (units > 0) { // Sync at overflow - if (m_time_of_day + units >= 24000) + if (m_time_of_day + units >= 24000) { sync_f = true; + m_day_count++; + } m_time_of_day = (m_time_of_day + units) % 24000; if (sync_f) m_time_of_day_f = (float)m_time_of_day / 24000.0; @@ -256,6 +260,13 @@ void Environment::stepTimeOfDay(float dtime) } } +u32 Environment::getDayCount() +{ + // Atomic counter + return m_day_count; +} + + /* ABMWithState */ @@ -727,6 +738,7 @@ void ServerEnvironment::saveMeta() args.setU64("lbm_introduction_times_version", 1); args.set("lbm_introduction_times", m_lbm_mgr.createIntroductionTimesString()); + args.setU64("day_count", m_day_count); args.writeLines(ss); ss<<"EnvArgsEnd\n"; @@ -792,6 +804,12 @@ void ServerEnvironment::loadMeta() } m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time); + try { + m_day_count = args.getU64("day_count"); + } catch (SettingNotFoundException &e) { + // If missing, start the day counter + m_day_count = 0; + } } void ServerEnvironment::loadDefaultMeta() diff --git a/src/environment.h b/src/environment.h index 448ed70e..9b91a092 100644 --- a/src/environment.h +++ b/src/environment.h @@ -95,6 +95,8 @@ public: void setDayNightRatioOverride(bool enable, u32 value); + u32 getDayCount(); + // counter used internally when triggering ABMs u32 m_added_objects; @@ -117,6 +119,9 @@ protected: // Overriding the day-night ratio is useful for custom sky visuals bool m_enable_day_night_ratio_override; u32 m_day_night_ratio_override; + // Days from the server start, accounts for time shift + // in game (e.g. /time or bed usage) + Atomic m_day_count; /* * Above: values managed by m_time_lock */ diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index f4ddc2af..af89da9a 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -561,6 +561,15 @@ int ModApiEnvMod::l_get_timeofday(lua_State *L) return 1; } +// get_day_count() -> int +int ModApiEnvMod::l_get_day_count(lua_State *L) +{ + GET_ENV_PTR; + + lua_pushnumber(L, env->getDayCount()); + return 1; +} + // get_gametime() int ModApiEnvMod::l_get_gametime(lua_State *L) { @@ -1055,6 +1064,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(set_timeofday); API_FCT(get_timeofday); API_FCT(get_gametime); + API_FCT(get_day_count); API_FCT(find_node_near); API_FCT(find_nodes_in_area); API_FCT(find_nodes_in_area_under_air); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 0e385ffe..89dd7978 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -113,6 +113,9 @@ private: // get_gametime() static int l_get_gametime(lua_State *L); + // get_day_count() -> int + static int l_get_day_count(lua_State *L); + // find_node_near(pos, radius, nodenames) -> pos or nil // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" static int l_find_node_near(lua_State *L); From b45a71ec0d891bac34831d2ef065aef23a617aed Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Mon, 7 Mar 2016 20:23:34 -0800 Subject: [PATCH 20/32] Avoid try/catch for settings. We can just test for the presence of these settings nicely here, no need to use a try / catch construct. --- src/environment.cpp | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 0d00ed17..1de44e75 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -776,19 +776,13 @@ void ServerEnvironment::loadMeta() throw SerializationError("Couldn't load env meta game_time"); } - try { - setTimeOfDay(args.getU64("time_of_day")); - } catch (SettingNotFoundException &e) { - // This is not as important - setTimeOfDay(9000); - } + setTimeOfDay(args.exists("time_of_day") ? + // set day to morning by default + args.getU64("time_of_day") : 9000); - try { - m_last_clear_objects_time = args.getU64("last_clear_objects_time"); - } catch (SettingNotFoundException &e) { + m_last_clear_objects_time = args.exists("last_clear_objects_time") ? // If missing, do as if clearObjects was never called - m_last_clear_objects_time = 0; - } + args.getU64("last_clear_objects_time") : 0; std::string lbm_introduction_times = ""; try { @@ -804,12 +798,8 @@ void ServerEnvironment::loadMeta() } m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time); - try { - m_day_count = args.getU64("day_count"); - } catch (SettingNotFoundException &e) { - // If missing, start the day counter - m_day_count = 0; - } + m_day_count = args.exists("day_count") ? + args.getU64("day_count") : 0; } void ServerEnvironment::loadDefaultMeta() From ac65ce9c1c9dcf3d2f9adf8d944cc730ab4ee643 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Sun, 28 Feb 2016 21:53:26 -0800 Subject: [PATCH 21/32] Introduce "protection_bypass" privilege. This privilege allows map protection bypassing for server operators and world moderators. Initially I had thought that bypassing protection mods would have been something that could entirely be done inside mods and minetest_game, but the concept of protection is defined in core, in the code of core.is_protected(). I don't feel that it would be logical to introduce a protection concept in core, but not some way around that for server operators to maintain map parts that need fixing, de-griefing or cleanup. Others had noticed the same problems, and proposed a patch to minetest_game. That patch is fine by itself, but it fails to add protection bypass functionality for digging normal nodes and placing nodes. So, instead, we indroduce the new priv "protection_bypass" in core, and modify 'on_place_node' and 'node_dig' to allow bypassing node protections if the player holds this priv. This priv was tested with protector redo by tenplus1. A followup patch to Minetest Game will include allowing special checks for doors, trapdoors, chests in Minetest Game. Protection mods will likely want to mimic the changes in their relevant code sections. --- builtin/game/item.lua | 6 ++++-- builtin/game/privileges.lua | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/builtin/game/item.lua b/builtin/game/item.lua index c42aff5b..36c2c1a6 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -233,7 +233,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) place_to = {x = under.x, y = under.y, z = under.z} end - if core.is_protected(place_to, placer:get_player_name()) then + if core.is_protected(place_to, placer:get_player_name()) and + not minetest.check_player_privs(placer, "protection_bypass") then core.log("action", placer:get_player_name() .. " tried to place " .. def.name .. " at protected position " @@ -444,7 +445,8 @@ function core.node_dig(pos, node, digger) return end - if core.is_protected(pos, digger:get_player_name()) then + 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() .. " tried to dig " .. node.name .. " at protected position " diff --git a/builtin/game/privileges.lua b/builtin/game/privileges.lua index 7e6387c7..bd5ead62 100644 --- a/builtin/game/privileges.lua +++ b/builtin/game/privileges.lua @@ -32,6 +32,7 @@ core.register_privilege("settime", "Can use /time") core.register_privilege("privs", "Can modify privileges") core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges") core.register_privilege("server", "Can do server maintenance stuff") +core.register_privilege("protection_bypass", "Can bypass node protection in the world") core.register_privilege("shout", "Can speak in chat") core.register_privilege("ban", "Can ban and unban players") core.register_privilege("kick", "Can kick players") From 7f54168a1000cebf6ef1448184f3e572370c26d8 Mon Sep 17 00:00:00 2001 From: paramat Date: Fri, 11 Mar 2016 16:24:55 +0000 Subject: [PATCH 22/32] Documentation: Clarify global and mapgen-specific mapgen flags --- builtin/settingtypes.txt | 31 +++++++++++++++++++++---------- minetest.conf.example | 31 +++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 3c808194..1672e08c 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -819,8 +819,10 @@ map_generation_limit (Map generation limit) int 31000 0 31000 # Global map generation attributes. # In Mapgen v6 the 'decorations' flag controls all decorations except trees # and junglegrass, in all other mapgens this flag controls all decorations. +# The default flags set in the engine are: caves, light, decorations +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. +# Flags starting with 'no' are used to explicitly disable them. mg_flags (Mapgen flags) flags caves,dungeons,light,decorations caves,dungeons,light,decorations,nocaves,nodungeons,nolight,nodecorations [**Advanced] @@ -875,9 +877,11 @@ mgv5_np_cave2 (Mapgen v5 cave2 noise parameters) noise_params 0, 12, (50, 50, 50 [***Mapgen v6] # Map generation attributes specific to Mapgen v6. -# When snowbiomes are enabled jungles are enabled and the jungles flag is ignored. +# When snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored. +# The default flags set in the engine are: biomeblend, mudflow +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. +# Flags starting with 'no' are used to explicitly disable them. mgv6_spflags (Mapgen v6 flags) flags jungles,biomeblend,mudflow,snowbiomes,trees jungles,biomeblend,mudflow,snowbiomes,flat,trees,nojungles,nobiomeblend,nomudflow,nosnowbiomes,noflat,notrees # Controls size of deserts and beaches in Mapgen v6. @@ -900,9 +904,11 @@ mgv6_np_apple_trees (Mapgen v6 apple trees noise parameters) noise_params 0, 1, [***Mapgen v7] # Map generation attributes specific to Mapgen v7. -# 'ridges' are the rivers. +# The 'ridges' flag controls the rivers. +# The default flags set in the engine are: mountains, ridges +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. +# Flags starting with 'no' are used to explicitly disable them. mgv7_spflags (Mapgen v7 flags) flags mountains,ridges mountains,ridges,nomountains,noridges mgv7_np_terrain_base (Mapgen v7 terrain base noise parameters) noise_params 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0 @@ -920,9 +926,11 @@ mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (100, 100, [***Mapgen flat] # Map generation attributes specific to Mapgen flat. -# Occasional lakes and hills added to the flat world. +# Occasional lakes and hills can be added to the flat world. +# The default flags set in the engine are: none +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. +# Flags starting with 'no' are used to explicitly disable them. mgflat_spflags (Mapgen flat flags) flags lakes,hills,,nolakes,nohills # Y of flat ground. @@ -1027,10 +1035,13 @@ mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, ( [****General] # Map generation attributes specific to Mapgen Valleys. +# 'altitude_chill' makes higher elevations colder, which may cause biome issues. +# 'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool, +# it may interfere with delicately adjusted biomes. +# The default flags set in the engine are: altitude_chill, humid_rivers +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. -# "altitude_chill" makes higher elevations colder, which may cause biome issues. -# "humid_rivers" modifies the humidity around rivers and in areas where water would tend to pool. It may interfere with delicately adjusted biomes. +# Flags starting with 'no' are used to explicitly disable them. mg_valleys_spflags (Valleys C Flags) flags altitude_chill,humid_rivers altitude_chill,noaltitude_chill,humid_rivers,nohumid_rivers # The altitude at which temperature drops by 20C diff --git a/minetest.conf.example b/minetest.conf.example index 6b7347cc..3177360c 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1009,8 +1009,10 @@ # Global map generation attributes. # In Mapgen v6 the 'decorations' flag controls all decorations except trees # and junglegrass, in all other mapgens this flag controls all decorations. +# The default flags set in the engine are: caves, light, decorations +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. +# Flags starting with 'no' are used to explicitly disable them. # type: flags possible values: caves, dungeons, light, decorations, nocaves, nodungeons, nolight, nodecorations # mg_flags = caves,dungeons,light,decorations @@ -1077,9 +1079,11 @@ #### Mapgen v6 # Map generation attributes specific to Mapgen v6. -# When snowbiomes are enabled jungles are enabled and the jungles flag is ignored. +# When snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored. +# The default flags set in the engine are: biomeblend, mudflow +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. +# Flags starting with 'no' are used to explicitly disable them. # type: flags possible values: jungles, biomeblend, mudflow, snowbiomes, flat, trees, nojungles, nobiomeblend, nomudflow, nosnowbiomes, noflat, notrees # mgv6_spflags = jungles,biomeblend,mudflow,snowbiomes,trees @@ -1127,9 +1131,11 @@ #### Mapgen v7 # Map generation attributes specific to Mapgen v7. -# 'ridges' are the rivers. +# The 'ridges' flag controls the rivers. +# The default flags set in the engine are: mountains, ridges +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. +# Flags starting with 'no' are used to explicitly disable them. # type: flags possible values: mountains, ridges, nomountains, noridges # mgv7_spflags = mountains,ridges @@ -1169,9 +1175,11 @@ #### Mapgen flat # Map generation attributes specific to Mapgen flat. -# Occasional lakes and hills added to the flat world. +# Occasional lakes and hills can be added to the flat world. +# The default flags set in the engine are: none +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. +# Flags starting with 'no' are used to explicitly disable them. # type: flags possible values: lakes, hills, , nolakes, nohills # mgflat_spflags = @@ -1303,10 +1311,13 @@ ##### General # Map generation attributes specific to Mapgen Valleys. +# 'altitude_chill' makes higher elevations colder, which may cause biome issues. +# 'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool, +# it may interfere with delicately adjusted biomes. +# The default flags set in the engine are: altitude_chill, humid_rivers +# The flags string modifies the engine defaults. # Flags that are not specified in the flag string are not modified from the default. -# Flags starting with "no" are used to explicitly disable them. -# "altitude_chill" makes higher elevations colder, which may cause biome issues. -# "humid_rivers" modifies the humidity around rivers and in areas where water would tend to pool. It may interfere with delicately adjusted biomes. +# Flags starting with 'no' are used to explicitly disable them. # type: flags possible values: altitude_chill, noaltitude_chill, humid_rivers, nohumid_rivers # mg_valleys_spflags = altitude_chill,humid_rivers From ff125e3a1bfb6e0b3d192d2a0007d22a68080fb4 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Fri, 11 Mar 2016 18:41:56 +0100 Subject: [PATCH 23/32] Fix ask_reconnect_on_crash option being ignored Since commit 3b50b2766aeb09c9fc0ad0ea07426bb2187df3d7 "Optional reconnect functionality" there is a config option named ask_reconnect_on_crash. It asks the client to reconnect to the server if the server crashed. It has been implemeted and works, but due to a function parameter not being passed it never showed effect. This patch adds the parameter and fixes the bug. Also fixes the `reconnect` option of minetest.request_shutdown being ignored. --- src/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index 75ca7448..2fa8c4cf 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2568,7 +2568,7 @@ void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode const std::string &str_reason, bool reconnect) { if (proto_ver >= 25) { - SendAccessDenied(peer_id, reason, str_reason); + SendAccessDenied(peer_id, reason, str_reason, reconnect); } else { std::wstring wreason = utf8_to_wide( reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason : From 207f76a87eed52448f13cc62eb0733e9c043c8a1 Mon Sep 17 00:00:00 2001 From: paramat Date: Fri, 11 Mar 2016 21:56:32 +0000 Subject: [PATCH 24/32] Documentation: Auto-update conf.example and settings_translation_file.cpp --- minetest.conf.example | 2 +- src/settings_translation_file.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index 3177360c..1587cba7 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -986,7 +986,7 @@ # Name of map generator to be used when creating a new world. # Creating a world in the main menu will override this. -# type: enum values: v5, v6, v7, flat, fractal, valleys, singlenode +# type: enum values: v5, v6, v7, flat, valleys, fractal, singlenode # mg_name = v6 # Water surface level of the world. diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index d8e08521..e92a34cb 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -421,7 +421,7 @@ fake_function() { gettext("Map generation limit"); gettext("Where the map generator stops.\nPlease note:\n- Limited to 31000 (setting above has no effect)\n- The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks).\n- Those groups have an offset of -32, -32 nodes from the origin.\n- Only groups which are within the map_generation_limit are generated"); gettext("Mapgen flags"); - gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand junglegrass, in all other mapgens this flag controls all decorations.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them."); + gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand junglegrass, in all other mapgens this flag controls all decorations.\nThe default flags set in the engine are: caves, light, decorations\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Advanced"); gettext("Chunk size"); gettext("Size of chunks to be generated at once by mapgen, stated in mapblocks (16 nodes)."); @@ -448,7 +448,7 @@ fake_function() { gettext("Mapgen v5 cave2 noise parameters"); gettext("Mapgen v6"); gettext("Mapgen v6 flags"); - gettext("Map generation attributes specific to Mapgen v6.\nWhen snowbiomes are enabled jungles are enabled and the jungles flag is ignored.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them."); + gettext("Map generation attributes specific to Mapgen v6.\nWhen snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.\nThe default flags set in the engine are: biomeblend, mudflow\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Mapgen v6 desert frequency"); gettext("Controls size of deserts and beaches in Mapgen v6.\nWhen snowbiomes are enabled 'mgv6_freq_desert' is ignored."); gettext("Mapgen v6 beach frequency"); @@ -465,7 +465,7 @@ fake_function() { gettext("Mapgen v6 apple trees noise parameters"); gettext("Mapgen v7"); gettext("Mapgen v7 flags"); - gettext("Map generation attributes specific to Mapgen v7.\n'ridges' are the rivers.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them."); + gettext("Map generation attributes specific to Mapgen v7.\nThe 'ridges' flag controls the rivers.\nThe default flags set in the engine are: mountains, ridges\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Mapgen v7 terrain base noise parameters"); gettext("Mapgen v7 terrain altitude noise parameters"); gettext("Mapgen v7 terrain persistation noise parameters"); @@ -479,7 +479,7 @@ fake_function() { gettext("Mapgen v7 cave2 noise parameters"); gettext("Mapgen flat"); gettext("Mapgen flat flags"); - gettext("Map generation attributes specific to Mapgen flat.\nOccasional lakes and hills added to the flat world.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them."); + gettext("Map generation attributes specific to Mapgen flat.\nOccasional lakes and hills can be added to the flat world.\nThe default flags set in the engine are: none\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Mapgen flat ground level"); gettext("Y of flat ground."); gettext("Mapgen flat large cave depth"); @@ -523,7 +523,7 @@ fake_function() { gettext("Mapgen Valleys"); gettext("General"); gettext("Valleys C Flags"); - gettext("Map generation attributes specific to Mapgen Valleys.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them.\n\"altitude_chill\" makes higher elevations colder, which may cause biome issues.\n\"humid_rivers\" modifies the humidity around rivers and in areas where water would tend to pool. It may interfere with delicately adjusted biomes."); + gettext("Map generation attributes specific to Mapgen Valleys.\n'altitude_chill' makes higher elevations colder, which may cause biome issues.\n'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool,\nit may interfere with delicately adjusted biomes.\nThe default flags set in the engine are: altitude_chill, humid_rivers\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Altitude Chill"); gettext("The altitude at which temperature drops by 20C"); gettext("Large cave depth"); From ac37bd8b97bd2e71c0f2c1c8e46c734b7a5a3e5a Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Sat, 12 Mar 2016 11:58:02 -0500 Subject: [PATCH 25/32] Fix chat console not opening after formspec opened over it The MainMenuManager set the console invisible when a formspec opened over it, but didn't properly close it, and the chat console never set itself visible again. --- src/guiChatConsole.cpp | 17 +++++++++++++++++ src/guiChatConsole.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index d59937c3..ec9d3ffd 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -122,6 +122,8 @@ void GUIChatConsole::openConsole(f32 height) m_desired_height_fraction = height; m_desired_height = height * m_screensize.Y; reformatConsole(); + m_animate_time_old = getTimeMs(); + IGUIElement::setVisible(true); Environment->setFocus(this); m_menumgr->createdMenu(this); } @@ -243,6 +245,11 @@ void GUIChatConsole::animate(u32 msec) { // animate the console height s32 goal = m_open ? m_desired_height : 0; + + // Set invisible if close animation finished (reset by openConsole) + if (!m_open && m_height == 0) + IGUIElement::setVisible(false); + if (m_height != goal) { s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0); @@ -628,3 +635,13 @@ bool GUIChatConsole::OnEvent(const SEvent& event) return Parent ? Parent->OnEvent(event) : false; } +void GUIChatConsole::setVisible(bool visible) +{ + m_open = visible; + IGUIElement::setVisible(visible); + if (!visible) { + m_height = 0; + recalculateConsolePosition(); + } +} + diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h index fe595f28..3013a1d3 100644 --- a/src/guiChatConsole.h +++ b/src/guiChatConsole.h @@ -77,6 +77,8 @@ public: virtual bool OnEvent(const SEvent& event); + virtual void setVisible(bool visible); + private: void reformatConsole(); void recalculateConsolePosition(); From f11d4734971a8a7caf016b70b70554115b08c1a6 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 25 Feb 2016 00:16:31 -0800 Subject: [PATCH 26/32] Nodebox: Allow nodeboxes to "connect" We introduce a new nodebox type "connected", and allow these nodes to have optional nodeboxes that connect it to other connecting nodeboxes. This is all done at scenedraw time in the client. The client will inspect the surrounding nodes and if they are to be connected to, it will draw the appropriate connecting nodeboxes to make those connections. In the node_box definition, we have to specify separate nodeboxes for each valid connection. This allows us to make nodes that connect only horizontally (the common case) by providing optional nodeboxes for +x, -x, +z, -z directions. Or this allows us to make wires that can connect up and down, by providing nodeboxes that connect it up and down (+y, -y) as well. The optional nodeboxes can be arrays. They are named "connect_top, "connect_bottom", "connect_front", "connect_left", "connect_back" and "connect_right". Here, "front" means the south facing side of the node that has facedir = 0. Additionally, a "fixed" nodebox list present will always be drawn, so one can make a central post, for instance. This "fixed" nodebox can be omitted, or it can be an array of nodeboxes. Collision boxes are also updated in exactly the same fashion, which allows you to walk over the upper extremities of the individual node boxes, or stand really close to them. You can also walk up node noxes that are small in height, all as expected, and unlike the NDT_FENCELIKE nodes. I've posted a screenshot demonstrating the flexibility at http://i.imgur.com/zaJq8jo.png In the screenshot, all connecting nodes are of this new subtype. Transparent textures render incorrectly, Which I don't think is related to this text, as other nodeboxes also have issues with this. A protocol bump is performed in order to be able to send older clients a nodeblock that is usable for them. In order to avoid abuse of users we send older clients a "full-size" node, so that it's impossible for them to try and walk through a fence or wall that's created in this fashion. This was tested with a pre-bump client connected against a server running the new protocol. These nodes connect to other nodes, and you can select which ones those are by specifying node names (or group names) in the connects_to string array: connects_to = { "group:fence", "default:wood" } By default, nodes do not connect to anything, allowing you to create nodes that always have to be paired in order to connect. lua_api.txt is updated to reflect the extension to the node_box API. Example lua code needed to generate these nodes can be found here: https://gist.github.com/sofar/b381c8c192c8e53e6062 --- doc/lua_api.txt | 16 ++++ src/collision.cpp | 40 +++++++++- src/content_mapblock.cpp | 41 +++++++++- src/game.cpp | 7 +- src/localplayer.cpp | 3 +- src/mapnode.cpp | 65 ++++++++++++--- src/mapnode.h | 6 +- src/network/networkprotocol.h | 1 + src/nodedef.cpp | 136 +++++++++++++++++++++++++++++--- src/nodedef.h | 13 +++ src/script/common/c_content.cpp | 54 ++++++++----- src/script/cpp_api/s_node.cpp | 1 + src/server.cpp | 3 + 13 files changed, 334 insertions(+), 52 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e2ab65dd..f51c950c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -617,6 +617,18 @@ A nodebox is defined as any of: wall_bottom = box, wall_side = box } + { + -- A node that has optional boxes depending on neighbouring nodes' + -- presence and type. See also `connects_to`. + type = "connected", + fixed = box OR {box1, box2, ...} + connect_top = box OR {box1, box2, ...} + connect_bottom = box OR {box1, box2, ...} + connect_front = box OR {box1, box2, ...} + connect_left = box OR {box1, box2, ...} + connect_back = box OR {box1, box2, ...} + connect_right = box OR {box1, box2, ...} + } A `box` is defined as: @@ -3461,6 +3473,10 @@ Definition tables light_source = 0, -- Amount of light emitted by node damage_per_second = 0, -- If player is inside node, this damage is caused node_box = {type="regular"}, -- See "Node boxes" + connects_to = nodenames, --[[ + * Used for nodebox nodes with the type == "connected" + * Specifies to what neighboring nodes connections will be drawn + * e.g. `{"group:fence", "default:wood"}` or `"default:stone"` ]] mesh = "model", selection_box = {type="regular"}, -- See "Node boxes" --[[ ^ If drawtype "nodebox" is used and selection_box is nil, then node_box is used. ]] diff --git a/src/collision.cpp b/src/collision.cpp index d847f629..a3979f1d 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -185,6 +185,13 @@ bool wouldCollideWithCeiling( return false; } +static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, + Map *map, MapNode n, int v, int *neighbors) +{ + MapNode n2 = map->getNodeNoEx(p); + if (nodedef->nodeboxConnects(n, n2)) + *neighbors |= v; +} collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, f32 pos_max_d, const aabb3f &box_0, @@ -261,12 +268,41 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Object collides into walkable nodes any_position_valid = true; - const ContentFeatures &f = gamedef->getNodeDefManager()->get(n); + INodeDefManager *nodedef = gamedef->getNodeDefManager(); + const ContentFeatures &f = nodedef->get(n); if(f.walkable == false) continue; int n_bouncy_value = itemgroup_get(f.groups, "bouncy"); - std::vector nodeboxes = n.getCollisionBoxes(gamedef->ndef()); + int neighbors = 0; + if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) { + v3s16 p2 = p; + + p2.Y++; + getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors); + + p2 = p; + p2.Y--; + getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors); + + p2 = p; + p2.Z--; + getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors); + + p2 = p; + p2.X--; + getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors); + + p2 = p; + p2.Z++; + getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors); + + p2 = p; + p2.X++; + getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors); + } + std::vector nodeboxes; + n.getCollisionBoxes(gamedef->ndef(), &nodeboxes, neighbors); for(std::vector::iterator i = nodeboxes.begin(); i != nodeboxes.end(); ++i) diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index c15b9c42..c2934f26 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -163,6 +163,14 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, } } +static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, + MeshMakeData *data, MapNode n, int v, int *neighbors) +{ + MapNode n2 = data->m_vmanip.getNodeNoEx(p); + if (nodedef->nodeboxConnects(n, n2)) + *neighbors |= v; +} + /* TODO: Fix alpha blending for special nodes Currently only the last element rendered is blended correct @@ -1501,7 +1509,38 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3f pos = intToFloat(p, BS); - std::vector boxes = n.getNodeBoxes(nodedef); + int neighbors = 0; + + // locate possible neighboring nodes to connect to + if (f.node_box.type == NODEBOX_CONNECTED) { + v3s16 p2 = p; + + p2.Y++; + getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors); + + p2 = p; + p2.Y--; + getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors); + + p2 = p; + p2.Z--; + getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors); + + p2 = p; + p2.X--; + getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors); + + p2 = p; + p2.Z++; + getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors); + + p2 = p; + p2.X++; + getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors); + } + + std::vector boxes; + n.getNodeBoxes(nodedef, &boxes, neighbors); for(std::vector::iterator i = boxes.begin(); i != boxes.end(); ++i) diff --git a/src/game.cpp b/src/game.cpp index c6125dd4..be4c46bc 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -358,7 +358,9 @@ PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_positio if (!isPointableNode(n, client, liquids_pointable)) { continue; } - std::vector boxes = n.getSelectionBoxes(nodedef); + + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes); v3s16 np(x, y, z); v3f npf = intToFloat(np, BS); @@ -389,7 +391,8 @@ PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_positio f32 d = 0.001 * BS; MapNode n = map.getNodeNoEx(pointed_pos); v3f npf = intToFloat(pointed_pos, BS); - std::vector boxes = n.getSelectionBoxes(nodedef); + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes); f32 face_min_distance = 1000 * BS; for (std::vector::const_iterator i = boxes.begin(); diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 524c6272..0c94582a 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -310,7 +310,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if (sneak_node_found) { f32 cb_max = 0; MapNode n = map->getNodeNoEx(m_sneak_node); - std::vector nodeboxes = n.getCollisionBoxes(nodemgr); + std::vector nodeboxes; + n.getCollisionBoxes(nodemgr, &nodeboxes); for (std::vector::iterator it = nodeboxes.begin(); it != nodeboxes.end(); ++it) { aabb3f box = *it; diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 671e949f..a5465887 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -214,12 +214,12 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot) } } -static std::vector transformNodeBox(const MapNode &n, - const NodeBox &nodebox, INodeDefManager *nodemgr) +void transformNodeBox(const MapNode &n, const NodeBox &nodebox, + INodeDefManager *nodemgr, std::vector *p_boxes, u8 neighbors = 0) { - std::vector boxes; - if(nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) - { + std::vector &boxes = *p_boxes; + + if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) { const std::vector &fixed = nodebox.fixed; int facedir = n.getFaceDir(nodemgr); u8 axisdir = facedir>>2; @@ -395,32 +395,71 @@ static std::vector transformNodeBox(const MapNode &n, boxes.push_back(box); } } + else if (nodebox.type == NODEBOX_CONNECTED) + { + size_t boxes_size = boxes.size(); + boxes_size += nodebox.fixed.size(); + if (neighbors & 1) + boxes_size += nodebox.connect_top.size(); + if (neighbors & 2) + boxes_size += nodebox.connect_bottom.size(); + if (neighbors & 4) + boxes_size += nodebox.connect_front.size(); + if (neighbors & 8) + boxes_size += nodebox.connect_left.size(); + if (neighbors & 16) + boxes_size += nodebox.connect_back.size(); + if (neighbors & 32) + boxes_size += nodebox.connect_right.size(); + boxes.reserve(boxes_size); + +#define BOXESPUSHBACK(c) do { \ + for (std::vector::const_iterator \ + it = (c).begin(); \ + it != (c).end(); ++it) \ + (boxes).push_back(*it); \ + } while (0) + + BOXESPUSHBACK(nodebox.fixed); + + if (neighbors & 1) + BOXESPUSHBACK(nodebox.connect_top); + if (neighbors & 2) + BOXESPUSHBACK(nodebox.connect_bottom); + if (neighbors & 4) + BOXESPUSHBACK(nodebox.connect_front); + if (neighbors & 8) + BOXESPUSHBACK(nodebox.connect_left); + if (neighbors & 16) + BOXESPUSHBACK(nodebox.connect_back); + if (neighbors & 32) + BOXESPUSHBACK(nodebox.connect_right); + } else // NODEBOX_REGULAR { boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2)); } - return boxes; } -std::vector MapNode::getNodeBoxes(INodeDefManager *nodemgr) const +void MapNode::getNodeBoxes(INodeDefManager *nodemgr, std::vector *boxes, u8 neighbors) { const ContentFeatures &f = nodemgr->get(*this); - return transformNodeBox(*this, f.node_box, nodemgr); + transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors); } -std::vector MapNode::getCollisionBoxes(INodeDefManager *nodemgr) const +void MapNode::getCollisionBoxes(INodeDefManager *nodemgr, std::vector *boxes, u8 neighbors) { const ContentFeatures &f = nodemgr->get(*this); if (f.collision_box.fixed.empty()) - return transformNodeBox(*this, f.node_box, nodemgr); + transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors); else - return transformNodeBox(*this, f.collision_box, nodemgr); + transformNodeBox(*this, f.collision_box, nodemgr, boxes, neighbors); } -std::vector MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const +void MapNode::getSelectionBoxes(INodeDefManager *nodemgr, std::vector *boxes) { const ContentFeatures &f = nodemgr->get(*this); - return transformNodeBox(*this, f.selection_box, nodemgr); + transformNodeBox(*this, f.selection_box, nodemgr, boxes); } u8 MapNode::getMaxLevel(INodeDefManager *nodemgr) const diff --git a/src/mapnode.h b/src/mapnode.h index 7cc25c60..4db88861 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -240,17 +240,17 @@ struct MapNode /* Gets list of node boxes (used for rendering (NDT_NODEBOX)) */ - std::vector getNodeBoxes(INodeDefManager *nodemgr) const; + void getNodeBoxes(INodeDefManager *nodemgr, std::vector *boxes, u8 neighbors = 0); /* Gets list of selection boxes */ - std::vector getSelectionBoxes(INodeDefManager *nodemgr) const; + void getSelectionBoxes(INodeDefManager *nodemg, std::vector *boxes); /* Gets list of collision boxes */ - std::vector getCollisionBoxes(INodeDefManager *nodemgr) const; + void getCollisionBoxes(INodeDefManager *nodemgr, std::vector *boxes, u8 neighbors = 0); /* Liquid helpers diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index dc15326d..7cde6d76 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -135,6 +135,7 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL_VERSION 27: backface_culling: backwards compatibility for playing with newer client on pre-27 servers. + Add nodedef v3 - connected nodeboxes */ #define LATEST_PROTOCOL_VERSION 27 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index ba9b4abf..85cd848a 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "exceptions.h" #include "debug.h" #include "gamedef.h" +#include "mapnode.h" #include // Used in applyTextureOverrides() /* @@ -48,44 +49,91 @@ void NodeBox::reset() wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2); wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2); wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2); + // no default for other parts + connect_top.clear(); + connect_bottom.clear(); + connect_front.clear(); + connect_left.clear(); + connect_back.clear(); + connect_right.clear(); } void NodeBox::serialize(std::ostream &os, u16 protocol_version) const { - int version = protocol_version >= 21 ? 2 : 1; + int version = 1; + if (protocol_version >= 27) + version = 3; + else if (protocol_version >= 21) + version = 2; writeU8(os, version); - if (version == 1 && type == NODEBOX_LEVELED) - writeU8(os, NODEBOX_FIXED); - else - writeU8(os, type); + switch (type) { + case NODEBOX_LEVELED: + case NODEBOX_FIXED: + if (version == 1) + writeU8(os, NODEBOX_FIXED); + else + writeU8(os, type); - if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED) - { writeU16(os, fixed.size()); - for(std::vector::const_iterator + for (std::vector::const_iterator i = fixed.begin(); i != fixed.end(); ++i) { writeV3F1000(os, i->MinEdge); writeV3F1000(os, i->MaxEdge); } - } - else if(type == NODEBOX_WALLMOUNTED) - { + break; + case NODEBOX_WALLMOUNTED: + writeU8(os, type); + writeV3F1000(os, wall_top.MinEdge); writeV3F1000(os, wall_top.MaxEdge); writeV3F1000(os, wall_bottom.MinEdge); writeV3F1000(os, wall_bottom.MaxEdge); writeV3F1000(os, wall_side.MinEdge); writeV3F1000(os, wall_side.MaxEdge); + break; + case NODEBOX_CONNECTED: + if (version <= 2) { + // send old clients nodes that can't be walked through + // to prevent abuse + writeU8(os, NODEBOX_FIXED); + + writeU16(os, 1); + writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2)); + writeV3F1000(os, v3f(BS/2, BS/2, BS/2)); + } else { + writeU8(os, type); + +#define WRITEBOX(box) do { \ + writeU16(os, (box).size()); \ + for (std::vector::const_iterator \ + i = (box).begin(); \ + i != (box).end(); ++i) { \ + writeV3F1000(os, i->MinEdge); \ + writeV3F1000(os, i->MaxEdge); \ + }; } while (0) + + WRITEBOX(fixed); + WRITEBOX(connect_top); + WRITEBOX(connect_bottom); + WRITEBOX(connect_front); + WRITEBOX(connect_left); + WRITEBOX(connect_back); + WRITEBOX(connect_right); + } + break; + default: + writeU8(os, type); + break; } } void NodeBox::deSerialize(std::istream &is) { int version = readU8(is); - if(version < 1 || version > 2) + if (version < 1 || version > 3) throw SerializationError("unsupported NodeBox version"); reset(); @@ -112,6 +160,26 @@ void NodeBox::deSerialize(std::istream &is) wall_side.MinEdge = readV3F1000(is); wall_side.MaxEdge = readV3F1000(is); } + else if (type == NODEBOX_CONNECTED) + { +#define READBOXES(box) do { \ + count = readU16(is); \ + (box).reserve(count); \ + while (count--) { \ + v3f min = readV3F1000(is); \ + v3f max = readV3F1000(is); \ + (box).push_back(aabb3f(min, max)); }; } while (0) + + u16 count; + + READBOXES(fixed); + READBOXES(connect_top); + READBOXES(connect_bottom); + READBOXES(connect_front); + READBOXES(connect_left); + READBOXES(connect_back); + READBOXES(connect_right); + } } /* @@ -261,6 +329,8 @@ void ContentFeatures::reset() sound_footstep = SimpleSoundSpec(); sound_dig = SimpleSoundSpec("__group"); sound_dug = SimpleSoundSpec(); + connects_to.clear(); + connects_to_ids.clear(); } void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const @@ -328,6 +398,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const os<::const_iterator i = connects_to_ids.begin(); + i != connects_to_ids.end(); ++i) + writeU16(os, *i); } void ContentFeatures::deSerialize(std::istream &is) @@ -402,6 +476,9 @@ void ContentFeatures::deSerialize(std::istream &is) mesh = deSerializeString(is); collision_box.deSerialize(is); floodable = readU8(is); + u16 connects_to_size = readU16(is); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); }catch(SerializationError &e) {}; } @@ -439,6 +516,8 @@ public: virtual bool cancelNodeResolveCallback(NodeResolver *nr); virtual void runNodeResolveCallbacks(); virtual void resetNodeResolveState(); + virtual void mapNodeboxConnections(); + virtual bool nodeboxConnects(MapNode from, MapNode to); private: void addNameIdMapping(content_t i, std::string name); @@ -1438,6 +1517,39 @@ void CNodeDefManager::resetNodeResolveState() m_pending_resolve_callbacks.clear(); } +void CNodeDefManager::mapNodeboxConnections() +{ + for (u32 i = 0; i < m_content_features.size(); i++) { + ContentFeatures *f = &m_content_features[i]; + if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED)) + continue; + for (std::vector::iterator it = f->connects_to.begin(); + it != f->connects_to.end(); ++it) { + getIds(*it, f->connects_to_ids); + } + } +} + +bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to) +{ + const ContentFeatures &f1 = get(from); + + if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED)) + return false; + + // lookup target in connected set + if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end()) + return false; + + const ContentFeatures &f2 = get(to); + + if ((f2.drawtype == NDT_NODEBOX) && (f1.node_box.type == NODEBOX_CONNECTED)) + // ignores actually looking if back connection exists + return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end()); + + // the target is just a regular node, so connect no matter back connection + return true; +} //// //// NodeResolver diff --git a/src/nodedef.h b/src/nodedef.h index bf2c7bc7..f92a3a94 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -80,6 +80,7 @@ enum NodeBoxType NODEBOX_FIXED, // Static separately defined box(es) NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side) NODEBOX_LEVELED, // Same as fixed, but with dynamic height from param2. for snow, ... + NODEBOX_CONNECTED, // optionally draws nodeboxes if a neighbor node attaches }; struct NodeBox @@ -92,6 +93,13 @@ struct NodeBox aabb3f wall_top; aabb3f wall_bottom; aabb3f wall_side; // being at the -X side + // NODEBOX_CONNECTED + std::vector connect_top; + std::vector connect_bottom; + std::vector connect_front; + std::vector connect_left; + std::vector connect_back; + std::vector connect_right; NodeBox() { reset(); } @@ -269,6 +277,9 @@ struct ContentFeatures SimpleSoundSpec sound_dig; SimpleSoundSpec sound_dug; + std::vector connects_to; + std::set connects_to_ids; + /* Methods */ @@ -314,6 +325,7 @@ public: virtual void pendNodeResolve(NodeResolver *nr)=0; virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0; + virtual bool nodeboxConnects(const MapNode from, const MapNode to)=0; }; class IWritableNodeDefManager : public INodeDefManager { @@ -368,6 +380,7 @@ public: virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0; virtual void runNodeResolveCallbacks()=0; virtual void resetNodeResolveState()=0; + virtual void mapNodeboxConnections()=0; }; IWritableNodeDefManager *createNodeDefManager(); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 96fb78d9..ababf071 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -535,6 +535,18 @@ ContentFeatures read_content_features(lua_State *L, int index) f.node_box = read_nodebox(L, -1); lua_pop(L, 1); + lua_getfield(L, index, "connects_to"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table) != 0) { + // Value at -1 + f.connects_to.push_back(lua_tostring(L, -1)); + lua_pop(L, 1); + } + } + lua_pop(L, 1); + lua_getfield(L, index, "selection_box"); if(lua_istable(L, -1)) f.selection_box = read_nodebox(L, -1); @@ -627,25 +639,31 @@ NodeBox read_nodebox(lua_State *L, int index) nodebox.type = (NodeBoxType)getenumfield(L, index, "type", ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR); - lua_getfield(L, index, "fixed"); - if(lua_istable(L, -1)) - nodebox.fixed = read_aabb3f_vector(L, -1, BS); - lua_pop(L, 1); +#define NODEBOXREAD(n, s) \ + do { \ + lua_getfield(L, index, (s)); \ + if (lua_istable(L, -1)) \ + (n) = read_aabb3f(L, -1, BS); \ + lua_pop(L, 1); \ + } while (0) - lua_getfield(L, index, "wall_top"); - if(lua_istable(L, -1)) - nodebox.wall_top = read_aabb3f(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, index, "wall_bottom"); - if(lua_istable(L, -1)) - nodebox.wall_bottom = read_aabb3f(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, index, "wall_side"); - if(lua_istable(L, -1)) - nodebox.wall_side = read_aabb3f(L, -1, BS); - lua_pop(L, 1); +#define NODEBOXREADVEC(n, s) \ + do { \ + lua_getfield(L, index, (s)); \ + if (lua_istable(L, -1)) \ + (n) = read_aabb3f_vector(L, -1, BS); \ + lua_pop(L, 1); \ + } while (0) + NODEBOXREADVEC(nodebox.fixed, "fixed"); + NODEBOXREAD(nodebox.wall_top, "wall_top"); + NODEBOXREAD(nodebox.wall_bottom, "wall_bottom"); + NODEBOXREAD(nodebox.wall_side, "wall_side"); + NODEBOXREADVEC(nodebox.connect_top, "connect_top"); + NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom"); + NODEBOXREADVEC(nodebox.connect_front, "connect_front"); + NODEBOXREADVEC(nodebox.connect_left, "connect_left"); + NODEBOXREADVEC(nodebox.connect_back, "connect_back"); + NODEBOXREADVEC(nodebox.connect_right, "connect_right"); } return nodebox; } diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index a905f843..17f0f0da 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -82,6 +82,7 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] = {NODEBOX_FIXED, "fixed"}, {NODEBOX_WALLMOUNTED, "wallmounted"}, {NODEBOX_LEVELED, "leveled"}, + {NODEBOX_CONNECTED, "connected"}, {0, NULL}, }; diff --git a/src/server.cpp b/src/server.cpp index 2fa8c4cf..6c008a2a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -320,6 +320,9 @@ Server::Server( // Perform pending node name resolutions m_nodedef->runNodeResolveCallbacks(); + // unmap node names for connected nodeboxes + m_nodedef->mapNodeboxConnections(); + // init the recipe hashes to speed up crafting m_craftdef->initHashes(this); From 14a15265db4da73873b9d327f5c945e4faa5586d Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 3 Mar 2016 23:18:04 -0800 Subject: [PATCH 27/32] Allow nodes to specify which sides to connect to. NDT_CONNECTED attempts to connect to any side of nodes that it can connect to, which is troublesome for FACEDIR type nodes that generally may only have one usable face, and can be rotated. We introduce a node parameter `connect_sides` that is valid for any node type. If specified, it lists faces of the node (in "top", "bottom", "front", "left", "back", "right", form, as array) that connecting nodeboxes can connect to. "front" corresponds to the south facing side of a node with facedir = 0. If the node is rotatable using *simple* FACEDIR, then the attached face is properly rotated before checking. This allows e.g. a chest to be attached to only from the rear side. --- doc/lua_api.txt | 2 ++ src/collision.cpp | 2 +- src/content_mapblock.cpp | 2 +- src/nodedef.cpp | 25 +++++++++++++++++++++++-- src/nodedef.h | 4 +++- src/script/common/c_content.cpp | 28 ++++++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f51c950c..733ac841 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3477,6 +3477,8 @@ Definition tables * Used for nodebox nodes with the type == "connected" * Specifies to what neighboring nodes connections will be drawn * e.g. `{"group:fence", "default:wood"}` or `"default:stone"` ]] + connect_sides = { "top", "bottom", "front", "left", "back", "right" }, --[[ + ^ Tells connected nodebox nodes to connect only to these sides of this node. ]] mesh = "model", selection_box = {type="regular"}, -- See "Node boxes" --[[ ^ If drawtype "nodebox" is used and selection_box is nil, then node_box is used. ]] diff --git a/src/collision.cpp b/src/collision.cpp index a3979f1d..16db3310 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -189,7 +189,7 @@ static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, Map *map, MapNode n, int v, int *neighbors) { MapNode n2 = map->getNodeNoEx(p); - if (nodedef->nodeboxConnects(n, n2)) + if (nodedef->nodeboxConnects(n, n2, v)) *neighbors |= v; } diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index c2934f26..6a83bd8f 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -167,7 +167,7 @@ static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, MeshMakeData *data, MapNode n, int v, int *neighbors) { MapNode n2 = data->m_vmanip.getNodeNoEx(p); - if (nodedef->nodeboxConnects(n, n2)) + if (nodedef->nodeboxConnects(n, n2, v)) *neighbors |= v; } diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 85cd848a..edd02d9f 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -331,6 +331,7 @@ void ContentFeatures::reset() sound_dug = SimpleSoundSpec(); connects_to.clear(); connects_to_ids.clear(); + connect_sides = 0; } void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const @@ -402,6 +403,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const for (std::set::const_iterator i = connects_to_ids.begin(); i != connects_to_ids.end(); ++i) writeU16(os, *i); + writeU8(os, connect_sides); } void ContentFeatures::deSerialize(std::istream &is) @@ -479,6 +481,7 @@ void ContentFeatures::deSerialize(std::istream &is) u16 connects_to_size = readU16(is); for (u16 i = 0; i < connects_to_size; i++) connects_to_ids.insert(readU16(is)); + connect_sides = readU8(is); }catch(SerializationError &e) {}; } @@ -517,7 +520,7 @@ public: virtual void runNodeResolveCallbacks(); virtual void resetNodeResolveState(); virtual void mapNodeboxConnections(); - virtual bool nodeboxConnects(MapNode from, MapNode to); + virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face); private: void addNameIdMapping(content_t i, std::string name); @@ -1530,7 +1533,7 @@ void CNodeDefManager::mapNodeboxConnections() } } -bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to) +bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face) { const ContentFeatures &f1 = get(from); @@ -1547,6 +1550,24 @@ bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to) // ignores actually looking if back connection exists return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end()); + // does to node declare usable faces? + if (f2.connect_sides > 0) { + if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) { + static const u8 rot[33 * 4] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back + 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 32, 16, 8, 4 // 32 - left + }; + return (f2.connect_sides & rot[(connect_face * 4) + to.param2]); + } + return (f2.connect_sides & connect_face); + } // the target is just a regular node, so connect no matter back connection return true; } diff --git a/src/nodedef.h b/src/nodedef.h index f92a3a94..58d0faff 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -271,6 +271,8 @@ struct ContentFeatures bool legacy_facedir_simple; // Set to true if wall_mounted used to be set to true bool legacy_wallmounted; + // for NDT_CONNECTED pairing + u8 connect_sides; // Sound properties SimpleSoundSpec sound_footstep; @@ -325,7 +327,7 @@ public: virtual void pendNodeResolve(NodeResolver *nr)=0; virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0; - virtual bool nodeboxConnects(const MapNode from, const MapNode to)=0; + virtual bool nodeboxConnects(const MapNode from, const MapNode to, u8 connect_face)=0; }; class IWritableNodeDefManager : public INodeDefManager { diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index ababf071..06e20c2a 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -547,6 +547,34 @@ ContentFeatures read_content_features(lua_State *L, int index) } lua_pop(L, 1); + lua_getfield(L, index, "connect_sides"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table) != 0) { + // Value at -1 + std::string side(lua_tostring(L, -1)); + // Note faces are flipped to make checking easier + if (side == "top") + f.connect_sides |= 2; + else if (side == "bottom") + f.connect_sides |= 1; + else if (side == "front") + f.connect_sides |= 16; + else if (side == "left") + f.connect_sides |= 32; + else if (side == "back") + f.connect_sides |= 4; + else if (side == "right") + f.connect_sides |= 8; + else + warningstream << "Unknown value for \"connect_sides\": " + << side << std::endl; + lua_pop(L, 1); + } + } + lua_pop(L, 1); + lua_getfield(L, index, "selection_box"); if(lua_istable(L, -1)) f.selection_box = read_nodebox(L, -1); From d5c0e09d40d40680258cae77f858a23361566c98 Mon Sep 17 00:00:00 2001 From: Diego Martinez Date: Mon, 7 Mar 2016 19:09:07 -0300 Subject: [PATCH 28/32] Add options for screenshot format and quality --- builtin/settingtypes.txt | 8 ++++++++ minetest.conf.example | 10 ++++++++++ src/client.cpp | 7 +++++-- src/defaultsettings.cpp | 2 ++ src/settings_translation_file.cpp | 4 ++++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 1672e08c..c1157b16 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -554,6 +554,14 @@ fallback_font_shadow_alpha (Fallback font shadow alpha) int 128 0 255 # Path to save screenshots at. screenshot_path (Screenshot folder) path +# Format of screenshots. +screenshot_format (Screenshot format) enum png png,jpg,bmp,pcx,ppm,tga + +# Screenshot quality. Only used for JPEG format. +# 1 means worst quality; 100 means best quality. +# Use 0 for default quality. +screenshot_quality (Screenshot quality) int 0 0 100 + [**Advanced] # Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens. diff --git a/minetest.conf.example b/minetest.conf.example index 1587cba7..cd0be529 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -653,6 +653,16 @@ # type: path # screenshot_path = +# Format of screenshots. +# type: enum values: png, jpg, bmp, pcx, ppm, tga +# screenshot_format = png + +# Screenshot quality. Only used for JPEG format. +# 1 means worst quality; 100 means best quality. +# Use 0 for default quality. +# type: int min: 0 max: 100 +# screenshot_quality = 0 + ### Advanced # Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens. diff --git a/src/client.cpp b/src/client.cpp index f27f031c..e13344ec 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1813,9 +1813,12 @@ void Client::makeScreenshot(IrrlichtDevice *device) + DIR_DELIM + std::string("screenshot_") + std::string(timetstamp_c); - std::string filename_ext = ".png"; + std::string filename_ext = "." + g_settings->get("screenshot_format"); std::string filename; + u32 quality = (u32)g_settings->getS32("screenshot_quality"); + quality = MYMIN(MYMAX(quality, 0), 100) / 100.0 * 255; + // Try to find a unique filename unsigned serial = 0; @@ -1837,7 +1840,7 @@ void Client::makeScreenshot(IrrlichtDevice *device) raw_image->copyTo(image); std::ostringstream sstr; - if (driver->writeImageToFile(image, filename.c_str())) { + if (driver->writeImageToFile(image, filename.c_str(), quality)) { sstr << "Saved screenshot to '" << filename << "'"; } else { sstr << "Failed to save screenshot '" << filename << "'"; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index db2950fc..53059e8a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -123,6 +123,8 @@ void set_default_settings(Settings *settings) settings->setDefault("invert_mouse", "false"); settings->setDefault("enable_clouds", "true"); settings->setDefault("screenshot_path", "."); + settings->setDefault("screenshot_format", "png"); + settings->setDefault("screenshot_quality", "0"); settings->setDefault("view_bobbing_amount", "1.0"); settings->setDefault("fall_bobbing_amount", "0.0"); settings->setDefault("enable_3d_clouds", "true"); diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index e92a34cb..f115bc01 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -272,6 +272,10 @@ fake_function() { gettext("Fallback font shadow alpha"); gettext("Screenshot folder"); gettext("Path to save screenshots at."); + gettext("Screenshot format"); + gettext("Format of screenshots."); + gettext("Screenshot quality"); + gettext("Screenshot quality. Only used for JPEG format.\n1 means worst quality; 100 means best quality.\nUse 0 for default quality."); gettext("Advanced"); gettext("DPI"); gettext("Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens."); From d3e029b4f61c038b884cbd065b414e7c3c319a2c Mon Sep 17 00:00:00 2001 From: Jeija Date: Mon, 15 Feb 2016 16:05:40 +0100 Subject: [PATCH 29/32] Resend blocks when modified while sending to client --- src/clientiface.cpp | 18 ++++++++++++------ src/clientiface.h | 10 ++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 8a1a6269..a3a17d43 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -370,17 +370,21 @@ queue_full_break: void RemoteClient::GotBlock(v3s16 p) { - if(m_blocks_sending.find(p) != m_blocks_sending.end()) - m_blocks_sending.erase(p); - else - { - m_excess_gotblocks++; + if (m_blocks_modified.find(p) == m_blocks_modified.end()) { + if (m_blocks_sending.find(p) != m_blocks_sending.end()) + m_blocks_sending.erase(p); + else + m_excess_gotblocks++; + + m_blocks_sent.insert(p); } - m_blocks_sent.insert(p); } void RemoteClient::SentBlock(v3s16 p) { + if (m_blocks_modified.find(p) != m_blocks_modified.end()) + m_blocks_modified.erase(p); + if(m_blocks_sending.find(p) == m_blocks_sending.end()) m_blocks_sending[p] = 0.0; else @@ -397,6 +401,7 @@ void RemoteClient::SetBlockNotSent(v3s16 p) m_blocks_sending.erase(p); if(m_blocks_sent.find(p) != m_blocks_sent.end()) m_blocks_sent.erase(p); + m_blocks_modified.insert(p); } void RemoteClient::SetBlocksNotSent(std::map &blocks) @@ -409,6 +414,7 @@ void RemoteClient::SetBlocksNotSent(std::map &blocks) i != blocks.end(); ++i) { v3s16 p = i->first; + m_blocks_modified.insert(p); if(m_blocks_sending.find(p) != m_blocks_sending.end()) m_blocks_sending.erase(p); diff --git a/src/clientiface.h b/src/clientiface.h index d7622cad..c0994290 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -394,6 +394,16 @@ private: */ std::map m_blocks_sending; + /* + Blocks that have been modified since last sending them. + These blocks will not be marked as sent, even if the + client reports it has received them to account for blocks + that are being modified while on the line. + + List of block positions. + */ + std::set m_blocks_modified; + /* Count of excess GotBlocks(). There is an excess amount because the client sometimes From 18acddb37344a145101c3bb3d79f3f529f380474 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Sun, 13 Mar 2016 14:25:54 -0700 Subject: [PATCH 30/32] Connected nodes: fix 2 minor bugs 1. Copy-paste error: properly test for back-connection. In the case of two different connected nodebox types, we want to assure that if A connects to B, that B also connects to A. This test was accidentally not implemented correctly. 2. Clear the connects_to_ids before deserializing. With each new connected node, the deserialization code added more and more targets to the map, since the map wasn't cleared in between deserialization steps. This caused e.g. wall blocks to connect to things in the fence connects_to map. --- src/nodedef.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nodedef.cpp b/src/nodedef.cpp index edd02d9f..3a2cb00b 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -479,6 +479,7 @@ void ContentFeatures::deSerialize(std::istream &is) collision_box.deSerialize(is); floodable = readU8(is); u16 connects_to_size = readU16(is); + connects_to_ids.clear(); for (u16 i = 0; i < connects_to_size; i++) connects_to_ids.insert(readU16(is)); connect_sides = readU8(is); @@ -1546,7 +1547,7 @@ bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face) const ContentFeatures &f2 = get(to); - if ((f2.drawtype == NDT_NODEBOX) && (f1.node_box.type == NODEBOX_CONNECTED)) + if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED)) // ignores actually looking if back connection exists return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end()); From 95a304db0808d1ee8261f8857f9c6f0ebd30f2f8 Mon Sep 17 00:00:00 2001 From: HybridDog Date: Mon, 4 Jan 2016 17:21:33 +0100 Subject: [PATCH 31/32] Fix player teleportation bug whilst sneaking Only set back position when sneaking if player wasn't teleported by adding and using a bool "got_teleported" to player it fixes #2876 --- src/localplayer.cpp | 5 ++++- src/network/clientpackethandler.cpp | 1 + src/player.cpp | 1 + src/player.h | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 0c94582a..dbd8c4a6 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -183,7 +183,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, */ if (control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid && - physics_override_sneak) { + physics_override_sneak && !got_teleported) { f32 maxd = 0.5 * BS + sneak_max; v3f lwn_f = intToFloat(m_sneak_node, BS); position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd); @@ -204,6 +204,9 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, } } + if (got_teleported) + got_teleported = false; + // this shouldn't be hardcoded but transmitted from server float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index a49bbe16..28d147d3 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -552,6 +552,7 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt) *pkt >> pos >> pitch >> yaw; + player->got_teleported = true; player->setPosition(pos); infostream << "Client got TOCLIENT_MOVE_PLAYER" diff --git a/src/player.cpp b/src/player.cpp index 623dde23..5949712a 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Player::Player(IGameDef *gamedef, const char *name): + got_teleported(false), touching_ground(false), in_liquid(false), in_liquid_stable(false), diff --git a/src/player.h b/src/player.h index 50267c72..b317cda4 100644 --- a/src/player.h +++ b/src/player.h @@ -318,6 +318,7 @@ public: // Use a function, if isDead can be defined by other conditions bool isDead() { return hp == 0; } + bool got_teleported; bool touching_ground; // This oscillates so that the player jumps a bit above the surface bool in_liquid; From 191dd5cdbc61dac7846c4cdd6d4ebe9c4d27b7bf Mon Sep 17 00:00:00 2001 From: paramat Date: Sun, 13 Mar 2016 07:56:27 +0000 Subject: [PATCH 32/32] Mapgen: Fix light in tunnels at mapchunk borders Don't excavate the overgenerated stone at node_max.Y + 1, this creates a 'roof' over the tunnel, preventing light in tunnels at mapchunk borders when generating mapchunks upwards. --- src/mapgen_flat.cpp | 6 ++++++ src/mapgen_fractal.cpp | 6 ++++++ src/mapgen_v5.cpp | 6 ++++++ src/mapgen_v7.cpp | 6 ++++++ src/mapgen_valleys.cpp | 6 ++++++ 5 files changed, 30 insertions(+) diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index 0d071411..d654a772 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -573,6 +573,12 @@ void MapgenFlat::generateCaves(s16 max_stone_y) for (s16 y = node_max.Y + 1; y >= node_min.Y - 1; y--, index3d -= ystride, vm->m_area.add_y(em, vi, -1)) { + // Don't excavate the overgenerated stone at node_max.Y + 1, + // this creates a 'roof' over the tunnel, preventing light in + // tunnels at mapchunk borders when generating mapchunks upwards. + if (y > node_max.Y) + continue; + content_t c = vm->m_data[vi].getContent(); if (c == CONTENT_AIR || c == biome->c_water_top || c == biome->c_water) { diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index 8a5c1e2b..cdea9c09 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -701,6 +701,12 @@ void MapgenFractal::generateCaves(s16 max_stone_y) for (s16 y = node_max.Y + 1; y >= node_min.Y - 1; y--, index3d -= ystride, vm->m_area.add_y(em, vi, -1)) { + // Don't excavate the overgenerated stone at node_max.Y + 1, + // this creates a 'roof' over the tunnel, preventing light in + // tunnels at mapchunk borders when generating mapchunks upwards. + if (y > node_max.Y) + continue; + content_t c = vm->m_data[vi].getContent(); if (c == CONTENT_AIR || c == biome->c_water_top || c == biome->c_water) { diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 0bb3715a..e9b1b58b 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -519,6 +519,12 @@ void MapgenV5::generateCaves(int max_stone_y) for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { u32 vi = vm->m_area.index(node_min.X, y, z); for (s16 x = node_min.X; x <= node_max.X; x++, vi++, index++) { + // Don't excavate the overgenerated stone at node_max.Y + 1, + // this creates a 'roof' over the tunnel, preventing light in + // tunnels at mapchunk borders when generating mapchunks upwards. + if (y > node_max.Y) + continue; + float d1 = contour(noise_cave1->result[index]); float d2 = contour(noise_cave2->result[index]); if (d1 * d2 > 0.125f) { diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 029f56a4..24867623 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -883,6 +883,12 @@ void MapgenV7::generateCaves(s16 max_stone_y) for (s16 y = node_max.Y + 1; y >= node_min.Y - 1; y--, index3d -= ystride, vm->m_area.add_y(em, vi, -1)) { + // Don't excavate the overgenerated stone at node_max.Y + 1, + // this creates a 'roof' over the tunnel, preventing light in + // tunnels at mapchunk borders when generating mapchunks upwards. + if (y > node_max.Y) + continue; + content_t c = vm->m_data[vi].getContent(); if (c == CONTENT_AIR || c == biome->c_water_top || c == biome->c_water) { diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index d6fcde5f..34a316ab 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -931,6 +931,12 @@ void MapgenValleys::generateCaves(s16 max_stone_y) for (s16 y = node_max.Y + 1; y >= node_min.Y - 1; y--, index_3d -= ystride, vm->m_area.add_y(em, index_data, -1)) { + // Don't excavate the overgenerated stone at node_max.Y + 1, + // this creates a 'roof' over the tunnel, preventing light in + // tunnels at mapchunk borders when generating mapchunks upwards. + if (y > node_max.Y) + continue; + float terrain = noise_terrain_height->result[index_2d]; // Saves some time.