diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..d2e2f2940 --- /dev/null +++ b/.clang-format @@ -0,0 +1,28 @@ +BasedOnStyle: LLVM +IndentWidth: 8 +UseTab: Always +BreakBeforeBraces: Custom +Standard: Cpp03 +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + BeforeCatch: false + BeforeElse: false +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false +AccessModifierOffset: -8 +ColumnLimit: 90 +AllowShortFunctionsOnASingleLine: Inline +SortIncludes: false +IncludeCategories: + - Regex: '^".*' + Priority: 2 + - Regex: '^<.*' + Priority: 1 +AlignAfterOpenBracket: DontAlign +ContinuationIndentWidth: 16 diff --git a/.gitignore b/.gitignore index d7e0daab4..7b5ecab67 100644 --- a/.gitignore +++ b/.gitignore @@ -22,9 +22,12 @@ tags !tags/ gtags.files .idea/* +# Codelite +*.project ## Files related to minetest development cycle /*.patch +*.diff # GNU Patch reject file *.rej @@ -43,11 +46,17 @@ gtags.files !/mods/minetest/mods_here.txt /worlds /world/ +/clientmods/* +!/clientmods/preview/ +/client/mod_storage/ ## Configuration/log files minetest.conf debug.txt +## Other files generated by minetest +screenshot_*.png + ## Doxygen files doc/Doxyfile doc/html/ @@ -74,16 +83,19 @@ locale/ *.a *.ninja .ninja* +*.gch +cmake-build-debug/ +cmake-build-release/ ## Android build files build/android/src/main/assets build/android/build build/android/deps build/android/libs +build/android/jni/lib build/android/jni/src build/android/src/main/jniLibs build/android/obj build/android/local.properties build/android/.gradle timestamp - diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..ef3b371a4 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,223 @@ +--- +# Github repository is cloned every day on Gitlab.com +# https://gitlab.com/minetest/minetest +# Pipelines URL: https://gitlab.com/minetest/minetest/pipelines + +stages: + - build + - package + - deploy + +variables: + MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git" + +.build_template: &build_definition + stage: build + script: + - mkdir cmakebuild + - mkdir -p artifact/minetest/usr/ + - cd cmakebuild + - cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE .. + - make -j2 + - make install + artifacts: + when: on_success + expire_in: 1h + paths: + - artifact/* + +.debpkg_template: &debpkg_template + stage: package + before_script: + - apt-get update -y + - apt-get install -y git + - mkdir -p build/deb/minetest/DEBIAN/ + - cp misc/debpkg-control build/deb/minetest/DEBIAN/control + - cp -Rp artifact/minetest/usr build/deb/minetest/ + script: + - git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest + - rm -Rf build/deb/minetest/usr/share/minetest/games/minetest/.git + - sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control + - sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control + - cd build/deb/ && dpkg-deb -b minetest/ + artifacts: + when: on_success + expire_in: 30 day + paths: + - build/deb/*.deb + +.debpkg_install: &debpkg_install + stage: deploy + before_script: + - apt-get update -y + - apt-get install -y libc6 libcurl3-gnutls libfreetype6 libirrlicht1.8 $LEVELDB_PKG liblua5.1-0 libluajit-5.1-2 libopenal1 libstdc++6 libvorbisfile3 libx11-6 zlib1g + script: + - dpkg -i build/deb/*.deb + +## +## Debian +## + +# Jessie + +build:debian-8: + <<: *build_definition + image: debian:8 + before_script: + - apt-get update -y + - apt-get -y 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 + +package:debian-8: + image: debian:8 + dependencies: + - build:debian-8 + variables: + LEVELDB_PKG: libleveldb1 + <<: *debpkg_template + +deploy:debian-8: + image: debian:8 + dependencies: + - package:debian-8 + variables: + LEVELDB_PKG: libleveldb1 + <<: *debpkg_install + +# Stretch + +build:debian-9: + <<: *build_definition + image: debian:9 + before_script: + - apt-get update -y + - apt-get -y 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 + +package:debian-9: + image: debian:9 + dependencies: + - build:debian-9 + variables: + LEVELDB_PKG: libleveldb1v5 + <<: *debpkg_template + +deploy:debian-9: + image: debian:9 + dependencies: + - package:debian-9 + variables: + LEVELDB_PKG: libleveldb1v5 + <<: *debpkg_install + +## +## Ubuntu +## + +# Trusty + +build:ubuntu-14.04: + <<: *build_definition + image: ubuntu:trusty + before_script: + - apt-get update -y + - apt-get -y 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 + +package:ubuntu-14.04: + image: ubuntu:trusty + dependencies: + - build:ubuntu-14.04 + variables: + LEVELDB_PKG: libleveldb1 + <<: *debpkg_template + +deploy:ubuntu-14.04: + image: ubuntu:trusty + dependencies: + - package:ubuntu-14.04 + variables: + LEVELDB_PKG: libleveldb1 + <<: *debpkg_install + +# Xenial + +build:ubuntu-16.04: + <<: *build_definition + image: ubuntu:xenial + before_script: + - apt-get update -y + - apt-get -y 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 + +package:ubuntu-16.04: + image: ubuntu:xenial + dependencies: + - build:ubuntu-16.04 + variables: + LEVELDB_PKG: libleveldb1v5 + <<: *debpkg_template + +deploy:ubuntu-16.04: + image: ubuntu:xenial + dependencies: + - package:ubuntu-16.04 + variables: + LEVELDB_PKG: libleveldb1v5 + <<: *debpkg_install + +# Yakkety + +build:ubuntu-16.10: + <<: *build_definition + image: ubuntu:yakkety + before_script: + - apt-get update -y + - apt-get -y 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 + +package:ubuntu-16.10: + image: ubuntu:yakkety + dependencies: + - build:ubuntu-16.10 + variables: + LEVELDB_PKG: libleveldb1v5 + <<: *debpkg_template + +deploy:ubuntu-16.10: + image: ubuntu:yakkety + dependencies: + - package:ubuntu-16.10 + variables: + LEVELDB_PKG: libleveldb1v5 + <<: *debpkg_install + +# Zesty + +build:ubuntu-17.04: + <<: *build_definition + image: ubuntu:zesty + before_script: + - apt-get update -y + - apt-get -y 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 + +package:ubuntu-17.04: + image: ubuntu:zesty + dependencies: + - build:ubuntu-17.04 + variables: + LEVELDB_PKG: libleveldb1v5 + <<: *debpkg_template + +deploy:ubuntu-17.04: + image: ubuntu:zesty + dependencies: + - package:ubuntu-17.04 + variables: + LEVELDB_PKG: libleveldb1v5 + <<: *debpkg_install + +## +## Fedora +## + +build:fedora-24: + <<: *build_definition + image: fedora:24 + before_script: + - dnf -y 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 diff --git a/.travis.yml b/.travis.yml index 534479efb..57d934c90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,14 @@ matrix: - env: PLATFORM=Unix COMPILER=clang compiler: clang os: linux + - env: PLATFORM=Unix COMPILER=clang VALGRIND=1 + compiler: clang + os: linux + dist: trusty + - env: COMPILER=none LINT=1 + compiler: clang + os: linux + dist: trusty - env: PLATFORM=Unix COMPILER=g++-6 compiler: gcc os: linux diff --git a/CMakeLists.txt b/CMakeLists.txt index 11ebe94a1..1a0dcb688 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 15) +set(VERSION_PATCH 16) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases @@ -26,6 +26,11 @@ elseif(DEVELOPMENT_BUILD) set(VERSION_STRING "${VERSION_STRING}-dev") endif() +if (CMAKE_BUILD_TYPE STREQUAL Debug) + # Append "-debug" to version string + set(VERSION_STRING "${VERSION_STRING}-debug") +endif() + message(STATUS "*** Will build version ${VERSION_STRING} ***") @@ -49,7 +54,6 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug or Release" FORCE) endif() - # Included stuff set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") @@ -82,7 +86,7 @@ elseif(UNIX) # Linux, BSD etc set(EXAMPLE_CONF_DIR ".") set(MANDIR "unix/man") set(XDG_APPS_DIR "unix/applications") - set(APPDATADIR "unix/appdata") + set(APPDATADIR "unix/metainfo") set(ICONDIR "unix/icons") set(LOCALEDIR "locale") else() @@ -92,7 +96,7 @@ elseif(UNIX) # Linux, BSD etc set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man") set(EXAMPLE_CONF_DIR ${DOCDIR}) set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications") - set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/appdata") + set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/metainfo") set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons") set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/locale") endif() @@ -152,6 +156,7 @@ endif() install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/builtin" DESTINATION "${SHAREDIR}") install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client" DESTINATION "${SHAREDIR}") +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/clientmods" DESTINATION "${SHAREDIR}") install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games" DESTINATION "${SHAREDIR}" PATTERN ".git*" EXCLUDE) if(BUILD_CLIENT) @@ -173,8 +178,8 @@ install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}") if(UNIX AND NOT APPLE) install(FILES "doc/minetest.6" "doc/minetestserver.6" DESTINATION "${MANDIR}/man6") - install(FILES "misc/minetest.desktop" DESTINATION "${XDG_APPS_DIR}") - install(FILES "misc/minetest.appdata.xml" DESTINATION "${APPDATADIR}") + install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}") + install(FILES "misc/net.minetest.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" @@ -186,6 +191,10 @@ if(APPLE) install(FILES "misc/Info.plist" DESTINATION "${BUNDLE_PATH}/Contents") endif() +# Library pack +find_package(GMP REQUIRED) +find_package(Json REQUIRED) +find_package(Lua REQUIRED) # Subdirectories # Be sure to add all relevant definitions above this diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 986f60027..468ba0514 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,7 +25,6 @@ If you are planning to start some significant coding, you would benefit from ask - Have a title which begins with a capital letter - Be descriptive. (e.g. no `Update init.lua` or `Fix a problem`) - Have a first line with less than *80 characters* and have a second line that is *empty* - - Do **not** [sign your commits](https://git-scm.com/book/uz/v2/Git-Tools-Signing-Your-Work), as Minetest offers automatically built ppas over launchpad and it [would break](https://bugs.launchpad.net/bzr-git/+bug/1084403) if there were signed commits in master 4. Once you are happy with your changes, submit a pull request. - Open the [pull-request form](https://github.com/minetest/minetest/pull/new/master) diff --git a/README.txt b/README.txt index 5d4d15263..a627bde8d 100644 --- a/README.txt +++ b/README.txt @@ -3,7 +3,7 @@ Minetest An InfiniMiner/Minecraft inspired game. -Copyright (c) 2010-2013 Perttu Ahola +Copyright (c) 2010-2017 Perttu Ahola and contributors (see source file comments and the version control log) In case you downloaded the source code: @@ -43,7 +43,7 @@ Default controls - 0-9: Select item - Z: Zoom (needs zoom privilege) - T: Chat -- /: Commad +- /: Command - Esc: Pause menu/abort/exit (pauses only singleplayer game) - R: Enable/disable full range view @@ -85,7 +85,7 @@ $bin = /usr/bin $share = /usr/share/minetest $user = ~/.minetest -OS X: +macOS: $bin = Contents/MacOS $share = Contents/Resources $user = Contents/User OR ~/Library/Application Support/minetest @@ -101,7 +101,9 @@ Configuration file: $user/minetest.conf - It is created by Minetest when it is ran the first time. - A specific file can be specified on the command line: - --config + --config +- A run-in-place build will look for the configuration file in + $location_of_exe/../minetest.conf and also $location_of_exe/../../minetest.conf Command-line options: --------------------- @@ -117,7 +119,7 @@ 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 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. +If you don’t 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 @@ -178,7 +180,7 @@ 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 -ENABLE_POSTGRESQL - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater required) +ENABLE_POSTGRESQL - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended) 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 @@ -212,7 +214,7 @@ 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_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 @@ -427,7 +429,7 @@ License of Minetest source code ------------------------------- Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2010-2017 celeron55, Perttu Ahola 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 @@ -502,7 +504,7 @@ Lua is licensed under the terms of the MIT license reproduced below. This means that Lua is free software and can be used for both academic and commercial purposes at absolutely no cost. -For details and rationale, see http://www.lua.org/license.html . +For details and rationale, see https://www.lua.org/license.html . Copyright (C) 1994-2008 Lua.org, PUC-Rio. @@ -527,25 +529,18 @@ THE SOFTWARE. Fonts --------------- -DejaVu Sans Mono: - - Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. - Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) - Bitstream Vera Fonts Copyright: Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. -Arev Fonts Copyright: +Arimo - Apache License, version 2.0 + Digitized data copyright (c) 2010-2012 Google Corporation. - Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. +Cousine - Apache License, version 2.0 + Digitized data copyright (c) 2010-2012 Google Corporation. -Liberation Fonts Copyright: - - Copyright (c) 2007 Red Hat, Inc. All rights reserved. LIBERATION is a trademark of Red Hat, Inc. - -DroidSansFallback: +DroidSansFallBackFull: Copyright (C) 2008 The Android Open Source Project diff --git a/build/android/Makefile b/build/android/Makefile index 6e7a389c9..d9a82da4d 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -35,7 +35,7 @@ TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON) TARGET_ARCH = armv7 CROSS_PREFIX = arm-linux-androideabi- COMPILER_VERSION = 4.9 -HAVE_LEVELDB = 1 +HAVE_LEVELDB = 0 ################################################################################ # toolchain config for little endian mips @@ -59,7 +59,7 @@ HAVE_LEVELDB = 1 #CROSS_PREFIX = i686-linux-android- #TARGET_ARCH = x86 #COMPILER_VERSION = 4.9 -#HAVE_LEVELDB = 1 +#HAVE_LEVELDB = 0 ################################################################################ ASSETS_TIMESTAMP = deps/assets_timestamp @@ -84,14 +84,14 @@ OGG_TIMESTAMP = $(OGG_DIR)timestamp OGG_TIMESTAMP_INT = $(ANDR_ROOT)/deps/ogg_timestamp OGG_URL_GIT = https://github.com/vincentjames501/libvorbis-libogg-android -IRRLICHT_REVISION = 5122 +IRRLICHT_REVISION = 5145 IRRLICHT_DIR = $(ANDR_ROOT)/deps/irrlicht/ IRRLICHT_LIB = $(IRRLICHT_DIR)lib/Android/libIrrlicht.a 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.2j +OPENSSL_VERSION = 1.0.2k OPENSSL_BASEDIR = openssl-$(OPENSSL_VERSION) OPENSSL_DIR = $(ANDR_ROOT)/deps/$(OPENSSL_BASEDIR)/ OPENSSL_LIB = $(OPENSSL_DIR)/libssl.so.1.0.0 @@ -99,7 +99,7 @@ OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp OPENSSL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/openssl_timestamp OPENSSL_URL = https://www.openssl.org/source/openssl-$(OPENSSL_VERSION).tar.gz -CURL_VERSION = 7.52.0 +CURL_VERSION = 7.54.0 CURL_DIR = $(ANDR_ROOT)/deps/curl-$(CURL_VERSION) CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a CURL_TIMESTAMP = $(CURL_DIR)/timestamp @@ -126,8 +126,8 @@ ICONV_TIMESTAMP = $(ICONV_DIR)timestamp ICONV_TIMESTAMP_INT = $(ANDR_ROOT)/deps/iconv_timestamp ICONV_URL_HTTP = https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$(ICONV_VERSION).tar.gz -SQLITE3_FOLDER = sqlite-amalgamation-3150200 -SQLITE3_URL = https://www.sqlite.org/2016/$(SQLITE3_FOLDER).zip +SQLITE3_FOLDER = sqlite-amalgamation-3180000 +SQLITE3_URL = https://www.sqlite.org/2017/$(SQLITE3_FOLDER).zip ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') ANDROID_NDK = $(shell grep '^ndk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') @@ -352,7 +352,7 @@ leveldb_download : fi leveldb : $(LEVELDB_LIB) - +ifeq ($(HAVE_LEVELDB),1) $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP) @REFRESH=0; \ if [ ! -e ${LEVELDB_TIMESTAMP_INT} ] ; then \ @@ -384,6 +384,7 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP) else \ echo "nothing to be done for leveldb"; \ fi +endif clean_leveldb : $(RM) -rf deps/leveldb @@ -679,8 +680,7 @@ deps/${SQLITE3_FOLDER}/sqlite3.c : wget ${SQLITE3_URL}; \ unzip ${SQLITE3_FOLDER}.zip; \ ln -s ${SQLITE3_FOLDER} sqlite; \ - cd ${SQLITE3_FOLDER}; \ - patch sqlite3.c < ${ANDR_ROOT}/patches/sqlite3-readonly-fix.patch + cd ${SQLITE3_FOLDER}; clean_sqlite3: cd deps && $(RM) -rf ${SQLITE3_FOLDER} && $(RM) -f ${SQLITE3_FOLDER}.zip && \ @@ -781,7 +781,7 @@ apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) fi; \ export VERSION_STR="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" && \ export BUILD_TYPE_C=$$(echo "$${BUILD_TYPE}" | sed 's/./\U&/') && \ - gradle assemble$$BUILD_TYPE_C && \ + ./gradlew assemble$$BUILD_TYPE_C && \ echo "APK stored at: build/outputs/apk/Minetest-$$BUILD_TYPE.apk" && \ echo "You can install it with \`make install_$$BUILD_TYPE\`" @@ -795,11 +795,14 @@ install_release: prep_srcdir : @if [ ! -e ${ANDR_ROOT}/jni/src ]; then \ - ln -s ${PROJ_ROOT}/src ${ANDR_ROOT}/jni/src; \ + ln -s ${PROJ_ROOT}/src ${ANDR_ROOT}/jni/src; \ + fi; \ + if [ ! -e ${ANDR_ROOT}/jni/lib ]; then \ + ln -s ${PROJ_ROOT}/lib ${ANDR_ROOT}/jni/lib; \ fi clean_apk : - gradle clean + ./gradlew clean clean_all : @$(MAKE) clean_apk; \ diff --git a/build/android/build.gradle b/build/android/build.gradle index 20c13e385..1263b2232 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -10,11 +10,11 @@ buildscript { apply plugin: "com.android.application" android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion 25 + buildToolsVersion "25.0.3" defaultConfig { - versionCode 16 + versionCode 17 versionName "${System.env.VERSION_STR}.${versionCode}" minSdkVersion 9 targetSdkVersion 9 @@ -46,4 +46,3 @@ android { } } } - diff --git a/build/android/gradle/wrapper/gradle-wrapper.jar b/build/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..8c0fb64a8 Binary files /dev/null and b/build/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..980438b75 --- /dev/null +++ b/build/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Aug 27 20:10:09 CEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/build/android/gradlew b/build/android/gradlew new file mode 100755 index 000000000..91a7e269e --- /dev/null +++ b/build/android/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/build/android/gradlew.bat b/build/android/gradlew.bat new file mode 100644 index 000000000..8a0b282aa --- /dev/null +++ b/build/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index c2186efaf..7b741e04b 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -98,8 +98,8 @@ endif LOCAL_C_INCLUDES := \ jni/src \ jni/src/script \ - jni/src/lua/src \ - jni/src/jsoncpp \ + jni/lib/lua/src \ + jni/lib/jsoncpp \ jni/src/cguittfont \ deps/irrlicht/include \ deps/libiconv/include \ @@ -117,6 +117,7 @@ LOCAL_SRC_FILES := \ jni/src/cavegen.cpp \ jni/src/chat.cpp \ jni/src/client.cpp \ + jni/src/clientenvironment.cpp \ jni/src/clientiface.cpp \ jni/src/clientmap.cpp \ jni/src/clientmedia.cpp \ @@ -133,6 +134,7 @@ LOCAL_SRC_FILES := \ jni/src/convert_json.cpp \ jni/src/craftdef.cpp \ jni/src/database-dummy.cpp \ + jni/src/database-files.cpp \ jni/src/database-sqlite3.cpp \ jni/src/database.cpp \ jni/src/debug.cpp \ @@ -141,6 +143,7 @@ LOCAL_SRC_FILES := \ jni/src/dungeongen.cpp \ jni/src/emerge.cpp \ jni/src/environment.cpp \ + jni/src/face_position_cache.cpp \ jni/src/filecache.cpp \ jni/src/filesys.cpp \ jni/src/fontengine.cpp \ @@ -163,6 +166,7 @@ LOCAL_SRC_FILES := \ jni/src/inventory.cpp \ jni/src/inventorymanager.cpp \ jni/src/itemdef.cpp \ + jni/src/itemstackmetadata.cpp \ jni/src/keycode.cpp \ jni/src/light.cpp \ jni/src/localplayer.cpp \ @@ -183,6 +187,8 @@ LOCAL_SRC_FILES := \ jni/src/mapnode.cpp \ jni/src/mapsector.cpp \ jni/src/mesh.cpp \ + jni/src/mesh_generator_thread.cpp \ + jni/src/metadata.cpp \ jni/src/mg_biome.cpp \ jni/src/mg_decoration.cpp \ jni/src/mg_ore.cpp \ @@ -203,12 +209,14 @@ LOCAL_SRC_FILES := \ jni/src/porting.cpp \ jni/src/profiler.cpp \ jni/src/quicktune.cpp \ + jni/src/raycast.cpp \ jni/src/reflowscan.cpp \ jni/src/remoteplayer.cpp \ jni/src/rollback.cpp \ jni/src/rollback_interface.cpp \ jni/src/serialization.cpp \ jni/src/server.cpp \ + jni/src/serverenvironment.cpp \ jni/src/serverlist.cpp \ jni/src/serverobject.cpp \ jni/src/shader.cpp \ @@ -218,6 +226,7 @@ LOCAL_SRC_FILES := \ jni/src/sound_openal.cpp \ jni/src/staticobject.cpp \ jni/src/subgame.cpp \ + jni/src/tileanimation.cpp \ jni/src/tool.cpp \ jni/src/treegen.cpp \ jni/src/version.cpp \ @@ -261,6 +270,7 @@ LOCAL_SRC_FILES := \ jni/src/settings.cpp \ jni/src/wieldmesh.cpp \ jni/src/client/clientlauncher.cpp \ + jni/src/client/inputhandler.cpp \ jni/src/client/tile.cpp \ jni/src/client/joystick_controller.cpp \ jni/src/irrlicht_changes/static_text.cpp @@ -284,6 +294,7 @@ LOCAL_SRC_FILES += \ jni/src/script/common/c_types.cpp \ jni/src/script/cpp_api/s_async.cpp \ jni/src/script/cpp_api/s_base.cpp \ + jni/src/script/cpp_api/s_client.cpp \ jni/src/script/cpp_api/s_entity.cpp \ jni/src/script/cpp_api/s_env.cpp \ jni/src/script/cpp_api/s_inventory.cpp \ @@ -296,12 +307,18 @@ LOCAL_SRC_FILES += \ jni/src/script/cpp_api/s_server.cpp \ jni/src/script/lua_api/l_areastore.cpp \ jni/src/script/lua_api/l_base.cpp \ + jni/src/script/lua_api/l_camera.cpp \ + jni/src/script/lua_api/l_client.cpp \ jni/src/script/lua_api/l_craft.cpp \ jni/src/script/lua_api/l_env.cpp \ jni/src/script/lua_api/l_inventory.cpp \ jni/src/script/lua_api/l_item.cpp \ + jni/src/script/lua_api/l_itemstackmeta.cpp\ + jni/src/script/lua_api/l_localplayer.cpp \ jni/src/script/lua_api/l_mainmenu.cpp \ jni/src/script/lua_api/l_mapgen.cpp \ + jni/src/script/lua_api/l_metadata.cpp \ + jni/src/script/lua_api/l_minimap.cpp \ jni/src/script/lua_api/l_nodemeta.cpp \ jni/src/script/lua_api/l_nodetimer.cpp \ jni/src/script/lua_api/l_noise.cpp \ @@ -310,10 +327,13 @@ LOCAL_SRC_FILES += \ jni/src/script/lua_api/l_rollback.cpp \ jni/src/script/lua_api/l_server.cpp \ jni/src/script/lua_api/l_settings.cpp \ + jni/src/script/lua_api/l_sound.cpp \ jni/src/script/lua_api/l_http.cpp \ + jni/src/script/lua_api/l_storage.cpp \ jni/src/script/lua_api/l_util.cpp \ jni/src/script/lua_api/l_vmanip.cpp \ - jni/src/script/scripting_game.cpp \ + jni/src/script/scripting_client.cpp \ + jni/src/script/scripting_server.cpp \ jni/src/script/scripting_mainmenu.cpp #freetype2 support @@ -321,36 +341,36 @@ LOCAL_SRC_FILES += jni/src/cguittfont/xCGUITTFont.cpp # Lua LOCAL_SRC_FILES += \ - jni/src/lua/src/lapi.c \ - jni/src/lua/src/lauxlib.c \ - jni/src/lua/src/lbaselib.c \ - jni/src/lua/src/lcode.c \ - jni/src/lua/src/ldblib.c \ - jni/src/lua/src/ldebug.c \ - jni/src/lua/src/ldo.c \ - jni/src/lua/src/ldump.c \ - jni/src/lua/src/lfunc.c \ - jni/src/lua/src/lgc.c \ - jni/src/lua/src/linit.c \ - jni/src/lua/src/liolib.c \ - jni/src/lua/src/llex.c \ - jni/src/lua/src/lmathlib.c \ - jni/src/lua/src/lmem.c \ - jni/src/lua/src/loadlib.c \ - jni/src/lua/src/lobject.c \ - jni/src/lua/src/lopcodes.c \ - jni/src/lua/src/loslib.c \ - jni/src/lua/src/lparser.c \ - jni/src/lua/src/lstate.c \ - jni/src/lua/src/lstring.c \ - jni/src/lua/src/lstrlib.c \ - jni/src/lua/src/ltable.c \ - jni/src/lua/src/ltablib.c \ - jni/src/lua/src/ltm.c \ - jni/src/lua/src/lundump.c \ - jni/src/lua/src/lvm.c \ - jni/src/lua/src/lzio.c \ - jni/src/lua/src/print.c + jni/lib/lua/src/lapi.c \ + jni/lib/lua/src/lauxlib.c \ + jni/lib/lua/src/lbaselib.c \ + jni/lib/lua/src/lcode.c \ + jni/lib/lua/src/ldblib.c \ + jni/lib/lua/src/ldebug.c \ + jni/lib/lua/src/ldo.c \ + jni/lib/lua/src/ldump.c \ + jni/lib/lua/src/lfunc.c \ + jni/lib/lua/src/lgc.c \ + jni/lib/lua/src/linit.c \ + jni/lib/lua/src/liolib.c \ + jni/lib/lua/src/llex.c \ + jni/lib/lua/src/lmathlib.c \ + jni/lib/lua/src/lmem.c \ + jni/lib/lua/src/loadlib.c \ + jni/lib/lua/src/lobject.c \ + jni/lib/lua/src/lopcodes.c \ + jni/lib/lua/src/loslib.c \ + jni/lib/lua/src/lparser.c \ + jni/lib/lua/src/lstate.c \ + jni/lib/lua/src/lstring.c \ + jni/lib/lua/src/lstrlib.c \ + jni/lib/lua/src/ltable.c \ + jni/lib/lua/src/ltablib.c \ + jni/lib/lua/src/ltm.c \ + jni/lib/lua/src/lundump.c \ + jni/lib/lua/src/lvm.c \ + jni/lib/lua/src/lzio.c \ + jni/lib/lua/src/print.c # SQLite3 LOCAL_SRC_FILES += deps/sqlite/sqlite3.c @@ -363,7 +383,7 @@ LOCAL_SRC_FILES += \ jni/src/threading/thread.cpp # JSONCPP -LOCAL_SRC_FILES += jni/src/jsoncpp/json/jsoncpp.cpp +LOCAL_SRC_FILES += jni/lib/jsoncpp/jsoncpp.cpp LOCAL_SHARED_LIBRARIES := iconv openal ogg vorbis gmp LOCAL_STATIC_LIBRARIES := Irrlicht freetype curl ssl crypto android_native_app_glue $(PROFILER_LIBS) @@ -380,4 +400,3 @@ ifdef GPROF $(call import-module,android-ndk-profiler) endif $(call import-module,android/native_app_glue) - diff --git a/build/android/patches/sqlite3-readonly-fix.patch b/build/android/patches/sqlite3-readonly-fix.patch deleted file mode 100644 index be19055ee..000000000 --- a/build/android/patches/sqlite3-readonly-fix.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- 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/builtin/client/chatcommands.lua b/builtin/client/chatcommands.lua new file mode 100644 index 000000000..2b8cc4acd --- /dev/null +++ b/builtin/client/chatcommands.lua @@ -0,0 +1,65 @@ +-- Minetest: builtin/client/chatcommands.lua + + +core.register_on_sending_chat_messages(function(message) + if message:sub(1,2) == ".." then + return false + end + + local first_char = message:sub(1,1) + if first_char == "/" or first_char == "." then + core.display_chat_message(core.gettext("issued command: ") .. message) + end + + if first_char ~= "." then + return false + end + + local cmd, param = string.match(message, "^%.([^ ]+) *(.*)") + param = param or "" + + if not cmd then + core.display_chat_message(core.gettext("-!- Empty command")) + return true + end + + local cmd_def = core.registered_chatcommands[cmd] + if cmd_def then + core.set_last_run_mod(cmd_def.mod_origin) + local _, message = cmd_def.func(param) + if message then + core.display_chat_message(message) + end + else + core.display_chat_message(core.gettext("-!- Invalid command: ") .. cmd) + end + + return true +end) + +core.register_chatcommand("list_players", { + description = core.gettext("List online players"), + func = function(param) + local players = table.concat(core.get_player_names(), ", ") + core.display_chat_message(core.gettext("Online players: ") .. players) + end +}) + +core.register_chatcommand("disconnect", { + description = core.gettext("Exit to main menu"), + func = function(param) + core.disconnect() + end, +}) + +core.register_chatcommand("clear_chat_queue", { + description = core.gettext("Clear the out chat queue"), + func = function(param) + core.clear_out_chat_queue() + return true, core.gettext("The out chat queue is now empty") + end, +}) + +function core.run_server_chatcommand(cmd, param) + core.send_chat_message("/" .. cmd .. " " .. param) +end diff --git a/builtin/client/init.lua b/builtin/client/init.lua new file mode 100644 index 000000000..3ac34d845 --- /dev/null +++ b/builtin/client/init.lua @@ -0,0 +1,23 @@ +-- Minetest: builtin/client/init.lua +local scriptpath = core.get_builtin_path()..DIR_DELIM +local clientpath = scriptpath.."client"..DIR_DELIM +local commonpath = scriptpath.."common"..DIR_DELIM + +dofile(clientpath .. "register.lua") +dofile(commonpath .. "after.lua") +dofile(commonpath .. "chatcommands.lua") +dofile(clientpath .. "chatcommands.lua") +dofile(commonpath .. "vector.lua") + +core.register_on_death(function() + core.display_chat_message("You died.") + local formspec = "size[11,5.5]bgcolor[#320000b4;true]" .. + "label[4.85,1.35;" .. fgettext("You died.") .. "]button_exit[4,3;3,0.5;btn_respawn;".. fgettext("Respawn") .."]" + core.show_formspec("bultin:death", formspec) +end) + +core.register_on_formspec_input(function(formname, fields) + if formname == "bultin:death" then + core.send_respawn() + end +end) diff --git a/builtin/client/register.lua b/builtin/client/register.lua new file mode 100644 index 000000000..6b12ddec8 --- /dev/null +++ b/builtin/client/register.lua @@ -0,0 +1,73 @@ + +core.callback_origins = {} + +local getinfo = debug.getinfo +debug.getinfo = nil + +function core.run_callbacks(callbacks, mode, ...) + assert(type(callbacks) == "table") + local cb_len = #callbacks + if cb_len == 0 then + if mode == 2 or mode == 3 then + return true + elseif mode == 4 or mode == 5 then + return false + end + end + local ret + for i = 1, cb_len do + local cb_ret = callbacks[i](...) + + if mode == 0 and i == 1 or mode == 1 and i == cb_len then + ret = cb_ret + elseif mode == 2 then + if not cb_ret or i == 1 then + ret = cb_ret + end + elseif mode == 3 then + if cb_ret then + return cb_ret + end + ret = cb_ret + elseif mode == 4 then + if (cb_ret and not ret) or i == 1 then + ret = cb_ret + end + elseif mode == 5 and cb_ret then + return cb_ret + end + end + return ret +end + +-- +-- Callback registration +-- + +local function make_registration() + local t = {} + local registerfunc = function(func) + t[#t + 1] = func + core.callback_origins[func] = { + mod = core.get_current_modname() or "??", + name = getinfo(1, "n").name or "??" + } + --local origin = core.callback_origins[func] + --print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func)) + end + return t, registerfunc +end + +core.registered_globalsteps, core.register_globalstep = make_registration() +core.registered_on_shutdown, core.register_on_shutdown = make_registration() +core.registered_on_connect, core.register_on_connect = make_registration() +core.registered_on_receiving_chat_messages, core.register_on_receiving_chat_messages = make_registration() +core.registered_on_sending_chat_messages, core.register_on_sending_chat_messages = make_registration() +core.registered_on_death, core.register_on_death = make_registration() +core.registered_on_hp_modification, core.register_on_hp_modification = make_registration() +core.registered_on_damage_taken, core.register_on_damage_taken = make_registration() +core.registered_on_formspec_input, core.register_on_formspec_input = make_registration() +core.registered_on_dignode, core.register_on_dignode = make_registration() +core.registered_on_punchnode, core.register_on_punchnode = make_registration() +core.registered_on_placenode, core.register_on_placenode = make_registration() +core.registered_on_item_use, core.register_on_item_use = make_registration() diff --git a/builtin/common/after.lua b/builtin/common/after.lua new file mode 100644 index 000000000..cdfaaab86 --- /dev/null +++ b/builtin/common/after.lua @@ -0,0 +1,33 @@ +local jobs = {} +local time = 0.0 + +core.register_globalstep(function(dtime) + time = time + dtime + + if #jobs < 1 then + return + end + + -- Iterate backwards so that we miss any new timers added by + -- a timer callback, and so that we don't skip the next timer + -- in the list if we remove one. + for i = #jobs, 1, -1 do + local job = jobs[i] + if time >= job.expire then + core.set_last_run_mod(job.mod_origin) + job.func(unpack(job.arg)) + table.remove(jobs, i) + end + end +end) + +function core.after(after, func, ...) + assert(tonumber(after) and type(func) == "function", + "Invalid core.after invocation") + jobs[#jobs + 1] = { + func = func, + expire = time + after, + arg = {...}, + mod_origin = core.get_last_run_mod() + } +end diff --git a/builtin/common/chatcommands.lua b/builtin/common/chatcommands.lua new file mode 100644 index 000000000..e8955c6b4 --- /dev/null +++ b/builtin/common/chatcommands.lua @@ -0,0 +1,112 @@ +-- Minetest: builtin/common/chatcommands.lua + +core.registered_chatcommands = {} + +function core.register_chatcommand(cmd, def) + def = def or {} + def.params = def.params or "" + def.description = def.description or "" + def.privs = def.privs or {} + def.mod_origin = core.get_current_modname() or "??" + core.registered_chatcommands[cmd] = def +end + +function core.unregister_chatcommand(name) + if core.registered_chatcommands[name] then + core.registered_chatcommands[name] = nil + else + core.log("warning", "Not unregistering chatcommand " ..name.. + " because it doesn't exist.") + end +end + +function core.override_chatcommand(name, redefinition) + local chatcommand = core.registered_chatcommands[name] + assert(chatcommand, "Attempt to override non-existent chatcommand "..name) + for k, v in pairs(redefinition) do + rawset(chatcommand, k, v) + end + core.registered_chatcommands[name] = chatcommand +end + +local cmd_marker = "/" + +local function gettext(...) + return ... +end + +local function gettext_replace(text, replace) + return text:gsub("$1", replace) +end + + +if INIT == "client" then + cmd_marker = "." + gettext = core.gettext + gettext_replace = fgettext_ne +end + +local function do_help_cmd(name, param) + local function format_help_line(cmd, def) + local msg = core.colorize("#00ffff", cmd_marker .. cmd) + if def.params and def.params ~= "" then + msg = msg .. " " .. def.params + end + if def.description and def.description ~= "" then + msg = msg .. ": " .. def.description + end + return msg + end + if param == "" then + local cmds = {} + for cmd, def in pairs(core.registered_chatcommands) do + if INIT == "client" or core.check_player_privs(name, def.privs) then + cmds[#cmds + 1] = cmd + end + end + table.sort(cmds) + return true, gettext("Available commands: ") .. table.concat(cmds, " ") .. "\n" + .. gettext_replace("Use '$1help ' to get more information," + .. " or '$1help all' to list everything.", cmd_marker) + elseif param == "all" then + local cmds = {} + for cmd, def in pairs(core.registered_chatcommands) do + if INIT == "client" or core.check_player_privs(name, def.privs) then + cmds[#cmds + 1] = format_help_line(cmd, def) + end + end + table.sort(cmds) + return true, gettext("Available commands:").."\n"..table.concat(cmds, "\n") + elseif INIT == "game" and param == "privs" then + local privs = {} + for priv, def in pairs(core.registered_privileges) do + privs[#privs + 1] = priv .. ": " .. def.description + end + table.sort(privs) + return true, "Available privileges:\n"..table.concat(privs, "\n") + else + local cmd = param + local def = core.registered_chatcommands[cmd] + if not def then + return false, gettext("Command not available: ")..cmd + else + return true, format_help_line(cmd, def) + end + end +end + +if INIT == "client" then + core.register_chatcommand("help", { + params = gettext("[all/]"), + description = gettext("Get help for commands"), + func = function(param) + return do_help_cmd(nil, param) + end, + }) +else + core.register_chatcommand("help", { + params = "[all/privs/]", + description = "Get help for commands or list privileges", + func = do_help_cmd, + }) +end diff --git a/builtin/common/filterlist.lua b/builtin/common/filterlist.lua index 2a62362e3..562231192 100644 --- a/builtin/common/filterlist.lua +++ b/builtin/common/filterlist.lua @@ -289,6 +289,9 @@ function sort_mod_list(self) table.sort(self.m_processed_list, function(a, b) -- Show game mods at bottom if a.typ ~= b.typ then + if b.typ == "game" then + return a.typ ~= "game_mod" + end return b.typ == "game_mod" end -- If in same or no modpack, sort by name diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index c2dc7514d..68481f7c8 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -197,16 +197,17 @@ assert(table.indexof({"foo", "bar"}, "foo") == 1) assert(table.indexof({"foo", "bar"}, "baz") == -1) -------------------------------------------------------------------------------- -function file_exists(filename) - local f = io.open(filename, "r") - if f == nil then - return false - else - f:close() - return true +if INIT ~= "client" then + function file_exists(filename) + local f = io.open(filename, "r") + if f == nil then + return false + else + f:close() + return true + end end end - -------------------------------------------------------------------------------- function string:trim() return (self:gsub("^%s*(.-)%s*$", "%1")) @@ -307,7 +308,7 @@ function core.formspec_escape(text) end -function core.splittext(text,charlimit) +function core.wrap_text(text, charlimit) local retval = {} local current_idx = 1 @@ -462,7 +463,7 @@ if INIT == "game" then core.rotate_node = function(itemstack, placer, pointed_thing) core.rotate_and_place(itemstack, placer, pointed_thing, - core.setting_getbool("creative_mode"), + core.settings:get_bool("creative_mode"), {invert_wall = placer:get_player_control().sneak}) return itemstack end @@ -606,7 +607,9 @@ if INIT == "mainmenu" then return nil end +end +if INIT == "client" or INIT == "mainmenu" then function fgettext_ne(text, ...) text = core.gettext(text) local arg = {n=select('#', ...), ...} @@ -637,3 +640,86 @@ if INIT == "mainmenu" then end end +local ESCAPE_CHAR = string.char(0x1b) + +-- Client-side mods don't have access to settings +if core.settings and core.settings:get_bool("disable_escape_sequences") then + + function core.get_color_escape_sequence(color) + return "" + end + + function core.get_background_escape_sequence(color) + return "" + end + + function core.colorize(color, message) + return message + end + +else + + function core.get_color_escape_sequence(color) + return ESCAPE_CHAR .. "(c@" .. color .. ")" + end + + function core.get_background_escape_sequence(color) + return ESCAPE_CHAR .. "(b@" .. color .. ")" + end + + function core.colorize(color, message) + local lines = tostring(message):split("\n", true) + local color_code = core.get_color_escape_sequence(color) + + for i, line in ipairs(lines) do + lines[i] = color_code .. line + end + + return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff") + end + +end + +function core.strip_foreground_colors(str) + return (str:gsub(ESCAPE_CHAR .. "%(c@[^)]+%)", "")) +end + +function core.strip_background_colors(str) + return (str:gsub(ESCAPE_CHAR .. "%(b@[^)]+%)", "")) +end + +function core.strip_colors(str) + return (str:gsub(ESCAPE_CHAR .. "%([bc]@[^)]+%)", "")) +end + +-------------------------------------------------------------------------------- +-- Returns the exact coordinate of a pointed surface +-------------------------------------------------------------------------------- +function core.pointed_thing_to_face_pos(placer, pointed_thing) + local eye_offset_first = placer:get_eye_offset() + local node_pos = pointed_thing.under + local camera_pos = placer:get_pos() + local pos_off = vector.multiply( + vector.subtract(pointed_thing.above, node_pos), 0.5) + local look_dir = placer:get_look_dir() + local offset, nc + local oc = {} + + for c, v in pairs(pos_off) do + if nc or v == 0 then + oc[#oc + 1] = c + else + offset = v + nc = c + end + end + + local fine_pos = {[nc] = node_pos[nc] + offset} + camera_pos.y = camera_pos.y + 1.625 + eye_offset_first.y / 10 + local f = (node_pos[nc] + offset - camera_pos[nc]) / look_dir[nc] + + for i = 1, #oc do + fine_pos[oc[i]] = camera_pos[oc[i]] + look_dir[oc[i]] * f + end + return fine_pos +end diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua index b2165648e..692ddd5f0 100644 --- a/builtin/common/serialize.lua +++ b/builtin/common/serialize.lua @@ -186,6 +186,10 @@ local safe_env = { } function core.deserialize(str, safe) + if type(str) ~= "string" then + return nil, "Cannot deserialize type '"..type(str) + .."'. Argument must be a string." + end if str:byte(1) == 0x1B then return nil, "Bytecode prohibited" end diff --git a/builtin/common/strict.lua b/builtin/common/strict.lua index 23ba3d727..ccde9676b 100644 --- a/builtin/common/strict.lua +++ b/builtin/common/strict.lua @@ -3,6 +3,7 @@ -- This ignores mod namespaces (variables with the same name as the current mod). local WARN_INIT = false +local getinfo = debug.getinfo function core.global_exists(name) if type(name) ~= "string" then @@ -18,7 +19,7 @@ local declared = {} local warned = {} function meta:__newindex(name, value) - local info = debug.getinfo(2, "Sl") + local info = getinfo(2, "Sl") local desc = ("%s:%d"):format(info.short_src, info.currentline) if not declared[name] then local warn_key = ("%s\0%d\0%s"):format(info.source, @@ -42,7 +43,7 @@ end function meta:__index(name) - local info = debug.getinfo(2, "Sl") + local info = getinfo(2, "Sl") local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name) if not declared[name] and not warned[warn_key] and info.what ~= "C" then core.log("warning", ("Undeclared global variable %q accessed at %s:%s") diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index 90ba3cc8b..0549f9a56 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -138,3 +138,8 @@ function vector.divide(a, b) z = a.z / b} end end + +function vector.sort(a, b) + return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)}, + {x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)} +end diff --git a/builtin/fstk/tabview.lua b/builtin/fstk/tabview.lua index 72551afd7..3715e231b 100644 --- a/builtin/fstk/tabview.lua +++ b/builtin/fstk/tabview.lua @@ -167,7 +167,7 @@ local function switch_to_tab(self, index) self.current_tab = self.tablist[index].name if (self.autosave_tab) then - core.setting_set(self.name .. "_LAST",self.current_tab) + core.settings:set(self.name .. "_LAST",self.current_tab) end -- call for tab to enter diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index 46fe3d342..8cb4ebf57 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -106,7 +106,7 @@ core.builtin_auth_handler = { end end -- For the admin, give everything - elseif name == core.setting_get("name") then + elseif name == core.settings:get("name") then for priv, def in pairs(core.registered_privileges) do privileges[priv] = true end @@ -125,7 +125,7 @@ core.builtin_auth_handler = { core.log('info', "Built-in authentication handler adding player '"..name.."'") core.auth_table[name] = { password = password, - privileges = core.string_to_privs(core.setting_get("default_privs")), + privileges = core.string_to_privs(core.settings:get("default_privs")), last_login = os.time(), } save_auth_file() @@ -148,7 +148,7 @@ core.builtin_auth_handler = { if not core.auth_table[name] then core.builtin_auth_handler.create_auth(name, core.get_password_hash(name, - core.setting_get("default_password"))) + core.settings:get("default_password"))) end core.auth_table[name].privileges = privileges core.notify_authentication_modified(name) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 2bd93855b..3dfc29ffa 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -1,27 +1,28 @@ --- Minetest: builtin/chatcommands.lua +-- Minetest: builtin/game/chatcommands.lua -- -- Chat command handler -- -core.chatcommands = {} -function core.register_chatcommand(cmd, def) - def = def or {} - def.params = def.params or "" - def.description = def.description or "" - def.privs = def.privs or {} - def.mod_origin = core.get_current_modname() or "??" - core.chatcommands[cmd] = def -end +core.chatcommands = core.registered_chatcommands -- BACKWARDS COMPATIBILITY core.register_on_chat_message(function(name, message) - local cmd, param = string.match(message, "^/([^ ]+) *(.*)") - if not param then - param = "" + if message:sub(1,1) ~= "/" then + return end - local cmd_def = core.chatcommands[cmd] + + local cmd, param = string.match(message, "^/([^ ]+) *(.*)") + if not cmd then + core.chat_send_player(name, "-!- Empty command") + return true + end + + param = param or "" + + local cmd_def = core.registered_chatcommands[cmd] if not cmd_def then - return false + core.chat_send_player(name, "-!- Invalid command: " .. cmd) + return true end local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs) if has_privs then @@ -38,7 +39,7 @@ core.register_on_chat_message(function(name, message) return true -- Handled chat message end) -if core.setting_getbool("profiler.load") then +if core.settings:get_bool("profiler.load") then -- Run after register_chatcommand and its register_on_chat_message -- Before any chattcommands that should be profiled profiler.init_chatcommand() @@ -70,7 +71,8 @@ end -- core.register_chatcommand("me", { params = "", - description = "chat action (eg. /me orders a pizza)", + description = "Display chat action (e.g., '/me orders a pizza' displays" + .. " ' orders a pizza')", privs = {shout=true}, func = function(name, param) core.chat_send_all("* " .. name .. " " .. param) @@ -80,7 +82,7 @@ core.register_chatcommand("me", { core.register_chatcommand("admin", { description = "Show the name of the server owner", func = function(name) - local admin = minetest.setting_get("name") + local admin = minetest.settings:get("name") if admin then return true, "The administrator of this server is "..admin.."." else @@ -89,64 +91,9 @@ core.register_chatcommand("admin", { end, }) -core.register_chatcommand("help", { - privs = {}, - params = "[all/privs/]", - description = "Get help for commands or list privileges", - func = function(name, param) - local function format_help_line(cmd, def) - local msg = core.colorize("#00ffff", "/"..cmd) - if def.params and def.params ~= "" then - msg = msg .. " " .. def.params - end - if def.description and def.description ~= "" then - msg = msg .. ": " .. def.description - end - return msg - end - if param == "" then - local msg = "" - local cmds = {} - for cmd, def in pairs(core.chatcommands) do - if core.check_player_privs(name, def.privs) then - cmds[#cmds + 1] = cmd - end - end - table.sort(cmds) - return true, "Available commands: " .. table.concat(cmds, " ") .. "\n" - .. "Use '/help ' to get more information," - .. " or '/help all' to list everything." - elseif param == "all" then - local cmds = {} - for cmd, def in pairs(core.chatcommands) do - if core.check_player_privs(name, def.privs) then - cmds[#cmds + 1] = format_help_line(cmd, def) - end - end - table.sort(cmds) - return true, "Available commands:\n"..table.concat(cmds, "\n") - elseif param == "privs" then - local privs = {} - for priv, def in pairs(core.registered_privileges) do - privs[#privs + 1] = priv .. ": " .. def.description - end - table.sort(privs) - return true, "Available privileges:\n"..table.concat(privs, "\n") - else - local cmd = param - local def = core.chatcommands[cmd] - if not def then - return false, "Command not available: "..cmd - else - return true, format_help_line(cmd, def) - end - end - end, -}) - core.register_chatcommand("privs", { params = "", - description = "print out privileges of player", + description = "Print privileges of player", func = function(caller, param) param = param:trim() local name = (param ~= "" and param or caller) @@ -161,8 +108,8 @@ local function handle_grant_command(caller, grantname, grantprivstr) 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 + + if not core.get_auth_handler().get_auth(grantname) then return false, "Player " .. grantname .. " does not exist." end local grantprivs = core.string_to_privs(grantprivstr) @@ -172,7 +119,7 @@ local function handle_grant_command(caller, grantname, grantprivstr) 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") + core.string_to_privs(core.settings: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." @@ -204,7 +151,7 @@ core.register_chatcommand("grant", { local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)") if not grantname or not grantprivstr then return false, "Invalid parameters (see /help grant)" - end + end return handle_grant_command(name, grantname, grantprivstr) end, }) @@ -215,7 +162,7 @@ core.register_chatcommand("grantme", { func = function(name, param) if param == "" then return false, "Invalid parameters (see /help grantme)" - end + end return handle_grant_command(name, name, param) end, }) @@ -232,13 +179,13 @@ core.register_chatcommand("revoke", { local revoke_name, revoke_priv_str = string.match(param, "([^ ]+) (.+)") if not revoke_name or not revoke_priv_str then return false, "Invalid parameters (see /help revoke)" - elseif not core.auth_table[revoke_name] then + elseif not core.get_auth_handler().get_auth(revoke_name) then return false, "Player " .. revoke_name .. " does not exist." end local revoke_privs = core.string_to_privs(revoke_priv_str) local privs = core.get_player_privs(revoke_name) local basic_privs = - core.string_to_privs(core.setting_get("basic_privs") or "interact,shout") + core.string_to_privs(core.settings:get("basic_privs") or "interact,shout") for priv, _ in pairs(revoke_privs) do if not basic_privs[priv] and not core.check_player_privs(name, {privs=true}) then @@ -269,7 +216,7 @@ core.register_chatcommand("revoke", { core.register_chatcommand("setpassword", { params = " ", - description = "set given password", + description = "Set player's password", privs = {password=true}, func = function(name, param) local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$") @@ -307,7 +254,7 @@ core.register_chatcommand("setpassword", { core.register_chatcommand("clearpassword", { params = "", - description = "set empty password", + description = "Set empty password", privs = {password=true}, func = function(name, param) local toname = param @@ -324,7 +271,7 @@ core.register_chatcommand("clearpassword", { core.register_chatcommand("auth_reload", { params = "", - description = "reload authentication data", + description = "Reload authentication data", privs = {server=true}, func = function(name, param) local done = core.auth_reload() @@ -332,9 +279,34 @@ core.register_chatcommand("auth_reload", { end, }) +core.register_chatcommand("remove_player", { + params = "", + description = "Remove player data", + privs = {server=true}, + func = function(name, param) + local toname = param + if toname == "" then + return false, "Name field required" + end + + local rc = core.remove_player(toname) + + if rc == 0 then + core.log("action", name .. " removed player data of " .. toname .. ".") + return true, "Player \"" .. toname .. "\" removed." + elseif rc == 1 then + return true, "No such player \"" .. toname .. "\" to remove." + elseif rc == 2 then + return true, "Player \"" .. toname .. "\" is connected, cannot remove." + end + + return false, "Unhandled remove_player return code " .. rc .. "" + end, +}) + core.register_chatcommand("teleport", { params = ",, | | ,, | ", - description = "teleport to given position", + description = "Teleport to player or position", privs = {teleport=true}, func = function(name, param) -- Returns (pos, true) if found, otherwise (pos, false) @@ -365,7 +337,7 @@ core.register_chatcommand("teleport", { p.y = tonumber(p.y) p.z = tonumber(p.z) if p.x and p.y and p.z then - local lm = tonumber(minetest.setting_get("map_generation_limit") or 31000) + local lm = 31000 if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm or p.z < -lm or p.z > lm then return false, "Cannot teleport out of map bounds!" end @@ -442,25 +414,25 @@ core.register_chatcommand("teleport", { core.register_chatcommand("set", { params = "[-n] | ", - description = "set or read server configuration setting", + description = "Set or read server configuration setting", privs = {server=true}, func = function(name, param) local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)") if arg and arg == "-n" and setname and setvalue then - core.setting_set(setname, setvalue) + core.settings:set(setname, setvalue) return true, setname .. " = " .. setvalue end local setname, setvalue = string.match(param, "([^ ]+) (.+)") if setname and setvalue then - if not core.setting_get(setname) then + if not core.settings:get(setname) then return false, "Failed. Use '/set -n ' to create a new setting." end - core.setting_set(setname, setvalue) + core.settings:set(setname, setvalue) return true, setname .. " = " .. setvalue end local setname = string.match(param, "([^ ]+)") if setname then - local setvalue = core.setting_get(setname) + local setvalue = core.settings:get(setname) if not setvalue then setvalue = "" end @@ -497,7 +469,7 @@ end core.register_chatcommand("emergeblocks", { params = "(here [radius]) | ( )", - description = "starts loading (or generating, if inexistent) map blocks " + description = "Load (or, if nonexistent, generate) map blocks " .. "contained in area pos1 to pos2", privs = {server=true}, func = function(name, param) @@ -523,7 +495,7 @@ core.register_chatcommand("emergeblocks", { core.register_chatcommand("deleteblocks", { params = "(here [radius]) | ( )", - description = "delete map blocks contained in area pos1 to pos2", + description = "Delete map blocks contained in area pos1 to pos2", privs = {server=true}, func = function(name, param) local p1, p2 = parse_range_str(name, param) @@ -540,6 +512,25 @@ core.register_chatcommand("deleteblocks", { end, }) +core.register_chatcommand("fixlight", { + params = "(here [radius]) | ( )", + description = "Resets lighting in the area between pos1 and pos2", + privs = {server = true}, + func = function(name, param) + local p1, p2 = parse_range_str(name, param) + if p1 == false then + return false, p2 + end + + if core.fix_light(p1, p2) then + return true, "Successfully reset light in the area ranging from " .. + core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1) + else + return false, "Failed to load one or more blocks in area" + end + end, +}) + core.register_chatcommand("mods", { params = "", description = "List mods installed on the server", @@ -587,7 +578,7 @@ end core.register_chatcommand("give", { params = " ", - description = "give item to player", + description = "Give item to player", privs = {give=true}, func = function(name, param) local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$") @@ -600,7 +591,7 @@ core.register_chatcommand("give", { core.register_chatcommand("giveme", { params = "", - description = "give item to yourself", + description = "Give item to yourself", privs = {give=true}, func = function(name, param) local itemstring = string.match(param, "(.+)$") @@ -671,12 +662,12 @@ end) core.register_chatcommand("rollback_check", { params = "[] [] [limit]", - description = "Check who has last touched a node or near it," - .. " max. ago (default range=0," - .. " seconds=86400=24h, limit=5)", + description = "Check who last touched a node or a node near it" + .. " within the time specified by . Default: range = 0," + .. " seconds = 86400 = 24h, limit = 5", privs = {rollback=true}, func = function(name, param) - if not core.setting_getbool("enable_rollback_recording") then + if not core.settings:get_bool("enable_rollback_recording") then return false, "Rollback functions are disabled." end local range, seconds, limit = @@ -724,10 +715,10 @@ core.register_chatcommand("rollback_check", { core.register_chatcommand("rollback", { params = " [] | : []", - description = "revert actions of a player; default for is 60", + description = "Revert actions of a player. Default for is 60", privs = {rollback=true}, func = function(name, param) - if not core.setting_getbool("enable_rollback_recording") then + if not core.settings:get_bool("enable_rollback_recording") then return false, "Rollback functions are disabled." end local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)") @@ -769,7 +760,7 @@ core.register_chatcommand("status", { core.register_chatcommand("time", { params = "<0..23>:<0..59> | <0..24000>", - description = "set time of day", + description = "Set time of day", privs = {}, func = function(name, param) if param == "" then @@ -815,12 +806,21 @@ core.register_chatcommand("days", { }) core.register_chatcommand("shutdown", { - description = "shutdown server", + description = "Shutdown server", + params = "[delay_in_seconds (non-negative number, or -1 to cancel)] [reconnect] [message]", privs = {server=true}, func = function(name, param) - core.log("action", name .. " shuts down server") - core.request_shutdown() - core.chat_send_all("*** Server shutting down (operator request).") + local delay, reconnect, message = param:match("([^ ][-]?[0-9]+)([^ ]+)(.*)") + message = message or "" + + if delay ~= "" then + delay = tonumber(param) or 0 + else + delay = 0 + core.log("action", name .. " shuts down server") + core.chat_send_all("*** Server shutting down (operator request).") + end + core.request_shutdown(message:trim(), core.is_yes(reconnect), delay) end, }) @@ -846,7 +846,7 @@ core.register_chatcommand("ban", { core.register_chatcommand("unban", { params = "", - description = "remove IP ban", + description = "Remove IP ban", privs = {ban=true}, func = function(name, param) if not core.unban_player_or_ip(param) then @@ -859,7 +859,7 @@ core.register_chatcommand("unban", { core.register_chatcommand("kick", { params = " [reason]", - description = "kick a player", + description = "Kick a player", privs = {kick=true}, func = function(name, param) local tokick, reason = param:match("([^ ]+) (.+)") @@ -878,7 +878,7 @@ core.register_chatcommand("kick", { core.register_chatcommand("clearobjects", { params = "[full|quick]", - description = "clear all objects in world", + description = "Clear all objects in world", privs = {server=true}, func = function(name, param) local options = {} @@ -938,3 +938,31 @@ core.register_chatcommand("last-login", { return false, "Last login time is unknown" end, }) + +core.register_chatcommand("clearinv", { + params = "[name]", + description = "Clear the inventory of yourself or another player", + func = function(name, param) + local player + if param and param ~= "" and param ~= name then + if not core.check_player_privs(name, {server=true}) then + return false, "You don't have permission" + .. " to run this command (missing privilege: server)" + end + player = core.get_player_by_name(param) + core.chat_send_player(param, name.." cleared your inventory.") + else + player = core.get_player_by_name(name) + end + + if player then + player:get_inventory():set_list("main", {}) + player:get_inventory():set_list("craft", {}) + player:get_inventory():set_list("craftpreview", {}) + core.log("action", name.." clears "..player:get_player_name().."'s inventory") + return true, "Cleared "..player:get_player_name().."'s inventory." + else + return false, "Player must be online to clear inventory!" + end + end, +}) diff --git a/builtin/game/deprecated.lua b/builtin/game/deprecated.lua index cd1cf5e2d..1a9a96f2a 100644 --- a/builtin/game/deprecated.lua +++ b/builtin/game/deprecated.lua @@ -49,3 +49,24 @@ setmetatable(core.env, { function core.rollback_get_last_node_actor(pos, range, seconds) return core.rollback_get_node_actions(pos, range, seconds, 1)[1] end + +-- +-- core.setting_* +-- + +local settings = core.settings + +local function setting_proxy(name) + return function(...) + core.log("deprecated", "WARNING: minetest.setting_* ".. + "functions are deprecated. ".. + "Use methods on the minetest.settings object.") + return settings[name](settings, ...) + end +end + +core.setting_set = setting_proxy("set") +core.setting_get = setting_proxy("get") +core.setting_setbool = setting_proxy("set_bool") +core.setting_getbool = setting_proxy("get_bool") +core.setting_save = setting_proxy("write") diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 4696ce481..b1beb1ab0 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -18,9 +18,11 @@ core.register_entity(":__builtin:falling_node", { }, node = {}, + meta = {}, - set_node = function(self, node) + set_node = function(self, node, meta) self.node = node + self.meta = meta or {} self.object:set_properties({ is_visible = true, textures = {node.name}, @@ -28,15 +30,21 @@ core.register_entity(":__builtin:falling_node", { end, get_staticdata = function(self) - return core.serialize(self.node) + local ds = { + node = self.node, + meta = self.meta, + } + return core.serialize(ds) end, on_activate = function(self, staticdata) self.object:set_armor_groups({immortal = 1}) - local node = core.deserialize(staticdata) - if node then - self:set_node(node) + local ds = core.deserialize(staticdata) + if ds and ds.node then + self:set_node(ds.node, ds.meta) + elseif ds then + self:set_node(ds) elseif staticdata ~= "" then self:set_node({name = staticdata}) end @@ -83,7 +91,7 @@ core.register_entity(":__builtin:falling_node", { -- it's drops if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then core.remove_node(np) - if nd.buildable_to == false then + if nd and nd.buildable_to == false then -- Add dropped items local drops = core.get_node_drops(n2.name, "") for _, dropped_item in pairs(drops) do @@ -98,6 +106,10 @@ core.register_entity(":__builtin:falling_node", { -- Create node and remove entity if core.registered_nodes[self.node.name] then core.add_node(np, self.node) + if self.meta then + local meta = core.get_meta(np) + meta:from_table(self.meta) + end end self.object:remove() core.check_for_falling(np) @@ -111,13 +123,27 @@ core.register_entity(":__builtin:falling_node", { end }) -local function spawn_falling_node(p, node) +local function spawn_falling_node(p, node, meta) local obj = core.add_entity(p, "__builtin:falling_node") if obj then - obj:get_luaentity():set_node(node) + obj:get_luaentity():set_node(node, meta) end end +function core.spawn_falling_node(pos) + local node = core.get_node(pos) + if node.name == "air" or node.name == "ignore" then + return false + end + local obj = core.add_entity(pos, "__builtin:falling_node") + if obj then + obj:get_luaentity():set_node(node) + core.remove_node(pos) + return true + end + return false +end + local function drop_attached_node(p) local nn = core.get_node(p).name core.remove_node(p) @@ -134,7 +160,8 @@ end 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 + if def.paramtype2 == "wallmounted" or + def.paramtype2 == "colorwallmounted" then -- 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. @@ -174,8 +201,13 @@ function core.check_single_for_falling(p) (not d_bottom.walkable or d_bottom.buildable_to) then n.level = core.get_node_level(p) + local meta = core.get_meta(p) + local metatable = {} + if meta ~= nil then + metatable = meta:to_table() + end core.remove_node(p) - spawn_falling_node(p, n) + spawn_falling_node(p, n, metatable) return true end end diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 2aad458da..ef85fbbc3 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -9,6 +9,8 @@ core.features = { no_legacy_abms = true, texture_names_parens = true, area_store_custom_ids = true, + add_entity_with_staticdata = true, + no_chat_message_prediction = true, } function core.has_feature(arg) diff --git a/builtin/game/forceloading.lua b/builtin/game/forceloading.lua index 8a05de36c..7c5537e85 100644 --- a/builtin/game/forceloading.lua +++ b/builtin/game/forceloading.lua @@ -40,7 +40,7 @@ function core.forceload_block(pos, transient) elseif other_table[hash] ~= nil then relevant_table[hash] = 1 else - if total_forceloaded >= (tonumber(core.setting_get("max_forceloaded_blocks")) or 16) then + if total_forceloaded >= (tonumber(core.settings:get("max_forceloaded_blocks")) or 16) then return false end total_forceloaded = total_forceloaded+1 diff --git a/builtin/game/init.lua b/builtin/game/init.lua index b5e2f7cca..e2635f07a 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -13,15 +13,17 @@ dofile(gamepath.."constants.lua") assert(loadfile(gamepath.."item.lua"))(builtin_shared) dofile(gamepath.."register.lua") -if core.setting_getbool("profiler.load") then +if core.settings:get_bool("profiler.load") then profiler = dofile(scriptpath.."profiler"..DIR_DELIM.."init.lua") end +dofile(commonpath .. "after.lua") dofile(gamepath.."item_entity.lua") dofile(gamepath.."deprecated.lua") dofile(gamepath.."misc.lua") dofile(gamepath.."privileges.lua") dofile(gamepath.."auth.lua") +dofile(commonpath .. "chatcommands.lua") dofile(gamepath.."chatcommands.lua") dofile(gamepath.."static_spawn.lua") dofile(gamepath.."detached_inventory.lua") diff --git a/builtin/game/item.lua b/builtin/game/item.lua index bf456a4e0..e36745f93 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -109,7 +109,7 @@ local facedir_to_dir_map = { 1, 4, 3, 2, } function core.facedir_to_dir(facedir) - return facedir_to_dir[facedir_to_dir_map[facedir]] + return facedir_to_dir[facedir_to_dir_map[facedir % 32]] end function core.dir_to_wallmounted(dir) @@ -144,11 +144,20 @@ local wallmounted_to_dir = { {x = 0, y = 0, z = -1}, } function core.wallmounted_to_dir(wallmounted) - return wallmounted_to_dir[wallmounted] + return wallmounted_to_dir[wallmounted % 8] +end + +function core.dir_to_yaw(dir) + return -math.atan2(dir.x, dir.z) +end + +function core.yaw_to_dir(yaw) + return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)} end function core.get_node_drops(nodename, toolname) - local drop = ItemStack({name=nodename}):get_definition().drop + local def = core.registered_nodes[nodename] + local drop = def and def.drop if drop == nil then -- default drop return {nodename} @@ -197,7 +206,6 @@ function core.get_node_drops(nodename, toolname) end function core.item_place_node(itemstack, placer, pointed_thing, param2) - local item = itemstack:peek_item() local def = itemstack:get_definition() if def.type ~= "node" or pointed_thing.type ~= "node" then return itemstack, false @@ -207,20 +215,21 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) local oldnode_under = core.get_node_or_nil(under) local above = pointed_thing.above local oldnode_above = core.get_node_or_nil(above) + local playername = placer:get_player_name() if not oldnode_under or not oldnode_above then - core.log("info", placer:get_player_name() .. " tried to place" + core.log("info", playername .. " tried to place" .. " node in unloaded position " .. core.pos_to_string(above)) return itemstack, false end - local olddef_under = ItemStack({name=oldnode_under.name}):get_definition() + local olddef_under = core.registered_nodes[oldnode_under.name] olddef_under = olddef_under or core.nodedef_default - local olddef_above = ItemStack({name=oldnode_above.name}):get_definition() + local olddef_above = core.registered_nodes[oldnode_above.name] olddef_above = olddef_above or core.nodedef_default if not olddef_above.buildable_to and not olddef_under.buildable_to then - core.log("info", placer:get_player_name() .. " tried to place" + core.log("info", playername .. " tried to place" .. " node in invalid position " .. core.pos_to_string(above) .. ", replacing " .. oldnode_above.name) return itemstack, false @@ -235,17 +244,17 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) place_to = {x = under.x, y = under.y, z = under.z} end - if core.is_protected(place_to, placer:get_player_name()) and + if core.is_protected(place_to, playername) and not minetest.check_player_privs(placer, "protection_bypass") then - core.log("action", placer:get_player_name() + core.log("action", playername .. " tried to place " .. def.name .. " at protected position " .. core.pos_to_string(place_to)) - core.record_protection_violation(place_to, placer:get_player_name()) + core.record_protection_violation(place_to, playername) return itemstack end - core.log("action", placer:get_player_name() .. " places node " + core.log("action", playername .. " places node " .. def.name .. " at " .. core.pos_to_string(place_to)) local oldnode = core.get_node(place_to) @@ -254,7 +263,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) -- Calculate direction for wall mounted stuff like torches and signs if def.place_param2 ~= nil then newnode.param2 = def.place_param2 - elseif def.paramtype2 == 'wallmounted' and not param2 then + elseif (def.paramtype2 == "wallmounted" or + def.paramtype2 == "colorwallmounted") and not param2 then local dir = { x = under.x - above.x, y = under.y - above.y, @@ -262,7 +272,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) } newnode.param2 = core.dir_to_wallmounted(dir) -- Calculate the direction for furnaces and chests and stuff - elseif def.paramtype2 == 'facedir' and not param2 then + elseif (def.paramtype2 == "facedir" or + def.paramtype2 == "colorfacedir") and not param2 then local placer_pos = placer:getpos() if placer_pos then local dir = { @@ -300,7 +311,6 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) end -- Run script hook - local _, callback for _, callback in ipairs(core.registered_on_placenodes) do -- Deepcopy pos, node and pointed_thing because callback can modify them local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} @@ -441,8 +451,9 @@ function core.handle_node_drops(pos, drops, digger) end function core.node_dig(pos, node, digger) - local def = ItemStack({name=node.name}):get_definition() - if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then + local def = core.registered_nodes[node.name] + if def and (not def.diggable or + (def.can_dig and not def.can_dig(pos, digger))) then core.log("info", digger:get_player_name() .. " tried to dig " .. node.name .. " which is not diggable " .. core.pos_to_string(pos)) @@ -467,12 +478,12 @@ function core.node_dig(pos, node, digger) local wdef = wielded:get_definition() local tp = wielded:get_tool_capabilities() - local dp = core.get_dig_params(def.groups, tp) + local dp = core.get_dig_params(def and def.groups, tp) if wdef and wdef.after_use then wielded = wdef.after_use(wielded, digger, node, dp) or wielded else -- Wear out tool - if not core.setting_getbool("creative_mode") then + if not core.settings:get_bool("creative_mode") then wielded:add_wear(dp.wear) if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then core.sound_play(wdef.sound.breaks, {pos = pos, gain = 0.5}) @@ -485,7 +496,7 @@ function core.node_dig(pos, node, digger) core.handle_node_drops(pos, drops, digger) local oldmetadata = nil - if def.after_dig_node then + if def and def.after_dig_node then oldmetadata = core.get_meta(pos):to_table() end @@ -493,7 +504,7 @@ function core.node_dig(pos, node, digger) core.remove_node(pos) -- Run callback - if def.after_dig_node then + if def and def.after_dig_node then -- Copy pos and node because callback can modify them local pos_copy = {x=pos.x, y=pos.y, z=pos.z} local node_copy = {name=node.name, param1=node.param1, param2=node.param2} diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index be158c119..c0e36be2d 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -14,7 +14,7 @@ end -- If item_entity_ttl is not set, enity will have default life time -- Setting it to -1 disables the feature -local time_to_live = tonumber(core.setting_get("item_entity_ttl")) +local time_to_live = tonumber(core.settings:get("item_entity_ttl")) if not time_to_live then time_to_live = 900 end @@ -53,6 +53,8 @@ core.register_entity(":__builtin:item", { if itemtable then itemname = stack:to_table().name end + -- Backwards compatibility: old clients use the texture + -- to get the type of the item local item_texture = nil local item_type = "" if core.registered_items[itemname] then @@ -66,6 +68,7 @@ core.register_entity(":__builtin:item", { visual_size = {x = s, y = s}, collisionbox = {-c, -c, -c, c, c, c}, automatic_rotate = math.pi * 0.5, + wield_item = itemstring, } self.object:set_properties(prop) end, @@ -101,31 +104,39 @@ core.register_entity(":__builtin:item", { self:set_item(self.itemstring) end, + -- moves items from this stack to an other stack try_merge_with = function(self, own_stack, object, obj) + -- other item's stack local stack = ItemStack(obj.itemstring) - if own_stack:get_name() == stack:get_name() and stack:get_free_space() > 0 then + -- only merge if items are the same + if own_stack:get_name() == stack:get_name() and + own_stack:get_meta() == stack:get_meta() and + own_stack:get_wear() == stack:get_wear() and + stack:get_free_space() > 0 then local overflow = false local count = stack:get_count() + own_stack:get_count() local max_count = stack:get_stack_max() if count > max_count then overflow = true + stack:set_count(max_count) count = count - max_count + own_stack:set_count(count) else self.itemstring = '' + stack:set_count(count) end local pos = object:getpos() pos.y = pos.y + (count - stack:get_count()) / max_count * 0.15 object:moveto(pos, false) local s, c - local max_count = stack:get_stack_max() - local name = stack:get_name() if not overflow then - obj.itemstring = name .. " " .. count + obj.itemstring = stack:to_string() s = 0.2 + 0.1 * (count / max_count) c = s object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = obj.itemstring }) self.object:remove() -- merging succeeded @@ -133,18 +144,20 @@ core.register_entity(":__builtin:item", { else s = 0.4 c = 0.3 + obj.itemstring = stack:to_string() object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = obj.itemstring }) - obj.itemstring = name .. " " .. max_count s = 0.2 + 0.1 * (count / max_count) c = s + self.itemstring = own_stack:to_string() self.object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = self.itemstring }) - self.itemstring = name .. " " .. count end end -- merging didn't succeed diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index 7caa9e7ba..bfe407b9d 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -4,50 +4,6 @@ -- Misc. API functions -- -local jobs = {} -local time = 0.0 -local last = core.get_us_time() / 1000000 - -core.register_globalstep(function(dtime) - local new = core.get_us_time() / 1000000 - if new > last then - time = time + (new - last) - else - -- Overflow, we may lose a little bit of time here but - -- only 1 tick max, potentially running timers slightly - -- too early. - time = time + new - end - last = new - - if #jobs < 1 then - return - end - - -- Iterate backwards so that we miss any new timers added by - -- a timer callback, and so that we don't skip the next timer - -- in the list if we remove one. - for i = #jobs, 1, -1 do - local job = jobs[i] - if time >= job.expire then - core.set_last_run_mod(job.mod_origin) - job.func(unpack(job.arg)) - table.remove(jobs, i) - end - end -end) - -function core.after(after, func, ...) - assert(tonumber(after) and type(func) == "function", - "Invalid core.after invocation") - jobs[#jobs + 1] = { - func = func, - expire = time + after, - arg = {...}, - mod_origin = core.get_last_run_mod() - } -end - function core.check_player_privs(name, ...) local arg_type = type(name) if (arg_type == "userdata" or arg_type == "table") and @@ -56,11 +12,11 @@ function core.check_player_privs(name, ...) elseif arg_type ~= "string" then error("Invalid core.check_player_privs argument type: " .. arg_type, 2) end - + local requested_privs = {...} local player_privs = core.get_player_privs(name) local missing_privileges = {} - + if type(requested_privs[1]) == "table" then -- We were provided with a table like { privA = true, privB = true }. for priv, value in pairs(requested_privs[1]) do @@ -76,11 +32,11 @@ function core.check_player_privs(name, ...) end end end - + if #missing_privileges > 0 then return false, missing_privileges end - + return true, "" end @@ -114,6 +70,10 @@ function core.get_connected_players() return temp_table end +function minetest.player_exists(name) + return minetest.get_auth_handler().get_auth(name) ~= nil +end + -- Returns two position vectors representing a box of `radius` in each -- direction centered around the player corresponding to `player_name` function core.get_player_radius_area(player_name, radius) @@ -161,7 +121,7 @@ function core.get_node_group(name, group) end function core.setting_get_pos(name) - local value = core.setting_get(name) + local value = core.settings:get(name) if not value then return nil end @@ -210,38 +170,11 @@ 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 +function core.cancel_shutdown_requests() + core.request_shutdown("", false, -1) +end + diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 90f095e9f..ec6f28097 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -331,8 +331,8 @@ core.register_item(":unknown", { core.register_node(":air", { description = "Air (you hacker you!)", - inventory_image = "unknown_node.png", - wield_image = "unknown_node.png", + inventory_image = "air.png", + wield_image = "air.png", drawtype = "airlike", paramtype = "light", sunlight_propagates = true, @@ -348,8 +348,8 @@ core.register_node(":air", { core.register_node(":ignore", { description = "Ignore (you hacker you!)", - inventory_image = "unknown_node.png", - wield_image = "unknown_node.png", + inventory_image = "ignore.png", + wield_image = "ignore.png", drawtype = "airlike", paramtype = "none", sunlight_propagates = false, diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 61a8b9077..6aa106140 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -1,3 +1,5 @@ +-- cache setting +local enable_damage = core.settings:get_bool("enable_damage") local health_bar_definition = { @@ -42,9 +44,8 @@ local function initialize_builtin_statbars(player) player:hud_set_flags(player:hud_get_flags()) end - if player:hud_get_flags().healthbar and - core.is_yes(core.setting_get("enable_damage")) then - if hud_ids[name].id_healthbar == nil then + if player:hud_get_flags().healthbar and enable_damage then + if hud_ids[name].id_healthbar == nil then health_bar_definition.number = player:get_hp() hud_ids[name].id_healthbar = player:hud_add(health_bar_definition) end @@ -56,8 +57,7 @@ local function initialize_builtin_statbars(player) end if (player:get_breath() < 11) then - if player:hud_get_flags().breathbar and - core.is_yes(core.setting_get("enable_damage")) then + if player:hud_get_flags().breathbar and enable_damage then if hud_ids[name].id_breathbar == nil then hud_ids[name].id_breathbar = player:hud_add(breath_bar_definition) end diff --git a/builtin/game/static_spawn.lua b/builtin/game/static_spawn.lua index 100334226..b1157b42e 100644 --- a/builtin/game/static_spawn.lua +++ b/builtin/game/static_spawn.lua @@ -1,10 +1,10 @@ -- Minetest: builtin/static_spawn.lua local function warn_invalid_static_spawnpoint() - if core.setting_get("static_spawnpoint") and + if core.settings:get("static_spawnpoint") and not core.setting_get_pos("static_spawnpoint") then core.log("error", "The static_spawnpoint setting is invalid: \"".. - core.setting_get("static_spawnpoint").."\"") + core.settings:get("static_spawnpoint").."\"") end end diff --git a/builtin/init.lua b/builtin/init.lua index 4400a19d6..356e119fb 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -27,6 +27,7 @@ minetest = core -- Load other files local scriptdir = core.get_builtin_path() .. DIR_DELIM local gamepath = scriptdir .. "game" .. DIR_DELIM +local clientpath = scriptdir .. "client" .. DIR_DELIM local commonpath = scriptdir .. "common" .. DIR_DELIM local asyncpath = scriptdir .. "async" .. DIR_DELIM @@ -37,15 +38,17 @@ dofile(commonpath .. "misc_helpers.lua") if INIT == "game" then dofile(gamepath .. "init.lua") elseif INIT == "mainmenu" then - local mainmenuscript = core.setting_get("main_menu_script") - if mainmenuscript ~= nil and mainmenuscript ~= "" then - dofile(mainmenuscript) + local mm_script = core.settings:get("main_menu_script") + if mm_script and mm_script ~= "" then + dofile(mm_script) else dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua") end elseif INIT == "async" then dofile(asyncpath .. "init.lua") +elseif INIT == "client" then + os.setlocale = nil + dofile(clientpath .. "init.lua") else error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT))) end - diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index da3667828..fa7ae583b 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -43,10 +43,10 @@ end local function configure_selected_world_params(idx) local worldconfig = modmgr.get_worldconfig(menudata.worldlist:get_list()[idx].path) if worldconfig.creative_mode then - core.setting_set("creative_mode", worldconfig.creative_mode) + core.settings:set("creative_mode", worldconfig.creative_mode) end if worldconfig.enable_damage then - core.setting_set("enable_damage", worldconfig.enable_damage) + core.settings:set("enable_damage", worldconfig.enable_damage) end end @@ -54,7 +54,12 @@ end function image_column(tooltip, flagname) return "image,tooltip=" .. core.formspec_escape(tooltip) .. "," .. "0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. "," .. - "1=" .. core.formspec_escape(defaulttexturedir .. "server_flags_" .. flagname .. ".png") + "1=" .. core.formspec_escape(defaulttexturedir .. + (flagname and "server_flags_" .. flagname .. ".png" or "blank.png")) .. "," .. + "2=" .. core.formspec_escape(defaulttexturedir .. "server_ping_4.png") .. "," .. + "3=" .. core.formspec_escape(defaulttexturedir .. "server_ping_3.png") .. "," .. + "4=" .. core.formspec_escape(defaulttexturedir .. "server_ping_2.png") .. "," .. + "5=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png") end -------------------------------------------------------------------------------- @@ -77,7 +82,7 @@ function order_favorite_list(list) end -------------------------------------------------------------------------------- -function render_favorite(spec, is_favorite) +function render_serverlist_row(spec, is_favorite) local text = "" if spec.name then text = text .. core.formspec_escape(spec.name:trim()) @@ -97,6 +102,21 @@ function render_favorite(spec, is_favorite) details = "0," end + if spec.ping then + local ping = spec.ping * 1000 + if ping <= 50 then + details = details .. "2," + elseif ping <= 100 then + details = details .. "3," + elseif ping <= 250 then + details = details .. "4," + else + details = details .. "5," + end + else + details = details .. "0," + end + if spec.clients and spec.clients_max then local clients_color = '' local clients_percent = 100 * spec.clients / spec.clients_max @@ -144,8 +164,8 @@ end -------------------------------------------------------------------------------- os.tempfolder = function() - if core.setting_get("TMPFolder") then - return core.setting_get("TMPFolder") .. DIR_DELIM .. "MT_" .. math.random(0,10000) + if core.settings:get("TMPFolder") then + return core.settings:get("TMPFolder") .. DIR_DELIM .. "MT_" .. math.random(0,10000) end local filetocheck = os.tmpname() @@ -186,7 +206,7 @@ function menu_handle_key_up_down(fields, textlist, settingname) oldidx < menudata.worldlist:size() then newidx = oldidx + 1 end - core.setting_set(settingname, menudata.worldlist:get_raw_index(newidx)) + core.settings:set(settingname, menudata.worldlist:get_raw_index(newidx)) configure_selected_world_params(newidx) return true end @@ -230,7 +250,7 @@ end -------------------------------------------------------------------------------- function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency) - local textlines = core.splittext(text, textlen) + local textlines = core.wrap_text(text, textlen) local retval = "textlist[" .. xpos .. "," .. ypos .. ";" .. width .. "," .. height .. ";" .. tl_name .. ";" @@ -308,9 +328,9 @@ function menu_worldmt_legacy(selected) for _, mode_name in pairs(modes_names) do local mode_val = menu_worldmt(selected, mode_name) if mode_val then - core.setting_set(mode_name, mode_val) + core.settings:set(mode_name, mode_val) else - menu_worldmt(selected, mode_name, core.setting_get(mode_name)) + menu_worldmt(selected, mode_name, core.settings:get(mode_name)) end end end diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua index 7b3ab9852..fcedadea8 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/dlg_config_world.lua @@ -17,14 +17,13 @@ -------------------------------------------------------------------------------- -local enabled_all = false +local enabled_all = false local function modname_valid(name) return not name:find("[^a-z0-9_]") end local function get_formspec(data) - local mod = data.list:get_list()[data.selected_mod] local retval = @@ -32,24 +31,12 @@ local function get_formspec(data) "label[0.5,0;" .. fgettext("World:") .. "]" .. "label[1.75,0;" .. data.worldspec.name .. "]" - if data.hide_gamemods then - retval = retval .. "checkbox[1,6;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";true]" - else - retval = retval .. "checkbox[1,6;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";false]" - end - - if data.hide_modpackcontents then - retval = retval .. "checkbox[6,6;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";true]" - else - retval = retval .. "checkbox[6,6;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";false]" - end - 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 .. "]" .. @@ -62,41 +49,45 @@ local function get_formspec(data) "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") .. "]" - if mod ~= nil and mod.name ~= "" and mod.typ ~= "game_mod" then + if mod and mod.name ~= "" and not mod.is_game_content then if mod.is_modpack then local rawlist = data.list:get_raw_list() local all_enabled = true - for j=1,#rawlist,1 do - if rawlist[j].modpack == mod.name and - rawlist[j].enabled ~= true then - all_enabled = false - break + for j = 1, #rawlist, 1 do + if rawlist[j].modpack == mod.name and not rawlist[j].enabled then + all_enabled = false + break end end - if all_enabled == false then - retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_enable;" .. fgettext("Enable MP") .. "]" + if all_enabled then + retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_disable;" .. + fgettext("Disable MP") .. "]" else - retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_disable;" .. fgettext("Disable MP") .. "]" + retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_enable;" .. + fgettext("Enable MP") .. "]" end else if mod.enabled then - retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" .. fgettext("enabled") .. ";true]" + retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" .. + fgettext("enabled") .. ";true]" else - retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" .. fgettext("enabled") .. ";false]" + retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" .. + fgettext("enabled") .. ";false]" end end end - if enabled_all then + 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;" + "button[8.75,0.125;2.5,0.5;btn_disable_all_mods;" .. fgettext("Disable all") .. "]" 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;" + "button[8.75,0.125;2.5,0.5;btn_enable_all_mods;" .. fgettext("Enable all") .. "]" end + retval = retval .. + "tablecolumns[color;tree;text]" .. + "table[5.5,0.75;5.75,6;world_config_modlist;" retval = retval .. modmgr.render_modlist(data.list) retval = retval .. ";" .. data.selected_mod .."]" @@ -106,7 +97,7 @@ end local function enable_mod(this, toset) local mod = this.data.list:get_list()[this.data.selected_mod] - if mod.typ == "game_mod" then + if mod.is_game_content then -- game mods can't be enabled or disabled elseif not mod.is_modpack then if toset == nil then @@ -129,16 +120,15 @@ end local function handle_buttons(this, fields) - if fields["world_config_modlist"] ~= nil then - local event = core.explode_textlist_event(fields["world_config_modlist"]) - this.data.selected_mod = event.index - core.setting_set("world_config_selected_mod", event.index) + local event = core.explode_table_event(fields["world_config_modlist"]) + this.data.selected_mod = event.row + core.settings:set("world_config_selected_mod", event.row) if event.type == "DCL" then enable_mod(this) end - + return true end @@ -160,44 +150,7 @@ local function handle_buttons(this, fields) return true end - if fields["cb_hide_gamemods"] ~= nil or - fields["cb_hide_mpcontent"] ~= nil then - local current = this.data.list:get_filtercriteria() - - if current == nil then - current = {} - end - - if fields["cb_hide_gamemods"] ~= nil then - if core.is_yes(fields["cb_hide_gamemods"]) then - current.hide_game = true - this.data.hide_gamemods = true - core.setting_set("world_config_hide_gamemods", "true") - else - current.hide_game = false - this.data.hide_gamemods = false - core.setting_set("world_config_hide_gamemods", "false") - end - end - - if fields["cb_hide_mpcontent"] ~= nil then - if core.is_yes(fields["cb_hide_mpcontent"]) then - current.hide_modpackcontents = true - this.data.hide_modpackcontents = true - core.setting_set("world_config_hide_modpackcontents", "true") - else - current.hide_modpackcontents = false - this.data.hide_modpackcontents = false - core.setting_set("world_config_hide_modpackcontents", "false") - end - end - - this.data.list:set_filtercriteria(current) - return true - end - if fields["btn_config_world_save"] then - local filename = this.data.worldspec.path .. DIR_DELIM .. "world.mt" @@ -209,7 +162,7 @@ local function handle_buttons(this, fields) local i,mod for i,mod in ipairs(rawlist) do if not mod.is_modpack and - mod.typ ~= "game_mod" then + not mod.is_game_content then if modname_valid(mod.name) then worldfile:set("load_mod_"..mod.name, tostring(mod.enabled)) else @@ -231,7 +184,7 @@ local function handle_buttons(this, fields) if not worldfile:write() then core.log("error", "Failed to write world config file") end - + this:delete() return true end @@ -245,19 +198,21 @@ local function handle_buttons(this, fields) 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 + if not list[i].is_game_content + 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 + if not list[i].is_game_content + and not list[i].is_modpack then list[i].enabled = false end end @@ -269,15 +224,12 @@ local function handle_buttons(this, fields) end function create_configure_world_dlg(worldidx) - local dlg = dialog_create("sp_config_world", get_formspec, handle_buttons, nil) - dlg.data.hide_gamemods = core.setting_getbool("world_config_hide_gamemods") - dlg.data.hide_modpackcontents = core.setting_getbool("world_config_hide_modpackcontents") - dlg.data.selected_mod = tonumber(core.setting_get("world_config_selected_mod")) + dlg.data.selected_mod = tonumber(core.settings:get("world_config_selected_mod")) if dlg.data.selected_mod == nil then dlg.data.selected_mod = 0 end @@ -286,14 +238,14 @@ function create_configure_world_dlg(worldidx) if dlg.data.worldspec == nil then dlg:delete() return nil end dlg.data.worldconfig = modmgr.get_worldconfig(dlg.data.worldspec.path) - + if dlg.data.worldconfig == nil or dlg.data.worldconfig.id == nil or dlg.data.worldconfig.id == "" then dlg:delete() return nil end - + dlg.data.list = filterlist.create( modmgr.preparemodlist, --refresh modmgr.comparemod, --compare @@ -302,16 +254,16 @@ function create_configure_world_dlg(worldidx) return true end end, - function(element,criteria) + function(element, criteria) if criteria.hide_game and - element.typ == "game_mod" then - return false + element.is_game_content then + return false end if criteria.hide_modpackcontents and - element.modpack ~= nil then - return false - end + element.modpack ~= nil then + return false + end return true end, --filter { worldpath= dlg.data.worldspec.path, diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua index 477b8bcb9..e9ca7799f 100644 --- a/builtin/mainmenu/dlg_create_world.lua +++ b/builtin/mainmenu/dlg_create_world.lua @@ -18,8 +18,8 @@ local function create_world_formspec(dialogdata) local mapgens = core.get_mapgen_names() - local current_seed = core.setting_get("fixed_map_seed") or "" - local current_mg = core.setting_get("mg_name") + local current_seed = core.settings:get("fixed_map_seed") or "" + local current_mg = core.settings:get("mg_name") local mglist = "" local selindex = 1 @@ -33,7 +33,7 @@ local function create_world_formspec(dialogdata) end mglist = mglist:sub(1, -2) - local gameid = core.setting_get("menu_last_game") + local gameid = core.settings:get("menu_last_game") local game, gameidx = nil , 0 if gameid ~= nil then @@ -90,10 +90,10 @@ local function create_world_buttonhandler(this, fields) local message = nil - core.setting_set("fixed_map_seed", fields["te_seed"]) + core.settings:set("fixed_map_seed", fields["te_seed"]) if not menudata.worldlist:uid_exists_raw(worldname) then - core.setting_set("mg_name",fields["dd_mapgen"]) + core.settings:set("mg_name",fields["dd_mapgen"]) message = core.create_world(worldname,gameindex) else message = fgettext("A world named \"$1\" already exists", worldname) @@ -102,13 +102,13 @@ local function create_world_buttonhandler(this, fields) if message ~= nil then gamedata.errormessage = message else - core.setting_set("menu_last_game",gamemgr.games[gameindex].id) + core.settings:set("menu_last_game",gamemgr.games[gameindex].id) if this.data.update_worldlist_filter then menudata.worldlist:set_filtercriteria(gamemgr.games[gameindex].id) mm_texture.update("singleplayer", gamemgr.games[gameindex].id) end menudata.worldlist:refresh() - core.setting_set("mainmenu_last_selected_world", + core.settings:set("mainmenu_last_selected_world", menudata.worldlist:raw_index_by_uid(worldname)) end else diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua index b0d923768..206ce1620 100644 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@ -344,11 +344,86 @@ local function parse_config_file(read_all, parse_mods) return settings end -local settings = parse_config_file(false, true) +local function filter_settings(settings, searchstring) + if not searchstring or searchstring == "" then + return settings, -1 + end + + -- Setup the keyword list + local keywords = {} + for word in searchstring:lower():gmatch("%S+") do + table.insert(keywords, word) + end + + local result = {} + local category_stack = {} + local current_level = 0 + local best_setting = nil + for _, entry in pairs(settings) do + if entry.type == "category" then + -- Remove all settingless categories + while #category_stack > 0 and entry.level <= current_level do + table.remove(category_stack, #category_stack) + if #category_stack > 0 then + current_level = category_stack[#category_stack].level + else + current_level = 0 + end + end + + -- Push category onto stack + category_stack[#category_stack + 1] = entry + current_level = entry.level + else + -- See if setting matches keywords + local setting_score = 0 + for k = 1, #keywords do + local keyword = keywords[k] + + if string.find(entry.name:lower(), keyword, 1, true) then + setting_score = setting_score + 1 + end + + if entry.readable_name and + string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then + setting_score = setting_score + 1 + end + + if entry.comment and + string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then + setting_score = setting_score + 1 + end + end + + -- Add setting to results if match + if setting_score > 0 then + -- Add parent categories + for _, category in pairs(category_stack) do + result[#result + 1] = category + end + category_stack = {} + + -- Add setting + result[#result + 1] = entry + entry.score = setting_score + + if not best_setting or + setting_score > result[best_setting].score then + best_setting = #result + end + end + end + end + return result, best_setting or -1 +end + +local full_settings = parse_config_file(false, true) +local search_string = "" +local settings = full_settings local selected_setting = 1 local function get_current_value(setting) - local value = core.setting_get(setting.name) + local value = core.settings:get(setting.name) if value == nil then value = setting.default end @@ -464,11 +539,11 @@ local function handle_change_setting_buttons(this, fields) if setting.type == "bool" then local new_value = fields["dd_setting_value"] -- Note: new_value is the actual (translated) value shown in the dropdown - core.setting_setbool(setting.name, new_value == fgettext("Enabled")) + core.settings:set_bool(setting.name, new_value == fgettext("Enabled")) elseif setting.type == "enum" then local new_value = fields["dd_setting_value"] - core.setting_set(setting.name, new_value) + core.settings:set(setting.name, new_value) elseif setting.type == "int" then local new_value = tonumber(fields["te_setting_value"]) @@ -490,7 +565,7 @@ local function handle_change_setting_buttons(this, fields) core.update_formspec(this:get_formspec()) return true end - core.setting_set(setting.name, new_value) + core.settings:set(setting.name, new_value) elseif setting.type == "float" then local new_value = tonumber(fields["te_setting_value"]) @@ -500,7 +575,7 @@ local function handle_change_setting_buttons(this, fields) core.update_formspec(this:get_formspec()) return true end - core.setting_set(setting.name, new_value) + core.settings:set(setting.name, new_value) elseif setting.type == "flags" then local new_value = fields["te_setting_value"] @@ -514,13 +589,13 @@ local function handle_change_setting_buttons(this, fields) return true end end - core.setting_set(setting.name, new_value) + core.settings:set(setting.name, new_value) else local new_value = fields["te_setting_value"] - core.setting_set(setting.name, new_value) + core.settings:set(setting.name, new_value) end - core.setting_save() + core.settings:write() this:delete() return true end @@ -544,14 +619,17 @@ end local function create_settings_formspec(tabview, name, tabdata) local formspec = "size[12,6.5;true]" .. - "tablecolumns[color;tree;text;text]" .. + "tablecolumns[color;tree;text,width=32;text]" .. "tableoptions[background=#00000000;border=false]" .. - "table[0,0;12,5.5;list_settings;" + "field[0.3,0.1;10.2,1;search_string;;" .. core.formspec_escape(search_string) .. "]" .. + "field_close_on_enter[search_string;false]" .. + "button[10.2,-0.2;2,1;search;" .. fgettext("Search") .. "]" .. + "table[0,0.8;12,4.5;list_settings;" local current_level = 0 for _, entry in ipairs(settings) do local name - if not core.setting_getbool("main_menu_technical_settings") and entry.readable_name then + if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then name = fgettext_ne(entry.readable_name) else name = entry.name @@ -588,7 +666,7 @@ local function create_settings_formspec(tabview, name, tabdata) "button[10,6;2,1;btn_edit;" .. fgettext("Edit") .. "]" .. "button[7,6;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" .. "checkbox[0,5.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";" - .. dump(core.setting_getbool("main_menu_technical_settings")) .. "]" + .. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]" return formspec end @@ -597,13 +675,13 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) local list_enter = false if fields["list_settings"] then selected_setting = core.get_table_index("list_settings") - if core.explode_table_event(fields["list_settings"]).type == "DCL" then + if core.explode_table_event(fields["list_settings"]).type == "DCL" then -- Directly toggle booleans local setting = settings[selected_setting] - if setting.type == "bool" then + if setting and setting.type == "bool" then local current_value = get_current_value(setting) - core.setting_setbool(setting.name, not core.is_yes(current_value)) - core.setting_save() + core.settings:set_bool(setting.name, not core.is_yes(current_value)) + core.settings:write() return true else list_enter = true @@ -613,9 +691,39 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) end end + if fields.search or fields.key_enter_field == "search_string" then + if search_string == fields.search_string then + if selected_setting > 0 then + -- Go to next result on enter press + local i = selected_setting + 1 + local looped = false + while i > #settings or settings[i].type == "category" do + i = i + 1 + if i > #settings then + -- Stop infinte looping + if looped then + return false + end + i = 1 + looped = true + end + end + selected_setting = i + core.update_formspec(this:get_formspec()) + return true + end + else + -- Search for setting + search_string = fields.search_string + settings, selected_setting = filter_settings(full_settings, search_string) + core.update_formspec(this:get_formspec()) + end + return true + end + if fields["btn_edit"] or list_enter then local setting = settings[selected_setting] - if setting.type ~= "category" then + if setting and setting.type ~= "category" then local edit_dialog = dialog_create("change_setting", create_change_setting_formspec, handle_change_setting_buttons) edit_dialog:set_parent(this) @@ -627,9 +735,9 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) if fields["btn_restore"] then local setting = settings[selected_setting] - if setting.type ~= "category" then - core.setting_set(setting.name, setting.default) - core.setting_save() + if setting and setting.type ~= "category" then + core.settings:set(setting.name, setting.default) + core.settings:write() core.update_formspec(this:get_formspec()) end return true @@ -641,8 +749,8 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) end if fields["cb_tech_settings"] then - core.setting_set("main_menu_technical_settings", fields["cb_tech_settings"]) - core.setting_save() + core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"]) + core.settings:write() core.update_formspec(this:get_formspec()) return true end @@ -661,10 +769,4 @@ end -- Generate minetest.conf.example and settings_translation_file.cpp --- *** 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) - ---assert(loadfile(core.get_mainmenu_path()..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false)) +--assert(loadfile(core.get_builtin_path()..DIR_DELIM.."mainmenu"..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false)) diff --git a/builtin/mainmenu/gamemgr.lua b/builtin/mainmenu/gamemgr.lua index b6faa71d9..fd6025fcd 100644 --- a/builtin/mainmenu/gamemgr.lua +++ b/builtin/mainmenu/gamemgr.lua @@ -68,10 +68,10 @@ end function gamemgr.gamelist() local retval = "" if #gamemgr.games > 0 then - retval = retval .. gamemgr.games[1].name + retval = retval .. core.formspec_escape(gamemgr.games[1].name) for i=2,#gamemgr.games,1 do - retval = retval .. "," .. gamemgr.games[i].name + retval = retval .. "," .. core.formspec_escape(gamemgr.games[i].name) end end return retval diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 7f0c1e386..7c6af7d27 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -16,9 +16,9 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. mt_color_grey = "#AAAAAA" -mt_color_blue = "#0000DD" -mt_color_green = "#00DD00" -mt_color_dark_green = "#003300" +mt_color_blue = "#6389FF" +mt_color_green = "#72FF63" +mt_color_dark_green = "#25C191" --for all other colors ask sfan5 to complete his work! @@ -56,9 +56,8 @@ tabs.credits = dofile(menupath .. DIR_DELIM .. "tab_credits.lua") if PLATFORM == "Android" then tabs.simple_main = dofile(menupath .. DIR_DELIM .. "tab_simple_main.lua") else - tabs.singleplayer = dofile(menupath .. DIR_DELIM .. "tab_singleplayer.lua") - tabs.multiplayer = dofile(menupath .. DIR_DELIM .. "tab_multiplayer.lua") - tabs.server = dofile(menupath .. DIR_DELIM .. "tab_server.lua") + tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua") + tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua") tabs.texturepacks = dofile(menupath .. DIR_DELIM .. "tab_texturepacks.lua") end @@ -119,25 +118,24 @@ local function init_globals() menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic) menudata.worldlist:set_sortmode("alphabetic") - if not core.setting_get("menu_last_game") then - local default_game = core.setting_get("default_game") or "minetest" - core.setting_set("menu_last_game", default_game) + if not core.settings:get("menu_last_game") then + local default_game = core.settings:get("default_game") or "minetest" + core.settings:set("menu_last_game", default_game) end mm_texture.init() end -- Create main tabview - local tv_main = tabview_create("maintab", {x = 12, y = 5.2}, {x = 0, y = 0}) + local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0}) if PLATFORM == "Android" then tv_main:add(tabs.simple_main) tv_main:add(tabs.settings) else tv_main:set_autosave_tab(true) - tv_main:add(tabs.singleplayer) - tv_main:add(tabs.multiplayer) - tv_main:add(tabs.server) + tv_main:add(tabs.local_game) + tv_main:add(tabs.play_online) tv_main:add(tabs.settings) tv_main:add(tabs.texturepacks) end @@ -149,7 +147,7 @@ local function init_globals() tv_main:set_fixed_size(false) if PLATFORM ~= "Android" then - tv_main:set_tab(core.setting_get("maintab_LAST")) + tv_main:set_tab(core.settings:get("maintab_LAST")) end ui.set_default("maintab") tv_main:show() @@ -167,4 +165,3 @@ local function init_globals() end init_globals() - diff --git a/builtin/mainmenu/modmgr.lua b/builtin/mainmenu/modmgr.lua index 2b7b371bf..dee048982 100644 --- a/builtin/mainmenu/modmgr.lua +++ b/builtin/mainmenu/modmgr.lua @@ -18,7 +18,7 @@ -------------------------------------------------------------------------------- function get_mods(path,retval,modpack) local mods = core.get_dir_list(path, true) - + for _, name in ipairs(mods) do if name:sub(1, 1) ~= "." then local prefix = path .. DIR_DELIM .. name .. DIR_DELIM @@ -237,49 +237,37 @@ function modmgr.render_modlist(render_list) local list = render_list:get_list() local last_modpack = nil - - for i,v in ipairs(list) do - if retval ~= "" then - retval = retval .."," - end - + local retval = {} + for i, v in ipairs(list) do local color = "" - if v.is_modpack then local rawlist = render_list:get_raw_list() + color = mt_color_dark_green - local all_enabled = true - for j=1,#rawlist,1 do + for j = 1, #rawlist, 1 do if rawlist[j].modpack == list[i].name and - rawlist[j].enabled ~= true then - all_enabled = false - break + rawlist[j].enabled ~= true then + -- Modpack not entirely enabled so showing as grey + color = mt_color_grey + break end end - - if all_enabled == false then - color = mt_color_grey - else - color = mt_color_dark_green - end - end - - if v.typ == "game_mod" then + elseif v.is_game_content then color = mt_color_blue - else - if v.enabled then - color = mt_color_green - end + elseif v.enabled then + color = mt_color_green end - retval = retval .. color - if v.modpack ~= nil then - retval = retval .. " " + retval[#retval + 1] = color + if v.modpack ~= nil or v.typ == "game_mod" then + retval[#retval + 1] = "1" + else + retval[#retval + 1] = "0" end - retval = retval .. v.name + retval[#retval + 1] = core.formspec_escape(v.name) end - return retval + return table.concat(retval, ",") end -------------------------------------------------------------------------------- @@ -425,8 +413,18 @@ function modmgr.preparemodlist(data) local gamespec = gamemgr.find_by_gameid(data.gameid) gamemgr.get_game_mods(gamespec, game_mods) + if #game_mods > 0 then + -- Add title + retval[#retval + 1] = { + typ = "game", + is_game_content = true, + name = fgettext("Subgame Mods") + } + end + for i=1,#game_mods,1 do game_mods[i].typ = "game_mod" + game_mods[i].is_game_content = true retval[#retval + 1] = game_mods[i] end diff --git a/builtin/mainmenu/store.lua b/builtin/mainmenu/store.lua index ad861082d..59391f8bc 100644 --- a/builtin/mainmenu/store.lua +++ b/builtin/mainmenu/store.lua @@ -233,14 +233,14 @@ function modstore.handle_buttons(parent, fields, name, data) if not core.handle_async( function(param) - local fullurl = core.setting_get("modstore_download_url") .. + local fullurl = core.settings:get("modstore_download_url") .. param.moddetails.download_url if param.version ~= nil then local found = false for i=1,#param.moddetails.versions, 1 do if param.moddetails.versions[i].date:sub(1,10) == param.version then - fullurl = core.setting_get("modstore_download_url") .. + fullurl = core.settings:get("modstore_download_url") .. param.moddetails.versions[i].download_url found = true end @@ -400,7 +400,7 @@ function modstore.getscreenshot(ypos,listentry) listentry.texturename = "in progress" --prepare url and filename - local fullurl = core.setting_get("modstore_download_url") .. + local fullurl = core.settings:get("modstore_download_url") .. listentry.details.screenshot_url local filename = os.tempfolder() .. "_MID_" .. listentry.id diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua index c2ad19183..0774433b6 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_credits.lua @@ -19,61 +19,68 @@ local core_developers = { "Perttu Ahola (celeron55) ", - "Ryan Kwolek (kwolekr) ", - "PilzAdam ", "sfan5 ", - "kahrl ", - "sapier", "ShadowNinja ", "Nathanaël Courant (Nore/Ekdohibs) ", "Loic Blot (nerzhul/nrz) ", - "Matt Gregory (paramat)", - "est31 ", + "paramat", "Craig Robbins (Zeno) ", "Auke Kok (sofar) ", - "Andrew Ward (rubenwardy) ", + "rubenwardy ", + "Krock/SmallJoker ", } local active_contributors = { - "Duane Robertson ", - "SmallJoker ", - "Lars Hofhansl ", - "Jeija ", - "Gregory Currie (gregorycu)", - "Sokomine ", - "TeTpaAka", - "Jean-Patrick G (kilbith) ", - "Diego Martínez (kaeza) ", - "Dániel Juhász (juhdanad) ", - "Rogier ", + "red-001 [CSM & Menu fixes]", + "Dániel Juhász (juhdanad) [Audiovisuals: lighting]", + "numberZero [Audiovisuals: meshgen]", + "Lars Hofhansl [Occulusion culling, fixes]", + "Jean-Patrick G (kilbith) [Audiovisuals]", + "Vincent Glize (Dumbeldor) [CSM]", + "bigfoot547 [CSM]", + "Rogier [Fixes]", + "Wuzzy [Audiovisuals]", + "Shara/Ezhh [Settings]", } local previous_core_developers = { "BlockMen", - "Maciej Kasatkin (RealBadAngel) ", + "Maciej Kasatkin (RealBadAngel) [RIP]", "Lisa Milne (darkrose) ", "proller", "Ilya Zhuravlev (xyz) ", + "PilzAdam ", + "est31 ", + "kahrl ", + "Ryan Kwolek (kwolekr) ", + "sapier", } local previous_contributors = { - "Vanessa Ezekowitz (VanessaE) ", - "Jurgen Doser (doserj) ", - "MirceaKitsune ", - "dannydark ", - "0gb.us <0gb.us@0gb.us>", - "Guiseppe Bilotta (Oblomov) ", - "Jonathan Neuschafer ", - "Nils Dagsson Moskopp (erlehmann) ", - "Břetislav Štec (t0suj4/TBC_x)", - "Aaron Suen ", - "Constantin Wenger (SpeedProg) ", - "matttpt ", - "JacobF ", - "TriBlade9 ", - "Zefram ", + "Gregory Currie (gregorycu) [optimisation]", + "Diego Martínez (kaeza) ", + "T4im [Profiler]", + "TeTpaAka [Hand overriding, nametag colors]", + "HybridDog [Fixes]", + "Duane Robertson [MGValleys]", + "neoascetic [OS X Fixes]", + "TriBlade9 [Audiovisuals]", + "Jurgen Doser (doserj) [Fixes]", + "MirceaKitsune [Audiovisuals]", + "Guiseppe Bilotta (Oblomov) [Fixes]", + "matttpt [Fixes]", + "Nils Dagsson Moskopp (erlehmann) [Minetest Logo]", + "Jeija [HTTP, particles]", } +local function buildCreditList(source) + ret = {} + for i = 1, #source do + ret[i] = core.formspec_escape(source[i]) + end + return table.concat(ret, ",,") +end + return { name = "credits", caption = fgettext("Credits"), @@ -85,15 +92,15 @@ return { "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;" .. + "table[3.5,-0.25;8.5,6.05;list_credits;" .. "#FFFF00," .. fgettext("Core Developers") .. ",," .. - table.concat(core_developers, ",,") .. ",,," .. + buildCreditList(core_developers) .. ",,," .. "#FFFF00," .. fgettext("Active Contributors") .. ",," .. - table.concat(active_contributors, ",,") .. ",,," .. + buildCreditList(active_contributors) .. ",,," .. "#FFFF00," .. fgettext("Previous Core Developers") ..",," .. - table.concat(previous_core_developers, ",,") .. ",,," .. + buildCreditList(previous_core_developers) .. ",,," .. "#FFFF00," .. fgettext("Previous Contributors") .. ",," .. - table.concat(previous_contributors, ",,") .. "," .. + buildCreditList(previous_contributors) .. "," .. ";1]" end } diff --git a/builtin/mainmenu/tab_singleplayer.lua b/builtin/mainmenu/tab_local.lua similarity index 65% rename from builtin/mainmenu/tab_singleplayer.lua rename to builtin/mainmenu/tab_local.lua index 05060cbc6..3e62078ce 100644 --- a/builtin/mainmenu/tab_singleplayer.lua +++ b/builtin/mainmenu/tab_local.lua @@ -16,9 +16,9 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. local function current_game() - local last_game_id = core.setting_get("menu_last_game") + local last_game_id = core.settings:get("menu_last_game") local game, index = gamemgr.find_by_gameid(last_game_id) - + return game end @@ -36,10 +36,10 @@ local function singleplayer_refresh_gamebar() if ("game_btnbar_" .. gamemgr.games[j].id == key) then mm_texture.update("singleplayer", gamemgr.games[j]) core.set_topleft_text(gamemgr.games[j].name) - core.setting_set("menu_last_game",gamemgr.games[j].id) + core.settings:set("menu_last_game",gamemgr.games[j].id) menudata.worldlist:set_filtercriteria(gamemgr.games[j].id) local index = filterlist.get_current_index(menudata.worldlist, - tonumber(core.setting_get("mainmenu_last_selected_world"))) + tonumber(core.settings:get("mainmenu_last_selected_world"))) if not index or index < 1 then local selected = core.get_textlist_index("sp_worlds") if selected ~= nil and selected < #menudata.worldlist:get_list() then @@ -57,24 +57,24 @@ local function singleplayer_refresh_gamebar() local btnbar = buttonbar_create("game_button_bar", game_buttonbar_button_handler, - {x=-0.3,y=5.65}, "horizontal", {x=12.4,y=1.15}) + {x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15}) for i=1,#gamemgr.games,1 do local btn_name = "game_btnbar_" .. gamemgr.games[i].id - + local image = nil local text = nil local tooltip = core.formspec_escape(gamemgr.games[i].name) - + if gamemgr.games[i].menuicon_path ~= nil and gamemgr.games[i].menuicon_path ~= "" then image = core.formspec_escape(gamemgr.games[i].menuicon_path) else - + local part1 = gamemgr.games[i].id:sub(1,5) local part2 = gamemgr.games[i].id:sub(6,10) local part3 = gamemgr.games[i].id:sub(11) - + text = part1 .. "\n" .. part2 if part3 ~= nil and part3 ~= "" then @@ -89,28 +89,57 @@ local function get_formspec(tabview, name, tabdata) local retval = "" local index = filterlist.get_current_index(menudata.worldlist, - tonumber(core.setting_get("mainmenu_last_selected_world")) + tonumber(core.settings:get("mainmenu_last_selected_world")) ) retval = retval .. "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" .. "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" .. "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" .. - "button[8.5,4.95;3.25,0.5;play;".. fgettext("Play") .. "]" .. "label[4,-0.25;".. fgettext("Select World:") .. "]".. "checkbox[0.25,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" .. - dump(core.setting_getbool("creative_mode")) .. "]".. + dump(core.settings:get_bool("creative_mode")) .. "]".. "checkbox[0.25,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" .. - dump(core.setting_getbool("enable_damage")) .. "]".. + dump(core.settings:get_bool("enable_damage")) .. "]".. + "checkbox[0.25,1.15;cb_server;".. fgettext("Host Server") ..";" .. + dump(core.settings:get_bool("enable_server")) .. "]" .. "textlist[4,0.25;7.5,3.7;sp_worlds;" .. menu_render_worldlist() .. ";" .. index .. "]" + + if core.settings:get_bool("enable_server") then + retval = retval .. + "button[8.5,5;3.25,0.5;play;".. fgettext("Host Game") .. "]" .. + "checkbox[0.25,1.6;cb_server_announce;" .. fgettext("Announce Server") .. ";" .. + dump(core.settings:get_bool("server_announce")) .. "]" .. + "label[0.25,2.2;" .. fgettext("Name/Password") .. "]" .. + "field[0.55,3.2;3.5,0.5;te_playername;;" .. + core.formspec_escape(core.settings:get("name")) .. "]" .. + "pwdfield[0.55,4;3.5,0.5;te_passwd;]" + + local bind_addr = core.settings:get("bind_address") + if bind_addr ~= nil and bind_addr ~= "" then + retval = retval .. + "field[0.55,5.2;2.25,0.5;te_serveraddr;" .. fgettext("Bind Address") .. ";" .. + core.formspec_escape(core.settings:get("bind_address")) .. "]" .. + "field[2.8,5.2;1.25,0.5;te_serverport;" .. fgettext("Port") .. ";" .. + core.formspec_escape(core.settings:get("port")) .. "]" + else + retval = retval .. + "field[0.55,5.2;3.5,0.5;te_serverport;" .. fgettext("Server Port") .. ";" .. + core.formspec_escape(core.settings:get("port")) .. "]" + end + else + retval = retval .. + "button[8.5,5;3.25,0.5;play;".. fgettext("Play Game") .. "]" + end + return retval end local function main_button_handler(this, fields, name, tabdata) - assert(name == "singleplayer") + assert(name == "local") local world_doubleclick = false @@ -125,7 +154,7 @@ local function main_button_handler(this, fields, name, tabdata) end if event.type == "CHG" and selected ~= nil then - core.setting_set("mainmenu_last_selected_world", + core.settings:set("mainmenu_last_selected_world", menudata.worldlist:get_raw_index(selected)) return true end @@ -136,7 +165,7 @@ local function main_button_handler(this, fields, name, tabdata) end if fields["cb_creative_mode"] then - core.setting_set("creative_mode", fields["cb_creative_mode"]) + core.settings:set("creative_mode", fields["cb_creative_mode"]) local selected = core.get_textlist_index("sp_worlds") menu_worldmt(selected, "creative_mode", fields["cb_creative_mode"]) @@ -144,27 +173,65 @@ local function main_button_handler(this, fields, name, tabdata) end if fields["cb_enable_damage"] then - core.setting_set("enable_damage", fields["cb_enable_damage"]) + core.settings:set("enable_damage", fields["cb_enable_damage"]) local selected = core.get_textlist_index("sp_worlds") menu_worldmt(selected, "enable_damage", fields["cb_enable_damage"]) return true end - if fields["play"] ~= nil or - world_doubleclick or - fields["key_enter"] then + if fields["cb_server"] then + core.settings:set("enable_server", fields["cb_server"]) + + return true + end + + if fields["cb_server_announce"] then + core.settings:set("server_announce", fields["cb_server_announce"]) + local selected = core.get_textlist_index("srv_worlds") + menu_worldmt(selected, "server_announce", fields["cb_server_announce"]) + + return true + end + + if fields["play"] ~= nil or world_doubleclick or fields["key_enter"] then local selected = core.get_textlist_index("sp_worlds") gamedata.selected_world = menudata.worldlist:get_raw_index(selected) - - if selected ~= nil and gamedata.selected_world ~= 0 then - gamedata.singleplayer = true - core.start() + + if core.settings:get_bool("enable_server") then + if selected ~= nil and gamedata.selected_world ~= 0 then + gamedata.playername = fields["te_playername"] + gamedata.password = fields["te_passwd"] + gamedata.port = fields["te_serverport"] + gamedata.address = "" + + core.settings:set("port",gamedata.port) + if fields["te_serveraddr"] ~= nil then + core.settings:set("bind_address",fields["te_serveraddr"]) + end + + --update last game + local world = menudata.worldlist:get_raw_element(gamedata.selected_world) + if world then + local game, index = gamemgr.find_by_gameid(world.gameid) + core.settings:set("menu_last_game", game.id) + end + + core.start() + else + gamedata.errormessage = + fgettext("No world created or selected!") + end else - gamedata.errormessage = - fgettext("No world created or selected!") + if selected ~= nil and gamedata.selected_world ~= 0 then + gamedata.singleplayer = true + core.start() + else + gamedata.errormessage = + fgettext("No world created or selected!") + end + return true end - return true end if fields["world_create"] ~= nil then @@ -192,7 +259,7 @@ local function main_button_handler(this, fields, name, tabdata) mm_texture.update("singleplayer",current_game()) end end - + return true end @@ -202,7 +269,7 @@ local function main_button_handler(this, fields, name, tabdata) local configdialog = create_configure_world_dlg( menudata.worldlist:get_raw_index(selected)) - + if (configdialog ~= nil) then configdialog:set_parent(this) this:hide() @@ -210,22 +277,22 @@ local function main_button_handler(this, fields, name, tabdata) mm_texture.update("singleplayer",current_game()) end end - + return true end end local function on_change(type, old_tab, new_tab) local buttonbar = ui.find_by_name("game_button_bar") - + if ( buttonbar == nil ) then singleplayer_refresh_gamebar() buttonbar = ui.find_by_name("game_button_bar") end - + if (type == "ENTER") then local game = current_game() - + if game then menudata.worldlist:set_filtercriteria(game.id) core.set_topleft_text(game.name) @@ -242,8 +309,8 @@ end -------------------------------------------------------------------------------- return { - name = "singleplayer", - caption = fgettext("Singleplayer"), + name = "local", + caption = fgettext("Local Game"), cbf_formspec = get_formspec, cbf_button_handler = main_button_handler, on_change = on_change diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua index 4a5b6c041..9510a9e18 100644 --- a/builtin/mainmenu/tab_mods.lua +++ b/builtin/mainmenu/tab_mods.lua @@ -28,7 +28,8 @@ local function get_formspec(tabview, name, tabdata) local retval = "label[0.05,-0.25;".. fgettext("Installed Mods:") .. "]" .. - "textlist[0,0.25;5.1,5;modlist;" .. + "tablecolumns[color;tree;text]" .. + "table[0,0.25;5.1,5;modlist;" .. modmgr.render_modlist(modmgr.global_mods) .. ";" .. tabdata.selected_mod .. "]" @@ -74,7 +75,7 @@ local function get_formspec(tabview, name, tabdata) if error == nil then local descriptiontext = descriptionfile:read("*all") - descriptionlines = core.splittext(descriptiontext,42) + descriptionlines = core.wrap_text(descriptiontext, 42) descriptionfile:close() else descriptionlines = {} @@ -127,8 +128,8 @@ end -------------------------------------------------------------------------------- local function handle_buttons(tabview, fields, tabname, tabdata) if fields["modlist"] ~= nil then - local event = core.explode_textlist_event(fields["modlist"]) - tabdata.selected_mod = event.index + local event = core.explode_table_event(fields["modlist"]) + tabdata.selected_mod = event.row return true end diff --git a/builtin/mainmenu/tab_multiplayer.lua b/builtin/mainmenu/tab_online.lua similarity index 55% rename from builtin/mainmenu/tab_multiplayer.lua rename to builtin/mainmenu/tab_online.lua index 00150f26d..ab23a4b7c 100644 --- a/builtin/mainmenu/tab_multiplayer.lua +++ b/builtin/mainmenu/tab_online.lua @@ -20,28 +20,48 @@ local function get_formspec(tabview, name, tabdata) -- Update the cached supported proto info, -- it may have changed after a change by the settings menu. common_update_cached_supp_proto() - local fav_selected = menudata.favorites[tabdata.fav_selected] + local fav_selected = nil + if menudata.search_result then + fav_selected = menudata.search_result[tabdata.fav_selected] + else + fav_selected = menudata.favorites[tabdata.fav_selected] + end + + if not tabdata.search_for then + tabdata.search_for = "" + end local retval = - "label[7.75,-0.15;" .. fgettext("Address / Port") .. "]" .. - "label[7.75,1.05;" .. fgettext("Name / Password") .. "]" .. - "field[8,0.75;3.3,0.5;te_address;;" .. - core.formspec_escape(core.setting_get("address")) .. "]" .. - "field[11.15,0.75;1.4,0.5;te_port;;" .. - core.formspec_escape(core.setting_get("remote_port")) .. "]" .. - "button[10.1,4.9;2,0.5;btn_mp_connect;" .. fgettext("Connect") .. "]" .. - "field[8,1.95;2.95,0.5;te_name;;" .. - core.formspec_escape(core.setting_get("name")) .. "]" .. - "pwdfield[10.78,1.95;1.77,0.5;te_pwd;]" .. - "box[7.73,2.35;4.3,2.28;#999999]" + -- Search + "field[0.15,0.35;6.05,0.27;te_search;;"..core.formspec_escape(tabdata.search_for).."]".. + "button[5.8,0.1;2,0.1;btn_mp_search;" .. fgettext("Search") .. "]" .. + + -- Address / Port + "label[7.75,-0.25;" .. fgettext("Address / Port") .. "]" .. + "field[8,0.65;3.25,0.5;te_address;;" .. + core.formspec_escape(core.settings:get("address")) .. "]" .. + "field[11.1,0.65;1.4,0.5;te_port;;" .. + core.formspec_escape(core.settings:get("remote_port")) .. "]" .. + + -- Name / Password + "label[7.75,0.95;" .. fgettext("Name / Password") .. "]" .. + "field[8,1.85;2.9,0.5;te_name;;" .. + core.formspec_escape(core.settings:get("name")) .. "]" .. + "pwdfield[10.73,1.85;1.77,0.5;te_pwd;]" .. + + -- Description Background + "box[7.73,2.25;4.25,2.6;#999999]".. + + -- Connect + "button[10.1,5.15;2,0.5;btn_mp_connect;" .. fgettext("Connect") .. "]" if tabdata.fav_selected and fav_selected then if gamedata.fav then - retval = retval .. "button[7.85,4.9;2.3,0.5;btn_delete_favorite;" .. + retval = retval .. "button[7.75,5.15;2.3,0.5;btn_delete_favorite;" .. fgettext("Del. Favorite") .. "]" end if fav_selected.description then - retval = retval .. "textarea[8.1,2.4;4.26,2.6;;" .. + retval = retval .. "textarea[8.1,2.3;4.23,2.9;;" .. core.formspec_escape((gamedata.serverdescription or ""), true) .. ";]" end end @@ -49,6 +69,7 @@ local function get_formspec(tabview, name, tabdata) --favourites retval = retval .. "tablecolumns[" .. image_column(fgettext("Favorite"), "favorite") .. ";" .. + image_column(fgettext("Ping")) .. ",padding=0.25;" .. "color,span=3;" .. "text,align=right;" .. -- clients "text,align=center,padding=0.25;" .. -- "/" @@ -58,9 +79,27 @@ local function get_formspec(tabview, name, tabdata) image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. "color,span=1;" .. "text,padding=1]" .. - "table[-0.15,-0.1;7.75,5.5;favourites;" + "table[-0.15,0.6;7.75,5.15;favourites;" - if #menudata.favorites > 0 then + if menudata.search_result then + for i = 1, #menudata.search_result do + local favs = core.get_favorites("local") + local server = menudata.search_result[i] + + for fav_id = 1, #favs do + if server.address == favs[fav_id].address and + server.port == favs[fav_id].port then + server.is_favorite = true + end + end + + if i ~= 1 then + retval = retval .. "," + end + + retval = retval .. render_serverlist_row(server, server.is_favorite) + end + elseif #menudata.favorites > 0 then local favs = core.get_favorites("local") if #favs > 0 then for i = 1, #favs do @@ -75,9 +114,9 @@ local function get_formspec(tabview, name, tabdata) end end end - retval = retval .. render_favorite(menudata.favorites[1], (#favs > 0)) + retval = retval .. render_serverlist_row(menudata.favorites[1], (#favs > 0)) for i = 2, #menudata.favorites do - retval = retval .. "," .. render_favorite(menudata.favorites[i], (i <= #favs)) + retval = retval .. "," .. render_serverlist_row(menudata.favorites[i], (i <= #favs)) end end @@ -92,17 +131,19 @@ end -------------------------------------------------------------------------------- local function main_button_handler(tabview, fields, name, tabdata) + local serverlist = menudata.search_result or menudata.favorites + if fields.te_name then gamedata.playername = fields.te_name - core.setting_set("name", fields.te_name) + core.settings:set("name", fields.te_name) end if fields.favourites then local event = core.explode_table_event(fields.favourites) - local fav = menudata.favorites[event.row] + local fav = serverlist[event.row] if event.type == "DCL" then - if event.row <= #menudata.favorites then + if event.row <= #serverlist then if menudata.favorites_is_public and not is_server_protocol_compat_or_error( fav.proto_min, fav.proto_max) then @@ -122,8 +163,8 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.serverdescription = fav.description if gamedata.address and gamedata.port then - core.setting_set("address", gamedata.address) - core.setting_set("remote_port", gamedata.port) + core.settings:set("address", gamedata.address) + core.settings:set("remote_port", gamedata.port) core.start() end end @@ -131,7 +172,7 @@ local function main_button_handler(tabview, fields, name, tabdata) end if event.type == "CHG" then - if event.row <= #menudata.favorites then + if event.row <= #serverlist then gamedata.fav = false local favs = core.get_favorites("local") local address = fav.address @@ -146,8 +187,8 @@ local function main_button_handler(tabview, fields, name, tabdata) end if address and port then - core.setting_set("address", address) - core.setting_set("remote_port", port) + core.settings:set("address", address) + core.settings:set("remote_port", port) end tabdata.fav_selected = event.row end @@ -157,7 +198,7 @@ local function main_button_handler(tabview, fields, name, tabdata) if fields.key_up or fields.key_down then local fav_idx = core.get_table_index("favourites") - local fav = menudata.favorites[fav_idx] + local fav = serverlist[fav_idx] if fav_idx then if fields.key_up and fav_idx > 1 then @@ -176,10 +217,10 @@ local function main_button_handler(tabview, fields, name, tabdata) local address = fav.address local port = fav.port - + gamedata.serverdescription = fav.description if address and port then - core.setting_set("address", address) - core.setting_set("remote_port", port) + core.settings:set("address", address) + core.settings:set("remote_port", port) end tabdata.fav_selected = fav_idx @@ -194,21 +235,81 @@ local function main_button_handler(tabview, fields, name, tabdata) asyncOnlineFavourites() tabdata.fav_selected = nil - core.setting_set("address", "") - core.setting_set("remote_port", "30000") + core.settings:set("address", "") + core.settings:set("remote_port", "30000") return true end - if (fields.btn_mp_connect or fields.key_enter) and fields.te_address and fields.te_port then + if fields.btn_mp_search or fields.key_enter_field == "te_search" then + tabdata.fav_selected = 1 + local input = fields.te_search:lower() + tabdata.search_for = fields.te_search + + if #menudata.favorites < 2 then + return true + end + + menudata.search_result = {} + + -- setup the keyword list + local keywords = {} + for word in input:gmatch("%S+") do + table.insert(keywords, word) + end + + if #keywords == 0 then + menudata.search_result = nil + return true + end + + -- Search the serverlist + local search_result = {} + for i = 1, #menudata.favorites do + local server = menudata.favorites[i] + local found = 0 + for k = 1, #keywords do + local keyword = keywords[k] + if server.name then + local name = server.name:lower() + local _, count = name:gsub(keyword, keyword) + found = found + count * 4 + end + + if server.description then + local desc = server.description:lower() + local _, count = desc:gsub(keyword, keyword) + found = found + count * 2 + end + end + if found > 0 then + local points = (#menudata.favorites - i) / 5 + found + server.points = points + table.insert(search_result, server) + end + end + if #search_result > 0 then + table.sort(search_result, function(a, b) + return a.points > b.points + end) + menudata.search_result = search_result + local first_server = search_result[1] + core.settings:set("address", first_server.address) + core.settings:set("remote_port", first_server.port) + end + return true + end + + if (fields.btn_mp_connect or fields.key_enter) + and fields.te_address ~= "" and fields.te_port then gamedata.playername = fields.te_name gamedata.password = fields.te_pwd gamedata.address = fields.te_address gamedata.port = fields.te_port gamedata.selected_world = 0 local fav_idx = core.get_table_index("favourites") - local fav = menudata.favorites[fav_idx] + local fav = serverlist[fav_idx] - if fav_idx and fav_idx <= #menudata.favorites and + if fav_idx and fav_idx <= #serverlist and fav.address == fields.te_address and fav.port == fields.te_port then @@ -225,8 +326,8 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.serverdescription = "" end - core.setting_set("address", fields.te_address) - core.setting_set("remote_port", fields.te_port) + core.settings:set("address", fields.te_address) + core.settings:set("remote_port", fields.te_port) core.start() return true @@ -241,8 +342,8 @@ end -------------------------------------------------------------------------------- return { - name = "multiplayer", - caption = fgettext("Client"), + name = "online", + caption = fgettext("Play Online"), cbf_formspec = get_formspec, cbf_button_handler = main_button_handler, on_change = on_change diff --git a/builtin/mainmenu/tab_server.lua b/builtin/mainmenu/tab_server.lua deleted file mode 100644 index 6b96825a0..000000000 --- a/builtin/mainmenu/tab_server.lua +++ /dev/null @@ -1,195 +0,0 @@ ---Minetest ---Copyright (C) 2014 sapier --- ---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 function get_formspec(tabview, name, tabdata) - - local index = menudata.worldlist:get_current_index( - tonumber(core.setting_get("mainmenu_last_selected_world")) - ) - - local retval = - "button[4,4.15;2.6,0.5;world_delete;" .. fgettext("Delete") .. "]" .. - "button[6.5,4.15;2.8,0.5;world_create;" .. fgettext("New") .. "]" .. - "button[9.2,4.15;2.55,0.5;world_configure;" .. fgettext("Configure") .. "]" .. - "button[8.5,4.95;3.25,0.5;start_server;" .. fgettext("Start Game") .. "]" .. - "label[4,-0.25;" .. fgettext("Select World:") .. "]" .. - "checkbox[0.25,0.25;cb_creative_mode;" .. fgettext("Creative Mode") .. ";" .. - dump(core.setting_getbool("creative_mode")) .. "]" .. - "checkbox[0.25,0.7;cb_enable_damage;" .. fgettext("Enable Damage") .. ";" .. - dump(core.setting_getbool("enable_damage")) .. "]" .. - "checkbox[0.25,1.15;cb_server_announce;" .. fgettext("Public") .. ";" .. - dump(core.setting_getbool("server_announce")) .. "]" .. - "label[0.25,2.2;" .. fgettext("Name/Password") .. "]" .. - "field[0.55,3.2;3.5,0.5;te_playername;;" .. - core.formspec_escape(core.setting_get("name")) .. "]" .. - "pwdfield[0.55,4;3.5,0.5;te_passwd;]" - - local bind_addr = core.setting_get("bind_address") - if bind_addr ~= nil and bind_addr ~= "" then - retval = retval .. - "field[0.55,5.2;2.25,0.5;te_serveraddr;" .. fgettext("Bind Address") .. ";" .. - core.formspec_escape(core.setting_get("bind_address")) .. "]" .. - "field[2.8,5.2;1.25,0.5;te_serverport;" .. fgettext("Port") .. ";" .. - core.formspec_escape(core.setting_get("port")) .. "]" - else - retval = retval .. - "field[0.55,5.2;3.5,0.5;te_serverport;" .. fgettext("Server Port") .. ";" .. - core.formspec_escape(core.setting_get("port")) .. "]" - end - - retval = retval .. - "textlist[4,0.25;7.5,3.7;srv_worlds;" .. - menu_render_worldlist() .. - ";" .. index .. "]" - - return retval -end - --------------------------------------------------------------------------------- -local function main_button_handler(this, fields, name, tabdata) - - local world_doubleclick = false - - if fields["srv_worlds"] ~= nil then - local event = core.explode_textlist_event(fields["srv_worlds"]) - local selected = core.get_textlist_index("srv_worlds") - - menu_worldmt_legacy(selected) - - if event.type == "DCL" then - world_doubleclick = true - end - if event.type == "CHG" then - core.setting_set("mainmenu_last_selected_world", - menudata.worldlist:get_raw_index(core.get_textlist_index("srv_worlds"))) - return true - end - end - - if menu_handle_key_up_down(fields,"srv_worlds","mainmenu_last_selected_world") then - return true - end - - if fields["cb_creative_mode"] then - core.setting_set("creative_mode", fields["cb_creative_mode"]) - local selected = core.get_textlist_index("srv_worlds") - menu_worldmt(selected, "creative_mode", fields["cb_creative_mode"]) - - return true - end - - if fields["cb_enable_damage"] then - core.setting_set("enable_damage", fields["cb_enable_damage"]) - local selected = core.get_textlist_index("srv_worlds") - menu_worldmt(selected, "enable_damage", fields["cb_enable_damage"]) - - return true - end - - if fields["cb_server_announce"] then - core.setting_set("server_announce", fields["cb_server_announce"]) - local selected = core.get_textlist_index("srv_worlds") - menu_worldmt(selected, "server_announce", fields["cb_server_announce"]) - - return true - end - - if fields["start_server"] ~= nil or - world_doubleclick or - fields["key_enter"] then - local selected = core.get_textlist_index("srv_worlds") - gamedata.selected_world = menudata.worldlist:get_raw_index(selected) - if selected ~= nil and gamedata.selected_world ~= 0 then - gamedata.playername = fields["te_playername"] - gamedata.password = fields["te_passwd"] - gamedata.port = fields["te_serverport"] - gamedata.address = "" - - core.setting_set("port",gamedata.port) - if fields["te_serveraddr"] ~= nil then - core.setting_set("bind_address",fields["te_serveraddr"]) - end - - --update last game - local world = menudata.worldlist:get_raw_element(gamedata.selected_world) - if world then - local game, index = gamemgr.find_by_gameid(world.gameid) - core.setting_set("menu_last_game", game.id) - end - - core.start() - else - gamedata.errormessage = - fgettext("No world created or selected!") - end - return true - end - - if fields["world_create"] ~= nil then - local create_world_dlg = create_create_world_dlg(true) - create_world_dlg:set_parent(this) - create_world_dlg:show() - this:hide() - return true - end - - if fields["world_delete"] ~= nil then - local selected = core.get_textlist_index("srv_worlds") - if selected ~= nil and - selected <= menudata.worldlist:size() then - local world = menudata.worldlist:get_list()[selected] - if world ~= nil and - world.name ~= nil and - world.name ~= "" then - local index = menudata.worldlist:get_raw_index(selected) - local delete_world_dlg = create_delete_world_dlg(world.name,index) - delete_world_dlg:set_parent(this) - delete_world_dlg:show() - this:hide() - end - end - - return true - end - - if fields["world_configure"] ~= nil then - local selected = core.get_textlist_index("srv_worlds") - if selected ~= nil then - local configdialog = - create_configure_world_dlg( - menudata.worldlist:get_raw_index(selected)) - - if (configdialog ~= nil) then - configdialog:set_parent(this) - configdialog:show() - this:hide() - end - end - return true - end - return false -end - --------------------------------------------------------------------------------- -return { - name = "server", - caption = fgettext("Server"), - cbf_formspec = get_formspec, - cbf_button_handler = main_button_handler, - on_change = nil -} diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index af8df0ccb..5a8cc19b8 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -25,7 +25,8 @@ local labels = { }, node_highlighting = { fgettext("Node Outlining"), - fgettext("Node Highlighting") + fgettext("Node Highlighting"), + fgettext("None") }, filters = { fgettext("No Filter"), @@ -52,7 +53,7 @@ local dd_options = { }, node_highlighting = { table.concat(labels.node_highlighting, ","), - {"box", "halo"} + {"box", "halo", "none"} }, filters = { table.concat(labels.filters, ","), @@ -70,39 +71,39 @@ local dd_options = { local getSettingIndex = { Leaves = function() - local style = core.setting_get("leaves_style") + local style = core.settings:get("leaves_style") for idx, name in pairs(dd_options.leaves[2]) do if style == name then return idx end end return 1 end, NodeHighlighting = function() - local style = core.setting_get("node_highlighting") + local style = core.settings:get("node_highlighting") for idx, name in pairs(dd_options.node_highlighting[2]) do if style == name then return idx end end return 1 end, Filter = function() - if core.setting_get(dd_options.filters[2][3]) == "true" then + if core.settings:get(dd_options.filters[2][3]) == "true" then return 3 - elseif core.setting_get(dd_options.filters[2][3]) == "false" and - core.setting_get(dd_options.filters[2][2]) == "true" then + elseif core.settings:get(dd_options.filters[2][3]) == "false" and + core.settings:get(dd_options.filters[2][2]) == "true" then return 2 end return 1 end, Mipmap = function() - if core.setting_get(dd_options.mipmap[2][3]) == "true" then + if core.settings:get(dd_options.mipmap[2][3]) == "true" then return 3 - elseif core.setting_get(dd_options.mipmap[2][3]) == "false" and - core.setting_get(dd_options.mipmap[2][2]) == "true" then + elseif core.settings:get(dd_options.mipmap[2][3]) == "false" and + core.settings:get(dd_options.mipmap[2][2]) == "true" then return 2 end return 1 end, Antialiasing = function() - local antialiasing_setting = core.setting_get("fsaa") + local antialiasing_setting = core.settings:get("fsaa") for i = 1, #dd_options.antialiasing[2] do if antialiasing_setting == dd_options.antialiasing[2][i] then return i @@ -177,20 +178,20 @@ local function formspec(tabview, name, tabdata) local tab_string = "box[0,0;3.5,4.5;#999999]" .. "checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") .. ";" - .. dump(core.setting_getbool("smooth_lighting")) .. "]" .. + .. dump(core.settings:get_bool("smooth_lighting")) .. "]" .. "checkbox[0.25,0.5;cb_particles;" .. fgettext("Particles") .. ";" - .. dump(core.setting_getbool("enable_particles")) .. "]" .. + .. dump(core.settings:get_bool("enable_particles")) .. "]" .. "checkbox[0.25,1;cb_3d_clouds;" .. fgettext("3D Clouds") .. ";" - .. dump(core.setting_getbool("enable_3d_clouds")) .. "]" .. + .. dump(core.settings:get_bool("enable_3d_clouds")) .. "]" .. "checkbox[0.25,1.5;cb_opaque_water;" .. fgettext("Opaque Water") .. ";" - .. dump(core.setting_getbool("opaque_water")) .. "]" .. + .. dump(core.settings:get_bool("opaque_water")) .. "]" .. "checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";" - .. dump(core.setting_getbool("connected_glass")) .. "]" .. + .. dump(core.settings:get_bool("connected_glass")) .. "]" .. "dropdown[0.25,2.8;3.3;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";" .. getSettingIndex.NodeHighlighting() .. "]" .. "dropdown[0.25,3.6;3.3;dd_leaves_style;" .. dd_options.leaves[1] .. ";" .. getSettingIndex.Leaves() .. "]" .. - "box[3.75,0;3.75,3.45;#999999]" .. + "box[3.75,0;3.75,4.45;#999999]" .. "label[3.85,0.1;" .. fgettext("Texturing:") .. "]" .. "dropdown[3.85,0.55;3.85;dd_filters;" .. dd_options.filters[1] .. ";" .. getSettingIndex.Filter() .. "]" .. @@ -199,9 +200,12 @@ local function formspec(tabview, name, tabdata) "label[3.85,2.15;" .. fgettext("Antialiasing:") .. "]" .. "dropdown[3.85,2.6;3.85;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";" .. getSettingIndex.Antialiasing() .. "]" .. + "label[3.85,3.45;" .. fgettext("Screen:") .. "]" .. + "checkbox[3.85,3.6;cb_autosave_screensize;" .. fgettext("Autosave screen size") .. ";" + .. dump(core.settings:get_bool("autosave_screensize")) .. "]" .. "box[7.75,0;4,4.4;#999999]" .. "checkbox[8,0;cb_shaders;" .. fgettext("Shaders") .. ";" - .. dump(core.setting_getbool("enable_shaders")) .. "]" + .. dump(core.settings:get_bool("enable_shaders")) .. "]" if PLATFORM == "Android" then tab_string = tab_string .. @@ -209,38 +213,38 @@ local function formspec(tabview, name, tabdata) .. fgettext("Reset singleplayer world") .. "]" else tab_string = tab_string .. - "button[8,4.75;3.75,0.5;btn_change_keys;" + "button[8,4.85;3.75,0.5;btn_change_keys;" .. fgettext("Change keys") .. "]" end tab_string = tab_string .. - "button[0,4.75;3.75,0.5;btn_advanced_settings;" + "button[0,4.85;3.75,0.5;btn_advanced_settings;" .. fgettext("Advanced Settings") .. "]" - if core.setting_get("touchscreen_threshold") ~= nil then + if core.settings:get("touchscreen_threshold") ~= nil then tab_string = tab_string .. "label[4.3,4.1;" .. fgettext("Touchthreshold (px)") .. "]" .. "dropdown[3.85,4.55;3.85;dd_touchthreshold;0,10,20,30,40,50;" .. - ((tonumber(core.setting_get("touchscreen_threshold")) / 10) + 1) .. "]" + ((tonumber(core.settings:get("touchscreen_threshold")) / 10) + 1) .. "]" end - if core.setting_getbool("enable_shaders") then + if core.settings:get_bool("enable_shaders") then tab_string = tab_string .. "checkbox[8,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";" - .. dump(core.setting_getbool("enable_bumpmapping")) .. "]" .. + .. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" .. "checkbox[8,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" - .. dump(core.setting_getbool("tone_mapping")) .. "]" .. + .. dump(core.settings:get_bool("tone_mapping")) .. "]" .. "checkbox[8,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";" - .. dump(core.setting_getbool("generate_normalmaps")) .. "]" .. + .. dump(core.settings:get_bool("generate_normalmaps")) .. "]" .. "checkbox[8,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";" - .. dump(core.setting_getbool("enable_parallax_occlusion")) .. "]" .. + .. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" .. "checkbox[8,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";" - .. dump(core.setting_getbool("enable_waving_water")) .. "]" .. + .. dump(core.settings:get_bool("enable_waving_water")) .. "]" .. "checkbox[8,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" - .. dump(core.setting_getbool("enable_waving_leaves")) .. "]" .. + .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. "checkbox[8,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" - .. dump(core.setting_getbool("enable_waving_plants")) .. "]" + .. dump(core.settings:get_bool("enable_waving_plants")) .. "]" else tab_string = tab_string .. "tablecolumns[color;text]" .. @@ -271,60 +275,64 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) return true end if fields["cb_smooth_lighting"] then - core.setting_set("smooth_lighting", fields["cb_smooth_lighting"]) + core.settings:set("smooth_lighting", fields["cb_smooth_lighting"]) return true end if fields["cb_particles"] then - core.setting_set("enable_particles", fields["cb_particles"]) + core.settings:set("enable_particles", fields["cb_particles"]) return true end if fields["cb_3d_clouds"] then - core.setting_set("enable_3d_clouds", fields["cb_3d_clouds"]) + core.settings:set("enable_3d_clouds", fields["cb_3d_clouds"]) return true end if fields["cb_opaque_water"] then - core.setting_set("opaque_water", fields["cb_opaque_water"]) + core.settings:set("opaque_water", fields["cb_opaque_water"]) return true end if fields["cb_connected_glass"] then - core.setting_set("connected_glass", fields["cb_connected_glass"]) + core.settings:set("connected_glass", fields["cb_connected_glass"]) + return true + end + if fields["cb_autosave_screensize"] then + core.settings:set("autosave_screensize", fields["cb_autosave_screensize"]) return true end if fields["cb_shaders"] then - if (core.setting_get("video_driver") == "direct3d8" or - core.setting_get("video_driver") == "direct3d9") then - core.setting_set("enable_shaders", "false") + if (core.settings:get("video_driver") == "direct3d8" or + core.settings:get("video_driver") == "direct3d9") then + core.settings:set("enable_shaders", "false") gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.") else - core.setting_set("enable_shaders", fields["cb_shaders"]) + core.settings:set("enable_shaders", fields["cb_shaders"]) end return true end if fields["cb_bumpmapping"] then - core.setting_set("enable_bumpmapping", fields["cb_bumpmapping"]) + core.settings:set("enable_bumpmapping", fields["cb_bumpmapping"]) return true end if fields["cb_tonemapping"] then - core.setting_set("tone_mapping", fields["cb_tonemapping"]) + core.settings:set("tone_mapping", fields["cb_tonemapping"]) return true end if fields["cb_generate_normalmaps"] then - core.setting_set("generate_normalmaps", fields["cb_generate_normalmaps"]) + core.settings:set("generate_normalmaps", fields["cb_generate_normalmaps"]) return true end if fields["cb_parallax"] then - core.setting_set("enable_parallax_occlusion", fields["cb_parallax"]) + core.settings:set("enable_parallax_occlusion", fields["cb_parallax"]) return true end if fields["cb_waving_water"] then - core.setting_set("enable_waving_water", fields["cb_waving_water"]) + core.settings:set("enable_waving_water", fields["cb_waving_water"]) return true end if fields["cb_waving_leaves"] then - core.setting_set("enable_waving_leaves", fields["cb_waving_leaves"]) + core.settings:set("enable_waving_leaves", fields["cb_waving_leaves"]) end if fields["cb_waving_plants"] then - core.setting_set("enable_waving_plants", fields["cb_waving_plants"]) + core.settings:set("enable_waving_plants", fields["cb_waving_plants"]) return true end if fields["btn_change_keys"] then @@ -332,7 +340,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) return true end if fields["cb_touchscreen_target"] then - core.setting_set("touchtarget", fields["cb_touchscreen_target"]) + core.settings:set("touchtarget", fields["cb_touchscreen_target"]) return true end if fields["btn_reset_singleplayer"] then @@ -345,49 +353,49 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) for i = 1, #labels.leaves do if fields["dd_leaves_style"] == labels.leaves[i] then - core.setting_set("leaves_style", dd_options.leaves[2][i]) + core.settings:set("leaves_style", dd_options.leaves[2][i]) ddhandled = true end end for i = 1, #labels.node_highlighting do if fields["dd_node_highlighting"] == labels.node_highlighting[i] then - core.setting_set("node_highlighting", dd_options.node_highlighting[2][i]) + core.settings:set("node_highlighting", dd_options.node_highlighting[2][i]) ddhandled = true end end if fields["dd_filters"] == labels.filters[1] then - core.setting_set("bilinear_filter", "false") - core.setting_set("trilinear_filter", "false") + core.settings:set("bilinear_filter", "false") + core.settings:set("trilinear_filter", "false") ddhandled = true elseif fields["dd_filters"] == labels.filters[2] then - core.setting_set("bilinear_filter", "true") - core.setting_set("trilinear_filter", "false") + core.settings:set("bilinear_filter", "true") + core.settings:set("trilinear_filter", "false") ddhandled = true elseif fields["dd_filters"] == labels.filters[3] then - core.setting_set("bilinear_filter", "false") - core.setting_set("trilinear_filter", "true") + core.settings:set("bilinear_filter", "false") + core.settings:set("trilinear_filter", "true") ddhandled = true end if fields["dd_mipmap"] == labels.mipmap[1] then - core.setting_set("mip_map", "false") - core.setting_set("anisotropic_filter", "false") + core.settings:set("mip_map", "false") + core.settings:set("anisotropic_filter", "false") ddhandled = true elseif fields["dd_mipmap"] == labels.mipmap[2] then - core.setting_set("mip_map", "true") - core.setting_set("anisotropic_filter", "false") + core.settings:set("mip_map", "true") + core.settings:set("anisotropic_filter", "false") ddhandled = true elseif fields["dd_mipmap"] == labels.mipmap[3] then - core.setting_set("mip_map", "true") - core.setting_set("anisotropic_filter", "true") + core.settings:set("mip_map", "true") + core.settings:set("anisotropic_filter", "true") ddhandled = true end if fields["dd_antialiasing"] then - core.setting_set("fsaa", + core.settings:set("fsaa", antialiasing_fname_to_name(fields["dd_antialiasing"])) ddhandled = true end if fields["dd_touchthreshold"] then - core.setting_set("touchscreen_threshold", fields["dd_touchthreshold"]) + core.settings:set("touchscreen_threshold", fields["dd_touchthreshold"]) ddhandled = true end diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua index 3818f321f..de4ae1751 100644 --- a/builtin/mainmenu/tab_simple_main.lua +++ b/builtin/mainmenu/tab_simple_main.lua @@ -25,12 +25,12 @@ local function get_formspec(tabview, name, tabdata) local retval = "label[9.5,0;".. fgettext("Name / Password") .. "]" .. "field[0.25,3.35;5.5,0.5;te_address;;" .. - core.formspec_escape(core.setting_get("address")) .."]" .. + core.formspec_escape(core.settings:get("address")) .."]" .. "field[5.75,3.35;2.25,0.5;te_port;;" .. - core.formspec_escape(core.setting_get("remote_port")) .."]" .. + core.formspec_escape(core.settings:get("remote_port")) .."]" .. "button[10,2.6;2,1.5;btn_mp_connect;".. fgettext("Connect") .. "]" .. "field[9.8,1;2.6,0.5;te_name;;" .. - core.formspec_escape(core.setting_get("name")) .."]" .. + core.formspec_escape(core.settings:get("name")) .."]" .. "pwdfield[9.8,2;2.6,0.5;te_pwd;]" @@ -43,6 +43,7 @@ local function get_formspec(tabview, name, tabdata) retval = retval .. "tablecolumns[" .. image_column(fgettext("Favorite"), "favorite") .. ";" .. + image_column(fgettext("Ping"), "") .. ",padding=0.25;" .. "color,span=3;" .. "text,align=right;" .. -- clients "text,align=center,padding=0.25;" .. -- "/" @@ -70,9 +71,9 @@ local function get_formspec(tabview, name, tabdata) end end end - retval = retval .. render_favorite(menudata.favorites[1], (#favs > 0)) + retval = retval .. render_serverlist_row(menudata.favorites[1], (#favs > 0)) for i = 2, #menudata.favorites do - retval = retval .. "," .. render_favorite(menudata.favorites[i], (i <= #favs)) + retval = retval .. "," .. render_serverlist_row(menudata.favorites[i], (i <= #favs)) end end @@ -88,9 +89,9 @@ local function get_formspec(tabview, name, tabdata) -- checkboxes retval = retval .. "checkbox[8.0,3.9;cb_creative;".. fgettext("Creative Mode") .. ";" .. - dump(core.setting_getbool("creative_mode")) .. "]".. + dump(core.settings:get_bool("creative_mode")) .. "]".. "checkbox[8.0,4.4;cb_damage;".. fgettext("Enable Damage") .. ";" .. - dump(core.setting_getbool("enable_damage")) .. "]" + dump(core.settings:get_bool("enable_damage")) .. "]" -- buttons retval = retval .. "button[0,3.7;8,1.5;btn_start_singleplayer;" .. fgettext("Start Singleplayer") .. "]" .. @@ -127,8 +128,8 @@ local function main_button_handler(tabview, fields, name, tabdata) end if address and port then - core.setting_set("address", address) - core.setting_set("remote_port", port) + core.settings:set("address", address) + core.settings:set("remote_port", port) end tabdata.fav_selected = event.row end @@ -144,18 +145,18 @@ local function main_button_handler(tabview, fields, name, tabdata) asyncOnlineFavourites() tabdata.fav_selected = nil - core.setting_set("address", "") - core.setting_set("remote_port", "30000") + core.settings:set("address", "") + core.settings:set("remote_port", "30000") return true end if fields.cb_creative then - core.setting_set("creative_mode", fields.cb_creative) + core.settings:set("creative_mode", fields.cb_creative) return true end if fields.cb_damage then - core.setting_set("enable_damage", fields.cb_damage) + core.settings:set("enable_damage", fields.cb_damage) return true end @@ -185,12 +186,12 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.selected_world = 0 - core.setting_set("address", fields.te_address) - core.setting_set("remote_port", fields.te_port) - - core.start() - return true - end + core.settings:set("address", fields.te_address) + core.settings:set("remote_port", fields.te_port) + + core.start() + return true + end if fields.btn_config_sp_world then local configdialog = create_configure_world_dlg(1) diff --git a/builtin/mainmenu/tab_texturepacks.lua b/builtin/mainmenu/tab_texturepacks.lua index 4638beaa1..2957481cf 100644 --- a/builtin/mainmenu/tab_texturepacks.lua +++ b/builtin/mainmenu/tab_texturepacks.lua @@ -54,9 +54,9 @@ local function get_formspec(tabview, name, tabdata) local retval = "label[4,-0.25;" .. fgettext("Select texture pack:") .. "]" .. "textlist[4,0.25;7.5,5.0;TPs;" - local current_texture_path = core.setting_get("texture_path") + local current_texture_path = core.settings:get("texture_path") local list = filter_texture_pack_list(core.get_dir_list(core.get_texturepath(), true)) - local index = tonumber(core.setting_get("mainmenu_last_selected_TP")) + local index = tonumber(core.settings:get("mainmenu_last_selected_TP")) if not index then index = 1 end @@ -106,7 +106,7 @@ local function main_button_handler(tabview, fields, name, tabdata) local event = core.explode_textlist_event(fields["TPs"]) if event.type == "CHG" or event.type == "DCL" then local index = core.get_textlist_index("TPs") - core.setting_set("mainmenu_last_selected_TP", index) + core.settings:set("mainmenu_last_selected_TP", index) local list = filter_texture_pack_list(core.get_dir_list(core.get_texturepath(), true)) local current_index = core.get_textlist_index("TPs") if current_index and #list >= current_index then @@ -114,7 +114,7 @@ local function main_button_handler(tabview, fields, name, tabdata) if list[current_index] == fgettext("None") then new_path = "" end - core.setting_set("texture_path", new_path) + core.settings:set("texture_path", new_path) end end return true diff --git a/builtin/mainmenu/textures.lua b/builtin/mainmenu/textures.lua index dadbb093e..9ba4ade7e 100644 --- a/builtin/mainmenu/textures.lua +++ b/builtin/mainmenu/textures.lua @@ -24,7 +24,7 @@ function mm_texture.init() DIR_DELIM .. "pack" .. DIR_DELIM mm_texture.basetexturedir = mm_texture.defaulttexturedir - mm_texture.texturepack = core.setting_get("texture_path") + mm_texture.texturepack = core.settings:get("texture_path") mm_texture.gameid = nil end @@ -61,7 +61,7 @@ function mm_texture.reset() mm_texture.set_generic("header") if not have_bg then - if core.setting_getbool("menu_clouds") then + if core.settings:get_bool("menu_clouds") then core.set_clouds(true) else mm_texture.set_dirt_bg() @@ -88,7 +88,7 @@ function mm_texture.update_game(gamedetails) if not have_bg then - if core.setting_getbool("menu_clouds") then + if core.settings:get_bool("menu_clouds") then core.set_clouds(true) else mm_texture.set_dirt_bg() diff --git a/builtin/profiler/init.lua b/builtin/profiler/init.lua index c1597d280..874950364 100644 --- a/builtin/profiler/init.lua +++ b/builtin/profiler/init.lua @@ -15,10 +15,18 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +local function get_bool_default(name, default) + local val = core.settings:get_bool(name) + if val == nil then + return default + end + return val +end + 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 instrumentation = assert(loadfile(profiler_path .. "instrumentation.lua"))(profiler, sampler, get_bool_default) local reporter = dofile(profiler_path .. "reporter.lua") profiler.instrument = instrumentation.instrument @@ -27,7 +35,7 @@ profiler.instrument = instrumentation.instrument -- Is called later, after `core.register_chatcommand` was set up. -- function profiler.init_chatcommand() - local instrument_profiler = core.setting_getbool("instrument.profiler") or false + local instrument_profiler = get_bool_default("instrument.profiler", false) if instrument_profiler then instrumentation.init_chatcommand() end diff --git a/builtin/profiler/instrumentation.lua b/builtin/profiler/instrumentation.lua index 4311215b2..be3a460e5 100644 --- a/builtin/profiler/instrumentation.lua +++ b/builtin/profiler/instrumentation.lua @@ -17,8 +17,9 @@ 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 profiler, sampler, get_bool_default = ... + +local instrument_builtin = get_bool_default("instrument.builtin", false) local register_functions = { register_globalstep = 0, @@ -137,7 +138,7 @@ local function instrument_register(func, func_name) end local function init_chatcommand() - if core.setting_getbool("instrument.chatcommand") or true then + if get_bool_default("instrument.chatcommand", true) then local orig_register_chatcommand = core.register_chatcommand core.register_chatcommand = function(cmd, def) def.func = instrument { @@ -153,8 +154,7 @@ end -- Start instrumenting selected functions -- local function init() - local is_set = core.setting_getbool - if is_set("instrument.entity") or true then + if get_bool_default("instrument.entity", true) then -- Explicitly declare entity api-methods. -- Simple iteration would ignore lookup via __index. local entity_instrumentation = { @@ -180,7 +180,7 @@ local function init() end end - if is_set("instrument.abm") or true then + if get_bool_default("instrument.abm", true) then -- Wrap register_abm() to automatically instrument abms. local orig_register_abm = core.register_abm core.register_abm = function(spec) @@ -193,7 +193,7 @@ local function init() end end - if is_set("instrument.lbm") or true then + if get_bool_default("instrument.lbm", true) then -- Wrap register_lbm() to automatically instrument lbms. local orig_register_lbm = core.register_lbm core.register_lbm = function(spec) @@ -206,13 +206,13 @@ local function init() end end - if is_set("instrument.global_callback") or true then + if get_bool_default("instrument.global_callback", 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 + if get_bool_default("instrument.profiler", false) then -- Measure overhead of instrumentation, but keep it down for functions -- So keep the `return` for better optimization. profiler.empty_instrument = instrument { diff --git a/builtin/profiler/reporter.lua b/builtin/profiler/reporter.lua index 5b38ed4df..fed47a36b 100644 --- a/builtin/profiler/reporter.lua +++ b/builtin/profiler/reporter.lua @@ -18,7 +18,7 @@ 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 core, settings = core, core.settings local reporter = {} --- @@ -229,7 +229,7 @@ end local worldpath = core.get_worldpath() local function get_save_path(format, filter) - local report_path = setting_get("profiler.report_path") or "" + local report_path = settings:get("profiler.report_path") or "" if report_path ~= "" then core.mkdir(sprintf("%s%s%s", worldpath, DIR_DELIM, report_path)) end @@ -249,7 +249,7 @@ end -- function reporter.save(profile, format, filter) if not format or format == "" then - format = setting_get("profiler.default_report_format") or "txt" + format = settings:get("profiler.default_report_format") or "txt" end if filter == "" then filter = nil diff --git a/builtin/profiler/sampling.lua b/builtin/profiler/sampling.lua index 1d1ef256d..4b53399a5 100644 --- a/builtin/profiler/sampling.lua +++ b/builtin/profiler/sampling.lua @@ -185,7 +185,7 @@ end function sampler.init() sampler.reset() - if core.setting_getbool("instrument.profiler") then + if core.settings:get_bool("instrument.profiler") then core.register_globalstep(function() if logged_time == 0 then return diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 1818b5a18..ba3339d32 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -107,6 +107,12 @@ continuous_forward (Continuous forward) bool false # Enable Joysticks enable_joysticks (Enable Joysticks) bool false +# The identifier of the joystick to use +joystick_id (Joystick ID) int 0 + +# The type of joystick +joystick_type (Joystick Type) enum auto auto,generic,xbox + # 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 @@ -156,6 +162,10 @@ keymap_chat (Chat key) key KEY_KEY_T # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_cmd (Command key) key / +# Key for opening the chat window to type local commands. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_cmd_local (Command key) key . + # Key for opening the chat console. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keyman_console (Console key) key KEY_F10 @@ -176,6 +186,26 @@ 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 selecting the next item in the hotbar. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_hotbar_next (Hotbar next key) key KEY_KEY_N + +# Key for selecting the previous item in the hotbar. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_hotbar_previous (Hotbar previous key) key KEY_KEY_B + +# Key for muting the game. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_mute (Mute key) key KEY_KEY_M + +# Key for increasing the volume. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_increase_volume (Inc. volume key) key + +# Key for decreasing the volume. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_decrease_volume (Dec. volume key) key + # Key for toggling autorun. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_autorun (Autorun key) key @@ -196,6 +226,10 @@ keymap_screenshot (Screenshot) key KEY_F12 # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_drop (Drop item key) key KEY_KEY_Q +# Key to use view zoom when possible. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_zoom (View zoom key) key KEY_KEY_Z + # Key for toggling the display of the HUD. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_toggle_hud (HUD toggle key) key KEY_F1 @@ -204,6 +238,10 @@ keymap_toggle_hud (HUD toggle key) key KEY_F1 # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_toggle_chat (Chat toggle key) key KEY_F2 +# Key for toggling the display of the large chat console. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_console (Large chat console key) key KEY_F10 + # Key for toggling the display of the fog. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_toggle_force_fog_off (Fog toggle key) key KEY_F3 @@ -264,12 +302,19 @@ show_entity_selectionbox (Show entity selection boxes) bool true # when connecting to the server. enable_remote_media_server (Connect to external media server) bool true +# Enable Lua modding support on client. +# This support is experimental and API can change. +enable_client_modding (Client modding) bool false + # URL to the server list displayed in the Multiplayer Tab. serverlist_url (Serverlist URL) string servers.minetest.net # File in client/serverlist/ that contains your favorite servers displayed in the Multiplayer Tab. serverlist_file (Serverlist file) string favoriteservers.txt +# Maximum size of the out chat queue. 0 to disable queueing and -1 to make the queue size unlimited +max_out_chat_queue_size (Maximum size of the out chat queue) int 20 + [*Graphics] [**In-Game] @@ -302,7 +347,10 @@ enable_clouds (Clouds) bool true enable_3d_clouds (3D clouds) bool true # Method used to highlight selected object. -node_highlighting (Node highlighting) enum box box,halo +node_highlighting (Node highlighting) enum box box,halo,none + +# Adds particles when digging a node. +enable_particles (Digging particles) bool true [***Filtering] @@ -337,12 +385,20 @@ texture_min_size (Minimum texture size for filters) int 64 # when set to higher number than 0. fsaa (FSAA) enum 0 0,1,2,4,8,16 +# Undersampling is similar to using lower screen resolution, but it applies +# to the game world only, keeping the GUI intact. +# It should give significant performance boost at the cost of less detailed image. +undersampling (Undersampling) enum 0 0,2,3,4 + [***Shaders] # Shaders allow advanced visual effects and may increase performance on some video cards. -# Thy only work with the OpenGL video backend. +# This only works with the OpenGL video backend. enable_shaders (Shaders) bool true +# Path to shader directory. If no path is defined, default location will be used. +shader_path (Shader path) path + [****Tone Mapping] # Enables filmic tone mapping @@ -377,7 +433,7 @@ enable_parallax_occlusion (Parallax occlusion) bool false parallax_occlusion_mode (Parallax occlusion mode) int 1 0 1 # Strength of parallax. -3d_parallax_strength (Parallax occlusion strength) float 0.025 +3d_paralax_strength (Parallax occlusion strength) float 0.025 # Number of parallax occlusion iterations. parallax_occlusion_iterations (Parallax occlusion iterations) int 4 @@ -426,6 +482,9 @@ screenW (Screen width) int 800 # Height component of the initial window size. screenH (Screen height) int 600 +# Save window size automatically when modified. +autosave_screensize (Autosave Screen Size) bool true + # Fullscreen mode. fullscreen (Full screen) bool false @@ -440,11 +499,11 @@ 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 +zoom_fov (Field of view for zoom) int 15 7 160 -# Adjust the gamma encoding for the light tables. Lower numbers are brighter. +# Adjust the gamma encoding for the light tables. Higher 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 +display_gamma (Gamma) float 2.2 1.0 3.0 # Path to texture directory. All textures are first searched from here. texture_path (Texture path) path @@ -459,13 +518,13 @@ cloud_height (Cloud height) int 120 # Values larger than 26 will start to produce sharp cutoffs at cloud area corners. cloud_radius (Cloud radius) int 12 -# Multiplier for view bobbing. +# Enable view bobbing and amount of view bobbing. # For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. -view_bobbing_amount (View bobbing) float 1.0 +view_bobbing_amount (View bobbing factor) float 1.0 # Multiplier for fall bobbing. # For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. -fall_bobbing_amount (Fall bobbing) float 0.0 +fall_bobbing_amount (Fall bobbing factor) float 0.0 # 3D support. # Currently supported: @@ -477,6 +536,9 @@ fall_bobbing_amount (Fall bobbing) float 0.0 # - pageflip: quadbuffer based 3d. 3d_mode (3D mode) enum none none,anaglyph,interlaced,topbottom,sidebyside,pageflip +# In-game chat console height, between 0.1 (10%) and 1.0 (100%). +console_height (Console height) float 1.0 0.1 1.0 + # In-game chat console background color (R,G,B). console_color (Console color) string (0,0,0) @@ -502,9 +564,21 @@ desynchronize_mapblock_texture_animation (Desynchronize block animation) bool tr # Useful if there's something to be displayed right or left of hotbar. hud_hotbar_max_width (Maximum hotbar width) float 1.0 +# Modifies the size of the hudbar elements. +hud_scaling (HUD scale factor) float 1.0 + # Enables caching of facedir rotated meshes. enable_mesh_cache (Mesh cache) bool false +# Delay between mesh updates on the client in ms. Increasing this will slow +# down the rate of mesh updates, thus reducing jitter on slower clients. +mesh_generation_interval (Mapblock mesh generation delay) int 0 0 50 + +# Size of the MapBlock cache of the mesh generator. Increasing this will +# increase the cache hit %, reducing the data being copied from the main +# thread, thus reducing jitter. +meshgen_block_cache_size (Mapblock mesh generator's MapBlock cache size MB) int 20 0 1000 + # Enables minimap. enable_minimap (Minimap) bool true @@ -528,9 +602,16 @@ 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 +# Android systems only: Tries to create inventory textures from meshes +# when no supported render was found. +inventory_image_hack (Inventory image hack) 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 +# Makes all liquids opaque +opaque_water (Opaque liquids) bool false + [**Menus] # Use a cloud animation for the main menu background. @@ -597,6 +678,10 @@ screenshot_quality (Screenshot quality) int 0 0 100 # Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens. screen_dpi (DPI) int 72 +# Windows systems only: Start Minetest with the command line window in the background. +# Contains the same information as the file debug.txt (default name). +enable_console (Enable console window) bool false + [*Sound] enable_sound (Sound) bool true @@ -703,9 +788,15 @@ map-dir (Map directory) path # Setting it to -1 disables the feature. item_entity_ttl (Item entity TTL) int 900 +# If enabled, show the server status message on player connection. +show_statusline_on_connect (Status message on connection) bool true + # Enable players getting damage and dying. enable_damage (Damage) bool false +# Enable creative mode for new created maps. +creative_mode (Creative) bool false + # A chosen map seed for a new map, leave empty for random. # Will be overridden when creating a new world in the main menu. fixed_map_seed (Fixed map seed) string @@ -758,7 +849,7 @@ active_object_send_range_blocks (Active object send range) int 3 # How large area of blocks are subject to the active block stuff, stated in mapblocks (16 nodes). # In active blocks objects are loaded and ABMs run. -active_block_range (Active block range) int 2 +active_block_range (Active block range) int 3 # From how far blocks are sent to clients, stated in mapblocks (16 nodes). max_block_send_distance (Max block send distance) int 10 @@ -793,9 +884,8 @@ movement_acceleration_fast (Fast mode acceleration) float 10 movement_speed_walk (Walking speed) float 4 movement_speed_crouch (Crouch speed) float 1.35 movement_speed_fast (Fast mode speed) float 20 -movement_speed_climb (Climbing speed) float 2 +movement_speed_climb (Climbing speed) float 3 movement_speed_jump (Jumping speed) float 6.5 -movement_speed_descend (Descending speed) float 6 movement_liquid_fluidity (Liquid fluidity) float 1 movement_liquid_fluidity_smooth (Liquid fluidity smoothing) float 0.5 movement_liquid_sink (Liquid sink) float 10 @@ -834,7 +924,7 @@ active_block_mgmt_interval (Active Block Management interval) float 2.0 abm_interval (Active Block Modifier interval) float 1.0 # Length of time between NodeTimer execution cycles -nodetimer_interval (NodeTimer interval) float 1.0 +nodetimer_interval (NodeTimer interval) float 0.2 # If enabled, invalid world data won't cause the server to shut down. # Only enable this if you know what you are doing. @@ -858,6 +948,12 @@ liquid_update (Liquid update tick) float 1.0 # Stated in mapblocks (16 nodes) block_send_optimize_distance (block send optimize distance) int 4 2 +# If enabled the server will perform map block occlusion culling based on +# on the eye position of the player. This can reduce the number of blocks +# sent to the client 50-80%. The client will not longer receive most invisible +# so that the utility of noclip mode is reduced. +server_side_occlusion_culling (Server side occlusion culling) bool true + [*Mapgen] # Name of map generator to be used when creating a new world. @@ -870,13 +966,10 @@ water_level (Water level) int 1 # From how far blocks are generated for clients, stated in mapblocks (16 nodes). max_block_generate_distance (Max block generate distance) int 6 -# Where the map generator stops. -# Please note: -# - Limited to 31000 (setting above has no effect) -# - The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks). -# - Those groups have an offset of -32, -32 nodes from the origin. -# - Only groups which are within the map_generation_limit are generated -map_generation_limit (Map generation limit) int 31000 0 31000 +# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0). +# Only mapchunks completely within the mapgen limit are generated. +# Value is stored per-world. +mapgen_limit (Map generation limit) int 31000 0 31000 # Global map generation attributes. # In Mapgen v6 the 'decorations' flag controls all decorations except trees @@ -909,23 +1002,63 @@ emergequeue_limit_generate (Limit of emerge queues to generate) int 32 # at the cost of slightly buggy caves. num_emerge_threads (Number of emerge threads) int 1 -# Noise parameters for biome API temperature, humidity and biome blend. -mg_biome_np_heat (Mapgen biome heat noise parameters) noise_params 50, 50, (750, 750, 750), 5349, 3, 0.5, 2.0 -mg_biome_np_heat_blend (Mapgen heat blend noise parameters) noise_params 0, 1.5, (8, 8, 8), 13, 2, 1.0, 2.0 -mg_biome_np_humidity (Mapgen biome humidity noise parameters) noise_params 50, 50, (750, 750, 750), 842, 3, 0.5, 2.0 -mg_biome_np_humidity_blend (Mapgen biome humidity blend noise parameters) noise_params 0, 1.5, (8, 8, 8), 90003, 2, 1.0, 2.0 +[***Biome API temperature and humidity noise parameters] + +# Temperature variation for biomes. +mg_biome_np_heat (Heat noise) noise_params 50, 50, (1000, 1000, 1000), 5349, 3, 0.5, 2.0 + +# Small-scale temperature variation for blending biomes on borders. +mg_biome_np_heat_blend (Heat blend noise) noise_params 0, 1.5, (8, 8, 8), 13, 2, 1.0, 2.0 + +# Humidity variation for biomes. +mg_biome_np_humidity (Humidity noise) noise_params 50, 50, (1000, 1000, 1000), 842, 3, 0.5, 2.0 + +# Small-scale humidity variation for blending biomes on borders. +mg_biome_np_humidity_blend (Humidity blend noise) noise_params 0, 1.5, (8, 8, 8), 90003, 2, 1.0, 2.0 [***Mapgen v5] -# Controls width of tunnels, a smaller value creates wider tunnels. -mgv5_cave_width (Mapgen v5 cave width) float 0.125 +# Map generation attributes specific to Mapgen v5. +# 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. +mgv5_spflags (Mapgen v5 specific flags) flags caverns caverns,nocaverns + +# Controls width of tunnels, a smaller value creates wider tunnels. +mgv5_cave_width (Cave width) float 0.125 + +# Y-level of cavern upper limit. +mgv5_cavern_limit (Cavern limit) int -256 + +# Y-distance over which caverns expand to full size. +mgv5_cavern_taper (Cavern taper) int 256 + +# Defines full size of caverns, smaller values create larger caverns. +mgv5_cavern_threshold (Cavern threshold) float 0.7 + +# Variation of biome filler depth. +mgv5_np_filler_depth (Filler depth noise) noise_params 0, 1, (150, 150, 150), 261, 4, 0.7, 2.0 + +# Variation of terrain vertical scale. +# When noise is < -0.55 terrain is near-flat. +mgv5_np_factor (Factor noise) noise_params 0, 1, (250, 250, 250), 920381, 3, 0.45, 2.0 + +# Y-level of average terrain surface. +mgv5_np_height (Height noise) noise_params 0, 10, (250, 250, 250), 84174, 4, 0.5, 2.0 + +# First of 2 3D noises that together define tunnels. +mgv5_np_cave1 (Cave1 noise) noise_params 0, 12, (50, 50, 50), 52534, 4, 0.5, 2.0 + +# Second of 2 3D noises that together define tunnels. +mgv5_np_cave2 (Cave2 noise) noise_params 0, 12, (50, 50, 50), 10325, 4, 0.5, 2.0 + +# 3D noise defining giant caverns. +mgv5_np_cavern (Cavern noise) noise_params 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0 -mgv5_np_filler_depth (Mapgen v5 filler depth noise parameters) noise_params 0, 1, (150, 150, 150), 261, 4, 0.7, 2.0 -mgv5_np_factor (Mapgen v5 factor noise parameters) noise_params 0, 1, (250, 250, 250), 920381, 3, 0.45, 2.0 -mgv5_np_height (Mapgen v5 height noise parameters) noise_params 0, 10, (250, 250, 250), 84174, 4, 0.5, 2.0 -mgv5_np_cave1 (Mapgen v5 cave1 noise parameters) noise_params 0, 12, (50, 50, 50), 52534, 4, 0.5, 2.0 -mgv5_np_cave2 (Mapgen v5 cave2 noise parameters) noise_params 0, 12, (50, 50, 50), 10325, 4, 0.5, 2.0 # TODO +# Noise parameters in group format, unsupported by advanced settings +# menu but settable in minetest.conf. +# See documentation of noise parameter formats in minetest.conf.example. +# 3D noise defining terrain. #mgv5_np_ground = { # offset = 0 # scale = 40 @@ -940,27 +1073,52 @@ mgv5_np_cave2 (Mapgen v5 cave2 noise parameters) noise_params 0, 12, (50, 50, 50 [***Mapgen v6] # Map generation attributes specific to Mapgen v6. -# When snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored. +# The 'snowbiomes' flag enables the new 5 biome system. +# When the new biome system is enabled jungles are automatically enabled and +# the 'jungles' flag is ignored. # 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 +mgv6_spflags (Mapgen v6 specific flags) flags jungles,biomeblend,mudflow,snowbiomes,trees jungles,biomeblend,mudflow,snowbiomes,flat,trees,nojungles,nobiomeblend,nomudflow,nosnowbiomes,noflat,notrees -# Controls size of deserts and beaches in Mapgen v6. -# When snowbiomes are enabled 'mgv6_freq_desert' is ignored. -mgv6_freq_desert (Mapgen v6 desert frequency) float 0.45 -mgv6_freq_beach (Mapgen v6 beach frequency) float 0.15 +# Deserts occur when np_biome exceeds this value. +# When the new biome system is enabled, this is ignored. +mgv6_freq_desert (Desert noise threshold) float 0.45 -mgv6_np_terrain_base (Mapgen v6 terrain base noise parameters) noise_params -4, 20, (250, 250, 250), 82341, 5, 0.6, 2.0 -mgv6_np_terrain_higher (Mapgen v6 terrain altitude noise parameters) noise_params 20, 16, (500, 500, 500), 85039, 5, 0.6, 2.0 -mgv6_np_steepness (Mapgen v6 steepness noise parameters) noise_params 0.85, 0.5, (125, 125, 125), -932, 5, 0.7, 2.0 -mgv6_np_height_select (Mapgen v6 height select noise parameters) noise_params 0.5, 1, (250, 250, 250), 4213, 5, 0.69, 2.0 -mgv6_np_mud (Mapgen v6 mud noise parameters) noise_params 4, 2, (200, 200, 200), 91013, 3, 0.55, 2.0 -mgv6_np_beach (Mapgen v6 beach noise parameters) noise_params 0, 1, (250, 250, 250), 59420, 3, 0.50, 2.0 -mgv6_np_biome (Mapgen v6 biome noise parameters) noise_params 0, 1, (500, 500, 500), 9130, 3, 0.50, 2.0 -mgv6_np_cave (Mapgen v6 cave noise parameters) noise_params 6, 6, (250, 250, 250), 34329, 3, 0.50, 2.0 -mgv6_np_humidity (Mapgen v6 humidity noise parameters) noise_params 0.5, 0.5, (500, 500, 500), 72384, 3, 0.50, 2.0 -mgv6_np_trees (Mapgen v6 trees noise parameters) noise_params 0, 1, (125, 125, 125), 2, 4, 0.66, 2.0 -mgv6_np_apple_trees (Mapgen v6 apple trees noise parameters) noise_params 0, 1, (100, 100, 100), 342902, 3, 0.45, 2.0 +# Sandy beaches occur when np_beach exceeds this value. +mgv6_freq_beach (Beach noise threshold) float 0.15 + +# Y-level of lower terrain and lakebeds. +mgv6_np_terrain_base (Terrain base noise) noise_params -4, 20, (250, 250, 250), 82341, 5, 0.6, 2.0 + +# Y-level of higher (cliff-top) terrain. +mgv6_np_terrain_higher (Terrain higher noise) noise_params 20, 16, (500, 500, 500), 85039, 5, 0.6, 2.0 + +# Varies steepness of cliffs. +mgv6_np_steepness (Steepness noise) noise_params 0.85, 0.5, (125, 125, 125), -932, 5, 0.7, 2.0 + +# Defines areas of 'terrain_higher' (cliff-top terrain). +mgv6_np_height_select (Height select noise) noise_params 0.5, 1, (250, 250, 250), 4213, 5, 0.69, 2.0 + +# Varies depth of biome surface nodes. +mgv6_np_mud (Mud noise) noise_params 4, 2, (200, 200, 200), 91013, 3, 0.55, 2.0 + +# Defines areas with sandy beaches. +mgv6_np_beach (Beach noise) noise_params 0, 1, (250, 250, 250), 59420, 3, 0.50, 2.0 + +# Temperature variation for biomes. +mgv6_np_biome (Biome noise) noise_params 0, 1, (500, 500, 500), 9130, 3, 0.50, 2.0 + +# Variation of number of caves. +mgv6_np_cave (Cave noise) noise_params 6, 6, (250, 250, 250), 34329, 3, 0.50, 2.0 + +# Humidity variation for biomes. +mgv6_np_humidity (Humidity noise) noise_params 0.5, 0.5, (500, 500, 500), 72384, 3, 0.50, 2.0 + +# Defines tree areas and tree density. +mgv6_np_trees (Trees noise) noise_params 0, 1, (125, 125, 125), 2, 4, 0.66, 2.0 + +# Defines areas where trees have apples. +mgv6_np_apple_trees (Apple trees noise) noise_params 0, 1, (100, 100, 100), 342902, 3, 0.45, 2.0 [***Mapgen v7] @@ -969,37 +1127,77 @@ mgv6_np_apple_trees (Mapgen v6 apple trees noise parameters) noise_params 0, 1, # 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,floatlands,nomountains,noridges,nofloatlands +mgv7_spflags (Mapgen v7 specific flags) flags mountains,ridges,nofloatlands,caverns mountains,ridges,floatlands,caverns,nomountains,noridges,nofloatlands,nocaverns # Controls width of tunnels, a smaller value creates wider tunnels. -mgv7_cave_width (Mapgen v7 cave width) float 0.09 +mgv7_cave_width (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 +mgv7_float_mount_density (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 +mgv7_float_mount_height (Floatland mountain height) float 128.0 # Y-level of floatland midpoint and lake surface. -mgv7_floatland_level (Mapgen v7 floatland level) int 1280 +mgv7_floatland_level (Floatland level) int 1280 # Y-level to which floatland shadows extend. -mgv7_shadow_limit (Mapgen v7 shadow limit) int 1024 +mgv7_shadow_limit (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 -mgv7_np_terrain_persist (Mapgen v7 terrain persistation noise parameters) noise_params 0.6, 0.1, (2000, 2000, 2000), 539, 3, 0.6, 2.0 -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 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 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 +# Y-level of cavern upper limit. +mgv7_cavern_limit (Cavern limit) int -256 + +# Y-distance over which caverns expand to full size. +mgv7_cavern_taper (Cavern taper) int 256 + +# Defines full size of caverns, smaller values create larger caverns. +mgv7_cavern_threshold (Cavern threshold) float 0.7 + +# Y-level of higher (cliff-top) terrain. +mgv7_np_terrain_base (Terrain base noise) noise_params 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0 + +# Y-level of lower terrain and lakebeds. +mgv7_np_terrain_alt (Terrain alt noise) noise_params 4, 25, (600, 600, 600), 5934, 5, 0.6, 2.0 + +# Varies roughness of terrain. +# Defines the 'persistence' value for terrain_base and terrain_alt noises. +mgv7_np_terrain_persist (Terrain persistence noise) noise_params 0.6, 0.1, (2000, 2000, 2000), 539, 3, 0.6, 2.0 + +# Defines areas of higher (cliff-top) terrain and affects steepness of cliffs. +mgv7_np_height_select (Height select noise) noise_params -8, 16, (500, 500, 500), 4213, 6, 0.7, 2.0 + +# Variation of biome filler depth. +mgv7_np_filler_depth (Filler depth noise) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 + +# Variation of maximum mountain height (in nodes). +mgv7_np_mount_height (Mountain height noise) noise_params 256, 112, (1000, 1000, 1000), 72449, 3, 0.6, 2.0 + +# Defines large-scale river channel structure. +mgv7_np_ridge_uwater (Ridge underwater noise) noise_params 0, 1, (1000, 1000, 1000), 85039, 5, 0.6, 2.0 + +# Defines areas of floatland smooth terrain. +# Smooth floatlands occur when noise > 0. +mgv7_np_floatland_base (Floatland base noise) noise_params -0.6, 1.5, (600, 600, 600), 114, 5, 0.6, 2.0 + +# Variation of hill height and lake depth on floatland smooth terrain. +mgv7_np_float_base_height (Floatland base height noise) noise_params 48, 24, (300, 300, 300), 907, 4, 0.7, 2.0 + +# 3D noise defining mountain structure and height. +# Also defines structure of floatland mountain terrain. +mgv7_np_mountain (Mountain noise) noise_params -0.6, 1, (250, 350, 250), 5333, 5, 0.63, 2.0 + +# 3D noise defining structure of river canyon walls. +mgv7_np_ridge (Ridge noise) noise_params 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0 + +# 3D noise defining giant caverns. +mgv7_np_cavern (Cavern noise) noise_params 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0 + +# First of 2 3D noises that together define tunnels. +mgv7_np_cave1 (Cave1 noise) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 + +# Second of 2 3D noises that together define tunnels. +mgv7_np_cave2 (Cave2 noise) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 [***Mapgen flat] @@ -1007,46 +1205,49 @@ mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (67, 67, 67 # Occasional lakes and hills can be added to the flat world. # 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 +mgflat_spflags (Mapgen flat specific flags) flags nolakes,nohills lakes,hills,nolakes,nohills # Y of flat ground. -mgflat_ground_level (Mapgen flat ground level) int 8 +mgflat_ground_level (Ground level) int 8 # Y of upper limit of large pseudorandom caves. -mgflat_large_cave_depth (Mapgen flat large cave depth) int -33 +mgflat_large_cave_depth (Large cave depth) int -33 # Controls width of tunnels, a smaller value creates wider tunnels. -mgflat_cave_width (Mapgen flat cave width) float 0.09 +mgflat_cave_width (Cave width) float 0.09 # Terrain noise threshold for lakes. # Controls proportion of world area covered by lakes. # Adjust towards 0.0 for a larger proportion. -mgflat_lake_threshold (Mapgen flat lake threshold) float -0.45 +mgflat_lake_threshold (Lake threshold) float -0.45 # Controls steepness/depth of lake depressions. -mgflat_lake_steepness (Mapgen flat lake steepness) float 48.0 +mgflat_lake_steepness (Lake steepness) float 48.0 # Terrain noise threshold for hills. # Controls proportion of world area covered by hills. # Adjust towards 0.0 for a larger proportion. -mgflat_hill_threshold (Mapgen flat hill threshold) float 0.45 +mgflat_hill_threshold (Hill threshold) float 0.45 # Controls steepness/height of hills. -mgflat_hill_steepness (Mapgen flat hill steepness) float 64.0 +mgflat_hill_steepness (Hill steepness) float 64.0 -# Determines terrain shape. -# The 3 numbers in brackets control the scale of the -# terrain, the 3 numbers should be identical. -mgflat_np_terrain (Mapgen flat terrain noise parameters) noise_params 0, 1, (600, 600, 600), 7244, 5, 0.6, 2.0 +# Defines location and terrain of optional hills and lakes. +mgflat_np_terrain (Terrain noise) 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, (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 +# Variation of biome filler depth. +mgflat_np_filler_depth (Filler depth noise) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 + +# First of 2 3D noises that together define tunnels. +mgflat_np_cave1 (Cave1 noise) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 + +# Second of 2 3D noises that together define tunnels. +mgflat_np_cave2 (Cave2 noise) 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.09 +mgfractal_cave_width (Cave width) float 0.09 # Choice of 18 fractals from 9 formulas. # 1 = 4D "Roundy" mandelbrot set. @@ -1067,48 +1268,55 @@ mgfractal_cave_width (Mapgen fractal cave width) float 0.09 # 16 = 3D "Cosine Mandelbulb" julia set. # 17 = 4D "Mandelbulb" mandelbrot set. # 18 = 4D "Mandelbulb" julia set. -mgfractal_fractal (Mapgen fractal fractal) int 1 1 18 +mgfractal_fractal (Fractal type) int 1 1 18 # Iterations of the recursive function. # Controls the amount of fine detail. -mgfractal_iterations (Mapgen fractal iterations) int 11 +mgfractal_iterations (Iterations) int 11 # Approximate (X,Y,Z) scale of fractal in nodes. -mgfractal_scale (Mapgen fractal scale) v3f (4096.0, 1024.0, 4096.0) +mgfractal_scale (Scale) v3f (4096.0, 1024.0, 4096.0) # (X,Y,Z) offset of fractal from world centre in units of 'scale'. # Used to move a suitable spawn area of low land close to (0, 0). # The default is suitable for mandelbrot sets, it needs to be edited for julia sets. # Range roughly -2 to 2. Multiply by 'scale' for offset in nodes. -mgfractal_offset (Mapgen fractal offset) v3f (1.79, 0.0, 0.0) +mgfractal_offset (Offset) v3f (1.79, 0.0, 0.0) # W co-ordinate of the generated 3D slice of a 4D fractal. # Determines which 3D slice of the 4D shape is generated. # Has no effect on 3D fractals. # Range roughly -2 to 2. -mgfractal_slice_w (Mapgen fractal slice w) float 0.0 +mgfractal_slice_w (Slice w) float 0.0 # Julia set only: X component of hypercomplex constant determining julia shape. # Range roughly -2 to 2. -mgfractal_julia_x (Mapgen fractal julia x) float 0.33 +mgfractal_julia_x (Julia x) float 0.33 # Julia set only: Y component of hypercomplex constant determining julia shape. # Range roughly -2 to 2. -mgfractal_julia_y (Mapgen fractal julia y) float 0.33 +mgfractal_julia_y (Julia y) float 0.33 # Julia set only: Z component of hypercomplex constant determining julia shape. # Range roughly -2 to 2. -mgfractal_julia_z (Mapgen fractal julia z) float 0.33 +mgfractal_julia_z (Julia z) float 0.33 # Julia set only: W component of hypercomplex constant determining julia shape. # Has no effect on 3D fractals. # Range roughly -2 to 2. -mgfractal_julia_w (Mapgen fractal julia w) float 0.33 +mgfractal_julia_w (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, (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 +# Y-level of seabed. +mgfractal_np_seabed (Seabed noise) noise_params -14, 9, (600, 600, 600), 41900, 5, 0.6, 2.0 + +# Variation of biome filler depth. +mgfractal_np_filler_depth (Filler depth noise) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 + +# First of 2 3D noises that together define tunnels. +mgfractal_np_cave1 (Cave1 noise) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 + +# Second of 2 3D noises that together define tunnels. +mgfractal_np_cave2 (Cave2 noise) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 # Mapgen Valleys parameters [***Mapgen Valleys] @@ -1186,7 +1394,7 @@ mgvalleys_np_inter_valley_slope (Valley Slope) noise_params 0.5, 0.5, (128, 128, [*Security] # Prevent mods from doing insecure things like running shell commands. -secure.enable_security (Enable mod security) bool false +secure.enable_security (Enable mod security) bool true # Comma-separated list of trusted mods that are allowed to access insecure # functions even when mod security is on (via request_insecure_environment()). @@ -1209,7 +1417,6 @@ profiler.load (Load the game profiler) bool false 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] @@ -1260,7 +1467,7 @@ language (Language) enum ,be,ca,cs,da,de,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko, # - action # - info # - verbose -debug_log_level (Debug log level) enum action ,warning,action,info,verbose +debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose # IPv6 support. enable_ipv6 (IPv6) bool true diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 71ded2b9d..7c5b9b613 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -37,7 +37,7 @@ const float fogShadingParameter = 1 / ( 1 - fogStart); vec3 uncharted2Tonemap(vec3 x) { - return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03334; + return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333; } vec4 applyToneMapping(vec4 color) @@ -163,7 +163,8 @@ void main(void) } #endif - if (GENERATE_NORMALMAPS == 1 && normalTexturePresent == false) { +#if GENERATE_NORMALMAPS == 1 + if (normalTexturePresent == false) { float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP)); float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP)); float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP)); @@ -177,7 +178,7 @@ void main(void) bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0); use_normalmap = true; } - +#endif vec4 base = texture2D(baseTexture, uv).rgba; #ifdef ENABLE_BUMPMAPPING @@ -200,20 +201,18 @@ void main(void) col = applyToneMapping(col); #endif - 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); - } + // 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; diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 44c48cc4c..3ac79c26d 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -1,7 +1,8 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; +// Color of the light emitted by the sun. +uniform vec3 dayLight; uniform vec3 eyePosition; uniform float animationTimer; @@ -14,6 +15,8 @@ varying vec3 tsEyeVec; varying vec3 tsLightVec; varying float area_enable_parallax; +// Color of the light emitted by the light sources. +const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const float e = 2.718281828459; const float BS = 10.0; @@ -119,31 +122,23 @@ float disp_z; v.z = dot(eyeVec, normal); tsEyeVec = normalize (v); + // Calculate color. + // Red, green and blue components are pre-multiplied with + // the brightness, so now we have to multiply these + // colors with the color of the incoming light. + // The pre-baked colors are halved to prevent overflow. vec4 color; - float day = gl_Color.r; - float night = gl_Color.g; - float light_source = gl_Color.b; - - float rg = mix(night, day, dayNightRatio); - rg += light_source * 2.5; // Make light sources brighter - float b = rg; - - // Moonlight is blue - b += (day - night) / 13.0; - rg -= (day - night) / 23.0; - + // The alpha gives the ratio of sunlight in the incoming light. + float nightRatio = 1 - gl_Color.a; + color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb + + nightRatio * artificialLight.rgb) * 2; + color.a = 1; + // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - b += max(0.0, (1.0 - abs(b - 0.13) / 0.17) * 0.025); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85) / 0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; + float brightness = (color.r + color.g + color.b) / 3; + color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) + + 0.07 * brightness); + gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0); } diff --git a/client/shaders/water_surface_shader/opengl_fragment.glsl b/client/shaders/water_surface_shader/opengl_fragment.glsl deleted file mode 100644 index c4e78470d..000000000 --- a/client/shaders/water_surface_shader/opengl_fragment.glsl +++ /dev/null @@ -1,176 +0,0 @@ -uniform sampler2D baseTexture; -uniform sampler2D normalTexture; -uniform sampler2D textureFlags; - -uniform vec4 skyBgColor; -uniform float fogDistance; -uniform vec3 eyePosition; - -varying vec3 vPosition; -varying vec3 worldPosition; - -varying vec3 eyeVec; -varying vec3 tsEyeVec; -varying vec3 lightVec; -varying vec3 tsLightVec; - -bool normalTexturePresent = false; -bool texTileableHorizontal = false; -bool texTileableVertical = false; -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 - -/* Hable's UC2 Tone mapping parameters - A = 0.22; - B = 0.30; - C = 0.10; - D = 0.20; - E = 0.01; - F = 0.30; - W = 11.2; - equation used: ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F -*/ - -vec3 uncharted2Tonemap(vec3 x) -{ - return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03334; -} - -vec4 applyToneMapping(vec4 color) -{ - color = vec4(pow(color.rgb, vec3(2.2)), color.a); - const float gamma = 1.6; - const float exposureBias = 5.5; - color.rgb = uncharted2Tonemap(exposureBias * color.rgb); - // Precalculated white_scale from - //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); - vec3 whiteScale = vec3(1.036015346); - color.rgb *= whiteScale; - return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a); -} -#endif - -void get_texture_flags() -{ - vec4 flags = texture2D(textureFlags, vec2(0.0, 0.0)); - if (flags.r > 0.5) { - normalTexturePresent = true; - } - if (flags.g > 0.5) { - texTileableHorizontal = true; - } - if (flags.b > 0.5) { - texTileableVertical = true; - } - if (texTileableHorizontal && texTileableVertical) { - texSeamless = true; - } -} - -float intensity(vec3 color) -{ - return (color.r + color.g + color.b) / 3.0; -} - -float get_rgb_height(vec2 uv) -{ - return intensity(texture2D(baseTexture,uv).rgb); -} - -vec4 get_normal_map(vec2 uv) -{ - vec4 bump = texture2D(normalTexture, uv).rgba; - bump.xyz = normalize(bump.xyz * 2.0 -1.0); - bump.y = -bump.y; - return bump; -} - -void main(void) -{ - vec3 color; - vec4 bump; - vec2 uv = gl_TexCoord[0].st; - bool use_normalmap = false; - get_texture_flags(); - -#ifdef ENABLE_PARALLAX_OCCLUSION - if (normalTexturePresent) { - vec3 tsEye = normalize(tsEyeVec); - float height = PARALLAX_OCCLUSION_SCALE * texture2D(normalTexture, uv).a - PARALLAX_OCCLUSION_BIAS; - uv = uv + texture2D(normalTexture, uv).z * height * vec2(tsEye.x,-tsEye.y); - } -#endif - -#ifdef USE_NORMALMAPS - if (normalTexturePresent) { - bump = get_normal_map(uv); - use_normalmap = true; - } -#endif - - if (GENERATE_NORMALMAPS == 1 && use_normalmap == false) { - float tl = get_rgb_height (vec2(uv.x-SAMPLE_STEP,uv.y+SAMPLE_STEP)); - float t = get_rgb_height (vec2(uv.x-SAMPLE_STEP,uv.y-SAMPLE_STEP)); - float tr = get_rgb_height (vec2(uv.x+SAMPLE_STEP,uv.y+SAMPLE_STEP)); - float r = get_rgb_height (vec2(uv.x+SAMPLE_STEP,uv.y)); - float br = get_rgb_height (vec2(uv.x+SAMPLE_STEP,uv.y-SAMPLE_STEP)); - float b = get_rgb_height (vec2(uv.x,uv.y-SAMPLE_STEP)); - float bl = get_rgb_height (vec2(uv.x-SAMPLE_STEP,uv.y-SAMPLE_STEP)); - float l = get_rgb_height (vec2(uv.x-SAMPLE_STEP,uv.y)); - float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl); - float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr); - bump = vec4 (normalize(vec3 (dX, -dY, NORMALMAPS_STRENGTH)),1.0); - use_normalmap = true; - } - -vec4 base = texture2D(baseTexture, uv).rgba; - -#ifdef ENABLE_BUMPMAPPING - if (use_normalmap) { - vec3 L = normalize(lightVec); - vec3 E = normalize(eyeVec); - float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0),0.5); - float diffuse = dot(E,bump.xyz); - /* Mathematic optimization - * Original: color = 0.05*base.rgb + diffuse*base.rgb + 0.2*specular*base.rgb; - * This optimization save 2 multiplications (orig: 4 multiplications + 3 additions - * end: 2 multiplications + 3 additions) - */ - color = (0.05 + diffuse + 0.2 * specular) * base.rgb; - } else { - color = base.rgb; - } -#else - color = base.rgb; -#endif - - vec4 col = vec4(color.rgb * gl_Color.rgb, 1.0); - -#ifdef ENABLE_TONE_MAPPING - col = applyToneMapping(col); -#endif - - 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; -} diff --git a/client/shaders/water_surface_shader/opengl_vertex.glsl b/client/shaders/water_surface_shader/opengl_vertex.glsl deleted file mode 100644 index a930e7b8f..000000000 --- a/client/shaders/water_surface_shader/opengl_vertex.glsl +++ /dev/null @@ -1,142 +0,0 @@ -uniform mat4 mWorldViewProj; -uniform mat4 mWorld; - -uniform float dayNightRatio; -uniform vec3 eyePosition; -uniform float animationTimer; - -varying vec3 vPosition; -varying vec3 worldPosition; - -varying vec3 eyeVec; -varying vec3 lightVec; -varying vec3 tsEyeVec; -varying vec3 tsLightVec; - -const float e = 2.718281828459; -const float BS = 10.0; - -float smoothCurve(float x) -{ - return x * x * (3.0 - 2.0 * x); -} -float triangleWave(float x) -{ - return abs(fract( x + 0.5 ) * 2.0 - 1.0); -} -float smoothTriangleWave(float x) -{ - return smoothCurve(triangleWave( x )) * 2.0 - 1.0; -} - -void main(void) -{ - gl_TexCoord[0] = gl_MultiTexCoord0; - -#if (MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_OPAQUE) && ENABLE_WAVING_WATER - vec4 pos = gl_Vertex; - pos.y -= 2.0; - - float posYbuf = (pos.z / WATER_WAVE_LENGTH + animationTimer * WATER_WAVE_SPEED * WATER_WAVE_LENGTH); - - pos.y -= sin(posYbuf) * WATER_WAVE_HEIGHT + sin(posYbuf / 7.0) * WATER_WAVE_HEIGHT; - gl_Position = mWorldViewProj * pos; -#elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES - vec4 pos = gl_Vertex; - vec4 pos2 = mWorld * gl_Vertex; - /* - * Mathematic optimization: pos2.x * A + pos2.z * A (2 multiplications + 1 addition) - * replaced with: (pos2.x + pos2.z) * A (1 addition + 1 multiplication) - * And bufferize calcul to a float - */ - float pos2XpZ = pos2.x + pos2.z; - pos.x += (smoothTriangleWave(animationTimer*10.0 + pos2XpZ * 0.01) * 2.0 - 1.0) * 0.4; - pos.y += (smoothTriangleWave(animationTimer*15.0 + pos2XpZ * -0.01) * 2.0 - 1.0) * 0.2; - pos.z += (smoothTriangleWave(animationTimer*10.0 + pos2XpZ * -0.01) * 2.0 - 1.0) * 0.4; - gl_Position = mWorldViewProj * pos; -#elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS - vec4 pos = gl_Vertex; - vec4 pos2 = mWorld * gl_Vertex; - if (gl_TexCoord[0].y < 0.05) { - /* - * Mathematic optimization: pos2.x * A + pos2.z * A (2 multiplications + 1 addition) - * replaced with: (pos2.x + pos2.z) * A (1 addition + 1 multiplication) - * And bufferize calcul to a float - */ - float pos2XpZ = pos2.x + pos2.z; - pos.x += (smoothTriangleWave(animationTimer * 20.0 + pos2XpZ * 0.1) * 2.0 - 1.0) * 0.8; - pos.y -= (smoothTriangleWave(animationTimer * 10.0 + pos2XpZ * -0.5) * 2.0 - 1.0) * 0.4; - } - gl_Position = mWorldViewProj * pos; -#else - gl_Position = mWorldViewProj * gl_Vertex; -#endif - - vPosition = gl_Position.xyz; - worldPosition = (mWorld * gl_Vertex).xyz; - vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0); - - vec3 normal, tangent, binormal; - normal = normalize(gl_NormalMatrix * gl_Normal); - if (gl_Normal.x > 0.5) { - // 1.0, 0.0, 0.0 - tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, -1.0)); - binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); - } else if (gl_Normal.x < -0.5) { - // -1.0, 0.0, 0.0 - tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); - binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); - } else if (gl_Normal.y > 0.5) { - // 0.0, 1.0, 0.0 - tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); - binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); - } else if (gl_Normal.y < -0.5) { - // 0.0, -1.0, 0.0 - tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); - binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0)); - } else if (gl_Normal.z > 0.5) { - // 0.0, 0.0, 1.0 - tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0)); - binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); - } else if (gl_Normal.z < -0.5) { - // 0.0, 0.0, -1.0 - tangent = normalize(gl_NormalMatrix * vec3(-1.0, 0.0, 0.0)); - binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0)); - } - mat3 tbnMatrix = mat3(tangent.x, binormal.x, normal.x, - tangent.y, binormal.y, normal.y, - tangent.z, binormal.z, normal.z); - - lightVec = sunPosition - worldPosition; - tsLightVec = lightVec * tbnMatrix; - eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; - tsEyeVec = eyeVec * tbnMatrix; - - vec4 color; - float day = gl_Color.r; - float night = gl_Color.g; - float light_source = gl_Color.b; - - float rg = mix(night, day, dayNightRatio); - rg += light_source * 2.5; // Make light sources brighter - float b = rg; - - // Moonlight is blue - b += (day - night) / 13.0; - rg -= (day - night) / 23.0; - - // Emphase blue a bit in darker places - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; - gl_FrontColor = gl_BackColor = clamp(color,0.0,1.0); -} diff --git a/client/shaders/wielded_shader/opengl_fragment.glsl b/client/shaders/wielded_shader/opengl_fragment.glsl index ba7a8f12d..546aef71d 100644 --- a/client/shaders/wielded_shader/opengl_fragment.glsl +++ b/client/shaders/wielded_shader/opengl_fragment.glsl @@ -75,7 +75,8 @@ void main(void) } #endif - if (GENERATE_NORMALMAPS == 1 && normalTexturePresent == false) { +#if GENERATE_NORMALMAPS == 1 + if (normalTexturePresent == false) { float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP)); float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP)); float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP)); @@ -89,6 +90,7 @@ void main(void) bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0); use_normalmap = true; } +#endif vec4 base = texture2D(baseTexture, uv).rgba; @@ -108,19 +110,18 @@ void main(void) vec4 col = vec4(color.rgb, base.a); col *= gl_Color; - 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); - } + // 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 86c626896..9f05b833a 100644 --- a/client/shaders/wielded_shader/opengl_vertex.glsl +++ b/client/shaders/wielded_shader/opengl_vertex.glsl @@ -1,7 +1,6 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; uniform vec3 eyePosition; uniform float animationTimer; diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua new file mode 100644 index 000000000..f3992612a --- /dev/null +++ b/clientmods/preview/init.lua @@ -0,0 +1,152 @@ +local modname = core.get_current_modname() or "??" +local modstorage = core.get_mod_storage() + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_shutdown(function() + print("[PREVIEW] shutdown client") +end) + +core.register_on_connect(function() + print("[PREVIEW] Player connection completed") + local server_info = core.get_server_info() + print("Server version: " .. server_info.protocol_version) + print("Server ip: " .. server_info.ip) + print("Server address: " .. server_info.address) + print("Server port: " .. server_info.port) +end) + +core.register_on_placenode(function(pointed_thing, node) + print("The local player place a node!") + print("pointed_thing :" .. dump(pointed_thing)) + print("node placed :" .. dump(node)) + return false +end) + +core.register_on_item_use(function(itemstack, pointed_thing) + print("The local player used an item!") + print("pointed_thing :" .. dump(pointed_thing)) + print("item = " .. itemstack:get_name()) + return false +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_receiving_chat_messages(function(message) + print("[PREVIEW] Received message " .. message) + return false +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_sending_chat_messages(function(message) + print("[PREVIEW] Sending message " .. message) + return false +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_hp_modification(function(hp) + print("[PREVIEW] HP modified " .. hp) +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_damage_taken(function(hp) + print("[PREVIEW] Damage taken " .. hp) +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_globalstep(function(dtime) + -- print("[PREVIEW] globalstep " .. dtime) +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_chatcommand("dump", { + func = function(param) + return true, dump(_G) + end, +}) + +core.register_chatcommand("colorize_test", { + func = function(param) + return true, core.colorize("red", param) + end, +}) + +core.register_chatcommand("test_node", { + func = function(param) + core.display_chat_message(dump(core.get_node({x=0, y=0, z=0}))) + core.display_chat_message(dump(core.get_node_or_nil({x=0, y=0, z=0}))) + end, +}) + +local function preview_minimap() + local minimap = core.ui.minimap + if not minimap then + print("[PREVIEW] Minimap is disabled. Skipping.") + return + end + minimap:set_mode(4) + minimap:show() + minimap:set_pos({x=5, y=50, z=5}) + minimap:set_shape(math.random(0, 1)) + + print("[PREVIEW] Minimap: mode => " .. dump(minimap:get_mode()) .. + " position => " .. dump(minimap:get_pos()) .. + " angle => " .. dump(minimap:get_angle())) +end + +core.after(2, function() + print("[PREVIEW] loaded " .. modname .. " mod") + modstorage:set_string("current_mod", modname) + print(modstorage:get_string("current_mod")) + preview_minimap() +end) + +core.after(5, function() + if core.ui.minimap then + core.ui.minimap:show() + end + + print("[PREVIEW] Day count: " .. core.get_day_count() .. + " time of day " .. core.get_timeofday()) + + print("[PREVIEW] Node level: " .. core.get_node_level({x=0, y=20, z=0}) .. + " max level " .. core.get_node_max_level({x=0, y=20, z=0})) + + print("[PREVIEW] Find node near: " .. dump(core.find_node_near({x=0, y=20, z=0}, 10, + {"group:tree", "default:dirt", "default:stone"}))) +end) + +core.register_on_dignode(function(pos, node) + print("The local player dug a node!") + print("pos:" .. dump(pos)) + print("node:" .. dump(node)) + return false +end) + +core.register_on_punchnode(function(pos, node) + print("The local player punched a node!") + local itemstack = core.get_wielded_item() + --[[ + -- getters + print(dump(itemstack:is_empty())) + print(dump(itemstack:get_name())) + print(dump(itemstack:get_count())) + print(dump(itemstack:get_wear())) + print(dump(itemstack:get_meta())) + print(dump(itemstack:get_metadata() + print(dump(itemstack:is_known())) + --print(dump(itemstack:get_definition())) + print(dump(itemstack:get_tool_capabilities())) + print(dump(itemstack:to_string())) + print(dump(itemstack:to_table())) + -- setters + print(dump(itemstack:set_name("default:dirt"))) + print(dump(itemstack:set_count("95"))) + print(dump(itemstack:set_wear(934))) + print(dump(itemstack:get_meta())) + print(dump(itemstack:get_metadata())) + --]] + print(dump(itemstack:to_table())) + print("pos:" .. dump(pos)) + print("node:" .. dump(node)) + return false +end) + diff --git a/cmake/Modules/FindGMP.cmake b/cmake/Modules/FindGMP.cmake index bb48289c5..64a57cfe0 100644 --- a/cmake/Modules/FindGMP.cmake +++ b/cmake/Modules/FindGMP.cmake @@ -1,4 +1,3 @@ - option(ENABLE_SYSTEM_GMP "Use GMP from system" TRUE) mark_as_advanced(GMP_LIBRARY GMP_INCLUDE_DIR) set(USE_SYSTEM_GMP FALSE) @@ -19,9 +18,9 @@ endif() if(NOT USE_SYSTEM_GMP) message(STATUS "Using bundled mini-gmp library.") - set(GMP_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/gmp) + set(GMP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/gmp) set(GMP_LIBRARY gmp) - add_subdirectory(gmp) + add_subdirectory(lib/gmp) endif() include(FindPackageHandleStandardArgs) diff --git a/cmake/Modules/FindJson.cmake b/cmake/Modules/FindJson.cmake index e69d6c4c0..26339a295 100644 --- a/cmake/Modules/FindJson.cmake +++ b/cmake/Modules/FindJson.cmake @@ -20,8 +20,7 @@ endif() if(NOT JSONCPP_FOUND) message(STATUS "Using bundled JSONCPP library.") - set(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/jsoncpp) + set(JSON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/jsoncpp) set(JSON_LIBRARY jsoncpp) - add_subdirectory(jsoncpp/json) + add_subdirectory(lib/jsoncpp) endif() - diff --git a/cmake/Modules/FindLua.cmake b/cmake/Modules/FindLua.cmake new file mode 100644 index 000000000..be5d92d8c --- /dev/null +++ b/cmake/Modules/FindLua.cmake @@ -0,0 +1,28 @@ +# Look for Lua library to use +# This selects LuaJIT by default + +option(ENABLE_LUAJIT "Enable LuaJIT support" TRUE) +set(USE_LUAJIT FALSE) +option(REQUIRE_LUAJIT "Require LuaJIT support" FALSE) +if(REQUIRE_LUAJIT) + set(ENABLE_LUAJIT TRUE) +endif() +if(ENABLE_LUAJIT) + find_package(LuaJIT) + if(LUAJIT_FOUND) + set(USE_LUAJIT TRUE) + message (STATUS "Using LuaJIT provided by system.") + elseif(REQUIRE_LUAJIT) + message(FATAL_ERROR "LuaJIT not found whereas REQUIRE_LUAJIT=\"TRUE\" is used.\n" + "To continue, either install LuaJIT or do not use REQUIRE_LUAJIT=\"TRUE\".") + endif() +else() + message (STATUS "LuaJIT detection disabled! (ENABLE_LUAJIT=0)") +endif() + +if(NOT USE_LUAJIT) + message(STATUS "LuaJIT not found, using bundled Lua.") + set(LUA_LIBRARY lua) + set(LUA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/lua/src) + add_subdirectory(lib/lua) +endif() diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md new file mode 100644 index 000000000..b3e494cc1 --- /dev/null +++ b/doc/client_lua_api.md @@ -0,0 +1,1149 @@ +Minetest Lua Client Modding API Reference 0.4.15 +================================================ +* More information at +* Developer Wiki: + +Introduction +------------ + +**WARNING: The client API is currently unstable, and may break/change without warning.** + +Content and functionality can be added to Minetest 0.4.15-dev+ by using Lua +scripting in run-time loaded mods. + +A mod is a self-contained bunch of scripts, textures and other related +things that is loaded by and interfaces with Minetest. + +Transfering client-sided mods form the server to the client is planned, but not implemented yet. + +If you see a deficiency in the API, feel free to attempt to add the +functionality in the engine and API. You can send such improvements as +source code patches on GitHub (https://github.com/minetest/minetest). + +Programming in Lua +------------------ +If you have any difficulty in understanding this, please read +[Programming in Lua](http://www.lua.org/pil/). + +Startup +------- +Mods are loaded during client startup from the mod load paths by running +the `init.lua` scripts in a shared environment. + +Paths +----- +* `RUN_IN_PLACE=1` (Windows release, local build) + * `$path_user`: + * Linux: `` + * Windows: `` + * `$path_share` + * Linux: `` + * Windows: `` +* `RUN_IN_PLACE=0`: (Linux release) + * `$path_share` + * Linux: `/usr/share/minetest` + * Windows: `/minetest-0.4.x` + * `$path_user`: + * Linux: `$HOME/.minetest` + * Windows: `C:/users//AppData/minetest` (maybe) + +Mod load path +------------- +Generic: + +* `$path_share/clientmods/` +* `$path_user/clientmods/` (User-installed mods) + +In a run-in-place version (e.g. the distributed windows version): + +* `minetest-0.4.x/clientmods/` (User-installed mods) + +On an installed version on Linux: + +* `/usr/share/minetest/clientmods/` +* `$HOME/.minetest/clientmods/` (User-installed mods) + +Modpack support +---------------- +**NOTE: Not implemented yet.** + +Mods can be put in a subdirectory, if the parent directory, which otherwise +should be a mod, contains a file named `modpack.txt`. This file shall be +empty, except for lines starting with `#`, which are comments. + +Mod directory structure +------------------------ + + clientmods + ├── modname + | ├── depends.txt + | ├── init.lua + └── another + +### modname +The location of this directory. + +### depends.txt +List of mods that have to be loaded before loading this mod. + +A single line contains a single modname. + +Optional dependencies can be defined by appending a question mark +to a single modname. Their meaning is that if the specified mod +is missing, that does not prevent this mod from being loaded. + +### init.lua +The main Lua script. Running this script should register everything it +wants to register. Subsequent execution depends on minetest calling the +registered callbacks. + +`minetest.setting_get(name)` and `minetest.setting_getbool(name)` can be used +to read custom or existing settings at load time, if necessary. + +### `sounds` +Media files (sounds) that will be transferred to the +client and will be available for use by the mod. + +Naming convention for registered textual names +---------------------------------------------- +Registered names should generally be in this format: + + "modname:" ( can have characters a-zA-Z0-9_) + +This is to prevent conflicting names from corrupting maps and is +enforced by the mod loader. + +### Example +In the mod `experimental`, there is the ideal item/node/entity name `tnt`. +So the name should be `experimental:tnt`. + +Enforcement can be overridden by prefixing the name with `:`. This can +be used for overriding the registrations of some other mod. + +Example: Any mod can redefine `experimental:tnt` by using the name + + :experimental:tnt + +when registering it. +(also that mod is required to have `experimental` as a dependency) + +The `:` prefix can also be used for maintaining backwards compatibility. + +Sounds +------ +**NOTE: max_hear_distance and connecting to objects is not implemented.** + +Only Ogg Vorbis files are supported. + +For positional playing of sounds, only single-channel (mono) files are +supported. Otherwise OpenAL will play them non-positionally. + +Mods should generally prefix their sounds with `modname_`, e.g. given +the mod name "`foomod`", a sound could be called: + + foomod_foosound.ogg + +Sounds are referred to by their name with a dot, a single digit and the +file extension stripped out. When a sound is played, the actual sound file +is chosen randomly from the matching sounds. + +When playing the sound `foomod_foosound`, the sound is chosen randomly +from the available ones of the following files: + +* `foomod_foosound.ogg` +* `foomod_foosound.0.ogg` +* `foomod_foosound.1.ogg` +* (...) +* `foomod_foosound.9.ogg` + +Examples of sound parameter tables: + + -- Play locationless + { + gain = 1.0, -- default + } + -- Play locationless, looped + { + gain = 1.0, -- default + loop = true, + } + -- Play in a location + { + pos = {x = 1, y = 2, z = 3}, + gain = 1.0, -- default + max_hear_distance = 32, -- default, uses an euclidean metric + } + -- Play connected to an object, looped + { + object = , + gain = 1.0, -- default + max_hear_distance = 32, -- default, uses an euclidean metric + loop = true, + } + +Looped sounds must either be connected to an object or played locationless. + +### 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}` + +Representations of simple things +-------------------------------- + +### Position/vector + + {x=num, y=num, z=num} + +For helper functions see "Vector helpers". + +### pointed_thing +* `{type="nothing"}` +* `{type="node", under=pos, above=pos}` +* `{type="object", id=ObjectID}` + +Flag Specifier Format +--------------------- +Flags using the standardized flag specifier format can be specified in either of +two ways, by string or table. + +The string format is a comma-delimited set of flag names; whitespace and +unrecognized flag fields are ignored. Specifying a flag in the string sets the +flag, and specifying a flag prefixed by the string `"no"` explicitly +clears the flag from whatever the default may be. + +In addition to the standard string flag format, the schematic flags field can +also be a table of flag names to boolean values representing whether or not the +flag is set. Additionally, if a field with the flag name prefixed with `"no"` +is present, mapped to a boolean of any value, the specified flag is unset. + +E.g. A flag field of value + + {place_center_x = true, place_center_y=false, place_center_z=true} + +is equivalent to + + {place_center_x = true, noplace_center_y=true, place_center_z=true} + +which is equivalent to + + "place_center_x, noplace_center_y, place_center_z" + +or even + + "place_center_x, place_center_z" + +since, by default, no schematic attributes are set. + +Formspec +-------- +Formspec defines a menu. It is a string, with a somewhat strange format. + +Spaces and newlines can be inserted between the blocks, as is used in the +examples. + +### Examples + +#### Chest + + size[8,9] + list[context;main;0,0;8,4;] + list[current_player;main;0,5;8,4;] + +#### Furnace + + size[8,9] + list[context;fuel;2,3;1,1;] + list[context;src;2,1;1,1;] + list[context;dst;5,1;2,2;] + list[current_player;main;0,5;8,4;] + +#### Minecraft-like player inventory + + size[8,7.5] + image[1,0.6;1,2;player.png] + list[current_player;main;0,3.5;8,4;] + list[current_player;craft;3,0;3,3;] + list[current_player;craftpreview;7,1;1,1;] + +### Elements + +#### `size[,,]` +* Define the size of the menu in inventory slots +* `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 + +#### `list[;;,;,;]` +* Show an inventory list + +#### `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 +* The first occurrence of an element inside the ring will + determine the inventory where items will be sent to + +#### `listring[]` +* Shorthand for doing `listring[;]` + for the last two inventory lists added by list[...] + +#### `listcolors[;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering + +#### `listcolors[;;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering +* Sets color of slots border + +#### `listcolors[;;;;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering +* Sets color of slots border +* Sets default background color of tooltips +* Sets default font color of tooltips + +#### `tooltip[;;,]` +* Adds tooltip for an element +* `` tooltip background color as `ColorString` (optional) +* `` tooltip font color as `ColorString` (optional) + +#### `image[,;,;]` +* Show an image +* Position and size units are inventory slots + +#### `item_image[,;,;]` +* Show an inventory image of registered item/node +* Position and size units are inventory slots + +#### `bgcolor[;]` +* Sets background color of formspec as `ColorString` +* If `true`, the background color is drawn fullscreen (does not effect the size of the formspec) + +#### `background[,;,;]` +* Use a background. Inventory rectangles are not drawn then. +* Position and size units are inventory slots +* Example for formspec 8x4 in 16x resolution: image shall be sized + 8 times 16px times 4 times 16px. + +#### `background[,;,;;]` +* Use a background. Inventory rectangles are not drawn then. +* Position and size units are inventory slots +* Example for formspec 8x4 in 16x resolution: + image shall be sized 8 times 16px times 4 times 16px +* If `true` the background is clipped to formspec size + (`x` and `y` are used as offset values, `w` and `h` are ignored) + +#### `pwdfield[,;,;;