diff --git a/.gitignore b/.gitignore index f9c664da6..d7e0daab4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ tags !tags/ gtags.files +.idea/* ## Files related to minetest development cycle /*.patch @@ -71,6 +72,8 @@ locale/ *.layout *.o *.a +*.ninja +.ninja* ## Android build files build/android/src/main/assets diff --git a/.travis.yml b/.travis.yml index 9d1600818..534479efb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,4 @@ language: cpp -compiler: - - gcc - - clang -os: - - osx - - linux -env: - - PLATFORM=Win32 - - PLATFORM=Win64 - - PLATFORM=Unix before_install: ./util/travis/before_install.sh script: ./util/travis/script.sh sudo: required @@ -16,14 +6,26 @@ notifications: email: false matrix: fast_finish: true - exclude: + include: - env: PLATFORM=Win32 - compiler: clang + compiler: gcc + os: linux - env: PLATFORM=Win64 + compiler: gcc + os: linux + - env: PLATFORM=Unix COMPILER=clang compiler: clang - - env: PLATFORM=Win32 - os: osx - - env: PLATFORM=Win64 - os: osx - - compiler: gcc os: osx + - env: PLATFORM=Unix COMPILER=g++ + compiler: gcc + os: linux + - env: PLATFORM=Unix COMPILER=clang + compiler: clang + os: linux + - env: PLATFORM=Unix COMPILER=g++-6 + compiler: gcc + os: linux +addons: + apt: + sources: &sources + - ubuntu-toolchain-r-test diff --git a/CMakeLists.txt b/CMakeLists.txt index 998cdc9a3..11ebe94a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(PROJECT_NAME_CAPITALIZED "Minetest") # Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 14) +set(VERSION_PATCH 15) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases @@ -167,6 +167,7 @@ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/fonts" DESTINATION "${SHAREDIR}") install(FILES "README.txt" DESTINATION "${DOCDIR}") install(FILES "doc/lua_api.txt" DESTINATION "${DOCDIR}") install(FILES "doc/menu_lua_api.txt" DESTINATION "${DOCDIR}") +install(FILES "doc/texture_packs.txt" DESTINATION "${DOCDIR}") install(FILES "doc/world_format.txt" DESTINATION "${DOCDIR}") install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}") @@ -175,6 +176,9 @@ if(UNIX AND NOT APPLE) install(FILES "misc/minetest.desktop" DESTINATION "${XDG_APPS_DIR}") install(FILES "misc/minetest.appdata.xml" DESTINATION "${APPDATADIR}") install(FILES "misc/minetest.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps") + install(FILES "misc/minetest-xorg-icon-128.png" + DESTINATION "${ICONDIR}/hicolor/128x128/apps" + RENAME "minetest.png") endif() if(APPLE) diff --git a/README.txt b/README.txt index 9ca9b331e..5d4d15263 100644 --- a/README.txt +++ b/README.txt @@ -27,39 +27,47 @@ This game is not finished - Don't expect it to work as well as a finished game will. - Please report any bugs. When doing that, debug.txt is useful. -Default Controls +Default controls ----------------- -- WASD: move -- Space: jump/climb -- Shift: sneak/go down -- Q: drop itemstack (+ SHIFT for single item) -- I: inventory -- Mouse: turn/look -- Mouse left: dig/punch -- Mouse right: place/use -- Mouse wheel: select item -- T: chat -- 1-8: select item +- Move mouse: Look around +- W, A, S, D: Move +- Space: Jump/move up +- Shift: Sneak/move down +- Q: Drop itemstack +- Shift + Q: Drop single item +- Left mouse button: Dig/punch/take item +- Right mouse button: Place/use +- Shift + right mouse button: Build (without using) +- I: Inventory menu +- Mouse wheel: Select item +- 0-9: Select item +- Z: Zoom (needs zoom privilege) +- T: Chat +- /: Commad -- Esc: pause menu (pauses only singleplayer game) -- R: Enable/Disable full range view +- Esc: Pause menu/abort/exit (pauses only singleplayer game) +- R: Enable/disable full range view - +: Increase view range - -: Decrease view range -- K: Enable/Disable fly (needs fly privilege) -- J: Enable/Disable fast (needs fast privilege) -- H: Enable/Disable noclip (needs noclip privilege) +- K: Enable/disable fly mode (needs fly privilege) +- J: Enable/disable fast mode (needs fast privilege) +- H: Enable/disable noclip mode (needs noclip privilege) -- F1: Hide/Show HUD -- F2: Hide/Show Chat -- F3: Disable/Enable Fog -- F4: Disable/Enable Camera update (Mapblocks are not updated anymore when disabled) -- F5: Toogle through debug info screens -- F6: Toogle through output data -- F7: Toggle through camera modes -- F10: Show/Hide console +- F1: Hide/show HUD +- F2: Hide/show chat +- F3: Disable/enable fog +- F4: Disable/enable camera update (Mapblocks are not updated anymore when disabled, disabled in release builds) +- F5: Cycle through debug info screens +- F6: Cycle through profiler info screens +- F7: Cycle through camera modes +- F8: Toggle cinematic mode +- F9: Cycle through minimap modes +- Shift + F9: Change minimap orientation +- F10: Show/hide console - F12: Take screenshot +- P: Write stack traces into debug.txt -- Settable in the configuration file, see the section below. +Most controls are settable in the configuration file, see the section below. Paths ------ @@ -103,7 +111,7 @@ Compiling on GNU/Linux: ----------------------- Install dependencies. Here's an example for Debian/Ubuntu: -$ sudo apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev +$ sudo apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev For Fedora users: $ sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl* openal* libvorbis* libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel bzip2-libs gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel doxygen spatialindex-devel bzip2-devel @@ -111,10 +119,10 @@ $ sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl* openal* You can install git for easily keeping your copy up to date. If you dont want git, read below on how to get the source without git. This is an example for installing git on Debian/Ubuntu: -$ sudo apt-get install git-core +$ sudo apt-get install git For Fedora users: -$ sudo dnf install git-core +$ sudo dnf install git Download source (this is the URL to the latest of source repository, which might not work at all times) using git: $ git clone --depth 1 https://github.com/minetest/minetest.git @@ -169,7 +177,8 @@ ENABLE_CURSES - Build with (n)curses; Enables a server side terminal (comm ENABLE_FREETYPE - Build with FreeType2; Allows using TTF fonts ENABLE_GETTEXT - Build with Gettext; Allows using translations ENABLE_GLES - Search for Open GLES headers & libraries and use them -ENABLE_LEVELDB - Build with LevelDB; Enables use of LevelDB map backend (faster than SQLite3) +ENABLE_LEVELDB - Build with LevelDB; Enables use of LevelDB map backend +ENABLE_POSTGRESQL - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater required) ENABLE_REDIS - Build with libhiredis; Enables use of Redis map backend ENABLE_SPATIAL - Build with LibSpatial; Speeds up AreaStores ENABLE_SOUND - Build with OpenAL, libogg & libvorbis; in-game Sounds @@ -203,6 +212,8 @@ IRRLICHT_LIBRARY - Path to libIrrlicht.a/libIrrlicht.so/libIrrlic LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll +POSTGRESQL_INCLUDE_DIR - Only when building with PostgreSQL; directory that contains libpq-fe.h +POSTGRESQL_LIBRARY - Only when building with PostgreSQL; path to libpq.a/libpq.so REDIS_INCLUDE_DIR - Only when building with Redis; directory that contains hiredis.h REDIS_LIBRARY - Only when building with Redis; path to libhiredis.a/libhiredis.so SPATIAL_INCLUDE_DIR - Only when building with LibSpatial; directory that contains spatialindex/SpatialIndex.h @@ -249,6 +260,8 @@ Compiling on Windows: http://www.winimage.com/zLibDll/index.html * Zlib library (zlibwapi.lib and zlibwapi.dll from zlib125dll.zip): http://www.winimage.com/zLibDll/index.html + * SQLite3 headers and library + https://www.sqlite.org/download.html * Optional: gettext library and tools: http://gnuwin32.sourceforge.net/downlinks/gettext.php - This is used for other UI languages. Feel free to leave it out. @@ -260,6 +273,10 @@ Compiling on Windows: - Download all the other stuff to DIR and extract them into there. ("extract here", not "extract to packagename/") NOTE: zlib125dll.zip needs to be extracted into zlib125dll + NOTE: You need to extract sqlite3.h & sqlite3ext.h from sqlite3 source + and sqlite3.dll & sqlite3.def from sqlite3 precompiled binaries + into "sqlite3" directory, and generate sqlite3.lib using command + "LIB /DEF:sqlite3.def /OUT:sqlite3.lib" - All those packages contain a nice base directory in them, which should end up being the direct subdirectories of DIR. - You will end up with a directory structure like this (+=dir, -=file): @@ -267,7 +284,9 @@ Compiling on Windows: + DIR - zlib-1.2.5.tar.gz - zlib125dll.zip - - irrlicht-1.7.1.zip + - irrlicht-1.8.3.zip + - sqlite-amalgamation-3130000.zip (SQLite3 headers) + - sqlite-dll-win32-x86-3130000.zip (SQLite3 library for 32bit system) - 110214175330.zip (or whatever, this is the minetest source) + zlib-1.2.5 - zlib.h @@ -277,10 +296,15 @@ Compiling on Windows: - readme.txt + dll32 ... - + irrlicht-1.7.1 + + irrlicht-1.8.3 + lib + include ... + + sqlite3 + sqlite3.h + sqlite3ext.h + sqlite3.lib + sqlite3.dll + gettext (optional) +bin +include @@ -307,7 +331,7 @@ Compiling on Windows: BUILD_SERVER [ ] CMAKE_BUILD_TYPE Release CMAKE_INSTALL_PREFIX DIR/minetest-install - IRRLICHT_SOURCE_DIR DIR/irrlicht-1.7.1 + IRRLICHT_SOURCE_DIR DIR/irrlicht-1.8.3 RUN_IN_PLACE [X] WARN_ALL [ ] ZLIB_DLL DIR/zlib125dll/dll32/zlibwapi.dll @@ -318,6 +342,11 @@ Compiling on Windows: GETTEXT_LIBRARIES DIR/gettext/lib/intl.lib GETTEXT_MSGFMT DIR/gettext/bin/msgfmt ----------------- + - If CMake complains it couldn't find SQLITE3, choose "Advanced" box on the + right top corner, then specify the location of SQLITE3_INCLUDE_DIR and + SQLITE3_LIBRARY manually. + - If you want to build 64-bit minetest, you will need to build 64-bit version + of irrlicht engine manually, as only 32-bit pre-built library is provided. - Hit "Configure" - Hit "Configure" once again 8) - If something is still coloured red, you have a problem. @@ -382,6 +411,9 @@ Authors of media files Everything not listed in here: Copyright (C) 2010-2012 celeron55, Perttu Ahola +ShadowNinja: + textures/base/pack/smoke_puff.png + Paramat: textures/base/pack/menu_header.png diff --git a/build/android/Makefile b/build/android/Makefile index 4ac9760f8..6e7a389c9 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -91,7 +91,7 @@ IRRLICHT_TIMESTAMP = $(IRRLICHT_DIR)timestamp IRRLICHT_TIMESTAMP_INT = $(ANDR_ROOT)/deps/irrlicht_timestamp IRRLICHT_URL_SVN = https://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@$(IRRLICHT_REVISION) -OPENSSL_VERSION = 1.0.2h +OPENSSL_VERSION = 1.0.2j OPENSSL_BASEDIR = openssl-$(OPENSSL_VERSION) OPENSSL_DIR = $(ANDR_ROOT)/deps/$(OPENSSL_BASEDIR)/ OPENSSL_LIB = $(OPENSSL_DIR)/libssl.so.1.0.0 @@ -99,14 +99,14 @@ OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp OPENSSL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/openssl_timestamp OPENSSL_URL = https://www.openssl.org/source/openssl-$(OPENSSL_VERSION).tar.gz -CURL_VERSION = 7.48.0 +CURL_VERSION = 7.52.0 CURL_DIR = $(ANDR_ROOT)/deps/curl-$(CURL_VERSION) CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a CURL_TIMESTAMP = $(CURL_DIR)/timestamp CURL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/curl_timestamp CURL_URL_HTTP = https://curl.haxx.se/download/curl-${CURL_VERSION}.tar.bz2 -GMP_VERSION = 6.1.0 +GMP_VERSION = 6.1.2 GMP_DIR = $(ANDR_ROOT)/deps/gmp-$(GMP_VERSION) GMP_LIB = $(GMP_DIR)/usr/lib/libgmp.so GMP_TIMESTAMP = $(GMP_DIR)/timestamp @@ -126,7 +126,7 @@ ICONV_TIMESTAMP = $(ICONV_DIR)timestamp ICONV_TIMESTAMP_INT = $(ANDR_ROOT)/deps/iconv_timestamp ICONV_URL_HTTP = https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$(ICONV_VERSION).tar.gz -SQLITE3_FOLDER = sqlite-amalgamation-3120200 +SQLITE3_FOLDER = sqlite-amalgamation-3150200 SQLITE3_URL = https://www.sqlite.org/2016/$(SQLITE3_FOLDER).zip ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') @@ -220,6 +220,8 @@ $(OPENAL_LIB): $(OPENAL_TIMESTAMP) ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ NDK_MODULE_PATH=${NDK_MODULE_PATH} APP_ABI=${TARGET_ABI} \ TARGET_ARCH_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \ + PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ + PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ @@ -266,6 +268,8 @@ $(OGG_LIB): $(OGG_TIMESTAMP) ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ NDK_MODULE_PATH=${NDK_MODULE_PATH} \ APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \ + PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ + PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ @@ -313,6 +317,7 @@ $(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) $(GMP_LIB) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ + --platform=android-9 \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ CC=${CROSS_PREFIX}gcc ./Configure enable-gmp -DL_ENDIAN -I${GMP_DIR} -L${GMP_DIR}/usr/lib android-${TARGET_ARCH};\ @@ -363,6 +368,7 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ + --platform=android-9 \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ @@ -416,6 +422,8 @@ $(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP) ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ NDK_MODULE_PATH=${NDK_MODULE_PATH} \ APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI} \ + PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ + PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ @@ -469,6 +477,7 @@ $(ICONV_LIB) : $(ICONV_TIMESTAMP) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ + --platform=android-9 \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ @@ -531,6 +540,8 @@ $(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB) ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ NDK_MODULE_PATH=${NDK_MODULE_PATH} \ APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \ + PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ + PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \ @@ -581,6 +592,7 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ + --platform=android-9 \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ @@ -640,6 +652,7 @@ $(GMP_LIB): $(GMP_TIMESTAMP) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-gmp; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ + --platform=android-9 \ --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ @@ -665,7 +678,9 @@ deps/${SQLITE3_FOLDER}/sqlite3.c : cd deps; \ wget ${SQLITE3_URL}; \ unzip ${SQLITE3_FOLDER}.zip; \ - ln -s ${SQLITE3_FOLDER} sqlite + ln -s ${SQLITE3_FOLDER} sqlite; \ + cd ${SQLITE3_FOLDER}; \ + patch sqlite3.c < ${ANDR_ROOT}/patches/sqlite3-readonly-fix.patch clean_sqlite3: cd deps && $(RM) -rf ${SQLITE3_FOLDER} && $(RM) -f ${SQLITE3_FOLDER}.zip && \ @@ -752,9 +767,11 @@ clean_assets : apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET) \ $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \ $(ANDR_ROOT)/jni/src/android_version_githash.h sqlite3_download - @${ANDROID_NDK}/ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} \ + + @${ANDROID_NDK}/ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} \ GPROF=${GPROF} APP_ABI=${TARGET_ABI} HAVE_LEVELDB=${HAVE_LEVELDB} \ APP_PLATFORM=${APP_PLATFORM} \ + PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ + PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ TARGET_LIBDIR=${TARGET_LIBDIR} \ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \ @@ -831,4 +848,3 @@ $(ANDR_ROOT)/jni/src/android_version.h : prep_srcdir fi clean : clean_apk clean_assets - diff --git a/build/android/build.gradle b/build/android/build.gradle index 391e250ad..20c13e385 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -14,7 +14,7 @@ android { buildToolsVersion "23.0.3" defaultConfig { - versionCode 14 + versionCode 16 versionName "${System.env.VERSION_STR}.${versionCode}" minSdkVersion 9 targetSdkVersion 9 diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index e8534eaac..c2186efaf 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -99,7 +99,7 @@ LOCAL_C_INCLUDES := \ jni/src \ jni/src/script \ jni/src/lua/src \ - jni/src/json \ + jni/src/jsoncpp \ jni/src/cguittfont \ deps/irrlicht/include \ deps/libiconv/include \ @@ -169,6 +169,7 @@ LOCAL_SRC_FILES := \ jni/src/log.cpp \ jni/src/main.cpp \ jni/src/map.cpp \ + jni/src/map_settings_manager.cpp \ jni/src/mapblock.cpp \ jni/src/mapblock_mesh.cpp \ jni/src/mapgen.cpp \ @@ -202,6 +203,8 @@ LOCAL_SRC_FILES := \ jni/src/porting.cpp \ jni/src/profiler.cpp \ jni/src/quicktune.cpp \ + jni/src/reflowscan.cpp \ + jni/src/remoteplayer.cpp \ jni/src/rollback.cpp \ jni/src/rollback_interface.cpp \ jni/src/serialization.cpp \ @@ -224,6 +227,7 @@ LOCAL_SRC_FILES := \ jni/src/util/auth.cpp \ jni/src/util/base64.cpp \ jni/src/util/directiontables.cpp \ + jni/src/util/enriched_string.cpp \ jni/src/util/numeric.cpp \ jni/src/util/pointedthing.cpp \ jni/src/util/serialize.cpp \ @@ -237,6 +241,7 @@ LOCAL_SRC_FILES := \ jni/src/unittest/test_connection.cpp \ jni/src/unittest/test_filepath.cpp \ jni/src/unittest/test_inventory.cpp \ + jni/src/unittest/test_map_settings_manager.cpp \ jni/src/unittest/test_mapnode.cpp \ jni/src/unittest/test_nodedef.cpp \ jni/src/unittest/test_noderesolver.cpp \ @@ -256,7 +261,9 @@ LOCAL_SRC_FILES := \ jni/src/settings.cpp \ jni/src/wieldmesh.cpp \ jni/src/client/clientlauncher.cpp \ - jni/src/client/tile.cpp + jni/src/client/tile.cpp \ + jni/src/client/joystick_controller.cpp \ + jni/src/irrlicht_changes/static_text.cpp # intentionally kept out (we already build openssl itself): jni/src/util/sha256.c @@ -356,7 +363,7 @@ LOCAL_SRC_FILES += \ jni/src/threading/thread.cpp # JSONCPP -LOCAL_SRC_FILES += jni/src/json/jsoncpp.cpp +LOCAL_SRC_FILES += jni/src/jsoncpp/json/jsoncpp.cpp LOCAL_SHARED_LIBRARIES := iconv openal ogg vorbis gmp LOCAL_STATIC_LIBRARIES := Irrlicht freetype curl ssl crypto android_native_app_glue $(PROFILER_LIBS) diff --git a/build/android/patches/sqlite3-readonly-fix.patch b/build/android/patches/sqlite3-readonly-fix.patch new file mode 100644 index 000000000..be19055ee --- /dev/null +++ b/build/android/patches/sqlite3-readonly-fix.patch @@ -0,0 +1,17 @@ +--- sqlite3.c 2016-11-29 02:29:24.000000000 +0000 ++++ sqlite3.c 2016-12-08 22:54:54.206465377 +0000 +@@ -30445,7 +30445,14 @@ + #if OS_VXWORKS + struct vxworksFileId *pId; /* Unique file ID for vxworks. */ + #else +- ino_t ino; /* Inode number */ ++ #ifdef ANDROID ++ // Bionic's struct stat has a 64 bit st_ino on both 32 and ++ // 64 bit architectures. ino_t remains 32 bits wide on 32 bit ++ // architectures and can lead to inode truncation. ++ unsigned long long ino; /* Inode number */ ++ #else ++ ino_t ino; /* Inode number */ ++ #endif + #endif + }; diff --git a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java b/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java index 3173a71f4..159521a50 100644 --- a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java +++ b/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java @@ -19,11 +19,6 @@ public class MtNativeActivity extends NativeActivity { public void onDestroy() { super.onDestroy(); } - - @Override - public void onBackPressed() { - } - public void copyAssets() { Intent intent = new Intent(this, MinetestAssetCopy.class); diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index e4653d41d..c2dc7514d 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -382,7 +382,6 @@ if INIT == "game" then itemstack, pointed_thing) return end - local pitch = placer:get_look_pitch() local fdir = core.dir_to_facedir(placer:get_look_dir()) local wield_name = itemstack:get_name() diff --git a/builtin/common/strict.lua b/builtin/common/strict.lua index 05ceadf7a..23ba3d727 100644 --- a/builtin/common/strict.lua +++ b/builtin/common/strict.lua @@ -5,6 +5,9 @@ local WARN_INIT = false function core.global_exists(name) + if type(name) ~= "string" then + error("core.global_exists: " .. tostring(name) .. " is not a string") + end return rawget(_G, name) ~= nil end diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index e9ed3aab3..90ba3cc8b 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -31,6 +31,14 @@ function vector.normalize(v) end end +function vector.floor(v) + return { + x = math.floor(v.x), + y = math.floor(v.y), + z = math.floor(v.z) + } +end + function vector.round(v) return { x = math.floor(v.x + 0.5), @@ -130,4 +138,3 @@ function vector.divide(a, b) z = a.z / b} end end - diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index deb811b14..46fe3d342 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -199,3 +199,19 @@ core.register_on_joinplayer(function(player) record_login(player:get_player_name()) end) +core.register_on_prejoinplayer(function(name, ip) + local auth = core.auth_table + if auth[name] ~= nil then + return + end + + local name_lower = name:lower() + for k in pairs(auth) do + if k:lower() == name_lower then + return string.format("\nCannot create new player called '%s'. ".. + "Another account called '%s' is already registered. ".. + "Please check the spelling if it's your account ".. + "or use a different nickname.", name, k) + end + end +end) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 3350140ee..2bd93855b 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -14,19 +14,6 @@ function core.register_chatcommand(cmd, def) core.chatcommands[cmd] = def end -if core.setting_getbool("mod_profiling") then - local tracefct = profiling_print_log - profiling_print_log = nil - core.register_chatcommand("save_mod_profile", - { - params = "", - description = "save mod profiling data to logfile " .. - "(depends on default loglevel)", - func = tracefct, - privs = { server=true } - }) -end - core.register_on_chat_message(function(name, message) local cmd, param = string.match(message, "^/([^ ]+) *(.*)") if not param then @@ -51,6 +38,12 @@ core.register_on_chat_message(function(name, message) return true -- Handled chat message end) +if core.setting_getbool("profiler.load") then + -- Run after register_chatcommand and its register_on_chat_message + -- Before any chattcommands that should be profiled + profiler.init_chatcommand() +end + -- Parses a "range" string in the format of "here (number)" or -- "(x1, y1, z1) (x2, y2, z2)", returning two position vectors local function parse_range_str(player_name, str) @@ -102,7 +95,7 @@ core.register_chatcommand("help", { description = "Get help for commands or list privileges", func = function(name, param) local function format_help_line(cmd, def) - local msg = "/"..cmd + local msg = core.colorize("#00ffff", "/"..cmd) if def.params and def.params ~= "" then msg = msg .. " " .. def.params end @@ -154,60 +147,79 @@ core.register_chatcommand("help", { core.register_chatcommand("privs", { params = "", description = "print out privileges of player", - func = function(name, param) - param = (param ~= "" and param or name) - return true, "Privileges of " .. param .. ": " + func = function(caller, param) + param = param:trim() + local name = (param ~= "" and param or caller) + return true, "Privileges of " .. name .. ": " .. core.privs_to_string( - core.get_player_privs(param), ' ') + core.get_player_privs(name), ' ') end, }) + +local function handle_grant_command(caller, grantname, grantprivstr) + local caller_privs = minetest.get_player_privs(caller) + if not (caller_privs.privs or caller_privs.basic_privs) then + return false, "Your privileges are insufficient." + end + + if not core.auth_table[grantname] then + return false, "Player " .. grantname .. " does not exist." + end + local grantprivs = core.string_to_privs(grantprivstr) + if grantprivstr == "all" then + grantprivs = core.registered_privileges + end + local privs = core.get_player_privs(grantname) + local privs_unknown = "" + local basic_privs = + core.string_to_privs(core.setting_get("basic_privs") or "interact,shout") + for priv, _ in pairs(grantprivs) do + if not basic_privs[priv] and not caller_privs.privs then + return false, "Your privileges are insufficient." + end + if not core.registered_privileges[priv] then + privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n" + end + privs[priv] = true + end + if privs_unknown ~= "" then + return false, privs_unknown + end + core.set_player_privs(grantname, privs) + core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname) + if grantname ~= caller then + core.chat_send_player(grantname, caller + .. " granted you privileges: " + .. core.privs_to_string(grantprivs, ' ')) + end + return true, "Privileges of " .. grantname .. ": " + .. core.privs_to_string( + core.get_player_privs(grantname), ' ') +end + core.register_chatcommand("grant", { params = " |all", description = "Give privilege to player", func = function(name, param) - if not core.check_player_privs(name, {privs=true}) and - not core.check_player_privs(name, {basic_privs=true}) then - return false, "Your privileges are insufficient." - end local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)") if not grantname or not grantprivstr then return false, "Invalid parameters (see /help grant)" - elseif not core.auth_table[grantname] then - return false, "Player " .. grantname .. " does not exist." - end - local grantprivs = core.string_to_privs(grantprivstr) - if grantprivstr == "all" then - grantprivs = core.registered_privileges - end - local privs = core.get_player_privs(grantname) - local privs_unknown = "" - local basic_privs = - core.string_to_privs(core.setting_get("basic_privs") or "interact,shout") - for priv, _ in pairs(grantprivs) do - if not basic_privs[priv] and - not core.check_player_privs(name, {privs=true}) then - return false, "Your privileges are insufficient." - end - if not core.registered_privileges[priv] then - privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n" - end - privs[priv] = true - end - if privs_unknown ~= "" then - return false, privs_unknown - end - core.set_player_privs(grantname, privs) - core.log("action", name..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname) - if grantname ~= name then - core.chat_send_player(grantname, name - .. " granted you privileges: " - .. core.privs_to_string(grantprivs, ' ')) - end - return true, "Privileges of " .. grantname .. ": " - .. core.privs_to_string( - core.get_player_privs(grantname), ' ') + end + return handle_grant_command(name, grantname, grantprivstr) end, }) + +core.register_chatcommand("grantme", { + params = "|all", + description = "Grant privileges to yourself", + func = function(name, param) + if param == "" then + return false, "Invalid parameters (see /help grantme)" + end + return handle_grant_command(name, name, param) + end, +}) + core.register_chatcommand("revoke", { params = " |all", description = "Remove privilege from player", diff --git a/builtin/game/constants.lua b/builtin/game/constants.lua index d0b7c753c..50c515b24 100644 --- a/builtin/game/constants.lua +++ b/builtin/game/constants.lua @@ -4,14 +4,24 @@ -- Constants values for use with the Lua API -- +-- mapnode.h -- Built-in Content IDs (for use with VoxelManip API) core.CONTENT_UNKNOWN = 125 core.CONTENT_AIR = 126 core.CONTENT_IGNORE = 127 +-- emerge.h -- Block emerge status constants (for use with core.emerge_area) core.EMERGE_CANCELLED = 0 core.EMERGE_ERRORED = 1 core.EMERGE_FROM_MEMORY = 2 core.EMERGE_FROM_DISK = 3 core.EMERGE_GENERATED = 4 + +-- constants.h +-- Size of mapblocks in nodes +core.MAP_BLOCKSIZE = 16 + +-- light.h +-- Maximum value for node 'light_source' parameter +core.LIGHT_MAX = 14 diff --git a/builtin/game/detached_inventory.lua b/builtin/game/detached_inventory.lua index b5d106b04..420e89ff2 100644 --- a/builtin/game/detached_inventory.lua +++ b/builtin/game/detached_inventory.lua @@ -2,7 +2,7 @@ core.detached_inventories = {} -function core.create_detached_inventory(name, callbacks) +function core.create_detached_inventory(name, callbacks, player_name) local stuff = {} stuff.name = name if callbacks then @@ -15,6 +15,6 @@ function core.create_detached_inventory(name, callbacks) end stuff.mod_origin = core.get_current_modname() or "??" core.detached_inventories[name] = stuff - return core.create_detached_inventory_raw(name) + return core.create_detached_inventory_raw(name, player_name) end diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 57bb98cfd..4696ce481 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -1,5 +1,7 @@ -- Minetest: builtin/item.lua +local builtin_shared = ... + -- -- Falling stuff -- @@ -41,19 +43,20 @@ core.register_entity(":__builtin:falling_node", { end, on_step = function(self, dtime) - -- Set gravity + -- Set gravity local acceleration = self.object:getacceleration() if not vector.equals(acceleration, {x = 0, y = -10, z = 0}) then self.object:setacceleration({x = 0, y = -10, z = 0}) end - -- Turn to actual sand when collides to ground or just move + -- Turn to actual node when colliding with ground, or continue to move local pos = self.object:getpos() - local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z} -- Position of bottom center point - local bcn = core.get_node(bcp) - local bcd = core.registered_nodes[bcn.name] - -- Note: walkable is in the node definition, not in item groups - if not bcd or - (bcd.walkable or + -- Position of bottom center point + local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z} + -- Avoid bugs caused by an unloaded node below + local bcn = core.get_node_or_nil(bcp) + local bcd = bcn and core.registered_nodes[bcn.name] + if bcn and + (not bcd or bcd.walkable or (core.get_item_group(self.node.name, "float") ~= 0 and bcd.liquidtype ~= "none")) then if bcd and bcd.leveled and @@ -75,20 +78,20 @@ core.register_entity(":__builtin:falling_node", { local np = {x = bcp.x, y = bcp.y + 1, z = bcp.z} -- Check what's here local n2 = core.get_node(np) + local nd = core.registered_nodes[n2.name] -- If it's not air or liquid, remove node and replace it with -- it's drops - if n2.name ~= "air" and (not core.registered_nodes[n2.name] or - core.registered_nodes[n2.name].liquidtype == "none") then + if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then core.remove_node(np) - if core.registered_nodes[n2.name].buildable_to == false then + if nd.buildable_to == false then -- Add dropped items local drops = core.get_node_drops(n2.name, "") - for _, dropped_item in ipairs(drops) do + for _, dropped_item in pairs(drops) do core.add_item(np, dropped_item) end end -- Run script hook - for _, callback in ipairs(core.registered_on_dignodes) do + for _, callback in pairs(core.registered_on_dignodes) do callback(np, n2) end end @@ -97,7 +100,7 @@ core.register_entity(":__builtin:falling_node", { core.add_node(np, self.node) end self.object:remove() - nodeupdate(np) + core.check_for_falling(np) return end local vel = self.object:getvelocity() @@ -108,15 +111,17 @@ core.register_entity(":__builtin:falling_node", { end }) -function spawn_falling_node(p, node) +local function spawn_falling_node(p, node) local obj = core.add_entity(p, "__builtin:falling_node") - obj:get_luaentity():set_node(node) + if obj then + obj:get_luaentity():set_node(node) + end end -function drop_attached_node(p) +local function drop_attached_node(p) local nn = core.get_node(p).name core.remove_node(p) - for _, item in ipairs(core.get_node_drops(nn, "")) do + for _, item in pairs(core.get_node_drops(nn, "")) do local pos = { x = p.x + math.random()/2 - 0.25, y = p.y + math.random()/2 - 0.25, @@ -126,11 +131,15 @@ function drop_attached_node(p) end end -function check_attached_node(p, n) +function builtin_shared.check_attached_node(p, n) local def = core.registered_nodes[n.name] local d = {x = 0, y = 0, z = 0} if def.paramtype2 == "wallmounted" then - d = core.wallmounted_to_dir(n.param2) + -- The fallback vector here is in case 'wallmounted to dir' is nil due + -- to voxelmanip placing a wallmounted node without resetting a + -- pre-existing param2 value that is out-of-range for wallmounted. + -- The fallback vector corresponds to param2 = 0. + d = core.wallmounted_to_dir(n.param2) or {x = 0, y = 1, z = 0} else d.y = -1 end @@ -147,19 +156,23 @@ end -- Some common functions -- -function nodeupdate_single(p) +function core.check_single_for_falling(p) local n = core.get_node(p) if core.get_item_group(n.name, "falling_node") ~= 0 then local p_bottom = {x = p.x, y = p.y - 1, z = p.z} - local n_bottom = core.get_node(p_bottom) - -- Note: walkable is in the node definition, not in item groups - if core.registered_nodes[n_bottom.name] and + -- Only spawn falling node if node below is loaded + local n_bottom = core.get_node_or_nil(p_bottom) + local d_bottom = n_bottom and core.registered_nodes[n_bottom.name] + if d_bottom and + (core.get_item_group(n.name, "float") == 0 or - core.registered_nodes[n_bottom.name].liquidtype == "none") and - (n.name ~= n_bottom.name or (core.registered_nodes[n_bottom.name].leveled and - core.get_node_level(p_bottom) < core.get_node_max_level(p_bottom))) and - (not core.registered_nodes[n_bottom.name].walkable or - core.registered_nodes[n_bottom.name].buildable_to) then + d_bottom.liquidtype == "none") and + + (n.name ~= n_bottom.name or (d_bottom.leveled and + core.get_node_level(p_bottom) < + core.get_node_max_level(p_bottom))) and + + (not d_bottom.walkable or d_bottom.buildable_to) then n.level = core.get_node_level(p) core.remove_node(p) spawn_falling_node(p, n) @@ -168,7 +181,7 @@ function nodeupdate_single(p) end if core.get_item_group(n.name, "attached_node") ~= 0 then - if not check_attached_node(p, n) then + if not builtin_shared.check_attached_node(p, n) then drop_attached_node(p) return true end @@ -181,7 +194,7 @@ end -- We don't walk diagonals, only our direct neighbors, and self. -- Down first as likely case, but always before self. The same with sides. -- Up must come last, so that things above self will also fall all at once. -local nodeupdate_neighbors = { +local check_for_falling_neighbors = { {x = -1, y = -1, z = 0}, {x = 1, y = -1, z = 0}, {x = 0, y = -1, z = -1}, @@ -195,7 +208,7 @@ local nodeupdate_neighbors = { {x = 0, y = 1, z = 0}, } -function nodeupdate(p) +function core.check_for_falling(p) -- Round p to prevent falling entities to get stuck. p = vector.round(p) @@ -214,10 +227,10 @@ function nodeupdate(p) n = n + 1 s[n] = {p = p, v = v} -- Select next node from neighbor list. - p = vector.add(p, nodeupdate_neighbors[v]) + p = vector.add(p, check_for_falling_neighbors[v]) -- Now we check out the node. If it is in need of an update, -- it will let us know in the return value (true = updated). - if not nodeupdate_single(p) then + if not core.check_single_for_falling(p) then -- If we don't need to "recurse" (walk) to it then pop -- our previous pos off the stack and continue from there, -- with the v value we were at when we last were at that @@ -249,12 +262,33 @@ end -- Global callbacks -- -function on_placenode(p, node) - nodeupdate(p) +local function on_placenode(p, node) + core.check_for_falling(p) end core.register_on_placenode(on_placenode) -function on_dignode(p, node) - nodeupdate(p) +local function on_dignode(p, node) + core.check_for_falling(p) end core.register_on_dignode(on_dignode) + +local function on_punchnode(p, node) + core.check_for_falling(p) +end +core.register_on_punchnode(on_punchnode) + +-- +-- Globally exported functions +-- + +-- TODO remove this function after the 0.4.15 release +function nodeupdate(p) + core.log("deprecated", "nodeupdate: deprecated, please use core.check_for_falling instead") + core.check_for_falling(p) +end + +-- TODO remove this function after the 0.4.15 release +function nodeupdate_single(p) + core.log("deprecated", "nodeupdate_single: deprecated, please use core.check_single_for_falling instead") + core.check_single_for_falling(p) +end diff --git a/builtin/game/forceloading.lua b/builtin/game/forceloading.lua index 8c9fbf512..8a05de36c 100644 --- a/builtin/game/forceloading.lua +++ b/builtin/game/forceloading.lua @@ -5,9 +5,10 @@ core.forceload_block = nil core.forceload_free_block = nil local blocks_forceloaded +local blocks_temploaded = {} local total_forceloaded = 0 -local BLOCKSIZE = 16 +local BLOCKSIZE = core.MAP_BLOCKSIZE local function get_blockpos(pos) return { x = math.floor(pos.x/BLOCKSIZE), @@ -15,32 +16,52 @@ local function get_blockpos(pos) z = math.floor(pos.z/BLOCKSIZE)} end -function core.forceload_block(pos) +-- When we create/free a forceload, it's either transient or persistent. We want +-- to add to/remove from the table that corresponds to the type of forceload, but +-- we also need the other table because whether we forceload a block depends on +-- both tables. +-- This function returns the "primary" table we are adding to/removing from, and +-- the other table. +local function get_relevant_tables(transient) + if transient then + return blocks_temploaded, blocks_forceloaded + else + return blocks_forceloaded, blocks_temploaded + end +end + +function core.forceload_block(pos, transient) local blockpos = get_blockpos(pos) local hash = core.hash_node_position(blockpos) - if blocks_forceloaded[hash] ~= nil then - blocks_forceloaded[hash] = blocks_forceloaded[hash] + 1 + local relevant_table, other_table = get_relevant_tables(transient) + if relevant_table[hash] ~= nil then + relevant_table[hash] = relevant_table[hash] + 1 return true + elseif other_table[hash] ~= nil then + relevant_table[hash] = 1 else if total_forceloaded >= (tonumber(core.setting_get("max_forceloaded_blocks")) or 16) then return false end total_forceloaded = total_forceloaded+1 - blocks_forceloaded[hash] = 1 + relevant_table[hash] = 1 forceload_block(blockpos) return true end end -function core.forceload_free_block(pos) +function core.forceload_free_block(pos, transient) local blockpos = get_blockpos(pos) local hash = core.hash_node_position(blockpos) - if blocks_forceloaded[hash] == nil then return end - if blocks_forceloaded[hash] > 1 then - blocks_forceloaded[hash] = blocks_forceloaded[hash] - 1 + local relevant_table, other_table = get_relevant_tables(transient) + if relevant_table[hash] == nil then return end + if relevant_table[hash] > 1 then + relevant_table[hash] = relevant_table[hash] - 1 + elseif other_table[hash] ~= nil then + relevant_table[hash] = nil else total_forceloaded = total_forceloaded-1 - blocks_forceloaded[hash] = nil + relevant_table[hash] = nil forceload_free_block(blockpos) end end diff --git a/builtin/game/init.lua b/builtin/game/init.lua index a6cfa3bf8..b5e2f7cca 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -3,14 +3,18 @@ local scriptpath = core.get_builtin_path()..DIR_DELIM local commonpath = scriptpath.."common"..DIR_DELIM local gamepath = scriptpath.."game"..DIR_DELIM +-- Shared between builtin files, but +-- not exposed to outer context +local builtin_shared = {} + dofile(commonpath.."vector.lua") dofile(gamepath.."constants.lua") -dofile(gamepath.."item.lua") +assert(loadfile(gamepath.."item.lua"))(builtin_shared) dofile(gamepath.."register.lua") -if core.setting_getbool("mod_profiling") then - dofile(gamepath.."mod_profiling.lua") +if core.setting_getbool("profiler.load") then + profiler = dofile(scriptpath.."profiler"..DIR_DELIM.."init.lua") end dofile(gamepath.."item_entity.lua") @@ -21,8 +25,10 @@ dofile(gamepath.."auth.lua") dofile(gamepath.."chatcommands.lua") dofile(gamepath.."static_spawn.lua") dofile(gamepath.."detached_inventory.lua") -dofile(gamepath.."falling.lua") +assert(loadfile(gamepath.."falling.lua"))(builtin_shared) dofile(gamepath.."features.lua") dofile(gamepath.."voxelarea.lua") dofile(gamepath.."forceloading.lua") dofile(gamepath.."statbars.lua") + +profiler = nil diff --git a/builtin/game/item.lua b/builtin/game/item.lua index 36c2c1a68..bf456a4e0 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -1,5 +1,7 @@ -- Minetest: builtin/item.lua +local builtin_shared = ... + local function copy_pointed_thing(pointed_thing) return { type = pointed_thing.type, @@ -250,7 +252,9 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) local newnode = {name = def.name, param1 = 0, param2 = param2} -- Calculate direction for wall mounted stuff like torches and signs - if def.paramtype2 == 'wallmounted' and not param2 then + if def.place_param2 ~= nil then + newnode.param2 = def.place_param2 + elseif def.paramtype2 == 'wallmounted' and not param2 then local dir = { x = under.x - above.x, y = under.y - above.y, @@ -273,7 +277,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) -- Check if the node is attached and if it can be placed there if core.get_item_group(def.name, "attached_node") ~= 0 and - not check_attached_node(place_to, newnode) then + not builtin_shared.check_attached_node(place_to, newnode) then core.log("action", "attached node " .. def.name .. " can not be placed at " .. core.pos_to_string(place_to)) return itemstack, false @@ -470,6 +474,9 @@ function core.node_dig(pos, node, digger) -- Wear out tool if not core.setting_getbool("creative_mode") then wielded:add_wear(dp.wear) + if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then + core.sound_play(wdef.sound.breaks, {pos = pos, gain = 0.5}) + end end end digger:set_wielded_item(wielded) diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index a66bf33d0..be158c119 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -31,7 +31,6 @@ core.register_entity(":__builtin:item", { spritediv = {x = 1, y = 1}, initial_sprite_basepos = {x = 0, y = 0}, is_visible = false, - infotext = "", }, itemstring = '', @@ -51,7 +50,6 @@ core.register_entity(":__builtin:item", { local c = s local itemtable = stack:to_table() local itemname = nil - local description = "" if itemtable then itemname = stack:to_table().name end @@ -60,7 +58,6 @@ core.register_entity(":__builtin:item", { if core.registered_items[itemname] then item_texture = core.registered_items[itemname].inventory_image item_type = core.registered_items[itemname].type - description = core.registered_items[itemname].description end local prop = { is_visible = true, @@ -69,7 +66,6 @@ core.register_entity(":__builtin:item", { visual_size = {x = s, y = s}, collisionbox = {-c, -c, -c, c, c, c}, automatic_rotate = math.pi * 0.5, - infotext = description, } self.object:set_properties(prop) end, diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index de41cfc91..7caa9e7ba 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -38,7 +38,7 @@ core.register_globalstep(function(dtime) end) function core.after(after, func, ...) - assert(tonumber(time) and type(func) == "function", + assert(tonumber(after) and type(func) == "function", "Invalid core.after invocation") jobs[#jobs + 1] = { func = func, @@ -48,11 +48,13 @@ function core.after(after, func, ...) } end -function core.check_player_privs(player_or_name, ...) - local name = player_or_name - -- Check if we have been provided with a Player object. - if type(name) ~= "string" then +function core.check_player_privs(name, ...) + local arg_type = type(name) + if (arg_type == "userdata" or arg_type == "table") and + name.get_player_name then -- If it quacks like a Player... name = name:get_player_name() + elseif arg_type ~= "string" then + error("Invalid core.check_player_privs argument type: " .. arg_type, 2) end local requested_privs = {...} @@ -85,11 +87,21 @@ end local player_list = {} core.register_on_joinplayer(function(player) - player_list[player:get_player_name()] = player + local player_name = player:get_player_name() + player_list[player_name] = player + if not minetest.is_singleplayer() then + core.chat_send_all("*** " .. player_name .. " joined the game.") + end end) -core.register_on_leaveplayer(function(player) - player_list[player:get_player_name()] = nil +core.register_on_leaveplayer(function(player, timed_out) + local player_name = player:get_player_name() + player_list[player_name] = nil + local announcement = "*** " .. player_name .. " left the game." + if timed_out then + announcement = announcement .. " (timed out)" + end + core.chat_send_all(announcement) end) function core.get_connected_players() @@ -197,3 +209,39 @@ function core.http_add_fetch(httpenv) return httpenv end + +if minetest.setting_getbool("disable_escape_sequences") then + + function core.get_color_escape_sequence(color) + return "" + end + + function core.get_background_escape_sequence(color) + return "" + end + + function core.colorize(color, message) + return message + end + +else + + local ESCAPE_CHAR = string.char(0x1b) + function core.get_color_escape_sequence(color) + return ESCAPE_CHAR .. "(c@" .. color .. ")" + end + + function core.get_background_escape_sequence(color) + return ESCAPE_CHAR .. "(b@" .. color .. ")" + end + + function core.colorize(color, message) + return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("#ffffff") + end + +end + +function core.close_formspec(player_name, formname) + return minetest.show_formspec(player_name, formname, "") +end + diff --git a/builtin/game/mod_profiling.lua b/builtin/game/mod_profiling.lua deleted file mode 100644 index df2d10221..000000000 --- a/builtin/game/mod_profiling.lua +++ /dev/null @@ -1,356 +0,0 @@ --- Minetest: builtin/game/mod_profiling.lua - -local mod_statistics = {} -mod_statistics.step_total = 0 -mod_statistics.data = {} -mod_statistics.stats = {} -mod_statistics.stats["total"] = { - min_us = math.huge, - max_us = 0, - avg_us = 0, - min_per = 100, - max_per = 100, - avg_per = 100 -} - -local replacement_table = { - "register_globalstep", - "register_on_placenode", - "register_on_dignode", - "register_on_punchnode", - "register_on_generated", - "register_on_newplayer", - "register_on_dieplayer", - "register_on_respawnplayer", - "register_on_prejoinplayer", - "register_on_joinplayer", - "register_on_leaveplayer", - "register_on_cheat", - "register_on_chat_message", - "register_on_player_receive_fields", - "register_on_mapgen_init", - "register_on_craft", - "register_craft_predict", - "register_on_item_eat" -} - --------------------------------------------------------------------------------- -function mod_statistics.log_time(type, modname, time_in_us) - - if modname == nil then - modname = "builtin" - end - - if mod_statistics.data[modname] == nil then - mod_statistics.data[modname] = {} - end - - if mod_statistics.data[modname][type] == nil then - mod_statistics.data[modname][type] = 0 - end - - mod_statistics.data[modname][type] = - mod_statistics.data[modname][type] + time_in_us - mod_statistics.step_total = mod_statistics.step_total + time_in_us -end - --------------------------------------------------------------------------------- -function mod_statistics.update_statistics(dtime) - for modname,types in pairs(mod_statistics.data) do - - if mod_statistics.stats[modname] == nil then - mod_statistics.stats[modname] = { - min_us = math.huge, - max_us = 0, - avg_us = 0, - min_per = 100, - max_per = 0, - avg_per = 0 - } - end - - local modtime = 0 - for type,time in pairs(types) do - modtime = modtime + time - if mod_statistics.stats[modname].types == nil then - mod_statistics.stats[modname].types = {} - end - if mod_statistics.stats[modname].types[type] == nil then - mod_statistics.stats[modname].types[type] = { - min_us = math.huge, - max_us = 0, - avg_us = 0, - min_per = 100, - max_per = 0, - avg_per = 0 - } - end - - local toupdate = mod_statistics.stats[modname].types[type] - - --update us values - toupdate.min_us = math.min(time, toupdate.min_us) - toupdate.max_us = math.max(time, toupdate.max_us) - --TODO find better algorithm - toupdate.avg_us = toupdate.avg_us * 0.99 + modtime * 0.01 - - --update percentage values - local cur_per = (time/mod_statistics.step_total) * 100 - toupdate.min_per = math.min(toupdate.min_per, cur_per) - - toupdate.max_per = math.max(toupdate.max_per, cur_per) - - --TODO find better algorithm - toupdate.avg_per = toupdate.avg_per * 0.99 + cur_per * 0.01 - - mod_statistics.data[modname][type] = 0 - end - - --per mod statistics - --update us values - mod_statistics.stats[modname].min_us = - math.min(modtime, mod_statistics.stats[modname].min_us) - mod_statistics.stats[modname].max_us = - math.max(modtime, mod_statistics.stats[modname].max_us) - --TODO find better algorithm - mod_statistics.stats[modname].avg_us = - mod_statistics.stats[modname].avg_us * 0.99 + modtime * 0.01 - - --update percentage values - local cur_per = (modtime/mod_statistics.step_total) * 100 - mod_statistics.stats[modname].min_per = - math.min(mod_statistics.stats[modname].min_per, cur_per) - - mod_statistics.stats[modname].max_per = - math.max(mod_statistics.stats[modname].max_per, cur_per) - - --TODO find better algorithm - mod_statistics.stats[modname].avg_per = - mod_statistics.stats[modname].avg_per * 0.99 + cur_per * 0.01 - end - - --update "total" - mod_statistics.stats["total"].min_us = - math.min(mod_statistics.step_total, mod_statistics.stats["total"].min_us) - mod_statistics.stats["total"].max_us = - math.max(mod_statistics.step_total, mod_statistics.stats["total"].max_us) - --TODO find better algorithm - mod_statistics.stats["total"].avg_us = - mod_statistics.stats["total"].avg_us * 0.99 + - mod_statistics.step_total * 0.01 - - mod_statistics.step_total = 0 -end - --------------------------------------------------------------------------------- -local function build_callback(log_id, fct) - return function( toregister ) - local modname = core.get_current_modname() - - fct(function(...) - local starttime = core.get_us_time() - -- note maximum 10 return values are supported unless someone finds - -- a way to store a variable lenght return value list - local r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 = toregister(...) - local delta = core.get_us_time() - starttime - mod_statistics.log_time(log_id, modname, delta) - return r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 - end - ) - end -end - --------------------------------------------------------------------------------- -function profiling_print_log(cmd, filter) - - print("Filter:" .. dump(filter)) - - core.log("action", "Values below show times/percentages per server step.") - core.log("action", "Following suffixes are used for entities:") - core.log("action", "\t#oa := on_activate, #os := on_step, #op := on_punch, #or := on_rightclick, #gs := get_staticdata") - core.log("action", - string.format("%16s | %25s | %10s | %10s | %10s | %9s | %9s | %9s", - "modname", "type" , "min µs", "max µs", "avg µs", "min %", "max %", "avg %") - ) - core.log("action", - "-----------------+---------------------------+-----------+" .. - "-----------+-----------+-----------+-----------+-----------") - for modname,statistics in pairs(mod_statistics.stats) do - if modname ~= "total" then - - if (filter == "") or (modname == filter) then - if modname:len() > 16 then - modname = "..." .. modname:sub(-13) - end - - core.log("action", - string.format("%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d", - modname, - " ", - statistics.min_us, - statistics.max_us, - statistics.avg_us, - statistics.min_per, - statistics.max_per, - statistics.avg_per) - ) - if core.setting_getbool("detailed_profiling") then - if statistics.types ~= nil then - for type,typestats in pairs(statistics.types) do - - if type:len() > 25 then - type = "..." .. type:sub(-22) - end - - core.log("action", - string.format( - "%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d", - " ", - type, - typestats.min_us, - typestats.max_us, - typestats.avg_us, - typestats.min_per, - typestats.max_per, - typestats.avg_per) - ) - end - end - end - end - end - end - core.log("action", - "-----------------+---------------------------+-----------+" .. - "-----------+-----------+-----------+-----------+-----------") - - if filter == "" then - core.log("action", - string.format("%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d", - "total", - " ", - mod_statistics.stats["total"].min_us, - mod_statistics.stats["total"].max_us, - mod_statistics.stats["total"].avg_us, - mod_statistics.stats["total"].min_per, - mod_statistics.stats["total"].max_per, - mod_statistics.stats["total"].avg_per) - ) - end - core.log("action", " ") - - return true -end - --------------------------------------------------------------------------------- -local function initialize_profiling() - core.log("action", "Initialize tracing") - - mod_statistics.entity_callbacks = {} - - -- first register our own globalstep handler - core.register_globalstep(mod_statistics.update_statistics) - - local rp_register_entity = core.register_entity - core.register_entity = function(name, prototype) - local modname = core.get_current_modname() - local new_on_activate = nil - local new_on_step = nil - local new_on_punch = nil - local new_rightclick = nil - local new_get_staticdata = nil - - if prototype.on_activate ~= nil then - local cbid = name .. "#oa" - mod_statistics.entity_callbacks[cbid] = prototype.on_activate - new_on_activate = function(self, staticdata, dtime_s) - local starttime = core.get_us_time() - mod_statistics.entity_callbacks[cbid](self, staticdata, dtime_s) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - end - end - - if prototype.on_step ~= nil then - local cbid = name .. "#os" - mod_statistics.entity_callbacks[cbid] = prototype.on_step - new_on_step = function(self, dtime) - local starttime = core.get_us_time() - mod_statistics.entity_callbacks[cbid](self, dtime) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - end - end - - if prototype.on_punch ~= nil then - local cbid = name .. "#op" - mod_statistics.entity_callbacks[cbid] = prototype.on_punch - new_on_punch = function(self, hitter) - local starttime = core.get_us_time() - mod_statistics.entity_callbacks[cbid](self, hitter) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - end - end - - if prototype.rightclick ~= nil then - local cbid = name .. "#rc" - mod_statistics.entity_callbacks[cbid] = prototype.rightclick - new_rightclick = function(self, clicker) - local starttime = core.get_us_time() - mod_statistics.entity_callbacks[cbid](self, clicker) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - end - end - - if prototype.get_staticdata ~= nil then - local cbid = name .. "#gs" - mod_statistics.entity_callbacks[cbid] = prototype.get_staticdata - new_get_staticdata = function(self) - local starttime = core.get_us_time() - local retval = mod_statistics.entity_callbacks[cbid](self) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - return retval - end - end - - prototype.on_activate = new_on_activate - prototype.on_step = new_on_step - prototype.on_punch = new_on_punch - prototype.rightclick = new_rightclick - prototype.get_staticdata = new_get_staticdata - - rp_register_entity(name,prototype) - end - - for i,v in ipairs(replacement_table) do - local to_replace = core[v] - core[v] = build_callback(v, to_replace) - end - - local rp_register_abm = core.register_abm - core.register_abm = function(spec) - - local modname = core.get_current_modname() - - local replacement_spec = { - nodenames = spec.nodenames, - neighbors = spec.neighbors, - interval = spec.interval, - chance = spec.chance, - action = function(pos, node, active_object_count, active_object_count_wider) - local starttime = core.get_us_time() - spec.action(pos, node, active_object_count, active_object_count_wider) - local delta = core.get_us_time() - starttime - mod_statistics.log_time("abm", modname, delta) - end - } - rp_register_abm(replacement_spec) - end - - core.log("action", "Mod profiling initialized") -end - -initialize_profiling() diff --git a/builtin/game/privileges.lua b/builtin/game/privileges.lua index bd5ead624..56e090f4c 100644 --- a/builtin/game/privileges.lua +++ b/builtin/game/privileges.lua @@ -26,18 +26,46 @@ function core.register_privilege(name, param) end core.register_privilege("interact", "Can interact with things and modify the world") -core.register_privilege("teleport", "Can use /teleport command") -core.register_privilege("bring", "Can teleport other players") -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") -core.register_privilege("give", "Can use /give and /giveme") -core.register_privilege("password", "Can use /setpassword and /clearpassword") +core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges") +core.register_privilege("privs", "Can modify privileges") + +core.register_privilege("teleport", { + description = "Can use /teleport command", + give_to_singleplayer = false, +}) +core.register_privilege("bring", { + description = "Can teleport other players", + give_to_singleplayer = false, +}) +core.register_privilege("settime", { + description = "Can use /time", + give_to_singleplayer = false, +}) +core.register_privilege("server", { + description = "Can do server maintenance stuff", + give_to_singleplayer = false, +}) +core.register_privilege("protection_bypass", { + description = "Can bypass node protection in the world", + give_to_singleplayer = false, +}) +core.register_privilege("ban", { + description = "Can ban and unban players", + give_to_singleplayer = false, +}) +core.register_privilege("kick", { + description = "Can kick players", + give_to_singleplayer = false, +}) +core.register_privilege("give", { + description = "Can use /give and /giveme", + give_to_singleplayer = false, +}) +core.register_privilege("password", { + description = "Can use /setpassword and /clearpassword", + give_to_singleplayer = false, +}) core.register_privilege("fly", { description = "Can fly using the free_move mode", give_to_singleplayer = false, @@ -50,5 +78,15 @@ core.register_privilege("noclip", { description = "Can fly through walls", give_to_singleplayer = false, }) -core.register_privilege("rollback", "Can use the rollback functionality") - +core.register_privilege("rollback", { + description = "Can use the rollback functionality", + give_to_singleplayer = false, +}) +core.register_privilege("zoom", { + description = "Can zoom the camera", + give_to_singleplayer = false, +}) +core.register_privilege("debug", { + description = "Allows enabling various debug options that may affect gameplay", + give_to_singleplayer = false, +}) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index f330491a2..90f095e9f 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -7,6 +7,9 @@ local register_item_raw = core.register_item_raw core.register_item_raw = nil +local unregister_item_raw = core.unregister_item_raw +core.unregister_item_raw = nil + local register_alias_raw = core.register_alias_raw core.register_alias_raw = nil @@ -124,6 +127,11 @@ function core.register_item(name, itemdef) fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8}, } end + if itemdef.light_source and itemdef.light_source > core.LIGHT_MAX then + itemdef.light_source = core.LIGHT_MAX + core.log("warning", "Node 'light_source' value exceeds maximum," .. + " limiting to maximum: " ..name) + end setmetatable(itemdef, {__index = core.nodedef_default}) core.registered_nodes[itemdef.name] = itemdef elseif itemdef.type == "craft" then @@ -172,6 +180,27 @@ function core.register_item(name, itemdef) register_item_raw(itemdef) end +function core.unregister_item(name) + if not core.registered_items[name] then + core.log("warning", "Not unregistering item " ..name.. + " because it doesn't exist.") + return + end + -- Erase from registered_* table + local type = core.registered_items[name].type + if type == "node" then + core.registered_nodes[name] = nil + elseif type == "craft" then + core.registered_craftitems[name] = nil + elseif type == "tool" then + core.registered_tools[name] = nil + end + core.registered_items[name] = nil + + + unregister_item_raw(name) +end + function core.register_node(name, nodedef) nodedef.type = "node" core.register_item(name, nodedef) @@ -242,6 +271,20 @@ function core.register_alias(name, convert_to) end end +function core.register_alias_force(name, convert_to) + if forbidden_item_names[name] then + error("Unable to register alias: Name is forbidden: " .. name) + end + if core.registered_items[name] ~= nil then + core.unregister_item(name) + core.log("info", "Removed item " ..name.. + " while attempting to force add an alias") + end + --core.log("Registering alias: " .. name .. " -> " .. convert_to) + core.registered_aliases[name] = convert_to + register_alias_raw(name, convert_to) +end + function core.on_craft(itemstack, player, old_craft_list, craft_inv) for _, func in ipairs(core.registered_on_crafts) do itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack diff --git a/builtin/game/voxelarea.lua b/builtin/game/voxelarea.lua index 6d926c940..724761414 100644 --- a/builtin/game/voxelarea.lua +++ b/builtin/game/voxelarea.lua @@ -50,7 +50,7 @@ end function VoxelArea:position(i) local p = {} local MinEdge = self.MinEdge - + i = i - 1 p.z = math.floor(i / self.zstride) + MinEdge.z @@ -84,23 +84,46 @@ end function VoxelArea:iter(minx, miny, minz, maxx, maxy, maxz) local i = self:index(minx, miny, minz) - 1 - local last = self:index(maxx, maxy, maxz) - local ystride = self.ystride - local zstride = self.zstride - local yoff = (last+1) % ystride - local zoff = (last+1) % zstride - local ystridediff = (i - last) % ystride - local zstridediff = (i - last) % zstride + local xrange = maxx - minx + 1 + local nextaction = i + 1 + xrange + + local y = 0 + local yrange = maxy - miny + 1 + local yreqstride = self.ystride - xrange + + local z = 0 + local zrange = maxz - minz + 1 + local multistride = self.zstride - ((yrange - 1) * self.ystride + xrange) + return function() + -- continue i until it needs to jump i = i + 1 - if i % zstride == zoff then - i = i + zstridediff - elseif i % ystride == yoff then - i = i + ystridediff - end - if i <= last then + if i ~= nextaction then return i end + + -- continue y until maxy is exceeded + y = y + 1 + if y ~= yrange then + -- set i to index(minx, miny + y, minz + z) - 1 + i = i + yreqstride + nextaction = i + xrange + return i + end + + -- continue z until maxz is exceeded + z = z + 1 + if z == zrange then + -- cuboid finished, return nil + return + end + + -- set i to index(minx, miny, minz + z) - 1 + i = i + multistride + + y = 0 + nextaction = i + xrange + return i end end diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index 1fd89ff77..da3667828 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -248,14 +248,18 @@ end -------------------------------------------------------------------------------- function is_server_protocol_compat(server_proto_min, server_proto_max) - return min_supp_proto <= (server_proto_max or 24) and max_supp_proto >= (server_proto_min or 13) + if (not server_proto_min) or (not server_proto_max) then + -- There is no info. Assume the best and act as if we would be compatible. + return true + end + return min_supp_proto <= server_proto_max and max_supp_proto >= server_proto_min end -------------------------------------------------------------------------------- function is_server_protocol_compat_or_error(server_proto_min, server_proto_max) if not is_server_protocol_compat(server_proto_min, server_proto_max) then local server_prot_ver_info, client_prot_ver_info - local s_p_min = server_proto_min or 13 - local s_p_max = server_proto_max or 24 + local s_p_min = server_proto_min + local s_p_max = server_proto_max if s_p_min ~= s_p_max then server_prot_ver_info = fgettext_ne("Server supports protocol versions between $1 and $2. ", diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua index eb0319ba0..7b3ab9852 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/dlg_config_world.lua @@ -16,6 +16,9 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -------------------------------------------------------------------------------- + +local enabled_all = false + local function modname_valid(name) return not name:find("[^a-z0-9_]") end @@ -44,13 +47,18 @@ local function get_formspec(data) if mod == nil then mod = {name=""} end + + local hard_deps, soft_deps = modmgr.get_dependencies(mod.path) retval = retval .. "label[0,0.7;" .. fgettext("Mod:") .. "]" .. "label[0.75,0.7;" .. mod.name .. "]" .. - "label[0,1.25;" .. fgettext("Depends:") .. "]" .. - "textlist[0,1.75;5,4.25;world_config_depends;" .. - modmgr.get_dependencies(mod.path) .. ";0]" .. + "label[0,1.25;" .. fgettext("Dependencies:") .. "]" .. + "textlist[0,1.75;5,2.125;world_config_depends;" .. + hard_deps .. ";0]" .. + "label[0,3.875;" .. fgettext("Optional dependencies:") .. "]" .. + "textlist[0,4.375;5,1.8;world_config_optdepends;" .. + soft_deps .. ";0]" .. "button[3.25,7;2.5,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" .. "button[5.75,7;2.5,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]" @@ -80,11 +88,15 @@ local function get_formspec(data) end end end - - retval = retval .. - "button[8.75,0.125;2.5,0.5;btn_all_mods;" .. fgettext("Enable all") .. "]" .. - "textlist[5.5,0.75;5.75,5.25;world_config_modlist;" - + if enabled_all then + retval = retval .. + "button[8.75,0.125;2.5,0.5;btn_disable_all_mods;" .. fgettext("Disable all") .. "]" .. + "textlist[5.5,0.75;5.75,5.4;world_config_modlist;" + else + retval = retval .. + "button[8.75,0.125;2.5,0.5;btn_enable_all_mods;" .. fgettext("Enable all") .. "]" .. + "textlist[5.5,0.75;5.75,5.4;world_config_modlist;" + end retval = retval .. modmgr.render_modlist(data.list) retval = retval .. ";" .. data.selected_mod .."]" @@ -229,15 +241,27 @@ local function handle_buttons(this, fields) return true end - if fields["btn_all_mods"] then + if fields.btn_enable_all_mods then local list = this.data.list:get_raw_list() - for i=1,#list,1 do - if list[i].typ ~= "game_mod" and - not list[i].is_modpack then + for i = 1, #list do + if list[i].typ ~= "game_mod" and not list[i].is_modpack then list[i].enabled = true end end + enabled_all = true + return true + end + + if fields.btn_disable_all_mods then + local list = this.data.list:get_raw_list() + + for i = 1, #list do + if list[i].typ ~= "game_mod" and not list[i].is_modpack then + list[i].enabled = false + end + end + enabled_all = false return true end diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua index f5e80c252..b0d923768 100644 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@ -479,13 +479,13 @@ local function handle_change_setting_buttons(this, fields) return true end if setting.min and new_value < setting.min then - this.data.error_message = fgettext_ne("The value must be greater than $1.", setting.min) + this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min) this.data.entered_text = fields["te_setting_value"] core.update_formspec(this:get_formspec()) return true end if setting.max and new_value > setting.max then - this.data.error_message = fgettext_ne("The value must be lower than $1.", setting.max) + this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max) this.data.entered_text = fields["te_setting_value"] core.update_formspec(this:get_formspec()) return true @@ -659,102 +659,12 @@ function create_adv_settings_dlg() return dlg end -local function create_minetest_conf_example() - local result = "# This file contains a list of all available settings and their default value for minetest.conf\n" .. - "\n" .. - "# By default, all the settings are commented and not functional.\n" .. - "# Uncomment settings by removing the preceding #.\n" .. - "\n" .. - "# minetest.conf is read by default from:\n" .. - "# ../minetest.conf\n" .. - "# ../../minetest.conf\n" .. - "# Any other path can be chosen by passing the path as a parameter\n" .. - "# to the program, eg. \"minetest.exe --config ../minetest.conf.example\".\n" .. - "\n" .. - "# Further documentation:\n" .. - "# http://wiki.minetest.net/\n" .. - "\n" +-- Generate minetest.conf.example and settings_translation_file.cpp - local settings = parse_config_file(true, false) - for _, entry in ipairs(settings) do - if entry.type == "category" then - if entry.level == 0 then - result = result .. "#\n# " .. entry.name .. "\n#\n\n" - else - for i = 1, entry.level do - result = result .. "#" - end - result = result .. "# " .. entry.name .. "\n\n" - end - else - if entry.comment ~= "" then - for _, comment_line in ipairs(entry.comment:split("\n", true)) do - result = result .."# " .. comment_line .. "\n" - end - end - result = result .. "# type: " .. entry.type - if entry.min then - result = result .. " min: " .. entry.min - end - if entry.max then - result = result .. " max: " .. entry.max - end - if entry.values then - result = result .. " values: " .. table.concat(entry.values, ", ") - end - if entry.possible then - result = result .. " possible values: " .. entry.possible:gsub(",", ", ") - end - result = result .. "\n" - local append = "" - if entry.default ~= "" then - append = " " .. entry.default - end - result = result .. "# " .. entry.name .. " =" .. append .. "\n\n" - end - end - return result -end +-- *** Please note *** +-- There is text in minetest.conf.example that will not be generated from +-- settingtypes.txt but must be preserved: +-- The documentation of mapgen noise parameter formats (title plus 16 lines) +-- Noise parameter 'mgv5_np_ground' in group format (13 lines) -local function create_translation_file() - local result = "// This file is automatically generated\n" .. - "// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files\n" .. - "// To update it, refer to the bottom of builtin/mainmenu/tab_settings.lua\n\n" .. - "fake_function() {\n" - - local settings = parse_config_file(true, false) - for _, entry in ipairs(settings) do - if entry.type == "category" then - local name_escaped = entry.name:gsub("\"", "\\\"") - result = result .. "\tgettext(\"" .. name_escaped .. "\");\n" - else - if entry.readable_name then - local readable_name_escaped = entry.readable_name:gsub("\"", "\\\"") - result = result .. "\tgettext(\"" .. readable_name_escaped .. "\");\n" - end - if entry.comment ~= "" then - local comment_escaped = entry.comment:gsub("\n", "\\n") - comment_escaped = comment_escaped:gsub("\"", "\\\"") - result = result .. "\tgettext(\"" .. comment_escaped .. "\");\n" - end - end - end - result = result .. "}\n" - return result -end - -if false then - local file = io.open("minetest.conf.example", "w") - if file then - file:write(create_minetest_conf_example()) - file:close() - end -end - -if false then - local file = io.open("src/settings_translation_file.cpp", "w") - if file then - file:write(create_translation_file()) - file:close() - end -end +--assert(loadfile(core.get_mainmenu_path()..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false)) diff --git a/builtin/mainmenu/generate_from_settingtypes.lua b/builtin/mainmenu/generate_from_settingtypes.lua new file mode 100644 index 000000000..6c9ba27fb --- /dev/null +++ b/builtin/mainmenu/generate_from_settingtypes.lua @@ -0,0 +1,99 @@ +local settings = ... + +local concat = table.concat +local insert = table.insert +local sprintf = string.format +local rep = string.rep + +local minetest_example_header = [[ +# This file contains a list of all available settings and their default value for minetest.conf + +# By default, all the settings are commented and not functional. +# Uncomment settings by removing the preceding #. + +# minetest.conf is read by default from: +# ../minetest.conf +# ../../minetest.conf +# Any other path can be chosen by passing the path as a parameter +# to the program, eg. "minetest.exe --config ../minetest.conf.example". + +# Further documentation: +# http://wiki.minetest.net/ + +]] + +local function create_minetest_conf_example() + local result = { minetest_example_header } + for _, entry in ipairs(settings) do + if entry.type == "category" then + if entry.level == 0 then + insert(result, "#\n# " .. entry.name .. "\n#\n\n") + else + insert(result, rep("#", entry.level)) + insert(result, "# " .. entry.name .. "\n\n") + end + else + if entry.comment ~= "" then + for _, comment_line in ipairs(entry.comment:split("\n", true)) do + insert(result, "# " .. comment_line .. "\n") + end + end + insert(result, "# type: " .. entry.type) + if entry.min then + insert(result, " min: " .. entry.min) + end + if entry.max then + insert(result, " max: " .. entry.max) + end + if entry.values then + insert(result, " values: " .. concat(entry.values, ", ")) + end + if entry.possible then + insert(result, " possible values: " .. entry.possible:gsub(",", ", ")) + end + insert(result, "\n") + local append + if entry.default ~= "" then + append = " " .. entry.default + end + insert(result, sprintf("# %s =%s\n\n", entry.name, append or "")) + end + end + return concat(result) +end + +local translation_file_header = [[ +// This file is automatically generated +// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files +// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua + +fake_function() {]] + +local function create_translation_file() + local result = { translation_file_header } + for _, entry in ipairs(settings) do + if entry.type == "category" then + insert(result, sprintf("\tgettext(%q);", entry.name)) + else + if entry.readable_name then + insert(result, sprintf("\tgettext(%q);", entry.readable_name)) + end + if entry.comment ~= "" then + local comment_escaped = entry.comment:gsub("\n", "\\n") + comment_escaped = comment_escaped:gsub("\"", "\\\"") + insert(result, "\tgettext(\"" .. comment_escaped .. "\");") + end + end + end + insert(result, "}\n") + return concat(result, "\n") +end + +local file = assert(io.open("minetest.conf.example", "w")) +file:write(create_minetest_conf_example()) +file:close() + +file = assert(io.open("src/settings_translation_file.cpp", "w")) +file:write(create_translation_file()) +file:close() + diff --git a/builtin/mainmenu/modmgr.lua b/builtin/mainmenu/modmgr.lua index f996df7ba..2b7b371bf 100644 --- a/builtin/mainmenu/modmgr.lua +++ b/builtin/mainmenu/modmgr.lua @@ -284,27 +284,33 @@ end -------------------------------------------------------------------------------- function modmgr.get_dependencies(modfolder) - local toadd = "" + local toadd_hard = "" + local toadd_soft = "" if modfolder ~= nil then local filename = modfolder .. DIR_DELIM .. "depends.txt" + local hard_dependencies = {} + local soft_dependencies = {} local dependencyfile = io.open(filename,"r") - if dependencyfile then local dependency = dependencyfile:read("*l") while dependency do - if toadd ~= "" then - toadd = toadd .. "," + dependency = dependency:gsub("\r", "") + if string.sub(dependency, -1, -1) == "?" then + table.insert(soft_dependencies, string.sub(dependency, 1, -2)) + else + table.insert(hard_dependencies, dependency) end - toadd = toadd .. dependency dependency = dependencyfile:read() end dependencyfile:close() end + toadd_hard = table.concat(hard_dependencies, ",") + toadd_soft = table.concat(soft_dependencies, ",") end - return toadd + return toadd_hard, toadd_soft end -------------------------------------------------------------------------------- diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua index 4d2ffd7f4..c2ad19183 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_credits.lua @@ -29,20 +29,23 @@ local core_developers = { "Loic Blot (nerzhul/nrz) ", "Matt Gregory (paramat)", "est31 ", - "Craig Robbins (Zeno)", + "Craig Robbins (Zeno) ", + "Auke Kok (sofar) ", + "Andrew Ward (rubenwardy) ", } local active_contributors = { - "Auke Kok (sofar) ", "Duane Robertson ", "SmallJoker ", - "Andrew Ward (rubenwardy) ", + "Lars Hofhansl ", "Jeija ", "Gregory Currie (gregorycu)", "Sokomine ", "TeTpaAka", "Jean-Patrick G (kilbith) ", "Diego Martínez (kaeza) ", + "Dániel Juhász (juhdanad) ", + "Rogier ", } local previous_core_developers = { @@ -76,17 +79,13 @@ return { caption = fgettext("Credits"), cbf_formspec = function(tabview, name, tabdata) local logofile = defaulttexturedir .. "logo.png" + local version = core.get_version() return "image[0.5,1;" .. core.formspec_escape(logofile) .. "]" .. - "label[0.5,3.2;Minetest " .. core.get_version() .. "]" .. + "label[0.5,3.2;" .. version.project .. " " .. version.string .. "]" .. "label[0.5,3.5;http://minetest.net]" .. "tablecolumns[color;text]" .. "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. "table[3.5,-0.25;8.5,5.8;list_credits;" .. - "#FFFF00," .. "Dedication of the current release" .. ",," .. - "The 0.4.14 release is dedicated to the memory of" .. ",," .. - "Minetest developer Maciej Kasatkin (RealBadAngel)" .. ",," .. - "who died on March 24 2016." .. ",," .. - "Our thoughts are with his family and friends." .. ",,," .. "#FFFF00," .. fgettext("Core Developers") .. ",," .. table.concat(core_developers, ",,") .. ",,," .. "#FFFF00," .. fgettext("Active Contributors") .. ",," .. diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua index 5b59aa110..4a5b6c041 100644 --- a/builtin/mainmenu/tab_mods.lua +++ b/builtin/mainmenu/tab_mods.lua @@ -98,12 +98,24 @@ local function get_formspec(tabview, name, tabdata) .. fgettext("Uninstall selected modpack") .. "]" else --show dependencies + local toadd_hard, toadd_soft = modmgr.get_dependencies(selected_mod.path) + if toadd_hard == "" and toadd_soft == "" then + retval = retval .. "," .. fgettext("No dependencies.") + else + if toadd_hard ~= "" then + retval = retval .. "," .. fgettext("Dependencies:") .. "," + retval = retval .. toadd_hard + end + if toadd_soft ~= "" then + if toadd_hard ~= "" then + retval = retval .. "," + end + retval = retval .. "," .. fgettext("Optional dependencies:") .. "," + retval = retval .. toadd_soft + end + end - retval = retval .. "," .. fgettext("Depends:") .. "," - - local toadd = modmgr.get_dependencies(selected_mod.path) - - retval = retval .. toadd .. ";0]" + retval = retval .. ";0]" retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;" .. fgettext("Uninstall selected mod") .. "]" diff --git a/builtin/mainmenu/tab_texturepacks.lua b/builtin/mainmenu/tab_texturepacks.lua index a102fd61d..4638beaa1 100644 --- a/builtin/mainmenu/tab_texturepacks.lua +++ b/builtin/mainmenu/tab_texturepacks.lua @@ -73,7 +73,7 @@ local function get_formspec(tabview, name, tabdata) if not file_exists(infofile) then infofile = current_texture_path .. DIR_DELIM .. "info.txt" if file_exists(infofile) then - core.log("info.txt is depreciated. description.txt should be used instead.") + core.log("deprecated", "info.txt is deprecated. description.txt should be used instead.") end end @@ -96,8 +96,8 @@ local function get_formspec(tabview, name, tabdata) return retval .. render_texture_pack_list(list) .. ";" .. index .. "]" .. - "image[0.25,0.25;4.0,3.7;" .. core.formspec_escape(screenfile or no_screenshot) .. "]" .. - "textarea[0.6,3.5;3.7,1.5;;" .. core.formspec_escape(infotext or "") .. ";]" + "image[0.25,0.25;4.05,2.7;" .. core.formspec_escape(screenfile or no_screenshot) .. "]" .. + "textarea[0.6,2.85;3.7,1.5;;" .. core.formspec_escape(infotext or "") .. ";]" end -------------------------------------------------------------------------------- diff --git a/builtin/profiler/init.lua b/builtin/profiler/init.lua new file mode 100644 index 000000000..c1597d280 --- /dev/null +++ b/builtin/profiler/init.lua @@ -0,0 +1,72 @@ +--Minetest +--Copyright (C) 2016 T4im +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +local profiler_path = core.get_builtin_path()..DIR_DELIM.."profiler"..DIR_DELIM +local profiler = {} +local sampler = assert(loadfile(profiler_path .. "sampling.lua"))(profiler) +local instrumentation = assert(loadfile(profiler_path .. "instrumentation.lua"))(profiler, sampler) +local reporter = dofile(profiler_path .. "reporter.lua") +profiler.instrument = instrumentation.instrument + +--- +-- Delayed registration of the /profiler chat command +-- Is called later, after `core.register_chatcommand` was set up. +-- +function profiler.init_chatcommand() + local instrument_profiler = core.setting_getbool("instrument.profiler") or false + if instrument_profiler then + instrumentation.init_chatcommand() + end + + local param_usage = "print [filter] | dump [filter] | save [format [filter]] | reset" + core.register_chatcommand("profiler", { + description = "handle the profiler and profiling data", + params = param_usage, + privs = { server=true }, + func = function(name, param) + local command, arg0 = string.match(param, "([^ ]+) ?(.*)") + local args = arg0 and string.split(arg0, " ") + + if command == "dump" then + core.log("action", reporter.print(sampler.profile, arg0)) + return true, "Statistics written to action log" + elseif command == "print" then + return true, reporter.print(sampler.profile, arg0) + elseif command == "save" then + return reporter.save(sampler.profile, args[1] or "txt", args[2]) + elseif command == "reset" then + sampler.reset() + return true, "Statistics were reset" + end + + return false, string.format( + "Usage: %s\n" .. + "Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).", + param_usage + ) + end + }) + + if not instrument_profiler then + instrumentation.init_chatcommand() + end +end + +sampler.init() +instrumentation.init() + +return profiler diff --git a/builtin/profiler/instrumentation.lua b/builtin/profiler/instrumentation.lua new file mode 100644 index 000000000..4311215b2 --- /dev/null +++ b/builtin/profiler/instrumentation.lua @@ -0,0 +1,232 @@ +--Minetest +--Copyright (C) 2016 T4im +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +local format, pairs, type = string.format, pairs, type +local core, get_current_modname = core, core.get_current_modname +local profiler, sampler = ... +local instrument_builtin = core.setting_getbool("instrument.builtin") or false + +local register_functions = { + register_globalstep = 0, + register_playerevent = 0, + register_on_placenode = 0, + register_on_dignode = 0, + register_on_punchnode = 0, + register_on_generated = 0, + register_on_newplayer = 0, + register_on_dieplayer = 0, + register_on_respawnplayer = 0, + register_on_prejoinplayer = 0, + register_on_joinplayer = 0, + register_on_leaveplayer = 0, + register_on_cheat = 0, + register_on_chat_message = 0, + register_on_player_receive_fields = 0, + register_on_craft = 0, + register_craft_predict = 0, + register_on_protection_violation = 0, + register_on_item_eat = 0, + register_on_punchplayer = 0, + register_on_player_hpchange = 0, +} + +--- +-- Create an unique instrument name. +-- Generate a missing label with a running index number. +-- +local counts = {} +local function generate_name(def) + local class, label, func_name = def.class, def.label, def.func_name + if label then + if class or func_name then + return format("%s '%s' %s", class or "", label, func_name or ""):trim() + end + return format("%s", label):trim() + elseif label == false then + return format("%s", class or func_name):trim() + end + + local index_id = def.mod .. (class or func_name) + local index = counts[index_id] or 1 + counts[index_id] = index + 1 + return format("%s[%d] %s", class or func_name, index, class and func_name or ""):trim() +end + +--- +-- Keep `measure` and the closure in `instrument` lean, as these, and their +-- directly called functions are the overhead that is caused by instrumentation. +-- +local time, log = core.get_us_time, sampler.log +local function measure(modname, instrument_name, start, ...) + log(modname, instrument_name, time() - start) + return ... +end +--- Automatically instrument a function to measure and log to the sampler. +-- def = { +-- mod = "", +-- class = "", +-- func_name = "", +-- -- if nil, will create a label based on registration order +-- label = "" | false, +-- } +local function instrument(def) + if not def or not def.func then + return + end + def.mod = def.mod or get_current_modname() + local modname = def.mod + local instrument_name = generate_name(def) + local func = def.func + + if not instrument_builtin and modname == "*builtin*" then + return func + end + + return function(...) + -- This tail-call allows passing all return values of `func` + -- also called https://en.wikipedia.org/wiki/Continuation_passing_style + -- Compared to table creation and unpacking it won't lose `nil` returns + -- and is expected to be faster + -- `measure` will be executed after time() and func(...) + return measure(modname, instrument_name, time(), func(...)) + end +end + +local function can_be_called(func) + -- It has to be a function or callable table + return type(func) == "function" or + ((type(func) == "table" or type(func) == "userdata") and + getmetatable(func) and getmetatable(func).__call) +end + +local function assert_can_be_called(func, func_name, level) + if not can_be_called(func) then + -- Then throw an *helpful* error, by pointing on our caller instead of us. + error(format("Invalid argument to %s. Expected function-like type instead of '%s'.", func_name, type(func)), level + 1) + end +end + +--- +-- Wraps a registration function `func` in such a way, +-- that it will automatically instrument any callback function passed as first argument. +-- +local function instrument_register(func, func_name) + local register_name = func_name:gsub("^register_", "", 1) + return function(callback, ...) + assert_can_be_called(callback, func_name, 2) + register_functions[func_name] = register_functions[func_name] + 1 + return func(instrument { + func = callback, + func_name = register_name + }), ... + end +end + +local function init_chatcommand() + if core.setting_getbool("instrument.chatcommand") or true then + local orig_register_chatcommand = core.register_chatcommand + core.register_chatcommand = function(cmd, def) + def.func = instrument { + func = def.func, + label = "/" .. cmd, + } + orig_register_chatcommand(cmd, def) + end + end +end + +--- +-- Start instrumenting selected functions +-- +local function init() + local is_set = core.setting_getbool + if is_set("instrument.entity") or true then + -- Explicitly declare entity api-methods. + -- Simple iteration would ignore lookup via __index. + local entity_instrumentation = { + "on_activate", + "on_step", + "on_punch", + "rightclick", + "get_staticdata", + } + -- Wrap register_entity() to instrument them on registration. + local orig_register_entity = core.register_entity + core.register_entity = function(name, prototype) + local modname = get_current_modname() + for _, func_name in pairs(entity_instrumentation) do + prototype[func_name] = instrument { + func = prototype[func_name], + mod = modname, + func_name = func_name, + label = prototype.label, + } + end + orig_register_entity(name,prototype) + end + end + + if is_set("instrument.abm") or true then + -- Wrap register_abm() to automatically instrument abms. + local orig_register_abm = core.register_abm + core.register_abm = function(spec) + spec.action = instrument { + func = spec.action, + class = "ABM", + label = spec.label, + } + orig_register_abm(spec) + end + end + + if is_set("instrument.lbm") or true then + -- Wrap register_lbm() to automatically instrument lbms. + local orig_register_lbm = core.register_lbm + core.register_lbm = function(spec) + spec.action = instrument { + func = spec.action, + class = "LBM", + label = spec.label or spec.name, + } + orig_register_lbm(spec) + end + end + + if is_set("instrument.global_callback") or true then + for func_name, _ in pairs(register_functions) do + core[func_name] = instrument_register(core[func_name], func_name) + end + end + + if is_set("instrument.profiler") or false then + -- Measure overhead of instrumentation, but keep it down for functions + -- So keep the `return` for better optimization. + profiler.empty_instrument = instrument { + func = function() return end, + mod = "*profiler*", + class = "Instrumentation overhead", + label = false, + } + end +end + +return { + register_functions = register_functions, + instrument = instrument, + init = init, + init_chatcommand = init_chatcommand, +} diff --git a/builtin/profiler/reporter.lua b/builtin/profiler/reporter.lua new file mode 100644 index 000000000..5b38ed4df --- /dev/null +++ b/builtin/profiler/reporter.lua @@ -0,0 +1,277 @@ +--Minetest +--Copyright (C) 2016 T4im +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +local DIR_DELIM, LINE_DELIM = DIR_DELIM, "\n" +local table, unpack, string, pairs, io, os = table, unpack, string, pairs, io, os +local rep, sprintf, tonumber = string.rep, string.format, tonumber +local core, setting_get = core, core.setting_get +local reporter = {} + +--- +-- Shorten a string. End on an ellipsis if shortened. +-- +local function shorten(str, length) + if str and str:len() > length then + return "..." .. str:sub(-(length-3)) + end + return str +end + +local function filter_matches(filter, text) + return not filter or string.match(text, filter) +end + +local function format_number(number, fmt) + number = tonumber(number) + if not number then + return "N/A" + end + return sprintf(fmt or "%d", number) +end + +local Formatter = { + new = function(self, object) + object = object or {} + object.out = {} -- output buffer + self.__index = self + return setmetatable(object, self) + end, + __tostring = function (self) + return table.concat(self.out, LINE_DELIM) + end, + print = function(self, text, ...) + if (...) then + text = sprintf(text, ...) + end + + if text then + -- Avoid format unicode issues. + text = text:gsub("Ms", "µs") + end + + table.insert(self.out, text or LINE_DELIM) + end, + flush = function(self) + table.insert(self.out, LINE_DELIM) + local text = table.concat(self.out, LINE_DELIM) + self.out = {} + return text + end +} + +local widths = { 55, 9, 9, 9, 5, 5, 5 } +local txt_row_format = sprintf(" %%-%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds", unpack(widths)) + +local HR = {} +for i=1, #widths do + HR[i]= rep("-", widths[i]) +end +-- ' | ' should break less with github than '-+-', when people are pasting there +HR = sprintf("-%s-", table.concat(HR, " | ")) + +local TxtFormatter = Formatter:new { + format_row = function(self, modname, instrument_name, statistics) + local label + if instrument_name then + label = shorten(instrument_name, widths[1] - 5) + label = sprintf(" - %s %s", label, rep(".", widths[1] - 5 - label:len())) + else -- Print mod_stats + label = shorten(modname, widths[1] - 2) .. ":" + end + + self:print(txt_row_format, label, + format_number(statistics.time_min), + format_number(statistics.time_max), + format_number(statistics:get_time_avg()), + format_number(statistics.part_min, "%.1f"), + format_number(statistics.part_max, "%.1f"), + format_number(statistics:get_part_avg(), "%.1f") + ) + end, + format = function(self, filter) + local profile = self.profile + self:print("Values below show absolute/relative times spend per server step by the instrumented function.") + self:print("A total of %d samples were taken", profile.stats_total.samples) + + if filter then + self:print("The output is limited to '%s'", filter) + end + + self:print() + self:print( + txt_row_format, + "instrumentation", "min Ms", "max Ms", "avg Ms", "min %", "max %", "avg %" + ) + self:print(HR) + for modname,mod_stats in pairs(profile.stats) do + if filter_matches(filter, modname) then + self:format_row(modname, nil, mod_stats) + + if mod_stats.instruments ~= nil then + for instrument_name, instrument_stats in pairs(mod_stats.instruments) do + self:format_row(nil, instrument_name, instrument_stats) + end + end + end + end + self:print(HR) + if not filter then + self:format_row("total", nil, profile.stats_total) + end + end +} + +local CsvFormatter = Formatter:new { + format_row = function(self, modname, instrument_name, statistics) + self:print( + "%q,%q,%d,%d,%d,%d,%d,%f,%f,%f", + modname, instrument_name, + statistics.samples, + statistics.time_min, + statistics.time_max, + statistics:get_time_avg(), + statistics.time_all, + statistics.part_min, + statistics.part_max, + statistics:get_part_avg() + ) + end, + format = function(self, filter) + self:print( + "%q,%q,%q,%q,%q,%q,%q,%q,%q,%q", + "modname", "instrumentation", + "samples", + "time min µs", + "time max µs", + "time avg µs", + "time all µs", + "part min %", + "part max %", + "part avg %" + ) + for modname, mod_stats in pairs(self.profile.stats) do + if filter_matches(filter, modname) then + self:format_row(modname, "*", mod_stats) + + if mod_stats.instruments ~= nil then + for instrument_name, instrument_stats in pairs(mod_stats.instruments) do + self:format_row(modname, instrument_name, instrument_stats) + end + end + end + end + end +} + +local function format_statistics(profile, format, filter) + local formatter + if format == "csv" then + formatter = CsvFormatter:new { + profile = profile + } + else + formatter = TxtFormatter:new { + profile = profile + } + end + formatter:format(filter) + return formatter:flush() +end + +--- +-- Format the profile ready for display and +-- @return string to be printed to the console +-- +function reporter.print(profile, filter) + if filter == "" then filter = nil end + return format_statistics(profile, "txt", filter) +end + +--- +-- Serialize the profile data and +-- @return serialized data to be saved to a file +-- +local function serialize_profile(profile, format, filter) + if format == "lua" or format == "json" or format == "json_pretty" then + local stats = filter and {} or profile.stats + if filter then + for modname, mod_stats in pairs(profile.stats) do + if filter_matches(filter, modname) then + stats[modname] = mod_stats + end + end + end + if format == "lua" then + return core.serialize(stats) + elseif format == "json" then + return core.write_json(stats) + elseif format == "json_pretty" then + return core.write_json(stats, true) + end + end + -- Fall back to textual formats. + return format_statistics(profile, format, filter) +end + +local worldpath = core.get_worldpath() +local function get_save_path(format, filter) + local report_path = setting_get("profiler.report_path") or "" + if report_path ~= "" then + core.mkdir(sprintf("%s%s%s", worldpath, DIR_DELIM, report_path)) + end + return (sprintf( + "%s/%s/profile-%s%s.%s", + worldpath, + report_path, + os.date("%Y%m%dT%H%M%S"), + filter and ("-" .. filter) or "", + format + ):gsub("[/\\]+", DIR_DELIM))-- Clean up delims +end + +--- +-- Save the profile to the world path. +-- @return success, log message +-- +function reporter.save(profile, format, filter) + if not format or format == "" then + format = setting_get("profiler.default_report_format") or "txt" + end + if filter == "" then + filter = nil + end + + local path = get_save_path(format, filter) + + local output, io_err = io.open(path, "w") + if not output then + return false, "Saving of profile failed with: " .. io_err + end + local content, err = serialize_profile(profile, format, filter) + if not content then + output:close() + return false, "Saving of profile failed with: " .. err + end + output:write(content) + output:close() + + local logmessage = "Profile saved to " .. path + core.log("action", logmessage) + return true, logmessage +end + +return reporter diff --git a/builtin/profiler/sampling.lua b/builtin/profiler/sampling.lua new file mode 100644 index 000000000..1d1ef256d --- /dev/null +++ b/builtin/profiler/sampling.lua @@ -0,0 +1,206 @@ +--Minetest +--Copyright (C) 2016 T4im +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +local setmetatable = setmetatable +local pairs, format = pairs, string.format +local min, max, huge = math.min, math.max, math.huge +local core = core + +local profiler = ... +-- Split sampler and profile up, to possibly allow for rotation later. +local sampler = {} +local profile +local stats_total +local logged_time, logged_data + +local _stat_mt = { + get_time_avg = function(self) + return self.time_all/self.samples + end, + get_part_avg = function(self) + if not self.part_all then + return 100 -- Extra handling for "total" + end + return self.part_all/self.samples + end, +} +_stat_mt.__index = _stat_mt + +function sampler.reset() + -- Accumulated logged time since last sample. + -- This helps determining, the relative time a mod used up. + logged_time = 0 + -- The measurements taken through instrumentation since last sample. + logged_data = {} + + profile = { + -- Current mod statistics (max/min over the entire mod lifespan) + -- Mod specific instrumentation statistics are nested within. + stats = {}, + -- Current stats over all mods. + stats_total = setmetatable({ + samples = 0, + time_min = huge, + time_max = 0, + time_all = 0, + part_min = 100, + part_max = 100 + }, _stat_mt) + } + stats_total = profile.stats_total + + -- Provide access to the most recent profile. + sampler.profile = profile +end + +--- +-- Log a measurement for the sampler to pick up later. +-- Keep `log` and its often called functions lean. +-- It will directly add to the instrumentation overhead. +-- +function sampler.log(modname, instrument_name, time_diff) + if time_diff <= 0 then + if time_diff < 0 then + -- This **might** have happened on a semi-regular basis with huge mods, + -- resulting in negative statistics (perhaps midnight time jumps or ntp corrections?). + core.log("warning", format( + "Time travel of %s::%s by %dµs.", + modname, instrument_name, time_diff + )) + end + -- Throwing these away is better, than having them mess with the overall result. + return + end + + local mod_data = logged_data[modname] + if mod_data == nil then + mod_data = {} + logged_data[modname] = mod_data + end + + mod_data[instrument_name] = (mod_data[instrument_name] or 0) + time_diff + -- Update logged time since last sample. + logged_time = logged_time + time_diff +end + +--- +-- Return a requested statistic. +-- Initialize if necessary. +-- +local function get_statistic(stats_table, name) + local statistic = stats_table[name] + if statistic == nil then + statistic = setmetatable({ + samples = 0, + time_min = huge, + time_max = 0, + time_all = 0, + part_min = 100, + part_max = 0, + part_all = 0, + }, _stat_mt) + stats_table[name] = statistic + end + return statistic +end + +--- +-- Update a statistic table +-- +local function update_statistic(stats_table, time) + stats_table.samples = stats_table.samples + 1 + + -- Update absolute time (µs) spend by the subject + stats_table.time_min = min(stats_table.time_min, time) + stats_table.time_max = max(stats_table.time_max, time) + stats_table.time_all = stats_table.time_all + time + + -- Update relative time (%) of this sample spend by the subject + local current_part = (time/logged_time) * 100 + stats_table.part_min = min(stats_table.part_min, current_part) + stats_table.part_max = max(stats_table.part_max, current_part) + stats_table.part_all = stats_table.part_all + current_part +end + +--- +-- Sample all logged measurements each server step. +-- Like any globalstep function, this should not be too heavy, +-- but does not add to the instrumentation overhead. +-- +local function sample(dtime) + -- Rare, but happens and is currently of no informational value. + if logged_time == 0 then + return + end + + for modname, instruments in pairs(logged_data) do + local mod_stats = get_statistic(profile.stats, modname) + if mod_stats.instruments == nil then + -- Current statistics for each instrumentation component + mod_stats.instruments = {} + end + + local mod_time = 0 + for instrument_name, time in pairs(instruments) do + if time > 0 then + mod_time = mod_time + time + local instrument_stats = get_statistic(mod_stats.instruments, instrument_name) + + -- Update time of this sample spend by the instrumented function. + update_statistic(instrument_stats, time) + -- Reset logged data for the next sample. + instruments[instrument_name] = 0 + end + end + + -- Update time of this sample spend by this mod. + update_statistic(mod_stats, mod_time) + end + + -- Update the total time spend over all mods. + stats_total.time_min = min(stats_total.time_min, logged_time) + stats_total.time_max = max(stats_total.time_max, logged_time) + stats_total.time_all = stats_total.time_all + logged_time + + stats_total.samples = stats_total.samples + 1 + logged_time = 0 +end + +--- +-- Setup empty profile and register the sampling function +-- +function sampler.init() + sampler.reset() + + if core.setting_getbool("instrument.profiler") then + core.register_globalstep(function() + if logged_time == 0 then + return + end + return profiler.empty_instrument() + end) + core.register_globalstep(profiler.instrument { + func = sample, + mod = "*profiler*", + class = "Sampler (update stats)", + label = false, + }) + else + core.register_globalstep(sample) + end +end + +return sampler diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index ad269d8b0..1818b5a18 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -3,7 +3,7 @@ # General format: # name (Readable name) type type_args # -# Note that the parts are seperated by exactly one space +# Note that the parts are separated by exactly one space # # `type` can be: # - int @@ -70,7 +70,7 @@ fast_move (Fast movement) bool false # This requires the "noclip" privilege on the server. noclip (Noclip) bool false -# Smooths camera when moving and looking around. +# Smooths camera when looking around. Also called look or mouse smoothing. # Useful for recording videos. cinematic (Cinematic mode) bool false @@ -104,6 +104,17 @@ random_input (Random input) bool false # Continuous forward movement (only used for testing). continuous_forward (Continuous forward) bool false +# Enable Joysticks +enable_joysticks (Enable Joysticks) bool false + +# The time in seconds it takes between repeated events +# when holding down a joystick button combination. +repeat_joystick_button_time (Joystick button repetition interval) float 0.17 + +# The sensitivity of the joystick axes for moving the +# ingame view frustum around. +joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170 + # Key for moving the player forward. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_forward (Forward key) key KEY_KEY_W @@ -165,9 +176,13 @@ keymap_fastmove (Fast key) key KEY_KEY_J # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_noclip (Noclip key) key KEY_KEY_H +# Key for toggling autorun. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_autorun (Autorun key) key + # Key for toggling cinematic mode. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_cinematic (Cinematic mode key) key KEY_F8 +keymap_cinematic (Cinematic mode key) key # Key for toggling display of minimap. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 @@ -193,7 +208,7 @@ keymap_toggle_chat (Chat toggle key) key KEY_F2 # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_toggle_force_fog_off (Fog toggle key) key KEY_F3 -# Key for toggling the camrea update. Only used for development +# Key for toggling the camera update. Only used for development # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_toggle_update_camera (Camera update toggle key) key @@ -236,7 +251,7 @@ remote_port (Remote port) int 30000 1 65535 # Enable if you want to connect to 0.4.12 servers and before. # Servers starting with 0.4.13 will work, 0.4.12-dev servers may work. # Disabling this option will protect your password better. -send_pre_v25_init (Support older servers) bool true +send_pre_v25_init (Support older servers) bool false # Save the map received by the client on disk. enable_local_map_saving (Saving map received from server) bool false @@ -324,7 +339,7 @@ fsaa (FSAA) enum 0 0,1,2,4,8,16 [***Shaders] -# Shaders allow advanced visul effects and may increase performance on some video cards. +# Shaders allow advanced visual effects and may increase performance on some video cards. # Thy only work with the OpenGL video backend. enable_shaders (Shaders) bool true @@ -403,8 +418,7 @@ fps_max (Maximum FPS) int 60 pause_fps_max (FPS in pause menu) int 20 # View distance in nodes. -# Min = 20 -viewing_range (Viewing range) int 100 +viewing_range (Viewing range) int 100 20 4000 # Width component of the initial window size. screenW (Screen width) int 800 @@ -424,6 +438,10 @@ vsync (V-Sync) bool false # Field of view in degrees. fov (Field of view) int 72 30 160 +# Field of view while zooming in degrees. +# This requires the "zoom" privilege on the server. +zoom_fov (Field of view for zoom) int 15 15 160 + # Adjust the gamma encoding for the light tables. Lower numbers are brighter. # This setting is for the client only and is ignored by the server. display_gamma (Gamma) float 1.8 1.0 3.0 @@ -510,6 +528,9 @@ ambient_occlusion_gamma (Ambient occlusion gamma) float 2.2 0.25 4.0 # Enables animation of inventory items. inventory_items_animations (Inventory items animations) bool false +# Fraction of the visible distance at which fog starts to be rendered +fog_start (Fog Start) float 0.4 0.0 0.99 + [**Menus] # Use a cloud animation for the main menu background. @@ -530,7 +551,7 @@ gui_scaling_filter (GUI scaling filter) bool false # When gui_scaling_filter_txr2img is true, copy those images # from hardware to software for scaling. When false, fall back # to the old scaling method, for video drivers that don't -# propery support downloading textures back from hardware. +# properly support downloading textures back from hardware. gui_scaling_filter_txr2img (GUI scaling filter txr2img) bool true # Delay showing tooltips, stated in milliseconds. @@ -542,13 +563,13 @@ freetype (Freetype fonts) bool true # Path to TrueTypeFont or bitmap. font_path (Font path) path fonts/liberationsans.ttf -font_size (Font size) int 15 +font_size (Font size) int 16 # Font shadow offset, if 0 then shadow will not be drawn. font_shadow (Font shadow) int 1 # Font shadow alpha (opaqueness, between 0 and 255). -font_shadow_alpha (Font shadow alpha) int 128 0 255 +font_shadow_alpha (Font shadow alpha) int 127 0 255 mono_font_path (Monospace font path) path fonts/liberationmono.ttf @@ -615,6 +636,11 @@ server_announce (Announce server) bool false # If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net. serverlist_url (Serverlist URL) string servers.minetest.net +# Disable escape sequences, e.g. chat coloring. +# Use this if you want to run a server with pre-0.4.14 clients and you want to disable +# the escape sequences generated by mods. +disable_escape_sequences (Disable escape sequences) bool false + [*Network] # Network port to listen (UDP). @@ -642,15 +668,15 @@ ipv6_server (IPv6 server) bool false [**Advanced] -# How many blocks are flying in the wire simultaneously per client. -max_simultaneous_block_sends_per_client (Maximum simultaneously blocks send per client) int 10 +# Maximum number of blocks that are simultaneously sent per client. +max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) int 10 -# How many blocks are flying in the wire simultaneously for the whole server. -max_simultaneous_block_sends_server_total (Maximum simultaneously bocks send total) int 40 +# Maximum number of blocks that are simultaneously sent in total. +max_simultaneous_block_sends_server_total (Maximum simultaneous block sends total) int 40 # To reduce lag, block transfers are slowed down when a player is building something. # This determines how long they are slowed down after placing or removing a node. -full_block_send_enable_min_time_from_building () float 2.0 +full_block_send_enable_min_time_from_building (Delay in sending blocks after building) float 2.0 # Maximum number of packets sent per send step, if you have a slow connection # try reducing it, but don't reduce it to a number below double of targeted @@ -750,6 +776,15 @@ time_speed (Time speed) int 72 # Interval of saving important changes in the world, stated in seconds. server_map_save_interval (Map save interval) float 5.3 +# Set the maximum character length of a chat message sent by clients. +# chat_message_max_size int 500 + +# Limit a single player to send X messages per 10 seconds. +# chat_message_limit_per_10sec float 10.0 + +# Kick player if send more than X messages per 10 seconds. +# chat_message_limit_trigger_kick int 50 + [**Physics] movement_acceleration_default (Default acceleration) float 3 @@ -774,15 +809,6 @@ movement_gravity (Gravity) float 9.81 # - error: abort on usage of deprecated call (suggested for mod developers). deprecated_lua_api_handling (Deprecated Lua API handling) enum legacy legacy,log,error -# Useful for mod developers. -mod_profiling (Mod profiling) bool false - -# Detailed mod profile data. Useful for mod developers. -detailed_profiling (Detailed mod profiling) bool false - -# Profiler data print interval. 0 = disable. Useful for developers. -profiler_print_interval (Profiling print interval) int 0 - # Number of extra blocks that can be loaded by /clearobjects at once. # This is a trade-off between sqlite transaction overhead and # memory consumption (4096=100MB, as a rule of thumb). @@ -793,7 +819,7 @@ max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096 server_unload_unused_data_timeout (Unload unused server data) int 29 # Maximum number of statically stored objects in a block. -max_objects_per_block (Maxmimum objects per block) int 49 +max_objects_per_block (Maximum objects per block) int 64 # See http://www.sqlite.org/pragma.html#pragma_synchronous sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2 @@ -825,11 +851,18 @@ liquid_queue_purge_time (Liquid queue purge time) int 0 # Liquid update interval in seconds. liquid_update (Liquid update tick) float 1.0 +# At this distance the server will aggressively optimize which blocks are sent to clients. +# Small values potentially improve performance a lot, at the expense of visible rendering glitches. +# (some blocks will not be rendered under water and in caves, as well as sometimes on land) +# Setting this to a value greater than max_block_send_distance disables this optimization. +# Stated in mapblocks (16 nodes) +block_send_optimize_distance (block send optimize distance) int 4 2 + [*Mapgen] # 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,valleys,fractal,singlenode +mg_name (Mapgen name) enum v7 v5,v6,v7,flat,valleys,fractal,singlenode # Water surface level of the world. water_level (Water level) int 1 @@ -848,8 +881,6 @@ 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. mg_flags (Mapgen flags) flags caves,dungeons,light,decorations caves,dungeons,light,decorations,nocaves,nodungeons,nolight,nodecorations @@ -910,8 +941,6 @@ mgv5_np_cave2 (Mapgen v5 cave2 noise parameters) noise_params 0, 12, (50, 50, 50 # Map generation attributes specific to Mapgen v6. # 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. mgv6_spflags (Mapgen v6 flags) flags jungles,biomeblend,mudflow,snowbiomes,trees jungles,biomeblend,mudflow,snowbiomes,flat,trees,nojungles,nobiomeblend,nomudflow,nosnowbiomes,noflat,notrees @@ -936,15 +965,27 @@ mgv6_np_apple_trees (Mapgen v6 apple trees noise parameters) noise_params 0, 1, [***Mapgen v7] # Map generation attributes specific to Mapgen v7. -# The 'ridges' flag controls the rivers. -# The default flags set in the engine are: mountains, ridges -# The flags string modifies the engine defaults. +# The 'ridges' flag enables the rivers. +# Floatlands are currently experimental and subject to change. # 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. -mgv7_spflags (Mapgen v7 flags) flags mountains,ridges mountains,ridges,nomountains,noridges +mgv7_spflags (Mapgen v7 flags) flags mountains,ridges mountains,ridges,floatlands,nomountains,noridges,nofloatlands # Controls width of tunnels, a smaller value creates wider tunnels. -mgv7_cave_width (Mapgen v7 cave width) float 0.3 +mgv7_cave_width (Mapgen v7 cave width) float 0.09 + +# Controls the density of floatland mountain terrain. +# Is an offset added to the 'np_mountain' noise value. +mgv7_float_mount_density (Mapgen v7 floatland mountain density) float 0.6 + +# Typical maximum height, above and below midpoint, of floatland mountain terrain. +mgv7_float_mount_height (Mapgen v7 floatland mountain height) float 128.0 + +# Y-level of floatland midpoint and lake surface. +mgv7_floatland_level (Mapgen v7 floatland level) int 1280 + +# Y-level to which floatland shadows extend. +mgv7_shadow_limit (Mapgen v7 shadow limit) int 1024 mgv7_np_terrain_base (Mapgen v7 terrain base noise parameters) noise_params 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0 mgv7_np_terrain_alt (Mapgen v7 terrain altitude noise parameters) noise_params 4, 25, (600, 600, 600), 5934, 5, 0.6, 2.0 @@ -952,18 +993,18 @@ mgv7_np_terrain_persist (Mapgen v7 terrain persistation noise parameters) noise_ mgv7_np_height_select (Mapgen v7 height select noise parameters) noise_params -8, 16, (500, 500, 500), 4213, 6, 0.7, 2.0 mgv7_np_filler_depth (Mapgen v7 filler depth noise parameters) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 mgv7_np_mount_height (Mapgen v7 mount height noise parameters) noise_params 256, 112, (1000, 1000, 1000), 72449, 3, 0.6, 2.0 -mgv7_np_ridge_uwater (Mapgen v7 ridge water noise parameters) noise_params 0, 1, (1000, 1000, 1000), 85039, 5, 0.6, 2.0 +mgv7_np_ridge_uwater (Mapgen v7 river course noise parameters) noise_params 0, 1, (1000, 1000, 1000), 85039, 5, 0.6, 2.0 +mgv7_np_floatland_base (Mapgen v7 floatland base terrain noise parameters) noise_params -0.6, 1.5, (600, 600, 600), 114, 5, 0.6, 2.0 +mgv7_np_float_base_height (Mapgen v7 floatland base terrain height noise parameters) noise_params 48, 24, (300, 300, 300), 907, 4, 0.7, 2.0 mgv7_np_mountain (Mapgen v7 mountain noise parameters) noise_params -0.6, 1, (250, 350, 250), 5333, 5, 0.63, 2.0 -mgv7_np_ridge (Mapgen v7 ridge noise parameters) noise_params 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0 -mgv7_np_cave1 (Mapgen v7 cave1 noise parameters) noise_params 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0 -mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0 +mgv7_np_ridge (Mapgen v7 river channel wall noise parameters) noise_params 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0 +mgv7_np_cave1 (Mapgen v7 cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 +mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 [***Mapgen flat] # Map generation attributes specific to Mapgen flat. # 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. mgflat_spflags (Mapgen flat flags) flags lakes,hills,,nolakes,nohills @@ -975,7 +1016,7 @@ mgflat_ground_level (Mapgen flat ground level) int 8 mgflat_large_cave_depth (Mapgen flat large cave depth) int -33 # Controls width of tunnels, a smaller value creates wider tunnels. -mgflat_cave_width (Mapgen flat cave width) float 0.3 +mgflat_cave_width (Mapgen flat cave width) float 0.09 # Terrain noise threshold for lakes. # Controls proportion of world area covered by lakes. @@ -999,13 +1040,13 @@ mgflat_hill_steepness (Mapgen flat hill steepness) float 64.0 mgflat_np_terrain (Mapgen flat terrain noise parameters) noise_params 0, 1, (600, 600, 600), 7244, 5, 0.6, 2.0 mgflat_np_filler_depth (Mapgen flat filler depth noise parameters) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 -mgflat_np_cave1 (Mapgen flat cave1 noise parameters) noise_params 0, 12, (128, 128, 128), 52534, 4, 0.5, 2.0 -mgflat_np_cave2 (Mapgen flat cave2 noise parameters) noise_params 0, 12, (128, 128, 128), 10325, 4, 0.5, 2.0 +mgflat_np_cave1 (Mapgen flat cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 +mgflat_np_cave2 (Mapgen flat cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 [***Mapgen fractal] # Controls width of tunnels, a smaller value creates wider tunnels. -mgfractal_cave_width (Mapgen fractal cave width) float 0.3 +mgfractal_cave_width (Mapgen fractal cave width) float 0.09 # Choice of 18 fractals from 9 formulas. # 1 = 4D "Roundy" mandelbrot set. @@ -1066,8 +1107,8 @@ mgfractal_julia_w (Mapgen fractal julia w) float 0.33 mgfractal_np_seabed (Mapgen fractal seabed noise parameters) noise_params -14, 9, (600, 600, 600), 41900, 5, 0.6, 2.0 mgfractal_np_filler_depth (Mapgen fractal filler depth noise parameters) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 -mgfractal_np_cave1 (Mapgen fractal cave1 noise parameters) noise_params 0, 12, (128, 128, 128), 52534, 4, 0.5, 2.0 -mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, (128, 128, 128), 10325, 4, 0.5, 2.0 +mgfractal_np_cave1 (Mapgen fractal cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 +mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 # Mapgen Valleys parameters [***Mapgen Valleys] @@ -1079,8 +1120,6 @@ mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, ( # '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. mg_valleys_spflags (Valleys C Flags) flags altitude_chill,humid_rivers altitude_chill,noaltitude_chill,humid_rivers,nohumid_rivers @@ -1109,16 +1148,16 @@ mgvalleys_river_size (River Size) int 5 mgvalleys_water_features (Water Features) int 0 # Controls width of tunnels, a smaller value creates wider tunnels. -mgvalleys_cave_width (Cave width) float 0.3 +mgvalleys_cave_width (Cave width) float 0.09 # Noise parameters [****Noises] # Caves and tunnels form at the intersection of the two noises -mgvalleys_np_cave1 (Cave noise #1) noise_params 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0 +mgvalleys_np_cave1 (Cave noise #1) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 # Caves and tunnels form at the intersection of the two noises -mgvalleys_np_cave2 (Cave noise #2) noise_params 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0 +mgvalleys_np_cave2 (Cave noise #2) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 # The depth of dirt or other filler mgvalleys_np_filler_depth (Filler Depth) noise_params 0, 1.2, (256, 256, 256), 1605, 3, 0.5, 2.0 @@ -1153,10 +1192,55 @@ secure.enable_security (Enable mod security) bool false # functions even when mod security is on (via request_insecure_environment()). secure.trusted_mods (Trusted mods) string -# Comma-seperated list of mods that are allowed to access HTTP APIs, which +# Comma-separated list of mods that are allowed to access HTTP APIs, which # allow them to upload and download data to/from the internet. secure.http_mods (HTTP Mods) string +[*Advanced] + +[**Profiling] +# Load the game profiler to collect game profiling data. +# Provides a /profiler command to access the compiled profile. +# Useful for mod developers and server operators. +profiler.load (Load the game profiler) bool false + +# The default format in which profiles are being saved, +# when calling `/profiler save [format]` without format. +profiler.default_report_format (Default report format) enum txt txt,csv,lua,json,json_pretty + +# The file path relative to your worldpath in which profiles will be saved to. +# +profiler.report_path (Report path) string "" + +[***Instrumentation] + +# Instrument the methods of entities on registration. +instrument.entity (Entity methods) bool true + +# Instrument the action function of Active Block Modifiers on registration. +instrument.abm (Active Block Modifiers) bool true + +# Instrument the action function of Loading Block Modifiers on registration. +instrument.lbm (Loading Block Modifiers) bool true + +# Instrument chatcommands on registration. +instrument.chatcommand (Chatcommands) bool true + +# Instrument global callback functions on registration. +# (anything you pass to a minetest.register_*() function) +instrument.global_callback (Global callbacks) bool true + +[****Advanced] +# Instrument builtin. +# This is usually only needed by core/builtin contributors +instrument.builtin (Builtin) bool false + +# Have the profiler instrument itself: +# * Instrument an empty function. +# This estimates the overhead, that instrumentation is adding (+1 function call). +# * Instrument the sampler being used to update the statistics. +instrument.profiler (Profiler) bool false + [Client and Server] # Name of the player. @@ -1166,7 +1250,7 @@ name (Player name) string # Set the language. Leave empty to use the system language. # A restart is required after changing this. -language (Language) enum ,be,cs,da,de,eo,es,et,fr,hu,id,it,ja,jbo,ko,ky,lt,nb,nl,pl,pt,pt_BR,ro,ru,tr,uk,zh_CN,zh_TW +language (Language) enum ,be,ca,cs,da,de,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko,ky,lt,nb,nl,pl,pt,pt_BR,ro,ru,sr_Cyrl,tr,uk,zh_CN,zh_TW # Level of logging to be written to debug.txt: # - (no logging) @@ -1212,3 +1296,6 @@ modstore_download_url (Modstore download URL) string https://forum.minetest.net/ modstore_listmods_url (Modstore mods list URL) string https://forum.minetest.net/mmdb/mods/ modstore_details_url (Modstore details URL) string https://forum.minetest.net/mmdb/mod/*/ + +# Print the engine's profiling data in regular intervals (in seconds). 0 = disable. Useful for developers. +profiler_print_interval (Engine profiling data print interval) int 0 diff --git a/client/shaders/minimap_shader/opengl_vertex.glsl b/client/shaders/minimap_shader/opengl_vertex.glsl index 06df5a3cf..88f9356d5 100644 --- a/client/shaders/minimap_shader/opengl_vertex.glsl +++ b/client/shaders/minimap_shader/opengl_vertex.glsl @@ -1,6 +1,4 @@ uniform mat4 mWorldViewProj; -uniform mat4 mInvWorld; -uniform mat4 mTransWorld; uniform mat4 mWorld; void main(void) diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 6862842a7..71ded2b9d 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -19,6 +19,8 @@ bool normalTexturePresent = false; const float e = 2.718281828459; const float BS = 10.0; +const float fogStart = FOG_START; +const float fogShadingParameter = 1 / ( 1 - fogStart); #ifdef ENABLE_TONE_MAPPING @@ -194,24 +196,25 @@ void main(void) vec4 col = vec4(color.rgb * gl_Color.rgb, 1.0); -#if MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT - float alpha = gl_Color.a; - if (fogDistance != 0.0) { - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - alpha = mix(alpha, 0.0, d); - } - col = vec4(col.rgb, alpha); -#else - if (fogDistance != 0.0) { - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - col = mix(col, skyBgColor, d); - } - col = vec4(col.rgb, base.a); +#ifdef ENABLE_TONE_MAPPING + col = applyToneMapping(col); #endif -#ifdef ENABLE_TONE_MAPPING - gl_FragColor = applyToneMapping(col); -#else + if (fogDistance != 0.0) { + // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), + // the fog will only be rendered correctly if the last operation before the + // clamp() is an addition. Else, the clamp() seems to be ignored. + // E.g. the following won't work: + // float clarity = clamp(fogShadingParameter + // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); + // As additions usually come for free following a multiplication, the new formula + // should be more efficient as well. + // Note: clarity = (1 - fogginess) + float clarity = clamp(fogShadingParameter + - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); + col = mix(skyBgColor, col, clarity); + } + col = vec4(col.rgb, base.a); + gl_FragColor = col; -#endif } diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 28c5f197e..44c48cc4c 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -1,6 +1,4 @@ uniform mat4 mWorldViewProj; -uniform mat4 mInvWorld; -uniform mat4 mTransWorld; uniform mat4 mWorld; uniform float dayNightRatio; diff --git a/client/shaders/water_surface_shader/opengl_fragment.glsl b/client/shaders/water_surface_shader/opengl_fragment.glsl index 1fa669541..c4e78470d 100644 --- a/client/shaders/water_surface_shader/opengl_fragment.glsl +++ b/client/shaders/water_surface_shader/opengl_fragment.glsl @@ -21,6 +21,8 @@ bool texSeamless = false; const float e = 2.718281828459; const float BS = 10.0; +const float fogStart = FOG_START; +const float fogShadingParameter = 1 / ( 1 - fogStart); #ifdef ENABLE_TONE_MAPPING @@ -150,24 +152,25 @@ vec4 base = texture2D(baseTexture, uv).rgba; vec4 col = vec4(color.rgb * gl_Color.rgb, 1.0); -#if MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_OPAQUE - float alpha = gl_Color.a; - if (fogDistance != 0.0) { - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - alpha = mix(alpha, 0.0, d); - } - col = vec4(col.rgb, alpha); -#else - if (fogDistance != 0.0) { - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - col = mix(col, skyBgColor, d); - } - col = vec4(col.rgb, base.a); +#ifdef ENABLE_TONE_MAPPING + col = applyToneMapping(col); #endif -#ifdef ENABLE_TONE_MAPPING - gl_FragColor = applyToneMapping(col); -#else + if (fogDistance != 0.0) { + // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), + // the fog will only be rendered correctly if the last operation before the + // clamp() is an addition. Else, the clamp() seems to be ignored. + // E.g. the following won't work: + // float clarity = clamp(fogShadingParameter + // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); + // As additions usually come for free following a multiplication, the new formula + // should be more efficient as well. + // Note: clarity = (1 - fogginess) + float clarity = clamp(fogShadingParameter + - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); + col = mix(skyBgColor, col, clarity); + } + col = vec4(col.rgb, base.a); + gl_FragColor = col; -#endif } diff --git a/client/shaders/water_surface_shader/opengl_vertex.glsl b/client/shaders/water_surface_shader/opengl_vertex.glsl index 9b461f4c1..a930e7b8f 100644 --- a/client/shaders/water_surface_shader/opengl_vertex.glsl +++ b/client/shaders/water_surface_shader/opengl_vertex.glsl @@ -1,6 +1,4 @@ uniform mat4 mWorldViewProj; -uniform mat4 mInvWorld; -uniform mat4 mTransWorld; uniform mat4 mWorld; uniform float dayNightRatio; diff --git a/client/shaders/wielded_shader/opengl_fragment.glsl b/client/shaders/wielded_shader/opengl_fragment.glsl index 75dd1b674..ba7a8f12d 100644 --- a/client/shaders/wielded_shader/opengl_fragment.glsl +++ b/client/shaders/wielded_shader/opengl_fragment.glsl @@ -19,6 +19,8 @@ bool texSeamless = false; const float e = 2.718281828459; const float BS = 10.0; +const float fogStart = FOG_START; +const float fogShadingParameter = 1 / ( 1 - fogStart); void get_texture_flags() { @@ -107,8 +109,18 @@ void main(void) vec4 col = vec4(color.rgb, base.a); col *= gl_Color; if (fogDistance != 0.0) { - float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0)); - col = mix(col, skyBgColor, d); + // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), + // the fog will only be rendered correctly if the last operation before the + // clamp() is an addition. Else, the clamp() seems to be ignored. + // E.g. the following won't work: + // float clarity = clamp(fogShadingParameter + // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); + // As additions usually come for free following a multiplication, the new formula + // should be more efficient as well. + // Note: clarity = (1 - fogginess) + float clarity = clamp(fogShadingParameter + - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); + col = mix(skyBgColor, col, clarity); } gl_FragColor = vec4(col.rgb, base.a); } diff --git a/client/shaders/wielded_shader/opengl_vertex.glsl b/client/shaders/wielded_shader/opengl_vertex.glsl index c33b0a7d3..86c626896 100644 --- a/client/shaders/wielded_shader/opengl_vertex.glsl +++ b/client/shaders/wielded_shader/opengl_vertex.glsl @@ -1,6 +1,4 @@ uniform mat4 mWorldViewProj; -uniform mat4 mInvWorld; -uniform mat4 mTransWorld; uniform mat4 mWorld; uniform float dayNightRatio; diff --git a/cmake/Modules/FindJson.cmake b/cmake/Modules/FindJson.cmake index 1558b0fcf..e69d6c4c0 100644 --- a/cmake/Modules/FindJson.cmake +++ b/cmake/Modules/FindJson.cmake @@ -20,8 +20,8 @@ endif() if(NOT JSONCPP_FOUND) message(STATUS "Using bundled JSONCPP library.") - set(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/json) + set(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/jsoncpp) set(JSON_LIBRARY jsoncpp) - add_subdirectory(json) + add_subdirectory(jsoncpp/json) endif() diff --git a/cmake/Modules/FindNcursesw.cmake b/cmake/Modules/FindNcursesw.cmake index b8c7f78f2..e572c704a 100644 --- a/cmake/Modules/FindNcursesw.cmake +++ b/cmake/Modules/FindNcursesw.cmake @@ -115,7 +115,7 @@ if(CURSES_USE_NCURSESW) get_filename_component(_cursesParentDir "${_cursesLibDir}" PATH) find_path(CURSES_INCLUDE_PATH - NAMES ncursesw/ncurses.h ncursesw/curses.h + NAMES ncursesw/ncurses.h ncursesw/curses.h ncurses.h curses.h HINTS "${_cursesParentDir}/include" ) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 593e0c438..34c64b8df 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Modding API Reference 0.4.14 +Minetest Lua Modding API Reference 0.4.15 ========================================= * More information at * Developer Wiki: @@ -65,15 +65,19 @@ e.g. The game directory can contain the file minetest.conf, which will be used to set default settings when running the particular game. It can also contain a settingtypes.txt in the same format as the one in builtin. -This settingtypes.txt will be parsed by the menu and the settings will be displayed in the "Games" category in the settings tab. +This settingtypes.txt will be parsed by the menu and the settings will be displayed +in the "Games" category in the settings tab. ### Menu images -Games can provide custom main menu images. They are put inside a `menu` directory inside the game directory. +Games can provide custom main menu images. They are put inside a `menu` directory +inside the game directory. -The images are named `$identifier.png`, where `$identifier` is one of `overlay,background,footer,header`. -If you want to specify multiple images for one identifier, add additional images named like `$identifier.$n.png`, with an ascending number $n starting with 1, -and a random image will be chosen from the provided ones. +The images are named `$identifier.png`, where `$identifier` is +one of `overlay,background,footer,header`. +If you want to specify multiple images for one identifier, add additional images named +like `$identifier.$n.png`, with an ascending number $n starting with 1, and a random +image will be chosen from the provided ones. Mod load path @@ -153,7 +157,8 @@ to a single modname. Their meaning is that if the specified mod is missing, that does not prevent this mod from being loaded. ### `screenshot.png` -A screenshot shown in modmanager within mainmenu. +A screenshot shown in the mod manager within the main menu. It should +have an aspect ratio of 3:2 and a minimum size of 300×200 pixels. ### `description.txt` A File containing description to be shown within mainmenu. @@ -203,11 +208,17 @@ when registering it. The `:` prefix can also be used for maintaining backwards compatibility. ### Aliases -Aliases can be added by using `minetest.register_alias(name, convert_to)`. +Aliases can be added by using `minetest.register_alias(name, convert_to)` or +`minetest.register_alias_force(name, convert_to). This will make Minetest to convert things called name to things called `convert_to`. +The only difference between `minetest.register_alias` and +`minetest.register_alias_force` is that if an item called `name` exists, +`minetest.register_alias` will do nothing while +`minetest.register_alias_force` will unregister it. + This can be used for maintaining backwards compatibility. This can be also used for setting quick access names for things, e.g. if @@ -243,7 +254,8 @@ Example: default_dirt.png^default_grass_side.png `default_grass_side.png` is overlayed over `default_dirt.png`. -The texture with the lower resolution will be automatically upscaled to the higher resolution texture. +The texture with the lower resolution will be automatically upscaled to +the higher resolution texture. ### Texture grouping Textures can be grouped together by enclosing them in `(` and `)`. @@ -251,7 +263,17 @@ Textures can be grouped together by enclosing them in `(` and `)`. Example: `cobble.png^(thing1.png^thing2.png)` A texture for `thing1.png^thing2.png` is created and the resulting -texture is overlaid over `cobble.png`. +texture is overlaid on top of `cobble.png`. + +### Escaping +Modifiers that accept texture names (e.g. `[combine`) accept escaping to allow +passing complex texture names as arguments. Escaping is done with backslash and +is required for `^` and `:`. + +Example: `cobble.png^[lowpart:50:color.png\^[mask\:trans.png` + +The lower 50 percent of `color.png^[mask:trans.png` are overlaid +on top of `cobble.png`. ### Advanced texture modifiers @@ -286,6 +308,25 @@ Example: default_sandstone.png^[resize:16x16 +#### `[opacity:` + Makes the base image transparent according to the given ratio. + r must be between 0 and 255. + 0 means totally transparent. + 255 means totally opaque. + +Example: + + default_sandstone.png^[opacity:127 + +#### `[invert:` +Inverts the given channels of the base image. +Mode may contain the characters "r", "g", "b", "a". +Only the channels that are mentioned in the mode string will be inverted. + +Example: + + default_apple.png^[invert:rgb + #### `[brighten` Brightens the texture. @@ -329,7 +370,7 @@ Example: default_stone.png^[transformFXR90 #### `[inventorycube{{{` -`^` is replaced by `&` in texture names. +Escaping does not apply here and `^` is replaced by `&` in texture names instead. Create an inventory cube texture using the side textures. @@ -400,18 +441,24 @@ from the available ones of the following files: Examples of sound parameter tables: - -- Play location-less on all clients + -- Play locationless on all clients { gain = 1.0, -- default } - -- Play location-less to a player + -- Play locationless to one player { to_player = name, gain = 1.0, -- default } + -- Play locationless to one player, looped + { + to_player = name, + gain = 1.0, -- default + loop = true, + } -- Play in a location { - pos = {x=1,y=2,z=3}, + pos = {x = 1, y = 2, z = 3}, gain = 1.0, -- default max_hear_distance = 32, -- default, uses an euclidean metric } @@ -420,15 +467,18 @@ Examples of sound parameter tables: object = , gain = 1.0, -- default max_hear_distance = 32, -- default, uses an euclidean metric - loop = true, -- only sounds connected to objects can be looped + loop = true, } +Looped sounds must either be connected to an object or played locationless to +one player using `to_player = name,` + ### `SimpleSoundSpec` * e.g. `""` * e.g. `"default_place_node"` * e.g. `{}` -* e.g. `{name="default_place_node"}` -* e.g. `{name="default_place_node", gain=1.0}` +* e.g. `{name = "default_place_node"}` +* e.g. `{name = "default_place_node", gain = 1.0}` Registered definitions of stuff ------------------------------- @@ -448,6 +498,11 @@ the global `minetest.registered_*` tables. * `minetest.register_craftitem(name, item definition)` * added to `minetest.registered_items[name]` +* `minetest.unregister_item(name)` + * Unregisters the item name from engine, and deletes the entry with key + * `name` from `minetest.registered_items` and from the associated item + * table according to its nature: minetest.registered_nodes[] etc + * `minetest.register_biome(biome definition)` * returns an integer uniquely identifying the registered biome * added to `minetest.registered_biome` with the key of `biome.name` @@ -468,8 +523,8 @@ the global `minetest.registered_*` tables. * added to `minetest.registered_schematic` with the key of `schematic.name` * if `schematic.name` is nil, the key is the returned ID * if the schematic is loaded from a file, schematic.name is set to the filename - * if the function is called when loading the mod, and schematic.name is a relative path, - * then the current mod path will be prepended to the schematic filename + * if the function is called when loading the mod, and schematic.name is a relative + path, then the current mod path will be prepended to the schematic filename * `minetest.clear_registered_biomes()` * clears all biomes currently registered @@ -562,6 +617,22 @@ node definition: ^ The rotation of this node is stored in param2. Plants are rotated this way. Values range 0 - 179. The value stored in param2 is multiplied by two to get the actual rotation of the node. + paramtype2 == "meshoptions" + ^ Only valid for "plantlike". The value of param2 becomes a bitfield which can + be used to change how the client draws plantlike nodes. Bits 0, 1 and 2 form + a mesh selector. Currently the following meshes are choosable: + 0 = a "x" shaped plant (ordinary plant) + 1 = a "+" shaped plant (just rotated 45 degrees) + 2 = a "*" shaped plant with 3 faces instead of 2 + 3 = a "#" shaped plant with 4 faces instead of 2 + 4 = a "#" shaped plant with 4 faces that lean outwards + 5-7 are unused and reserved for future meshes. + Bits 3 through 7 are optional flags that can be combined and give these + effects: + bit 3 (0x08) - Makes the plant slightly vary placement horizontally + bit 4 (0x10) - Makes the plant mesh 1.4x larger + bit 5 (0x20) - Moves each face randomly a small bit down (1/8 max) + bits 6-7 are reserved for future use. collision_box = { type = "fixed", fixed = { @@ -1407,6 +1478,15 @@ examples. * `fixed_size`: `true`/`false` (optional) * deprecated: `invsize[,;]` +#### `container[,]` +* Start of a container block, moves all physical elements in the container by (X, Y) +* Must have matching container_end +* Containers can be nested, in which case the offsets are added + (child containers are relative to parent containers) + +#### `container_end[]` +* End of a container, following elements are no longer relative to this container + #### `list[;;,;,;]` * Show an inventory list @@ -1416,13 +1496,13 @@ examples. #### `listring[;]` * Allows to create a ring of inventory lists * Shift-clicking on items in one element of the ring -* will send them to the next inventory list inside the ring + will send them to the next inventory list inside the ring * The first occurrence of an element inside the ring will -* determine the inventory where items will be sent to + determine the inventory where items will be sent to #### `listring[]` * Shorthand for doing `listring[;]` -* for the last two inventory lists added by list[...] + for the last two inventory lists added by list[...] #### `listcolors[;]` * Sets background color of slots as `ColorString` @@ -1473,18 +1553,23 @@ examples. #### `pwdfield[,;,;;