diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..ae359f5d8 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,289 @@ +name: build + +# build on c/cpp changes or workflow changes +on: + push: + paths: + - 'lib/**.[ch]' + - 'lib/**.cpp' + - 'src/**.[ch]' + - 'src/**.cpp' + - '**/CMakeLists.txt' + - 'cmake/Modules/**' + - 'util/buildbot/**' + - 'util/ci/**' + - '.github/workflows/**.yml' + pull_request: + paths: + - 'lib/**.[ch]' + - 'lib/**.cpp' + - 'src/**.[ch]' + - 'src/**.cpp' + - '**/CMakeLists.txt' + - 'cmake/Modules/**' + - 'util/buildbot/**' + - 'util/ci/**' + - '.github/workflows/**.yml' + +jobs: + # This is our minor gcc compiler + gcc_6: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install deps + run: | + sudo apt-get install g++-6 gcc-6 -qyy + source ./util/ci/common.sh + install_linux_deps + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: gcc-6 + CXX: g++-6 + + - name: Test + run: | + ./bin/minetest --run-unittests + + # This is the current gcc compiler (available in bionic) + gcc_8: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install deps + run: | + sudo apt-get install g++-8 gcc-8 -qyy + source ./util/ci/common.sh + install_linux_deps + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: gcc-8 + CXX: g++-8 + + - name: Test + run: | + ./bin/minetest --run-unittests + + # This is our minor clang compiler + clang_3_9: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install deps + run: | + sudo apt-get install clang-3.9 -qyy + source ./util/ci/common.sh + install_linux_deps + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: clang-3.9 + CXX: clang++-3.9 + + - name: Test + run: | + ./bin/minetest --run-unittests + + # This is the current clang version + clang_9: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install deps + run: | + sudo apt-get install clang-9 valgrind -qyy + source ./util/ci/common.sh + install_linux_deps + env: + WITH_LUAJIT: 1 + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: clang-9 + CXX: clang++-9 + + - name: Test + run: | + ./bin/minetest --run-unittests + + - name: Valgrind + run: | + valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/minetest --run-unittests + + # Build with prometheus-cpp (server-only) + clang_9_prometheus: + name: "clang_9 (PROMETHEUS=1)" + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install deps + run: | + sudo apt-get install clang-9 -qyy + source ./util/ci/common.sh + install_linux_deps + + - name: Build prometheus-cpp + run: | + ./util/ci/build_prometheus_cpp.sh + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: clang-9 + CXX: clang++-9 + CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0" + + - name: Test + run: | + ./bin/minetestserver --run-unittests + + # Build without freetype (client-only) + clang_9_no_freetype: + name: "clang_9 (FREETYPE=0)" + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install deps + run: | + sudo apt-get install clang-9 -qyy + source ./util/ci/common.sh + install_linux_deps + + - name: Build + run: | + ./util/ci/build.sh + env: + CC: clang-9 + CXX: clang++-9 + CMAKE_FLAGS: "-DENABLE_FREETYPE=0 -DBUILD_SERVER=0" + + - name: Test + run: | + ./bin/minetest --run-unittests + + docker: + name: "Docker image" + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Build docker image + run: | + docker build . + + win32: + name: "MinGW cross-compiler (32-bit)" + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install compiler + run: | + sudo apt-get install gettext -qyy + wget http://minetest.kitsunemimi.pw/mingw-w64-i686_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz + sudo tar -xaf mingw.tar.xz -C /usr + + - name: Build + run: | + EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh winbuild + env: + NO_MINETEST_GAME: 1 + NO_PACKAGE: 1 + + win64: + name: "MinGW cross-compiler (64-bit)" + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install compiler + run: | + sudo apt-get install gettext -qyy + wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz + sudo tar -xaf mingw.tar.xz -C /usr + + - name: Build + run: | + EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh winbuild + env: + NO_MINETEST_GAME: 1 + NO_PACKAGE: 1 + + msvc: + name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }} + runs-on: windows-2019 + env: + VCPKG_VERSION: c7ab9d3110813979a873b2dbac630a9ab79850dc +# 2020.04 + vcpkg_packages: irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit + strategy: + fail-fast: false + matrix: + config: + - { + arch: x86, + generator: "-G'Visual Studio 16 2019' -A Win32", + vcpkg_triplet: x86-windows + } + - { + arch: x64, + generator: "-G'Visual Studio 16 2019' -A x64", + vcpkg_triplet: x64-windows + } + type: [portable] +# type: [portable, installer] +# The installer type is working, but disabled, to save runner jobs. +# Enable it, when working on the installer. + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Restore from cache and run vcpkg + uses: lukka/run-vcpkg@v2 + with: + vcpkgArguments: ${{env.vcpkg_packages}} + vcpkgDirectory: '${{ github.workspace }}\vcpkg' + appendedCacheKey: ${{ matrix.config.vcpkg_triplet }} + vcpkgGitCommitId: ${{ env.VCPKG_VERSION }} + vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }} + + - name: CMake + run: | + cmake ${{matrix.config.generator}} ` + -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" ` + -DCMAKE_BUILD_TYPE=Release ` + -DENABLE_POSTGRESQL=OFF ` + -DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} . + + - name: Build + run: cmake --build . --config Release + + - name: CPack + run: | + If ($env:TYPE -eq "installer") + { + cpack -G WIX -B "$env:GITHUB_WORKSPACE\Package" + } + ElseIf($env:TYPE -eq "portable") + { + cpack -G ZIP -B "$env:GITHUB_WORKSPACE\Package" + } + env: + TYPE: ${{matrix.type}} + + - name: Package Clean + run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages + + - uses: actions/upload-artifact@v1 + with: + name: msvc-${{ matrix.config.arch }}-${{ matrix.type }} + path: .\Package\ diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml new file mode 100644 index 000000000..1f97d105a --- /dev/null +++ b/.github/workflows/cpp_lint.yml @@ -0,0 +1,54 @@ +name: cpp_lint + +# lint on c/cpp changes or workflow changes +on: + push: + paths: + - 'lib/**.[ch]' + - 'lib/**.cpp' + - 'src/**.[ch]' + - 'src/**.cpp' + - '**/CMakeLists.txt' + - 'cmake/Modules/**' + - 'util/ci/**' + - '.github/workflows/**.yml' + pull_request: + paths: + - 'lib/**.[ch]' + - 'lib/**.cpp' + - 'src/**.[ch]' + - 'src/**.cpp' + - '**/CMakeLists.txt' + - 'cmake/Modules/**' + - 'util/ci/**' + - '.github/workflows/**.yml' + +jobs: + clang_format: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install clang-format + run: | + sudo apt-get install clang-format-9 -qyy + + - name: Run clang-format + run: | + source ./util/ci/lint.sh + perform_lint + env: + CLANG_FORMAT: clang-format-9 + + clang_tidy: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install deps + run: | + sudo apt-get install clang-tidy-9 -qyy + source ./util/ci/common.sh + install_linux_deps + + - name: Run clang-tidy + run: | + ./util/ci/clang-tidy.sh diff --git a/.github/workflows/lua_lint.yml b/.github/workflows/lua_lint.yml new file mode 100644 index 000000000..738e5afff --- /dev/null +++ b/.github/workflows/lua_lint.yml @@ -0,0 +1,32 @@ +name: lua_lint + +# Lint on lua changes on builtin or if workflow changed +on: + push: + paths: + - 'builtin/**.lua' + - '.github/workflows/**.yml' + pull_request: + paths: + - 'builtin/**.lua' + - '.github/workflows/**.yml' + +jobs: + luacheck: + name: "Builtin Luacheck and Unit Tests" + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Install luarocks + run: | + sudo apt-get install luarocks -qyy + + - name: Install luarocks tools + run: | + luarocks install --local luacheck + luarocks install --local busted + + - name: Run checks + run: | + $HOME/.luarocks/bin/luacheck builtin + $HOME/.luarocks/bin/busted builtin diff --git a/.gitignore b/.gitignore index 0f47efafa..78b047f32 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.swp *.bak* *.orig +.DS_Store # Vim *.vim # Kate @@ -24,8 +25,11 @@ gtags.files .idea # Codelite *.project -# Visual Studio Code +# Visual Studio Code & plugins .vscode/ +build/.cmake/ +# Gradle +.gradle ## Files related to Minetest development cycle /*.patch @@ -36,7 +40,7 @@ gtags.files ## Non-static Minetest directories or symlinks to these /bin/ /games/* -!/games/minimal/ +!/games/devtest/ /cache /textures/* !/textures/base/ @@ -72,17 +76,11 @@ doc/mkdocs/mkdocs.yml ## Build files CMakeFiles Makefile -!build/android/Makefile -build/android/path.cfg -build/android/*.apk -build/android/.externalNativeBuild cmake_install.cmake CMakeCache.txt CPackConfig.cmake CPackSourceConfig.cmake src/test_config.h -src/android_version.h -src/android_version_githash.h src/cmake_config.h src/cmake_config_githash.h src/unittest/test_world/world.mt @@ -104,16 +102,5 @@ cmake_config.h cmake_config_githash.h CMakeDoxy* compile_commands.json - -## 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 +*.apk +*.zip diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e4691284f..d03b7b601 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,7 +12,7 @@ variables: MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git" CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH -.build_template: &build_definition +.build_template: stage: build script: - mkdir cmakebuild @@ -27,7 +27,7 @@ variables: paths: - artifact/* -.debpkg_template: &debpkg_template +.debpkg_template: stage: package before_script: - apt-get update -y @@ -47,7 +47,7 @@ variables: paths: - ./*.deb -.debpkg_install: &debpkg_install +.debpkg_install: stage: deploy before_script: - apt-get update -y @@ -62,7 +62,7 @@ variables: # Jessie build:debian-8: - <<: *build_definition + extends: .build_template image: debian:8 before_script: - echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list @@ -74,46 +74,70 @@ build:debian-8: CXX: g++-6 package:debian-8: + extends: .debpkg_template image: debian:8 dependencies: - build:debian-8 variables: LEVELDB_PKG: libleveldb1 - <<: *debpkg_template deploy:debian-8: + extends: .debpkg_install image: debian:8 dependencies: - package:debian-8 variables: LEVELDB_PKG: libleveldb1 - <<: *debpkg_install # Stretch build:debian-9: - <<: *build_definition + extends: .build_template 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: + extends: .debpkg_template image: debian:9 dependencies: - build:debian-9 variables: LEVELDB_PKG: libleveldb1v5 - <<: *debpkg_template deploy:debian-9: + extends: .debpkg_install image: debian:9 dependencies: - package:debian-9 variables: LEVELDB_PKG: libleveldb1v5 - <<: *debpkg_install +# Stretch + +build:debian-10: + extends: .build_template + image: debian:10 + 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-10: + extends: .debpkg_template + image: debian:10 + dependencies: + - build:debian-10 + variables: + LEVELDB_PKG: libleveldb1d + +deploy:debian-10: + extends: .debpkg_install + image: debian:10 + dependencies: + - package:debian-10 + variables: + LEVELDB_PKG: libleveldb1d ## ## Ubuntu ## @@ -121,7 +145,7 @@ deploy:debian-9: # Trusty build:ubuntu-14.04: - <<: *build_definition + extends: .build_template image: ubuntu:trusty before_script: - echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list @@ -133,102 +157,53 @@ build:ubuntu-14.04: CXX: g++-6 package:ubuntu-14.04: + extends: .debpkg_template image: ubuntu:trusty dependencies: - build:ubuntu-14.04 variables: LEVELDB_PKG: libleveldb1 - <<: *debpkg_template deploy:ubuntu-14.04: + extends: .debpkg_install image: ubuntu:trusty dependencies: - package:ubuntu-14.04 variables: LEVELDB_PKG: libleveldb1 - <<: *debpkg_install # Xenial build:ubuntu-16.04: - <<: *build_definition + extends: .build_template 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: + extends: .debpkg_template image: ubuntu:xenial dependencies: - build:ubuntu-16.04 variables: LEVELDB_PKG: libleveldb1v5 - <<: *debpkg_template deploy:ubuntu-16.04: + extends: .debpkg_install 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 ## +# Do we need to support this old version ? build:fedora-24: - <<: *build_definition + extends: .build_template 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 @@ -238,17 +213,16 @@ build:fedora-24: ## Mingw for Windows ## -.generic_win_template: &generic_win_template +.generic_win_template: image: ubuntu:bionic before_script: - apt-get update -y - apt-get install -y wget xz-utils unzip git cmake gettext - wget -q http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz - - sed -e "s|%PREFIX%|${WIN_ARCH}-w64-mingw32|" -e "s|%ROOTPATH%|/usr/${WIN_ARCH}-w64-mingw32|" < util/travis/toolchain_mingw.cmake.in > ${TOOLCHAIN_OUTPUT} - tar -xaf mingw.tar.xz -C /usr -.build_win_template: &build_win_template - <<: *generic_win_template +.build_win_template: + extends: .generic_win_template stage: build artifacts: when: on_success @@ -256,8 +230,8 @@ build:fedora-24: paths: - build/* -.package_win_template: &package_win_template - <<: *generic_win_template +.package_win_template: + extends: .generic_win_template stage: package script: - cd build/minetest/_build @@ -275,40 +249,36 @@ build:fedora-24: - minetest-win-*/* build:win32: - <<: *build_win_template + extends: .build_win_template script: - ./util/buildbot/buildwin32.sh build variables: NO_PACKAGE: "1" WIN_ARCH: "i686" - TOOLCHAIN_OUTPUT: "util/buildbot/toolchain_mingw.cmake" package:win32: - <<: *package_win_template + extends: .package_win_template dependencies: - build:win32 variables: NO_PACKAGE: "1" WIN_ARCH: "i686" - TOOLCHAIN_OUTPUT: "util/buildbot/toolchain_mingw.cmake" build:win64: - <<: *build_win_template + extends: .build_win_template script: - ./util/buildbot/buildwin64.sh build variables: NO_PACKAGE: "1" WIN_ARCH: "x86_64" - TOOLCHAIN_OUTPUT: "util/buildbot/toolchain_mingw64.cmake" package:win64: - <<: *package_win_template + extends: .package_win_template dependencies: - build:win64 variables: NO_PACKAGE: "1" WIN_ARCH: "x86_64" - TOOLCHAIN_OUTPUT: "util/buildbot/toolchain_mingw64.cmake" package:docker: stage: package @@ -325,9 +295,8 @@ package:docker: pages: stage: deploy - image: python:3.7 + image: python:3.8 before_script: - - pip install pip==18.1 - pip install git+https://github.com/Python-Markdown/markdown.git - pip install git+https://github.com/mkdocs/mkdocs.git - pip install pygments @@ -338,3 +307,4 @@ pages: - public only: - master + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7b224b549..000000000 --- a/.travis.yml +++ /dev/null @@ -1,98 +0,0 @@ -language: cpp -before_install: ./util/travis/before_install.sh -script: ./util/travis/script.sh -os: linux -dist: bionic -group: edge -notifications: - email: false -matrix: - fast_finish: true - include: - - - env: CLANG_FORMAT=clang-format-8 - compiler: clang - os: linux - addons: - apt: - packages: ['clang-format-8'] - - - name: "Builtin Luacheck and Unit Tests" - language: generic - compiler: null - os: linux - addons: - apt: - packages: - - luarocks - before_install: - - luarocks install --local luacheck - - luarocks install --local busted - script: - - $HOME/.luarocks/bin/luacheck builtin - - $HOME/.luarocks/bin/busted builtin - - - env: CLANG_TIDY=clang-tidy-8 - compiler: clang - os: linux - script: ./util/travis/clangtidy.sh - addons: - apt: - packages: ['clang-tidy-8'] - - - name: "MinGW cross-compiler (32-bit)" - env: PLATFORM=Win32 - compiler: gcc - os: linux - - - name: "MinGW cross-compiler (64-bit)" - env: PLATFORM=Win64 - compiler: gcc - os: linux - -# - env: PLATFORM=Unix -# compiler: clang -# os: osx -# osx_image: xcode8 - - - env: PLATFORM=Unix COMPILER=gcc-6 - compiler: gcc - os: linux - addons: - apt: - packages: ['gcc-6', 'g++-6'] - - - env: PLATFORM=Unix COMPILER=gcc-8 - compiler: gcc - os: linux - addons: - apt: - packages: ['gcc-8', 'g++-8'] - - - env: PLATFORM=Unix COMPILER=clang-3.9 - compiler: clang - os: linux - addons: - apt: - packages: ['clang-3.9'] - - - env: PLATFORM=Unix COMPILER=clang-9 - compiler: clang - os: linux - addons: - apt: - packages: ['clang-9'] - - - env: PLATFORM=Unix COMPILER=clang-9 FREETYPE=0 - compiler: clang - os: linux - addons: - apt: - packages: ['clang-9'] - - - env: PLATFORM=Unix COMPILER=clang-9 VALGRIND=1 - compiler: clang - os: linux - addons: - apt: - packages: ['valgrind', 'clang-9'] diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f36037ef..69424793f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ endif() # This can be read from ${PROJECT_NAME} after project() is called project(minetest) -set(PROJECT_NAME_CAPITALIZED "Minetest") +set(PROJECT_NAME_CAPITALIZED "Dragonfire") # Works only for cmake 3.1 and greater set(CMAKE_CXX_STANDARD 11) @@ -16,7 +16,7 @@ set(CLANG_MINIMUM_VERSION "3.4") # Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing set(VERSION_MAJOR 5) -set(VERSION_MINOR 2) +set(VERSION_MINOR 4) set(VERSION_PATCH 0) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") @@ -49,6 +49,7 @@ set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL set(BUILD_CLIENT TRUE CACHE BOOL "Build client") set(BUILD_SERVER FALSE CACHE BOOL "Build server") +set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests") set(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build") @@ -102,7 +103,7 @@ elseif(UNIX) # Linux, BSD etc set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications") set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/metainfo") set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons") - set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/locale") + set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/locale") endif() endif() @@ -166,7 +167,7 @@ endif() install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game" DESTINATION "${SHAREDIR}/games/" COMPONENT "SUBGAME_MINETEST_GAME" OPTIONAL PATTERN ".git*" EXCLUDE ) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minimal" DESTINATION "${SHAREDIR}/games/" +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/devtest" DESTINATION "${SHAREDIR}/games/" COMPONENT "SUBGAME_MINIMAL" OPTIONAL PATTERN ".git*" EXCLUDE ) if(BUILD_CLIENT) @@ -253,8 +254,8 @@ cpack_add_component(SUBGAME_MINETEST_GAME ) cpack_add_component(SUBGAME_MINIMAL - DISPLAY_NAME "Minimal development test" - DESCRIPTION "A minimal subgame helping to develop the engine." + DISPLAY_NAME "Development Test" + DESCRIPTION "A minimal test game helping to develop the engine." DISABLED #DISABLED does not mean it is disabled, and is just not selected by default. GROUP "Subgames" ) @@ -325,4 +326,3 @@ if(DOXYGEN_FOUND) COMMENT "Generating API documentation with Doxygen" VERBATIM ) endif() - diff --git a/Dockerfile b/Dockerfile index 37b90e483..72343ab9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,59 @@ -FROM debian:stretch +FROM alpine:3.11 -USER root -RUN apt-get update -y && \ - apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev \ - libsqlite3-dev libcurl4-gnutls-dev zlib1g-dev libgmp-dev libjsoncpp-dev git +ENV MINETEST_GAME_VERSION master -COPY . /usr/src/minetest +COPY .git /usr/src/minetest/.git +COPY CMakeLists.txt /usr/src/minetest/CMakeLists.txt +COPY README.md /usr/src/minetest/README.md +COPY minetest.conf.example /usr/src/minetest/minetest.conf.example +COPY builtin /usr/src/minetest/builtin +COPY cmake /usr/src/minetest/cmake +COPY doc /usr/src/minetest/doc +COPY fonts /usr/src/minetest/fonts +COPY lib /usr/src/minetest/lib +COPY misc /usr/src/minetest/misc +COPY po /usr/src/minetest/po +COPY src /usr/src/minetest/src +COPY textures /usr/src/minetest/textures -RUN mkdir -p /usr/src/minetest/cmakebuild && cd /usr/src/minetest/cmakebuild && \ - cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE \ +WORKDIR /usr/src/minetest + +RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \ + jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \ + libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \ + gmp-dev jsoncpp-dev postgresql-dev ca-certificates && \ + git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \ + rm -fr ./games/minetest_game/.git + +WORKDIR /usr/src/ +RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \ + mkdir prometheus-cpp/build && \ + cd prometheus-cpp/build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DCMAKE_BUILD_TYPE=Release \ + -DENABLE_TESTING=0 && \ + make -j2 && \ + make install + +WORKDIR /usr/src/minetest +RUN mkdir build && \ + cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DCMAKE_BUILD_TYPE=Release \ -DBUILD_SERVER=TRUE \ - -DBUILD_CLIENT=FALSE \ - -DENABLE_SYSTEM_JSONCPP=1 \ - .. && \ - make -j2 && \ - rm -Rf ../games/minetest_game && git clone --depth 1 https://github.com/minetest/minetest_game ../games/minetest_game && \ - rm -Rf ../games/minetest_game/.git && \ - make install + -DENABLE_PROMETHEUS=TRUE \ + -DBUILD_UNITTESTS=FALSE \ + -DBUILD_CLIENT=FALSE && \ + make -j2 && \ + make install -FROM debian:stretch +FROM alpine:3.11 -USER root -RUN groupadd minetest && useradd -m -g minetest -d /var/lib/minetest minetest && \ - apt-get update -y && \ - apt-get -y install libcurl3-gnutls libjsoncpp1 liblua5.1-0 libluajit-5.1-2 libpq5 libsqlite3-0 \ - libstdc++6 zlib1g libc6 && \ - apt-get clean && rm -rf /var/cache/apt/archives/* && \ - rm -rf /var/lib/apt/lists/* +RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq && \ + adduser -D minetest --uid 30000 -h /var/lib/minetest && \ + chown -R minetest:minetest /var/lib/minetest WORKDIR /var/lib/minetest @@ -34,8 +61,8 @@ COPY --from=0 /usr/local/share/minetest /usr/local/share/minetest COPY --from=0 /usr/local/bin/minetestserver /usr/local/bin/minetestserver COPY --from=0 /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/minetest.conf -USER minetest +USER minetest:minetest -EXPOSE 30000/udp +EXPOSE 30000/udp 30000/tcp CMD ["/usr/local/bin/minetestserver", "--config", "/etc/minetest/minetest.conf"] diff --git a/LICENSE.txt b/LICENSE.txt index 994e024c9..f5c51833b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -21,6 +21,12 @@ ShadowNinja: paramat: textures/base/pack/menu_header.png + textures/base/pack/next_icon.png + textures/base/pack/prev_icon.png + +rubenwardy, paramat: + textures/base/pack/start_icon.png + textures/base/pack/end_icon.png erlehmann: misc/minetest-icon-24x24.png diff --git a/README.md b/README.md index 4d136b137..6a3c11f40 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ Minetest ======== -[![Build Status](https://travis-ci.org/minetest/minetest.svg?branch=master)](https://travis-ci.org/minetest/minetest) +![Build Status](https://github.com/minetest/minetest/workflows/build/badge.svg) [![Translation status](https://hosted.weblate.org/widgets/minetest/-/svg-badge.svg)](https://hosted.weblate.org/engage/minetest/?utm_source=widget) [![License](https://img.shields.io/badge/license-LGPLv2.1%2B-blue.svg)](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) Minetest is a free open-source voxel game engine with easy modding and game creation. -Copyright (C) 2010-2019 Perttu Ahola +Copyright (C) 2010-2020 Perttu Ahola and contributors (see source file comments and the version control log) In case you downloaded the source code @@ -69,15 +69,15 @@ Some can be changed in the key config dialog in the settings tab. | J | Enable/disable fast mode (needs fast privilege) | | H | Enable/disable noclip mode (needs noclip privilege) | | E | Move fast in fast mode | +| C | Cycle through camera modes | +| V | Cycle through minimap modes | +| Shift + V | Change minimap orientation | | F1 | Hide/show HUD | | F2 | Hide/show chat | | F3 | Disable/enable fog | | F4 | Disable/enable camera update (Mapblocks are not updated anymore when disabled, disabled in release builds) | | F5 | Cycle through debug information screens | | F6 | Cycle through profiler info screens | -| F7 | Cycle through camera modes | -| F9 | Cycle through minimap modes | -| Shift + F9 | Change minimap orientation | | F10 | Show/hide console | | F12 | Take screenshot | @@ -173,7 +173,7 @@ Download source (this is the URL to the latest of source repository, which might git clone --depth 1 https://github.com/minetest/minetest.git cd minetest -Download minetest_game (otherwise only the "Minimal development test" game is available) using Git: +Download minetest_game (otherwise only the "Development Test" game is available) using Git: git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game @@ -218,6 +218,7 @@ General options and their default values: BUILD_CLIENT=TRUE - Build Minetest client BUILD_SERVER=FALSE - Build Minetest server + BUILD_UNITTESTS=TRUE - Build unittest sources CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug) Release - Release build Debug - Debug build @@ -235,6 +236,7 @@ General options and their default values: ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua) + ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default) ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp) ENABLE_SYSTEM_JSONCPP=OFF - Use JsonCPP from system OPENGL_GL_PREFERENCE=LEGACY - Linux client build only; See CMake Policy CMP0072 for reference diff --git a/build/android/.gitignore b/build/android/.gitignore new file mode 100644 index 000000000..e0613f8b3 --- /dev/null +++ b/build/android/.gitignore @@ -0,0 +1,11 @@ +*.iml +.externalNativeBuild +.gradle +app/build +app/release +app/src/main/assets +build +local.properties +native/.* +native/build +native/deps diff --git a/build/android/Makefile b/build/android/Makefile deleted file mode 100644 index 9ec237a75..000000000 --- a/build/android/Makefile +++ /dev/null @@ -1,763 +0,0 @@ -# build options - -OS := $(shell uname) - -# compile with GPROF -# GPROF = 1 - -# build for build platform -API = 14 -APP_PLATFORM = android-$(API) - -ANDR_ROOT = $(shell pwd) -PROJ_ROOT = $(shell realpath $(ANDR_ROOT)/../..) -APP_ROOT = $(ANDR_ROOT)/src/main - -VERSION_MAJOR := $(shell cat $(PROJ_ROOT)/CMakeLists.txt | \ - grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | cut -f2 -d' ') -VERSION_MINOR := $(shell cat $(PROJ_ROOT)/CMakeLists.txt | \ - grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | cut -f2 -d' ') -VERSION_PATCH := $(shell cat $(PROJ_ROOT)/CMakeLists.txt | \ - grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | cut -f2 -d' ') - -################################################################################ -# toolchain config for arm new processors -################################################################################ -TARGET_HOST = arm-linux -TARGET_ABI = armeabi-v7a -TARGET_LIBDIR = armeabi-v7a -TARGET_TOOLCHAIN = arm-linux-androideabi- -TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3 -TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON) -TARGET_ARCH = armv7 -CROSS_CC = clang -CROSS_CXX = clang++ -COMPILER_VERSION = clang -HAVE_LEVELDB = 0 - -################################################################################ -# toolchain config for little endian mips -################################################################################ -#TARGET_HOST = mipsel-linux -#TARGET_ABI = mips -#TARGET_LIBDIR = mips -#TARGET_TOOLCHAIN = mipsel-linux-android- -#TARGET_ARCH = mips32 -#CROSS_CC = mipsel-linux-android-gcc -#CROSS_CXX = mipsel-linux-android-g++ -#COMPILER_VERSION = 4.9 -#HAVE_LEVELDB = 0 - -################################################################################ -# toolchain config for x86 -################################################################################ -#TARGET_HOST = x86-linux -#TARGET_ABI = x86 -#TARGET_LIBDIR = x86 -#TARGET_TOOLCHAIN = x86- -#TARGET_ARCH = x86 -#CROSS_CC = clang -#CROSS_CXX = clang++ -#COMPILER_VERSION = clang -#HAVE_LEVELDB = 0 - -################################################################################ -ASSETS_TIMESTAMP = deps/assets_timestamp - -LEVELDB_DIR = $(ANDR_ROOT)/deps/leveldb/ -LEVELDB_LIB = $(LEVELDB_DIR)libleveldb.a -LEVELDB_TIMESTAMP = $(LEVELDB_DIR)/timestamp -LEVELDB_TIMESTAMP_INT = $(ANDR_ROOT)/deps/leveldb_timestamp -LEVELDB_URL_GIT = https://github.com/google/leveldb -LEVELDB_COMMIT = 2d0320a458d0e6a20fff46d5f80b18bfdcce7018 - -OPENAL_DIR = $(ANDR_ROOT)/deps/openal-soft/ -OPENAL_LIB = $(OPENAL_DIR)libs/$(TARGET_ABI)/libopenal.so -OPENAL_TIMESTAMP = $(OPENAL_DIR)/timestamp -OPENAL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/openal_timestamp -OPENAL_URL_GIT = https://github.com/apportable/openal-soft - -OGG_DIR = $(ANDR_ROOT)/deps/libvorbis-libogg-android/ -OGG_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so -VORBIS_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so -OGG_TIMESTAMP = $(OGG_DIR)timestamp -OGG_TIMESTAMP_INT = $(ANDR_ROOT)/deps/ogg_timestamp -OGG_URL_GIT = https://gitlab.com/minetest/libvorbis-libogg-android - -IRRLICHT_REVISION = 5150 -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.2n -OPENSSL_BASEDIR = openssl-$(OPENSSL_VERSION) -OPENSSL_DIR = $(ANDR_ROOT)/deps/$(OPENSSL_BASEDIR)/ -OPENSSL_LIB = $(OPENSSL_DIR)/libssl.a -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.60.0 -CURL_DIR = $(ANDR_ROOT)/deps/curl-$(CURL_VERSION) -CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a -CURL_TIMESTAMP = $(CURL_DIR)/timestamp -CURL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/curl_timestamp -CURL_URL_HTTP = https://curl.haxx.se/download/curl-${CURL_VERSION}.tar.bz2 - -FREETYPE_DIR = $(ANDR_ROOT)/deps/freetype2-android/ -FREETYPE_LIB = $(FREETYPE_DIR)/Android/obj/local/$(TARGET_ABI)/libfreetype2-static.a -FREETYPE_TIMESTAMP = $(FREETYPE_DIR)timestamp -FREETYPE_TIMESTAMP_INT = $(ANDR_ROOT)/deps/freetype_timestamp -FREETYPE_URL_GIT = https://github.com/cdave1/freetype2-android - -ICONV_VERSION = 1.16 -ICONV_DIR = $(ANDR_ROOT)/deps/libiconv/ -ICONV_LIB = $(ICONV_DIR)/lib/.libs/libiconv.so -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-3240000 -SQLITE3_URL = https://www.sqlite.org/2018/$(SQLITE3_FOLDER).zip - -ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') -ANDROID_NDK = $(shell grep '^ndk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') - -#use interim target variable to switch leveldb on or off -ifeq ($(HAVE_LEVELDB),1) - LEVELDB_TARGET = $(LEVELDB_LIB) -endif - -.PHONY : debug release reconfig delconfig \ - leveldb_download clean_leveldb leveldb\ - irrlicht_download clean_irrlicht irrlicht \ - clean_assets assets sqlite3_download \ - freetype_download clean_freetype freetype \ - apk clean_apk \ - clean_all clean prep_srcdir \ - install_debug install_release envpaths all \ - $(ASSETS_TIMESTAMP) $(LEVELDB_TIMESTAMP) \ - $(OPENAL_TIMESTAMP) $(OGG_TIMESTAMP) \ - $(IRRLICHT_TIMESTAMP) $(CURL_TIMESTAMP) \ - $(OPENSSL_TIMESTAMP) \ - $(ANDR_ROOT)/jni/src/android_version.h \ - $(ANDR_ROOT)/jni/src/android_version_githash.h - -debug : local.properties - export NDEBUG=; \ - export BUILD_TYPE=debug; \ - $(MAKE) apk - -all : debug release - -release : local.properties - @export NDEBUG=1; \ - export BUILD_TYPE=release; \ - $(MAKE) apk - -reconfig: delconfig - @$(MAKE) local.properties - -delconfig: - $(RM) local.properties - -local.properties: - @echo "Please specify path of ANDROID NDK"; \ - echo "e.g. $$HOME/Android/Sdk/ndk-bundle/"; \ - read ANDROID_NDK ; \ - if [ ! -d $$ANDROID_NDK ] ; then \ - echo "$$ANDROID_NDK is not a valid folder"; \ - exit 1; \ - fi; \ - echo "ndk.dir = $$ANDROID_NDK" > local.properties; \ - echo "Please specify path of ANDROID SDK"; \ - echo "e.g. $$HOME/Android/Sdk/"; \ - read SDKFLDR ; \ - if [ ! -d $$SDKFLDR ] ; then \ - echo "$$SDKFLDR is not a valid folder"; \ - exit 1; \ - fi; \ - echo "sdk.dir = $$SDKFLDR" >> local.properties; - - -$(OPENAL_TIMESTAMP) : openal_download - @LAST_MODIF=$$(find ${OPENAL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${OPENAL_TIMESTAMP}; \ - fi - -openal_download : - @if [ ! -d ${OPENAL_DIR} ] ; then \ - echo "openal sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps ; \ - git clone ${OPENAL_URL_GIT} || exit 1; \ - fi - -openal : $(OPENAL_LIB) - -$(OPENAL_LIB): $(OPENAL_TIMESTAMP) - + @REFRESH=0; \ - if [ ! -e ${OPENAL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${OPENAL_TIMESTAMP} -nt ${OPENAL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "changed timestamp for openal detected building..."; \ - cd ${OPENAL_DIR}; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - export TARGET_CFLAGS_ADDON="${TARGET_CFLAGS_ADDON}"; \ - export TARGET_CXXFLAGS_ADDON="${TARGET_CXXFLAGS_ADDON}"; \ - export COMPILER_VERSION=${COMPILER_VERSION}; \ - ${ANDROID_NDK}/ndk-build \ - NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ - touch ${OPENAL_TIMESTAMP}; \ - touch ${OPENAL_TIMESTAMP_INT}; \ - else \ - echo "nothing to be done for openal"; \ - fi - -clean_openal : - $(RM) -rf ${OPENAL_DIR} - -$(OGG_TIMESTAMP) : ogg_download - @LAST_MODIF=$$(find ${OGG_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${OGG_TIMESTAMP}; \ - fi - -ogg_download : - @if [ ! -d ${OGG_DIR} ] ; then \ - echo "ogg sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps ; \ - git clone ${OGG_URL_GIT}|| exit 1; \ - cd libvorbis-libogg-android ; \ - patch -p1 < ${ANDR_ROOT}/patches/libvorbis-libogg-fpu.patch || exit 1; \ - fi - -ogg : $(OGG_LIB) - -$(OGG_LIB): $(OGG_TIMESTAMP) - + @REFRESH=0; \ - if [ ! -e ${OGG_TIMESTAMP_INT} ] ; then \ - echo "${OGG_TIMESTAMP_INT} doesn't exist"; \ - REFRESH=1; \ - fi; \ - if [ ${OGG_TIMESTAMP} -nt ${OGG_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "changed timestamp for ogg detected building..."; \ - cd ${OGG_DIR}; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --install-dir=$${TOOLCHAIN}; \ - touch ${OGG_TIMESTAMP}; \ - touch ${OGG_TIMESTAMP_INT}; \ - else \ - echo "nothing to be done for libogg/libvorbis"; \ - fi - -clean_ogg : - $(RM) -rf ${OGG_DIR} - -$(OPENSSL_TIMESTAMP) : openssl_download - @LAST_MODIF=$$(find ${OPENSSL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${OPENSSL_TIMESTAMP}; \ - fi - -openssl_download : - @if [ ! -d ${OPENSSL_DIR} ] ; then \ - echo "openssl sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps ; \ - wget ${OPENSSL_URL} || exit 1; \ - tar -xzf ${OPENSSL_BASEDIR}.tar.gz; \ - cd ${OPENSSL_BASEDIR}; \ - patch -p1 < ${ANDR_ROOT}/patches/openssl_arch.patch; \ - sed -i 's/-mandroid //g' Configure; \ - fi - -openssl : $(OPENSSL_LIB) - -$(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) - @REFRESH=0; \ - if [ ! -e ${OPENSSL_TIMESTAMP_INT} ] ; then \ - echo "${OPENSSL_TIMESTAMP_INT} doesn't exist"; \ - REFRESH=1; \ - fi; \ - if [ ${OPENSSL_TIMESTAMP} -nt ${OPENSSL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "changed timestamp for openssl detected building..."; \ - cd ${OPENSSL_DIR}; \ - ln -s ${OPENSSL_DIR} ../openssl; \ - export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --stl=libc++ \ - --install-dir=$${TOOLCHAIN}; \ - export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ - export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON}"; \ - CC=${CROSS_CC} ./Configure -DL_ENDIAN no-asm android-${TARGET_ARCH} \ - -D__ANDROID_API__=$(API); \ - CC=${CROSS_CC} ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make depend; \ - CC=${CROSS_CC} ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make build_libs; \ - touch ${OPENSSL_TIMESTAMP}; \ - touch ${OPENSSL_TIMESTAMP_INT}; \ - $(RM) -rf $${TOOLCHAIN}; \ - else \ - echo "nothing to be done for openssl"; \ - fi - -clean_openssl : - $(RM) -rf ${OPENSSL_DIR}; \ - $(RM) -rf $(ANDR_ROOT)/deps/${OPENSSL_BASEDIR}.tar.gz; \ - $(RM) -rf $(ANDR_ROOT)/deps/openssl - -$(LEVELDB_TIMESTAMP) : leveldb_download - @LAST_MODIF=$$(find ${LEVELDB_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${LEVELDB_TIMESTAMP}; \ - fi - -leveldb_download : - @if [ ! -d ${LEVELDB_DIR} ] ; then \ - echo "leveldb sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps ; \ - git clone ${LEVELDB_URL_GIT} || exit 1; \ - cd ${LEVELDB_DIR} || exit 1; \ - git checkout ${LEVELDB_COMMIT} || exit 1; \ - fi - -leveldb : $(LEVELDB_LIB) -ifeq ($(HAVE_LEVELDB),1) -$(LEVELDB_LIB): $(LEVELDB_TIMESTAMP) - @REFRESH=0; \ - if [ ! -e ${LEVELDB_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${LEVELDB_TIMESTAMP} -nt ${LEVELDB_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "changed timestamp for leveldb detected building..."; \ - cd deps/leveldb; \ - export CROSS_PREFIX=${TARGET_TOOLCHAIN}; \ - export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --stl=libc++ \ - --install-dir=$${TOOLCHAIN}; \ - export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - export CC=${CROSS_CC}; \ - export CXX=${CROSS_CXX}; \ - export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ - export CPPFLAGS="$${CPPFLAGS} ${TARGET_CXXFLAGS_ADDON}"; \ - export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON}"; \ - export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ - $(MAKE) || exit 1; \ - touch ${LEVELDB_TIMESTAMP}; \ - touch ${LEVELDB_TIMESTAMP_INT}; \ - $(RM) -rf $${TOOLCHAIN}; \ - else \ - echo "nothing to be done for leveldb"; \ - fi -endif - -clean_leveldb : - ./gradlew cleanLevelDB - -$(FREETYPE_TIMESTAMP) : freetype_download - @LAST_MODIF=$$(find ${FREETYPE_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${FREETYPE_TIMESTAMP}; \ - fi - -freetype_download : - @if [ ! -d ${FREETYPE_DIR} ] ; then \ - echo "freetype sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd deps; \ - git clone ${FREETYPE_URL_GIT} || exit 1; \ - fi - -freetype : $(FREETYPE_LIB) - -$(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP) - + @REFRESH=0; \ - if [ ! -e ${FREETYPE_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -e ${FREETYPE_LIB} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${FREETYPE_TIMESTAMP} -nt ${FREETYPE_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - mkdir -p ${FREETYPE_DIR}; \ - echo "changed timestamp for freetype detected building..."; \ - cd ${FREETYPE_DIR}/Android/jni; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - export TARGET_CFLAGS_ADDON="${TARGET_CFLAGS_ADDON}"; \ - export TARGET_CXXFLAGS_ADDON="${TARGET_CXXFLAGS_ADDON}"; \ - export COMPILER_VERSION=${COMPILER_VERSION}; \ - ${ANDROID_NDK}/ndk-build \ - NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ - touch ${FREETYPE_TIMESTAMP}; \ - touch ${FREETYPE_TIMESTAMP_INT}; \ - else \ - echo "nothing to be done for freetype"; \ - fi - -clean_freetype : - ./gradlew cleanFreetype - -$(ICONV_TIMESTAMP) : iconv_download - @LAST_MODIF=$$(find ${ICONV_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${ICONV_TIMESTAMP}; \ - fi - -iconv_download : - @if [ ! -d ${ICONV_DIR} ] ; then \ - echo "iconv sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps; \ - wget ${ICONV_URL_HTTP} || exit 1; \ - tar -xzf libiconv-${ICONV_VERSION}.tar.gz || exit 1; \ - rm libiconv-${ICONV_VERSION}.tar.gz; \ - ln -s libiconv-${ICONV_VERSION} libiconv; \ - fi - -iconv : $(ICONV_LIB) - -$(ICONV_LIB) : $(ICONV_TIMESTAMP) - @REFRESH=0; \ - if [ ! -e ${ICONV_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -e ${ICONV_LIB} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${ICONV_TIMESTAMP} -nt ${ICONV_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - mkdir -p ${ICONV_DIR}; \ - echo "changed timestamp for iconv detected building..."; \ - cd ${ICONV_DIR}; \ - export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --stl=libc++ \ - --install-dir=$${TOOLCHAIN}; \ - export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ - export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON} -lstdc++"; \ - export CC=${CROSS_CC}; \ - export CXX=${CROSS_CXX}; \ - export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ - ./configure --host=${TARGET_HOST} || exit 1; \ - sed -i 's/LIBICONV_VERSION_INFO) /LIBICONV_VERSION_INFO) -avoid-version /g' lib/Makefile; \ - grep "iconv_LDFLAGS" src/Makefile; \ - $(MAKE) -s || exit 1; \ - touch ${ICONV_TIMESTAMP}; \ - touch ${ICONV_TIMESTAMP_INT}; \ - rm -rf ${TOOLCHAIN}; \ - else \ - echo "nothing to be done for iconv"; \ - fi - -clean_iconv : - ./gradlew cleanIconv - -#Note: Texturehack patch is required for gpu's not supporting color format -# correctly. Known bad GPU: -# -geforce on emulator -# -Vivante Corporation GC1000 core (e.g. Galaxy Tab 3) - -irrlicht_download : - @if [ ! -d "deps/irrlicht" ] ; then \ - echo "irrlicht sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd deps; \ - svn co ${IRRLICHT_URL_SVN} irrlicht || exit 1; \ - cd irrlicht; \ - patch -p1 < ${ANDR_ROOT}/patches/irrlicht-touchcount.patch || exit 1; \ - patch -p1 < ${ANDR_ROOT}/patches/irrlicht-back_button.patch || exit 1; \ - patch -p1 < ${ANDR_ROOT}/patches/irrlicht-texturehack.patch || exit 1; \ - patch -p1 < ${ANDR_ROOT}/patches/irrlicht-native_activity.patch || exit 1; \ - fi - -$(IRRLICHT_TIMESTAMP) : irrlicht_download - @LAST_MODIF=$$(find ${IRRLICHT_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${IRRLICHT_TIMESTAMP}; \ - fi - -irrlicht : $(IRRLICHT_LIB) - -$(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB) - + @REFRESH=0; \ - if [ ! -e ${IRRLICHT_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -e ${IRRLICHT_LIB} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${IRRLICHT_TIMESTAMP} -nt ${IRRLICHT_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - mkdir -p ${IRRLICHT_DIR}; \ - echo "changed timestamp for irrlicht detected building..."; \ - cd deps/irrlicht/source/Irrlicht/Android; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - export TARGET_CFLAGS_ADDON="${TARGET_CFLAGS_ADDON}"; \ - export TARGET_CXXFLAGS_ADDON="${TARGET_CXXFLAGS_ADDON}"; \ - export COMPILER_VERSION=${COMPILER_VERSION}; \ - ${ANDROID_NDK}/ndk-build \ - NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ - touch ${IRRLICHT_TIMESTAMP}; \ - touch ${IRRLICHT_TIMESTAMP_INT}; \ - else \ - echo "nothing to be done for irrlicht"; \ - fi - -clean_irrlicht : - ./gradlew cleanIrrlicht - -$(CURL_TIMESTAMP) : curl_download - @LAST_MODIF=$$(find ${CURL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${CURL_TIMESTAMP}; \ - fi - -curl_download : - @if [ ! -d "deps/curl-${CURL_VERSION}" ] ; then \ - echo "curl sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd deps; \ - wget ${CURL_URL_HTTP} || exit 1; \ - tar -xjf curl-${CURL_VERSION}.tar.bz2 || exit 1; \ - rm curl-${CURL_VERSION}.tar.bz2; \ - ln -s curl-${CURL_VERSION} curl; \ - fi - -curl : $(CURL_LIB) - -$(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB) - @REFRESH=0; \ - if [ ! -e ${CURL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -e ${CURL_LIB} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${CURL_TIMESTAMP} -nt ${CURL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - mkdir -p ${CURL_DIR}; \ - echo "changed timestamp for curl detected building..."; \ - cd deps/curl-${CURL_VERSION}; \ - export CROSS_PREFIX=${TARGET_TOOLCHAIN}; \ - export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --stl=libc++ \ - --install-dir=$${TOOLCHAIN}; \ - export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - export CC=${CROSS_CC}; \ - export CXX=${CROSS_CXX}; \ - export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ - export CPPFLAGS="$${CPPFLAGS} -I${OPENSSL_DIR}/include ${TARGET_CFLAGS_ADDON}"; \ - export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ - export LDFLAGS="$${LDFLAGS} -L${OPENSSL_DIR} ${TARGET_LDFLAGS_ADDON}"; \ - ./configure --host=${TARGET_HOST} --disable-shared --enable-static --with-ssl; \ - $(MAKE) -s || exit 1; \ - touch ${CURL_TIMESTAMP}; \ - touch ${CURL_TIMESTAMP_INT}; \ - $(RM) -rf $${TOOLCHAIN}; \ - else \ - echo "nothing to be done for curl"; \ - fi - -clean_curl : - ./gradlew cleanCURL - -sqlite3_download: deps/${SQLITE3_FOLDER}/sqlite3.c - -deps/${SQLITE3_FOLDER}/sqlite3.c : - cd deps; \ - wget ${SQLITE3_URL}; \ - unzip ${SQLITE3_FOLDER}.zip; \ - ln -s ${SQLITE3_FOLDER} sqlite; \ - cd ${SQLITE3_FOLDER}; - -clean_sqlite3: - ./gradlew cleanSQLite3 - -$(ASSETS_TIMESTAMP) : $(IRRLICHT_LIB) - @mkdir -p ${ANDR_ROOT}/deps; \ - for DIRNAME in {builtin,client,doc,fonts,games,mods,po,textures}; do \ - LAST_MODIF=$$(find ${PROJ_ROOT}/${DIRNAME} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ]; then \ - touch ${PROJ_ROOT}/${DIRNAME}/timestamp; \ - touch ${ASSETS_TIMESTAMP}; \ - echo ${DIRNAME} changed $$LAST_MODIF; \ - fi; \ - done; \ - LAST_MODIF=$$(find ${IRRLICHT_DIR}/media -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${IRRLICHT_DIR}/media/timestamp; \ - touch ${ASSETS_TIMESTAMP}; \ - fi; \ - if [ ${PROJ_ROOT}/minetest.conf.example -nt ${ASSETS_TIMESTAMP} ] ; then \ - echo "conf changed"; \ - touch ${ASSETS_TIMESTAMP}; \ - fi; \ - if [ ${PROJ_ROOT}/README.txt -nt ${ASSETS_TIMESTAMP} ] ; then \ - touch ${ASSETS_TIMESTAMP}; \ - fi; \ - if [ ! -e $(ASSETS_TIMESTAMP) ] ; then \ - touch $(ASSETS_TIMESTAMP); \ - fi - -assets : $(ASSETS_TIMESTAMP) - @REFRESH=0; \ - if [ ! -e ${ASSETS_TIMESTAMP}.old ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${ASSETS_TIMESTAMP} -nt ${ASSETS_TIMESTAMP}.old ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -d ${APP_ROOT}/assets ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "assets changed, refreshing..."; \ - $(MAKE) clean_assets; \ - ./gradlew copyAssets; \ - cp -r ${IRRLICHT_DIR}/media/Shaders ${APP_ROOT}/assets/Minetest/media; \ - cd ${APP_ROOT}/assets || exit 1; \ - find . -name "timestamp" -exec rm {} \; ; \ - find . -name "*.blend" -exec rm {} \; ; \ - find . -name "*~" -exec rm {} \; ; \ - find . -type d -path "*.git" -exec rm -rf {} \; ; \ - find . -type d -path "*.svn" -exec rm -rf {} \; ; \ - find . -type f -path "*.gitignore" -exec rm -rf {} \; ; \ - ls -R | grep ":$$" | sed -e 's/:$$//' -e 's/\.//' -e 's/^\///' > "index.txt"; \ - find -L Minetest > filelist.txt; \ - cp ${ANDR_ROOT}/${ASSETS_TIMESTAMP} ${ANDR_ROOT}/${ASSETS_TIMESTAMP}.old; \ - else \ - echo "nothing to be done for assets"; \ - fi - -clean_assets : - ./gradlew cleanAssets - -apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(LEVELDB_TARGET) \ - $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \ - $(ANDR_ROOT)/jni/src/android_version_githash.h sqlite3_download - + @export TARGET_LIBDIR=${TARGET_LIBDIR}; \ - export HAVE_LEVELDB=${HAVE_LEVELDB}; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - export TARGET_CFLAGS_ADDON="${TARGET_CFLAGS_ADDON}"; \ - export TARGET_CXXFLAGS_ADDON="${TARGET_CXXFLAGS_ADDON}"; \ - export COMPILER_VERSION=${COMPILER_VERSION}; \ - export GPROF=${GPROF}; \ - ${ANDROID_NDK}/ndk-build || exit 1; \ - if [ ! -e ${APP_ROOT}/jniLibs ]; then \ - ln -s ${ANDR_ROOT}/libs ${APP_ROOT}/jniLibs || exit 1; \ - fi; \ - export VERSION_STR="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" && \ - export BUILD_TYPE_C=$$(echo "$${BUILD_TYPE}" | sed 's/./\U&/') && \ - ./gradlew assemble$$BUILD_TYPE_C && \ - echo "APK stored at: build/outputs/apk/$$BUILD_TYPE/Minetest-$$BUILD_TYPE.apk" && \ - echo "You can install it with \`make install_$$BUILD_TYPE\`" - -# These Intentionally doesn't depend on their respective build steps, -# because it takes a while to verify that everything's up-to-date. -install_debug: - ${ANDROID_SDK}/platform-tools/adb install -r build/outputs/apk/debug/Minetest-debug.apk - -install_release: - ${ANDROID_SDK}/platform-tools/adb install -r build/outputs/apk/release/Minetest-release.apk - -prep_srcdir : - @if [ ! -e ${ANDR_ROOT}/jni/src ]; then \ - 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 : - ./gradlew clean - -clean_all : - ./gradlew cleanAll - -$(ANDR_ROOT)/jni/src/android_version_githash.h : prep_srcdir - @export VERSION_FILE=${ANDR_ROOT}/jni/src/android_version_githash.h; \ - export VERSION_FILE_NEW=$${VERSION_FILE}.new; \ - { \ - echo "#ifndef ANDROID_MT_VERSION_GITHASH_H"; \ - echo "#define ANDROID_MT_VERSION_GITHASH_H"; \ - export GITHASH=$$(git rev-parse --short=8 HEAD); \ - export VERSION_STR="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}"; \ - echo "#define VERSION_GITHASH \"$$VERSION_STR-$$GITHASH-Android\""; \ - echo "#endif"; \ - } > "$${VERSION_FILE_NEW}"; \ - if ! cmp -s $${VERSION_FILE} $${VERSION_FILE_NEW}; then \ - echo "android_version_githash.h changed, updating..."; \ - mv "$${VERSION_FILE_NEW}" "$${VERSION_FILE}"; \ - else \ - rm "$${VERSION_FILE_NEW}"; \ - fi - - -$(ANDR_ROOT)/jni/src/android_version.h : prep_srcdir - @export VERSION_FILE=${ANDR_ROOT}/jni/src/android_version.h; \ - export VERSION_FILE_NEW=$${VERSION_FILE}.new; \ - { \ - echo "#ifndef ANDROID_MT_VERSION_H"; \ - echo "#define ANDROID_MT_VERSION_H"; \ - echo "#define VERSION_MAJOR ${VERSION_MAJOR}"; \ - echo "#define VERSION_MINOR ${VERSION_MINOR}"; \ - echo "#define VERSION_PATCH ${VERSION_PATCH}"; \ - echo "#define VERSION_STRING STR(VERSION_MAJOR) \".\" STR(VERSION_MINOR) \ - \".\" STR(VERSION_PATCH)"; \ - echo "#endif"; \ - } > $${VERSION_FILE_NEW}; \ - if ! cmp -s $${VERSION_FILE} $${VERSION_FILE_NEW}; then \ - echo "android_version.h changed, updating..."; \ - mv "$${VERSION_FILE_NEW}" "$${VERSION_FILE}"; \ - else \ - rm "$${VERSION_FILE_NEW}"; \ - fi - -clean : clean_apk clean_assets diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle new file mode 100644 index 000000000..e3619af17 --- /dev/null +++ b/build/android/app/build.gradle @@ -0,0 +1,111 @@ +apply plugin: 'com.android.application' +android { + compileSdkVersion 29 + buildToolsVersion '29.0.3' + ndkVersion '21.1.6352462' + defaultConfig { + applicationId 'net.minetest.minetest' + minSdkVersion 16 + targetSdkVersion 29 + versionName "${versionMajor}.${versionMinor}.${versionPatch}" + versionCode project.versionCode + } + + Properties props = new Properties() + props.load(new FileInputStream(file('../local.properties'))) + + if (props.getProperty('keystore') != null) { + signingConfigs { + release { + storeFile file(props['keystore']) + storePassword props['keystore.password'] + keyAlias props['key'] + keyPassword props['key.password'] + } + } + + buildTypes { + release { + minifyEnabled true + signingConfig signingConfigs.release + } + } + } + + // for multiple APKs + splits { + abi { + enable true + reset() + include 'armeabi-v7a', 'arm64-v8a' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +task prepareAssets() { + def assetsFolder = "build/assets" + def projRoot = "../../.." + def gameToCopy = "minetest_game" + + copy { + from "${projRoot}/minetest.conf.example", "${projRoot}/README.md" into assetsFolder + } + copy { + from "${projRoot}/doc/lgpl-2.1.txt" into "${assetsFolder}" + } + copy { + from "${projRoot}/builtin" into "${assetsFolder}/builtin" + } + /*copy { + // ToDo: fix Minetest shaders that currently don't work with OpenGL ES + from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders" + }*/ + copy { + from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht" + } + copy { + from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts" + } + copy { + from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}" + } + /*copy { + // ToDo: fix broken locales + from "${projRoot}/po" into "${assetsFolder}/po" + }*/ + copy { + from "${projRoot}/textures" into "${assetsFolder}/textures" + } + + file("${assetsFolder}/.nomedia").text = ""; + + task zipAssets(type: Zip) { + archiveName "Minetest.zip" + from "${assetsFolder}" + destinationDir file("src/main/assets") + } +} + +preBuild.dependsOn zipAssets + +// Map for the version code that gives each ABI a value. +import com.android.build.OutputFile + +def abiCodes = ['armeabi-v7a': 0, 'arm64-v8a': 1] +android.applicationVariants.all { variant -> + variant.outputs.each { + output -> + def abiName = output.getFilter(OutputFile.ABI) + output.versionCodeOverride = abiCodes.get(abiName, 0) + variant.versionCode + } +} + +dependencies { + implementation project(':native') + implementation 'androidx.appcompat:appcompat:1.1.0' +} diff --git a/build/android/src/main/AndroidManifest.xml b/build/android/app/src/main/AndroidManifest.xml similarity index 64% rename from build/android/src/main/AndroidManifest.xml rename to build/android/app/src/main/AndroidManifest.xml index fb1de9cf0..0a7c8d95a 100644 --- a/build/android/src/main/AndroidManifest.xml +++ b/build/android/app/src/main/AndroidManifest.xml @@ -4,28 +4,31 @@ package="net.minetest.minetest" android:installLocation="auto"> - - + + + android:requestLegacyExternalStorage="true" + tools:ignore="UnusedAttribute"> + android:value="3.0" /> @@ -33,11 +36,13 @@ + @@ -45,16 +50,18 @@ + android:value="Minetest" /> + - + android:name=".InputDialogActivity" + android:maxAspectRatio="3.0" + android:theme="@style/InputTheme" /> + + + diff --git a/build/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java b/build/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java new file mode 100644 index 000000000..6d4b6ab0f --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java @@ -0,0 +1,82 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +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. +*/ + +package net.minetest.minetest; + +import android.content.Intent; +import android.os.AsyncTask; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.ref.WeakReference; + +public class CopyZipTask extends AsyncTask { + + private final WeakReference activityRef; + + CopyZipTask(AppCompatActivity activity) { + activityRef = new WeakReference<>(activity); + } + + protected String doInBackground(String... params) { + copyAsset(params[0]); + return params[0]; + } + + @Override + protected void onPostExecute(String result) { + startUnzipService(result); + } + + private void copyAsset(String zipName) { + String filename = zipName.substring(zipName.lastIndexOf("/") + 1); + try (InputStream in = activityRef.get().getAssets().open(filename); + OutputStream out = new FileOutputStream(zipName)) { + copyFile(in, out); + } catch (IOException e) { + AppCompatActivity activity = activityRef.get(); + if (activity != null) { + activity.runOnUiThread(() -> Toast.makeText(activityRef.get(), e.getLocalizedMessage(), Toast.LENGTH_LONG).show()); + } + cancel(true); + } + } + + private void copyFile(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) != -1) + out.write(buffer, 0, read); + } + + private void startUnzipService(String file) { + Intent intent = new Intent(activityRef.get(), UnzipService.class); + intent.putExtra(UnzipService.EXTRA_KEY_IN_FILE, file); + AppCompatActivity activity = activityRef.get(); + if (activity != null) { + activity.startService(intent); + } + } +} diff --git a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java new file mode 100644 index 000000000..635512569 --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -0,0 +1,126 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +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. +*/ + +package net.minetest.minetest; + +import android.app.NativeActivity; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.view.WindowManager; + +public class GameActivity extends NativeActivity { + static { + System.loadLibrary("c++_shared"); + System.loadLibrary("Minetest"); + } + + private int messageReturnCode; + private String messageReturnValue; + + public static native void putMessageBoxResult(String text); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + messageReturnCode = -1; + messageReturnValue = ""; + } + + private void makeFullScreen() { + if (Build.VERSION.SDK_INT >= 19) + this.getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) + makeFullScreen(); + } + + @Override + protected void onResume() { + super.onResume(); + makeFullScreen(); + } + + @Override + public void onBackPressed() { + // Ignore the back press so Minetest can handle it + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 101) { + if (resultCode == RESULT_OK) { + String text = data.getStringExtra("text"); + messageReturnCode = 0; + messageReturnValue = text; + } else + messageReturnCode = 1; + } + } + + public void showDialog(String acceptButton, String hint, String current, int editType) { + Intent intent = new Intent(this, InputDialogActivity.class); + Bundle params = new Bundle(); + params.putString("acceptButton", acceptButton); + params.putString("hint", hint); + params.putString("current", current); + params.putInt("editType", editType); + intent.putExtras(params); + startActivityForResult(intent, 101); + messageReturnValue = ""; + messageReturnCode = -1; + } + + public int getDialogState() { + return messageReturnCode; + } + + public String getDialogValue() { + messageReturnCode = -1; + return messageReturnValue; + } + + public float getDensity() { + return getResources().getDisplayMetrics().density; + } + + public int getDisplayHeight() { + return getResources().getDisplayMetrics().heightPixels; + } + + public int getDisplayWidth() { + return getResources().getDisplayMetrics().widthPixels; + } + + public void openURL(String url) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + startActivity(browserIntent); + } +} diff --git a/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java b/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java new file mode 100644 index 000000000..7c6aa111d --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java @@ -0,0 +1,98 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +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. +*/ + +package net.minetest.minetest; + +import android.app.Activity; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.text.InputType; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; + +import java.util.Objects; + +public class InputDialogActivity extends AppCompatActivity { + private AlertDialog alertDialog; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle b = getIntent().getExtras(); + int editType = Objects.requireNonNull(b).getInt("editType"); + String hint = b.getString("hint"); + String current = b.getString("current"); + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + EditText editText = new EditText(this); + builder.setView(editText); + editText.requestFocus(); + editText.setHint(hint); + editText.setText(current); + final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); + Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED, + InputMethodManager.HIDE_IMPLICIT_ONLY); + if (editType == 3) + editText.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_VARIATION_PASSWORD); + else + editText.setInputType(InputType.TYPE_CLASS_TEXT); + editText.setOnKeyListener((view, KeyCode, event) -> { + if (KeyCode == KeyEvent.KEYCODE_ENTER) { + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + pushResult(editText.getText().toString()); + return true; + } + return false; + }); + alertDialog = builder.create(); + if (!this.isFinishing()) + alertDialog.show(); + alertDialog.setOnCancelListener(dialog -> { + pushResult(editText.getText().toString()); + setResult(Activity.RESULT_CANCELED); + alertDialog.dismiss(); + makeFullScreen(); + finish(); + }); + } + + private void pushResult(String text) { + Intent resultData = new Intent(); + resultData.putExtra("text", text); + setResult(AppCompatActivity.RESULT_OK, resultData); + alertDialog.dismiss(); + makeFullScreen(); + finish(); + } + + private void makeFullScreen() { + if (Build.VERSION.SDK_INT >= 19) + this.getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } +} diff --git a/build/android/app/src/main/java/net/minetest/minetest/MainActivity.java b/build/android/app/src/main/java/net/minetest/minetest/MainActivity.java new file mode 100644 index 000000000..2aa50d9ad --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/MainActivity.java @@ -0,0 +1,153 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +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. +*/ + +package net.minetest.minetest; + +import android.Manifest; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static net.minetest.minetest.UnzipService.ACTION_FAILURE; +import static net.minetest.minetest.UnzipService.ACTION_PROGRESS; +import static net.minetest.minetest.UnzipService.ACTION_UPDATE; +import static net.minetest.minetest.UnzipService.FAILURE; +import static net.minetest.minetest.UnzipService.SUCCESS; + +public class MainActivity extends AppCompatActivity { + private final static int versionCode = BuildConfig.VERSION_CODE; + private final static int PERMISSIONS = 1; + private static final String[] REQUIRED_SDK_PERMISSIONS = + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; + private static final String SETTINGS = "MinetestSettings"; + private static final String TAG_VERSION_CODE = "versionCode"; + private ProgressBar mProgressBar; + private TextView mTextView; + private SharedPreferences sharedPreferences; + private final BroadcastReceiver myReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int progress = 0; + if (intent != null) + progress = intent.getIntExtra(ACTION_PROGRESS, 0); + if (progress >= 0) { + if (mProgressBar != null) { + mProgressBar.setVisibility(View.VISIBLE); + mProgressBar.setProgress(progress); + } + mTextView.setVisibility(View.VISIBLE); + } else if (progress == FAILURE) { + Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show(); + finish(); + } else if (progress == SUCCESS) + startNative(); + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + IntentFilter filter = new IntentFilter(ACTION_UPDATE); + registerReceiver(myReceiver, filter); + mProgressBar = findViewById(R.id.progressBar); + mTextView = findViewById(R.id.textView); + sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + checkPermission(); + else + checkAppVersion(); + } + + private void checkPermission() { + final List missingPermissions = new ArrayList<>(); + for (final String permission : REQUIRED_SDK_PERMISSIONS) { + final int result = ContextCompat.checkSelfPermission(this, permission); + if (result != PackageManager.PERMISSION_GRANTED) + missingPermissions.add(permission); + } + if (!missingPermissions.isEmpty()) { + final String[] permissions = missingPermissions + .toArray(new String[0]); + ActivityCompat.requestPermissions(this, permissions, PERMISSIONS); + } else { + final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length]; + Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED); + onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS, grantResults); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, + @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == PERMISSIONS) { + for (int grantResult : grantResults) { + if (grantResult != PackageManager.PERMISSION_GRANTED) { + Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show(); + finish(); + } + } + checkAppVersion(); + } + } + + private void checkAppVersion() { + if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode) + startNative(); + else + new CopyZipTask(this).execute(getCacheDir() + "/Minetest.zip"); + } + + private void startNative() { + sharedPreferences.edit().putInt(TAG_VERSION_CODE, versionCode).apply(); + Intent intent = new Intent(this, GameActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + } + + @Override + public void onBackPressed() { + // Prevent abrupt interruption when copy game files from assets + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(myReceiver); + } +} diff --git a/build/android/app/src/main/java/net/minetest/minetest/UnzipService.java b/build/android/app/src/main/java/net/minetest/minetest/UnzipService.java new file mode 100644 index 000000000..b69f7f36e --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/UnzipService.java @@ -0,0 +1,157 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +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. +*/ + +package net.minetest.minetest; + +import android.app.IntentService; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Environment; +import android.widget.Toast; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + +public class UnzipService extends IntentService { + public static final String ACTION_UPDATE = "net.minetest.minetest.UPDATE"; + public static final String ACTION_PROGRESS = "net.minetest.minetest.PROGRESS"; + public static final String ACTION_FAILURE = "net.minetest.minetest.FAILURE"; + public static final String EXTRA_KEY_IN_FILE = "file"; + public static final int SUCCESS = -1; + public static final int FAILURE = -2; + private final int id = 1; + private NotificationManager mNotifyManager; + private boolean isSuccess = true; + private String failureMessage; + + public UnzipService() { + super("net.minetest.minetest.UnzipService"); + } + + private void isDir(String dir, String location) { + File f = new File(location, dir); + if (!f.isDirectory()) + f.mkdirs(); + } + + @Override + protected void onHandleIntent(Intent intent) { + createNotification(); + unzip(intent); + } + + private void createNotification() { + String name = "net.minetest.minetest"; + String channelId = "Minetest channel"; + String description = "notifications from Minetest"; + Notification.Builder builder; + if (mNotifyManager == null) + mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + int importance = NotificationManager.IMPORTANCE_LOW; + NotificationChannel mChannel = null; + if (mNotifyManager != null) + mChannel = mNotifyManager.getNotificationChannel(channelId); + if (mChannel == null) { + mChannel = new NotificationChannel(channelId, name, importance); + mChannel.setDescription(description); + // Configure the notification channel, NO SOUND + mChannel.setSound(null, null); + mChannel.enableLights(false); + mChannel.enableVibration(false); + mNotifyManager.createNotificationChannel(mChannel); + } + builder = new Notification.Builder(this, channelId); + } else { + builder = new Notification.Builder(this); + } + builder.setContentTitle(getString(R.string.notification_title)) + .setSmallIcon(R.mipmap.ic_launcher) + .setContentText(getString(R.string.notification_description)); + mNotifyManager.notify(id, builder.build()); + } + + private void unzip(Intent intent) { + String zip = intent.getStringExtra(EXTRA_KEY_IN_FILE); + isDir("Minetest", Environment.getExternalStorageDirectory().toString()); + String location = Environment.getExternalStorageDirectory() + File.separator + "Minetest" + File.separator; + int per = 0; + int size = getSummarySize(zip); + File zipFile = new File(zip); + int readLen; + byte[] readBuffer = new byte[8192]; + try (FileInputStream fileInputStream = new FileInputStream(zipFile); + ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) { + ZipEntry ze; + while ((ze = zipInputStream.getNextEntry()) != null) { + if (ze.isDirectory()) { + ++per; + isDir(ze.getName(), location); + } else { + publishProgress(100 * ++per / size); + try (OutputStream outputStream = new FileOutputStream(location + ze.getName())) { + while ((readLen = zipInputStream.read(readBuffer)) != -1) { + outputStream.write(readBuffer, 0, readLen); + } + } + } + zipFile.delete(); + } + } catch (IOException e) { + isSuccess = false; + failureMessage = e.getLocalizedMessage(); + } + } + + private void publishProgress(int progress) { + Intent intentUpdate = new Intent(ACTION_UPDATE); + intentUpdate.putExtra(ACTION_PROGRESS, progress); + if (!isSuccess) intentUpdate.putExtra(ACTION_FAILURE, failureMessage); + sendBroadcast(intentUpdate); + } + + private int getSummarySize(String zip) { + int size = 0; + try { + ZipFile zipSize = new ZipFile(zip); + size += zipSize.size(); + } catch (IOException e) { + Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); + } + return size; + } + + @Override + public void onDestroy() { + super.onDestroy(); + mNotifyManager.cancel(id); + publishProgress(isSuccess ? SUCCESS : FAILURE); + } +} diff --git a/build/android/src/main/res/drawable/background.png b/build/android/app/src/main/res/drawable/background.png similarity index 100% rename from build/android/src/main/res/drawable/background.png rename to build/android/app/src/main/res/drawable/background.png diff --git a/build/android/src/main/res/drawable/bg.xml b/build/android/app/src/main/res/drawable/bg.xml similarity index 82% rename from build/android/src/main/res/drawable/bg.xml rename to build/android/app/src/main/res/drawable/bg.xml index c76ec372d..903335ed9 100644 --- a/build/android/src/main/res/drawable/bg.xml +++ b/build/android/app/src/main/res/drawable/bg.xml @@ -1,4 +1,4 @@ \ No newline at end of file + android:tileMode="repeat" /> diff --git a/build/android/app/src/main/res/layout/activity_main.xml b/build/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..e6f461f14 --- /dev/null +++ b/build/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/build/android/src/main/res/mipmap/ic_launcher.png b/build/android/app/src/main/res/mipmap/ic_launcher.png similarity index 100% rename from build/android/src/main/res/mipmap/ic_launcher.png rename to build/android/app/src/main/res/mipmap/ic_launcher.png diff --git a/build/android/app/src/main/res/values/strings.xml b/build/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..a6fba70d5 --- /dev/null +++ b/build/android/app/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ + + + + Minetest + Loading… + Required permission wasn\'t granted, Minetest can\'t run without it + Loading Minetest + Less than 1 minute… + + diff --git a/build/android/app/src/main/res/values/styles.xml b/build/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..618507e63 --- /dev/null +++ b/build/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/build/android/build.gradle b/build/android/build.gradle index 3601434f5..8707b563c 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -1,10 +1,24 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +project.ext.set("versionMajor", 5) // Version Major +project.ext.set("versionMinor", 3) // Version Minor +project.ext.set("versionPatch", 0) // Version Patch +project.ext.set("versionExtra", "-dev") // Version Extra +project.ext.set("versionCode", 30) // Android Version Code +// NOTE: +2 after each release! +// +1 for ARM and +1 for ARM64 APK's, because +// each APK must have a larger `versionCode` than the previous + buildscript { repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' + classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'org.ajoberstar.grgit:grgit-gradle:4.0.2' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files } } @@ -15,161 +29,6 @@ allprojects { } } -def curl_version = "7.60.0" -def irrlicht_revision = "5150" -def openal_version = "1.18.2" -def openssl_version = "1.0.2n" -def sqlite3_version = "3240000" - -apply plugin: "com.android.application" - -android { - compileSdkVersion 29 - buildToolsVersion '29.0.2' - - defaultConfig { - versionCode 25 - versionName "${System.env.VERSION_STR}.${versionCode}" - minSdkVersion 14 - targetSdkVersion 29 - applicationId "net.minetest.minetest" - manifestPlaceholders = [package: "net.minetest.minetest", project: project.name] - ndk { - // Specifies the ABI configurations of your native - // libraries Gradle should build and package with your APK. - // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' - abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - lintOptions { - disable "OldTargetApi", "GoogleAppIndexingWarning" - } - - Properties props = new Properties() - props.load(new FileInputStream(file("local.properties"))) - - if (props.getProperty("keystore") != null) { - signingConfigs { - release { - storeFile file(props["keystore"]) - storePassword props["keystore.password"] - keyAlias props["key"] - keyPassword props["key.password"] - } - } - - buildTypes { - release { - signingConfig signingConfigs.release - } - } - } -} - -task cleanAssets(type: Delete) { - delete 'src/main/assets' -} - -task copyAssets { - dependsOn 'cleanAssets' - mkdir "src/main/assets" - - def mtAssetsFolder = "src/main/assets/Minetest" - def projRoot = "../.." - def gameToCopy = "minetest_game" - - doLast { - mkdir "${mtAssetsFolder}" - mkdir "${mtAssetsFolder}/client" - mkdir "${mtAssetsFolder}/fonts" - mkdir "${mtAssetsFolder}/games" - mkdir "${mtAssetsFolder}/media" - - copy { - from "${projRoot}/minetest.conf.example", "${projRoot}/README.md" into mtAssetsFolder - } - copy { - from "${projRoot}/doc/lgpl-2.1.txt" into "${mtAssetsFolder}/LICENSE.txt" - } - copy { - from "${projRoot}/builtin" into "${mtAssetsFolder}/builtin" - } - copy { - from "${projRoot}/client/shaders" into "${mtAssetsFolder}/client/shaders" - } - copy { - from "${projRoot}/fonts" include "*.ttf" into "${mtAssetsFolder}/fonts" - } - copy { - from "${projRoot}/games/${gameToCopy}" into "${mtAssetsFolder}/games/${gameToCopy}" - } - copy { - from "${projRoot}/po" into "${mtAssetsFolder}/po" - } - copy { - from "${projRoot}/textures" into "${mtAssetsFolder}/textures" - } - } -} - -task cleanIconv(type: Delete) { - delete 'deps/libiconv' -} - -task cleanIrrlicht(type: Delete) { - delete 'deps/irrlicht' -} - -task cleanLevelDB(type: Delete) { - delete 'deps/leveldb' -} - -task cleanCURL(type: Delete) { - delete 'deps/curl' - delete 'deps/curl-' + curl_version -} - -task cleanOpenSSL(type: Delete) { - delete 'deps/openssl' - delete 'deps/openssl-' + openssl_version - delete 'deps/openssl-' + openssl_version + '.tar.gz' -} - -task cleanOpenAL(type: Delete) { - delete 'deps/openal-soft' -} - -task cleanFreetype(type: Delete) { - delete 'deps/freetype2-android' -} - -task cleanOgg(type: Delete) { - delete 'deps/libvorbis-libogg-android' -} - -task cleanSQLite3(type: Delete) { - delete 'deps/sqlite-amalgamation-' + sqlite3_version - delete 'deps/sqlite-amalgamation-' + sqlite3_version + '.zip' -} - -task cleanAll(type: Delete, dependsOn: [clean, cleanAssets, cleanIconv, - cleanFreetype, cleanIrrlicht, cleanLevelDB, cleanSQLite3, cleanCURL, - cleanOpenSSL, cleanOpenAL, cleanOgg]) { - delete 'deps' - delete 'gen' - delete 'libs' - delete 'obj' - delete 'bin' - delete 'Debug' - delete 'and_env' -} - -dependencies { - implementation 'androidx.core:core:1.1.0' +task clean(type: Delete) { + delete rootProject.buildDir } diff --git a/build/android/gradle.properties b/build/android/gradle.properties index 5465fec0e..53b475cf9 100644 --- a/build/android/gradle.properties +++ b/build/android/gradle.properties @@ -1,2 +1,11 @@ +<#if isLowMemory> +org.gradle.jvmargs=-Xmx4G -XX:MaxPermSize=2G -XX:+HeapDumpOnOutOfMemoryError +<#else> +org.gradle.jvmargs=-Xmx16G -XX:MaxPermSize=8G -XX:+HeapDumpOnOutOfMemoryError + +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.parallel.threads=8 +org.gradle.configureondemand=true android.enableJetifier=true -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true diff --git a/build/android/gradle/wrapper/gradle-wrapper.jar b/build/android/gradle/wrapper/gradle-wrapper.jar index 6b6ea3ab4..5c2d1cf01 100644 Binary files a/build/android/gradle/wrapper/gradle-wrapper.jar 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 index 22ac96460..d612cf333 100644 --- a/build/android/gradle/wrapper/gradle-wrapper.properties +++ b/build/android/gradle/wrapper/gradle-wrapper.properties @@ -1 +1,2 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +#Mon Apr 06 00:06:16 CEST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip diff --git a/build/android/gradlew b/build/android/gradlew index cccdd3d51..83f2acfdc 100755 --- a/build/android/gradlew +++ b/build/android/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ 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 +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/build/android/gradlew.bat b/build/android/gradlew.bat index f9553162f..9618d8d96 100644 --- a/build/android/gradlew.bat +++ b/build/android/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk deleted file mode 100644 index 72b0daab6..000000000 --- a/build/android/jni/Android.mk +++ /dev/null @@ -1,446 +0,0 @@ -LOCAL_PATH := $(call my-dir)/.. - -#LOCAL_ADDRESS_SANITIZER:=true - -include $(CLEAR_VARS) -LOCAL_MODULE := Irrlicht -LOCAL_SRC_FILES := deps/irrlicht/lib/Android/libIrrlicht.a -include $(PREBUILT_STATIC_LIBRARY) - -ifeq ($(HAVE_LEVELDB), 1) - include $(CLEAR_VARS) - LOCAL_MODULE := LevelDB - LOCAL_SRC_FILES := deps/leveldb/libleveldb.a - include $(PREBUILT_STATIC_LIBRARY) -endif - -include $(CLEAR_VARS) -LOCAL_MODULE := curl -LOCAL_SRC_FILES := deps/curl/lib/.libs/libcurl.a -include $(PREBUILT_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := freetype -LOCAL_SRC_FILES := deps/freetype2-android/Android/obj/local/$(TARGET_ARCH_ABI)/libfreetype2-static.a -include $(PREBUILT_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := iconv -LOCAL_SRC_FILES := deps/libiconv/lib/.libs/libiconv.so -include $(PREBUILT_SHARED_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := openal -LOCAL_SRC_FILES := deps/openal-soft/libs/$(TARGET_LIBDIR)/libopenal.so -include $(PREBUILT_SHARED_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := ogg -LOCAL_SRC_FILES := deps/libvorbis-libogg-android/libs/$(TARGET_LIBDIR)/libogg.so -include $(PREBUILT_SHARED_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := vorbis -LOCAL_SRC_FILES := deps/libvorbis-libogg-android/libs/$(TARGET_LIBDIR)/libvorbis.so -include $(PREBUILT_SHARED_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := ssl -LOCAL_SRC_FILES := deps/openssl/libssl.a -include $(PREBUILT_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := crypto -LOCAL_SRC_FILES := deps/openssl/libcrypto.a -include $(PREBUILT_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := minetest - -LOCAL_CPP_FEATURES += exceptions - -ifdef GPROF -GPROF_DEF=-DGPROF -endif - -LOCAL_CFLAGS := -D_IRR_ANDROID_PLATFORM_ \ - -DHAVE_TOUCHSCREENGUI \ - -DENABLE_GLES=1 \ - -DUSE_CURL=1 \ - -DUSE_SOUND=1 \ - -DUSE_FREETYPE=1 \ - -DUSE_LEVELDB=$(HAVE_LEVELDB) \ - $(GPROF_DEF) \ - -pipe -fstrict-aliasing - -ifndef NDEBUG -LOCAL_CFLAGS += -g -D_DEBUG -O0 -fno-omit-frame-pointer -else -LOCAL_CFLAGS += $(TARGET_CFLAGS_ADDON) -endif - -ifdef GPROF -PROFILER_LIBS := android-ndk-profiler -LOCAL_CFLAGS += -pg -endif - -# LOCAL_CFLAGS += -fsanitize=address -# LOCAL_LDFLAGS += -fsanitize=address - -ifeq ($(TARGET_ABI),x86) -LOCAL_CFLAGS += -fno-stack-protector -endif - -LOCAL_C_INCLUDES := \ - jni/src \ - jni/src/script \ - jni/lib/gmp \ - jni/lib/lua/src \ - jni/lib/jsoncpp \ - jni/src/cguittfont \ - deps/irrlicht/include \ - deps/libiconv/include \ - deps/freetype2-android/include \ - deps/curl/include \ - deps/openal-soft/jni/OpenAL/include \ - deps/libvorbis-libogg-android/jni/include \ - deps/leveldb/include \ - deps/sqlite/ - -LOCAL_SRC_FILES := \ - jni/src/ban.cpp \ - jni/src/chat.cpp \ - jni/src/client/activeobjectmgr.cpp \ - jni/src/client/camera.cpp \ - jni/src/client/client.cpp \ - jni/src/client/clientenvironment.cpp \ - jni/src/client/clientlauncher.cpp \ - jni/src/client/clientmap.cpp \ - jni/src/client/clientmedia.cpp \ - jni/src/client/clientobject.cpp \ - jni/src/client/clouds.cpp \ - jni/src/client/content_cao.cpp \ - jni/src/client/content_cso.cpp \ - jni/src/client/content_mapblock.cpp \ - jni/src/client/filecache.cpp \ - jni/src/client/fontengine.cpp \ - jni/src/client/game.cpp \ - jni/src/client/gameui.cpp \ - jni/src/client/guiscalingfilter.cpp \ - jni/src/client/hud.cpp \ - jni/src/clientiface.cpp \ - jni/src/client/imagefilters.cpp \ - jni/src/client/inputhandler.cpp \ - jni/src/client/joystick_controller.cpp \ - jni/src/client/keycode.cpp \ - jni/src/client/localplayer.cpp \ - jni/src/client/mapblock_mesh.cpp \ - jni/src/client/mesh.cpp \ - jni/src/client/meshgen/collector.cpp \ - jni/src/client/mesh_generator_thread.cpp \ - jni/src/client/minimap.cpp \ - jni/src/client/particles.cpp \ - jni/src/client/render/anaglyph.cpp \ - jni/src/client/render/core.cpp \ - jni/src/client/render/factory.cpp \ - jni/src/client/renderingengine.cpp \ - jni/src/client/render/interlaced.cpp \ - jni/src/client/render/pageflip.cpp \ - jni/src/client/render/plain.cpp \ - jni/src/client/render/sidebyside.cpp \ - jni/src/client/render/stereo.cpp \ - jni/src/client/shader.cpp \ - jni/src/client/sky.cpp \ - jni/src/client/sound.cpp \ - jni/src/client/sound_openal.cpp \ - jni/src/client/tile.cpp \ - jni/src/client/wieldmesh.cpp \ - jni/src/collision.cpp \ - jni/src/content/content.cpp \ - jni/src/content_mapnode.cpp \ - jni/src/content/mods.cpp \ - jni/src/content_nodemeta.cpp \ - jni/src/content/packages.cpp \ - jni/src/content_sao.cpp \ - jni/src/content/subgames.cpp \ - jni/src/convert_json.cpp \ - jni/src/craftdef.cpp \ - jni/src/database/database.cpp \ - jni/src/database/database-dummy.cpp \ - jni/src/database/database-files.cpp \ - jni/src/database/database-leveldb.cpp \ - jni/src/database/database-sqlite3.cpp \ - jni/src/debug.cpp \ - jni/src/defaultsettings.cpp \ - jni/src/emerge.cpp \ - jni/src/environment.cpp \ - jni/src/face_position_cache.cpp \ - jni/src/filesys.cpp \ - jni/src/genericobject.cpp \ - jni/src/gettext.cpp \ - jni/src/gui/guiAnimatedImage.cpp \ - jni/src/gui/guiBackgroundImage.cpp \ - jni/src/gui/guiBox.cpp \ - jni/src/gui/guiButton.cpp \ - jni/src/gui/guiButtonImage.cpp \ - jni/src/gui/guiButtonItemImage.cpp \ - jni/src/gui/guiChatConsole.cpp \ - jni/src/gui/guiConfirmRegistration.cpp \ - jni/src/gui/guiEditBoxWithScrollbar.cpp \ - jni/src/gui/guiEngine.cpp \ - jni/src/gui/guiFormSpecMenu.cpp \ - jni/src/gui/guiHyperText.cpp \ - jni/src/gui/guiInventoryList.cpp \ - jni/src/gui/guiItemImage.cpp \ - jni/src/gui/guiKeyChangeMenu.cpp \ - jni/src/gui/guiPasswordChange.cpp \ - jni/src/gui/guiPathSelectMenu.cpp \ - jni/src/gui/guiScrollBar.cpp \ - jni/src/gui/guiSkin.cpp \ - jni/src/gui/guiTable.cpp \ - jni/src/gui/guiVolumeChange.cpp \ - jni/src/gui/intlGUIEditBox.cpp \ - jni/src/gui/modalMenu.cpp \ - jni/src/gui/profilergraph.cpp \ - jni/src/gui/touchscreengui.cpp \ - jni/src/httpfetch.cpp \ - jni/src/hud.cpp \ - jni/src/inventory.cpp \ - jni/src/inventorymanager.cpp \ - jni/src/irrlicht_changes/CGUITTFont.cpp \ - jni/src/irrlicht_changes/static_text.cpp \ - jni/src/itemdef.cpp \ - jni/src/itemstackmetadata.cpp \ - jni/src/light.cpp \ - jni/src/log.cpp \ - jni/src/main.cpp \ - jni/src/mapblock.cpp \ - jni/src/map.cpp \ - jni/src/mapgen/cavegen.cpp \ - jni/src/mapgen/dungeongen.cpp \ - jni/src/mapgen/mapgen_carpathian.cpp \ - jni/src/mapgen/mapgen.cpp \ - jni/src/mapgen/mapgen_flat.cpp \ - jni/src/mapgen/mapgen_fractal.cpp \ - jni/src/mapgen/mapgen_singlenode.cpp \ - jni/src/mapgen/mapgen_v5.cpp \ - jni/src/mapgen/mapgen_v6.cpp \ - jni/src/mapgen/mapgen_v7.cpp \ - jni/src/mapgen/mapgen_valleys.cpp \ - jni/src/mapgen/mg_biome.cpp \ - jni/src/mapgen/mg_decoration.cpp \ - jni/src/mapgen/mg_ore.cpp \ - jni/src/mapgen/mg_schematic.cpp \ - jni/src/mapgen/treegen.cpp \ - jni/src/mapnode.cpp \ - jni/src/mapsector.cpp \ - jni/src/map_settings_manager.cpp \ - jni/src/metadata.cpp \ - jni/src/modchannels.cpp \ - jni/src/nameidmapping.cpp \ - jni/src/nodedef.cpp \ - jni/src/nodemetadata.cpp \ - jni/src/nodetimer.cpp \ - jni/src/noise.cpp \ - jni/src/objdef.cpp \ - jni/src/object_properties.cpp \ - jni/src/pathfinder.cpp \ - jni/src/player.cpp \ - jni/src/porting_android.cpp \ - jni/src/porting.cpp \ - jni/src/profiler.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/activeobjectmgr.cpp \ - jni/src/server.cpp \ - jni/src/serverenvironment.cpp \ - jni/src/serverlist.cpp \ - jni/src/server/mods.cpp \ - jni/src/serverobject.cpp \ - jni/src/settings.cpp \ - jni/src/staticobject.cpp \ - jni/src/tileanimation.cpp \ - jni/src/tool.cpp \ - jni/src/translation.cpp \ - jni/src/unittest/test_authdatabase.cpp \ - jni/src/unittest/test_collision.cpp \ - jni/src/unittest/test_compression.cpp \ - jni/src/unittest/test_connection.cpp \ - jni/src/unittest/test.cpp \ - jni/src/unittest/test_filepath.cpp \ - jni/src/unittest/test_gameui.cpp \ - jni/src/unittest/test_inventory.cpp \ - jni/src/unittest/test_mapnode.cpp \ - jni/src/unittest/test_map_settings_manager.cpp \ - jni/src/unittest/test_nodedef.cpp \ - jni/src/unittest/test_noderesolver.cpp \ - jni/src/unittest/test_noise.cpp \ - jni/src/unittest/test_objdef.cpp \ - jni/src/unittest/test_profiler.cpp \ - jni/src/unittest/test_random.cpp \ - jni/src/unittest/test_schematic.cpp \ - jni/src/unittest/test_serialization.cpp \ - jni/src/unittest/test_settings.cpp \ - jni/src/unittest/test_socket.cpp \ - jni/src/unittest/test_utilities.cpp \ - jni/src/unittest/test_voxelalgorithms.cpp \ - jni/src/unittest/test_voxelmanipulator.cpp \ - jni/src/util/areastore.cpp \ - jni/src/util/auth.cpp \ - jni/src/util/base64.cpp \ - jni/src/util/directiontables.cpp \ - jni/src/util/enriched_string.cpp \ - jni/src/util/ieee_float.cpp \ - jni/src/util/numeric.cpp \ - jni/src/util/pointedthing.cpp \ - jni/src/util/quicktune.cpp \ - jni/src/util/serialize.cpp \ - jni/src/util/sha1.cpp \ - jni/src/util/srp.cpp \ - jni/src/util/string.cpp \ - jni/src/util/timetaker.cpp \ - jni/src/version.cpp \ - jni/src/voxelalgorithms.cpp \ - jni/src/voxel.cpp - - -# intentionally kept out (we already build openssl itself): jni/src/util/sha256.c - -# Network -LOCAL_SRC_FILES += \ - jni/src/network/address.cpp \ - jni/src/network/connection.cpp \ - jni/src/network/networkpacket.cpp \ - jni/src/network/clientopcodes.cpp \ - jni/src/network/clientpackethandler.cpp \ - jni/src/network/connectionthreads.cpp \ - jni/src/network/serveropcodes.cpp \ - jni/src/network/serverpackethandler.cpp \ - jni/src/network/socket.cpp \ - -# lua api -LOCAL_SRC_FILES += \ - jni/src/script/common/c_content.cpp \ - jni/src/script/common/c_converter.cpp \ - jni/src/script/common/c_internal.cpp \ - jni/src/script/common/c_types.cpp \ - jni/src/script/common/helper.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 \ - jni/src/script/cpp_api/s_item.cpp \ - jni/src/script/cpp_api/s_mainmenu.cpp \ - jni/src/script/cpp_api/s_modchannels.cpp \ - jni/src/script/cpp_api/s_node.cpp \ - jni/src/script/cpp_api/s_nodemeta.cpp \ - jni/src/script/cpp_api/s_player.cpp \ - jni/src/script/cpp_api/s_security.cpp \ - jni/src/script/cpp_api/s_server.cpp \ - jni/src/script/lua_api/l_areastore.cpp \ - jni/src/script/lua_api/l_auth.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_modchannels.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 \ - jni/src/script/lua_api/l_object.cpp \ - jni/src/script/lua_api/l_playermeta.cpp \ - jni/src/script/lua_api/l_particles.cpp \ - jni/src/script/lua_api/l_particles_local.cpp\ - 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_client.cpp \ - jni/src/script/scripting_server.cpp \ - jni/src/script/scripting_mainmenu.cpp - -#freetype2 support -#LOCAL_SRC_FILES += jni/src/cguittfont/xCGUITTFont.cpp - -# GMP -LOCAL_SRC_FILES += jni/lib/gmp/mini-gmp.c - -# Lua -LOCAL_SRC_FILES += \ - 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 - -# Threading -LOCAL_SRC_FILES += \ - jni/src/threading/event.cpp \ - jni/src/threading/semaphore.cpp \ - jni/src/threading/thread.cpp - -# JSONCPP -LOCAL_SRC_FILES += jni/lib/jsoncpp/jsoncpp.cpp - -LOCAL_SHARED_LIBRARIES := iconv openal ogg vorbis -LOCAL_STATIC_LIBRARIES := Irrlicht freetype curl ssl crypto android_native_app_glue $(PROFILER_LIBS) - -ifeq ($(HAVE_LEVELDB), 1) - LOCAL_STATIC_LIBRARIES += LevelDB -endif -LOCAL_LDLIBS := -lEGL -llog -lGLESv1_CM -lGLESv2 -lz -landroid - -include $(BUILD_SHARED_LIBRARY) - -# at the end of Android.mk -ifdef GPROF -$(call import-module,android-ndk-profiler) -endif -$(call import-module,android/native_app_glue) diff --git a/build/android/jni/Application.mk b/build/android/jni/Application.mk deleted file mode 100644 index f5eb96ed1..000000000 --- a/build/android/jni/Application.mk +++ /dev/null @@ -1,9 +0,0 @@ -APP_PLATFORM := ${APP_PLATFORM} -APP_ABI := ${TARGET_ABI} -APP_STL := c++_shared -APP_MODULES := minetest -ifndef NDEBUG -APP_OPTIM := debug -endif - -APP_CPPFLAGS += -fexceptions -std=c++11 -frtti diff --git a/build/android/jni/Deps.mk b/build/android/jni/Deps.mk deleted file mode 100644 index 73db2353f..000000000 --- a/build/android/jni/Deps.mk +++ /dev/null @@ -1,7 +0,0 @@ -APP_PLATFORM := ${APP_PLATFORM} -APP_ABI := ${TARGET_ABI} -APP_STL := c++_shared -APP_DEPRECATED_HEADERS := true - -APP_CFLAGS += ${TARGET_CFLAGS_ADDON} -APP_CPPFLAGS += ${TARGET_CXXFLAGS_ADDON} -fexceptions -std=c++11 diff --git a/build/android/jni/Irrlicht.mk b/build/android/jni/Irrlicht.mk deleted file mode 100644 index cedfe3139..000000000 --- a/build/android/jni/Irrlicht.mk +++ /dev/null @@ -1,8 +0,0 @@ -APP_PLATFORM := ${APP_PLATFORM} -APP_ABI := ${TARGET_ABI} -APP_STL := c++_shared -APP_DEPRECATED_HEADERS := true -APP_MODULES := Irrlicht - -APP_CLAFGS += ${TARGET_CFLAGS_ADDON} -APP_CPPFLAGS += ${TARGET_CXXFLAGS_ADDON} -fexceptions diff --git a/build/android/native/build.gradle b/build/android/native/build.gradle new file mode 100644 index 000000000..cbd50db6a --- /dev/null +++ b/build/android/native/build.gradle @@ -0,0 +1,59 @@ +apply plugin: 'com.android.library' +import org.ajoberstar.grgit.Grgit + +android { + compileSdkVersion 29 + buildToolsVersion '29.0.3' + ndkVersion '21.1.6352462' + defaultConfig { + minSdkVersion 16 + targetSdkVersion 29 + externalNativeBuild { + ndkBuild { + arguments '-j8', + "versionMajor=${versionMajor}", + "versionMinor=${versionMinor}", + "versionPatch=${versionPatch}", + "versionExtra=${versionExtra}" + } + } + } + + externalNativeBuild { + ndkBuild { + path file('jni/Android.mk') + } + } + + // supported architectures + splits { + abi { + enable true + reset() + include 'armeabi-v7a', 'arm64-v8a'//, 'x86' + } + } + + buildTypes { + release { + externalNativeBuild { + ndkBuild { + arguments 'NDEBUG=1' + } + } + } + } +} + +task cloneGitRepo() { + def destination = file('deps') + if(!destination.exists()) { + def grgit = Grgit.clone( + dir: destination, + uri: 'https://github.com/minetest/minetest_android_deps_binaries' + ) + grgit.close() + } +} + +preBuild.dependsOn cloneGitRepo diff --git a/build/android/native/jni/Android.mk b/build/android/native/jni/Android.mk new file mode 100644 index 000000000..140947e6a --- /dev/null +++ b/build/android/native/jni/Android.mk @@ -0,0 +1,219 @@ +LOCAL_PATH := $(call my-dir)/.. + +#LOCAL_ADDRESS_SANITIZER:=true + +include $(CLEAR_VARS) +LOCAL_MODULE := Curl +LOCAL_SRC_FILES := deps/Android/Curl/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcurl.a +include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := Freetype +LOCAL_SRC_FILES := deps/Android/Freetype/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libfreetype.a +include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := Irrlicht +LOCAL_SRC_FILES := deps/Android/Irrlicht/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libIrrlicht.a +include $(PREBUILT_STATIC_LIBRARY) + +#include $(CLEAR_VARS) +#LOCAL_MODULE := LevelDB +#LOCAL_SRC_FILES := deps/Android/LevelDB/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libleveldb.a +#include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := LuaJIT +LOCAL_SRC_FILES := deps/Android/LuaJIT/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libluajit.a +include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := mbedTLS +LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedtls.a +include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := mbedx509 +LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedx509.a +include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := mbedcrypto +LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedcrypto.a +include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OpenAL +LOCAL_SRC_FILES := deps/Android/OpenAL-Soft/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libopenal.a +include $(PREBUILT_STATIC_LIBRARY) + +# You can use `OpenSSL and Crypto` instead `mbedTLS mbedx509 mbedcrypto`, +#but it increase APK size on ~0.7MB +#include $(CLEAR_VARS) +#LOCAL_MODULE := OpenSSL +#LOCAL_SRC_FILES := deps/Android/OpenSSL/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libssl.a +#include $(PREBUILT_STATIC_LIBRARY) + +#include $(CLEAR_VARS) +#LOCAL_MODULE := Crypto +#LOCAL_SRC_FILES := deps/Android/OpenSSL/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcrypto.a +#include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := Vorbis +LOCAL_SRC_FILES := deps/Android/Vorbis/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libvorbis.a +include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := Minetest + +LOCAL_CFLAGS += \ + -DJSONCPP_NO_LOCALE_SUPPORT \ + -DHAVE_TOUCHSCREENGUI \ + -DENABLE_GLES=1 \ + -DUSE_CURL=1 \ + -DUSE_SOUND=1 \ + -DUSE_FREETYPE=1 \ + -DUSE_LEVELDB=0 \ + -DUSE_LUAJIT=1 \ + -DVERSION_MAJOR=${versionMajor} \ + -DVERSION_MINOR=${versionMinor} \ + -DVERSION_PATCH=${versionPatch} \ + -DVERSION_EXTRA=${versionExtra} \ + $(GPROF_DEF) + +ifdef NDEBUG + LOCAL_CFLAGS += -DNDEBUG=1 +endif + +ifdef GPROF + GPROF_DEF := -DGPROF + PROFILER_LIBS := android-ndk-profiler + LOCAL_CFLAGS += -pg +endif + +LOCAL_C_INCLUDES := \ + ../../../src \ + ../../../src/script \ + ../../../lib/gmp \ + ../../../lib/jsoncpp \ + deps/Android/Curl/include \ + deps/Android/Freetype/include \ + deps/Android/Irrlicht/include \ + deps/Android/LevelDB/include \ + deps/Android/libiconv/include \ + deps/Android/libiconv/libcharset/include \ + deps/Android/LuaJIT/src \ + deps/Android/OpenAL-Soft/include \ + deps/Android/sqlite \ + deps/Android/Vorbis/include + +LOCAL_SRC_FILES := \ + $(wildcard ../../../src/client/*.cpp) \ + $(wildcard ../../../src/client/*/*.cpp) \ + $(wildcard ../../../src/content/*.cpp) \ + ../../../src/database/database.cpp \ + ../../../src/database/database-dummy.cpp \ + ../../../src/database/database-files.cpp \ + ../../../src/database/database-sqlite3.cpp \ + $(wildcard ../../../src/gui/*.cpp) \ + $(wildcard ../../../src/irrlicht_changes/*.cpp) \ + $(wildcard ../../../src/mapgen/*.cpp) \ + $(wildcard ../../../src/network/*.cpp) \ + $(wildcard ../../../src/script/*.cpp) \ + $(wildcard ../../../src/script/*/*.cpp) \ + $(wildcard ../../../src/server/*.cpp) \ + $(wildcard ../../../src/threading/*.cpp) \ + $(wildcard ../../../src/util/*.c) \ + $(wildcard ../../../src/util/*.cpp) \ + ../../../src/ban.cpp \ + ../../../src/chat.cpp \ + ../../../src/clientiface.cpp \ + ../../../src/collision.cpp \ + ../../../src/content_mapnode.cpp \ + ../../../src/content_nodemeta.cpp \ + ../../../src/convert_json.cpp \ + ../../../src/craftdef.cpp \ + ../../../src/debug.cpp \ + ../../../src/defaultsettings.cpp \ + ../../../src/emerge.cpp \ + ../../../src/environment.cpp \ + ../../../src/face_position_cache.cpp \ + ../../../src/filesys.cpp \ + ../../../src/gettext.cpp \ + ../../../src/httpfetch.cpp \ + ../../../src/hud.cpp \ + ../../../src/inventory.cpp \ + ../../../src/inventorymanager.cpp \ + ../../../src/itemdef.cpp \ + ../../../src/itemstackmetadata.cpp \ + ../../../src/light.cpp \ + ../../../src/log.cpp \ + ../../../src/main.cpp \ + ../../../src/map.cpp \ + ../../../src/map_settings_manager.cpp \ + ../../../src/mapblock.cpp \ + ../../../src/mapnode.cpp \ + ../../../src/mapsector.cpp \ + ../../../src/metadata.cpp \ + ../../../src/modchannels.cpp \ + ../../../src/nameidmapping.cpp \ + ../../../src/nodedef.cpp \ + ../../../src/nodemetadata.cpp \ + ../../../src/nodetimer.cpp \ + ../../../src/noise.cpp \ + ../../../src/objdef.cpp \ + ../../../src/object_properties.cpp \ + ../../../src/particles.cpp \ + ../../../src/pathfinder.cpp \ + ../../../src/player.cpp \ + ../../../src/porting.cpp \ + ../../../src/porting_android.cpp \ + ../../../src/profiler.cpp \ + ../../../src/raycast.cpp \ + ../../../src/reflowscan.cpp \ + ../../../src/remoteplayer.cpp \ + ../../../src/rollback.cpp \ + ../../../src/rollback_interface.cpp \ + ../../../src/serialization.cpp \ + ../../../src/server.cpp \ + ../../../src/serverenvironment.cpp \ + ../../../src/serverlist.cpp \ + ../../../src/settings.cpp \ + ../../../src/staticobject.cpp \ + ../../../src/texture_override.cpp \ + ../../../src/tileanimation.cpp \ + ../../../src/tool.cpp \ + ../../../src/translation.cpp \ + ../../../src/version.cpp \ + ../../../src/voxel.cpp \ + ../../../src/voxelalgorithms.cpp + +# LevelDB backend is disabled +# ../../../src/database/database-leveldb.cpp + +# GMP +LOCAL_SRC_FILES += ../../../lib/gmp/mini-gmp.c + +# JSONCPP +LOCAL_SRC_FILES += ../../../lib/jsoncpp/jsoncpp.cpp + +# iconv +LOCAL_SRC_FILES += \ + deps/Android/libiconv/lib/iconv.c \ + deps/Android/libiconv/libcharset/lib/localcharset.c + +# SQLite3 +LOCAL_SRC_FILES += deps/Android/sqlite/sqlite3.c + +LOCAL_STATIC_LIBRARIES += Curl Freetype Irrlicht OpenAL mbedTLS mbedx509 mbedcrypto Vorbis LuaJIT android_native_app_glue $(PROFILER_LIBS) #LevelDB +#OpenSSL Crypto + +LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES + +include $(BUILD_SHARED_LIBRARY) + +ifdef GPROF +$(call import-module,android-ndk-profiler) +endif +$(call import-module,android/native_app_glue) diff --git a/build/android/native/jni/Application.mk b/build/android/native/jni/Application.mk new file mode 100644 index 000000000..82f0148f0 --- /dev/null +++ b/build/android/native/jni/Application.mk @@ -0,0 +1,32 @@ +APP_PLATFORM := ${APP_PLATFORM} +APP_ABI := ${TARGET_ABI} +APP_STL := c++_shared +NDK_TOOLCHAIN_VERSION := clang +APP_SHORT_COMMANDS := true +APP_MODULES := Minetest + +APP_CPPFLAGS := -Ofast -fvisibility=hidden -fexceptions -Wno-deprecated-declarations -Wno-extra-tokens + +ifeq ($(APP_ABI),armeabi-v7a) +APP_CPPFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb +endif + +#ifeq ($(APP_ABI),x86) +#APP_CPPFLAGS += -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32 -funroll-loops +#endif + +ifndef NDEBUG +APP_CPPFLAGS := -g -D_DEBUG -O0 -fno-omit-frame-pointer -fexceptions +endif + +APP_CFLAGS := $(APP_CPPFLAGS) -Wno-parentheses-equality #-Werror=shorten-64-to-32 +APP_CXXFLAGS := $(APP_CPPFLAGS) -frtti -std=gnu++17 +APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections,--icf=safe + +ifeq ($(APP_ABI),arm64-v8a) +APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections +endif + +ifndef NDEBUG +APP_LDFLAGS := +endif diff --git a/build/android/native/src/main/AndroidManifest.xml b/build/android/native/src/main/AndroidManifest.xml new file mode 100644 index 000000000..19451c7fd --- /dev/null +++ b/build/android/native/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/build/android/patches/irrlicht-back_button.patch b/build/android/patches/irrlicht-back_button.patch deleted file mode 100644 index e17b81347..000000000 --- a/build/android/patches/irrlicht-back_button.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp.orig 2015-08-29 15:43:09.000000000 +0300 -+++ irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2016-05-13 21:36:22.880388505 +0300 -@@ -486,7 +486,7 @@ - event.KeyInput.Char = 0; - } - -- device->postEventFromUser(event); -+ status = device->postEventFromUser(event); - } - break; - default: -@@ -543,7 +543,7 @@ - KeyMap[1] = KEY_LBUTTON; // AKEYCODE_SOFT_LEFT - KeyMap[2] = KEY_RBUTTON; // AKEYCODE_SOFT_RIGHT - KeyMap[3] = KEY_HOME; // AKEYCODE_HOME -- KeyMap[4] = KEY_BACK; // AKEYCODE_BACK -+ KeyMap[4] = KEY_CANCEL; // AKEYCODE_BACK - KeyMap[5] = KEY_UNKNOWN; // AKEYCODE_CALL - KeyMap[6] = KEY_UNKNOWN; // AKEYCODE_ENDCALL - KeyMap[7] = KEY_KEY_0; // AKEYCODE_0 diff --git a/build/android/patches/irrlicht-native_activity.patch b/build/android/patches/irrlicht-native_activity.patch deleted file mode 100644 index a9d2610c3..000000000 --- a/build/android/patches/irrlicht-native_activity.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2018-09-11 18:19:51.453403631 +0300 -+++ irrlicht/source/Irrlicht/CEGLManager.cpp 2018-09-11 18:36:24.603471869 +0300 -@@ -9,6 +9,10 @@ - #include "irrString.h" - #include "os.h" - -+#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_) -+#include -+#endif -+ - namespace irr - { - namespace video diff --git a/build/android/patches/irrlicht-texturehack.patch b/build/android/patches/irrlicht-texturehack.patch deleted file mode 100644 index a458ede72..000000000 --- a/build/android/patches/irrlicht-texturehack.patch +++ /dev/null @@ -1,240 +0,0 @@ ---- irrlicht/source/Irrlicht/COGLESTexture.cpp.orig 2014-06-22 17:01:13.266568869 +0200 -+++ irrlicht/source/Irrlicht/COGLESTexture.cpp 2014-06-22 17:03:59.298572810 +0200 -@@ -366,112 +366,140 @@ - void(*convert)(const void*, s32, void*) = 0; - getFormatParameters(ColorFormat, InternalFormat, filtering, PixelFormat, PixelType, convert); - -- // make sure we don't change the internal format of existing images -- if (!newTexture) -- InternalFormat = oldInternalFormat; -- -- Driver->setActiveTexture(0, this); -- -- if (Driver->testGLError()) -- os::Printer::log("Could not bind Texture", ELL_ERROR); -- -- // mipmap handling for main texture -- if (!level && newTexture) -- { -- // auto generate if possible and no mipmap data is given -- if (!IsCompressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) -- { -- if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED)) -- glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST); -- else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY)) -- glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); -- else -- glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); -+ bool retry = false; -+ -+ do { -+ if (retry) { -+ InternalFormat = GL_RGBA; -+ PixelFormat = GL_RGBA; -+ convert = CColorConverter::convert_A8R8G8B8toA8B8G8R8; -+ } -+ // make sure we don't change the internal format of existing images -+ if (!newTexture) -+ InternalFormat = oldInternalFormat; - -- glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); -- AutomaticMipmapUpdate=true; -- } -+ Driver->setActiveTexture(0, this); - -- // enable bilinear filter without mipmaps -- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); -- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); -- } -+ if (Driver->testGLError()) -+ os::Printer::log("Could not bind Texture", ELL_ERROR); - -- // now get image data and upload to GPU -+ // mipmap handling for main texture -+ if (!level && newTexture) -+ { -+ // auto generate if possible and no mipmap data is given -+ if (!IsCompressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) -+ { -+ if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED)) -+ glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST); -+ else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY)) -+ glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); -+ else -+ glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); -+ -+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); -+ AutomaticMipmapUpdate=true; -+ } -+ -+ // enable bilinear filter without mipmaps -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); -+ } - -- u32 compressedImageSize = IImage::getCompressedImageSize(ColorFormat, image->getDimension().Width, image->getDimension().Height); -+ // now get image data and upload to GPU - -- void* source = image->lock(); -+ u32 compressedImageSize = IImage::getCompressedImageSize(ColorFormat, image->getDimension().Width, image->getDimension().Height); - -- IImage* tmpImage = 0; -+ void* source = image->lock(); - -- if (convert) -- { -- tmpImage = new CImage(image->getColorFormat(), image->getDimension()); -- void* dest = tmpImage->lock(); -- convert(source, image->getDimension().getArea(), dest); -- image->unlock(); -- source = dest; -- } -+ IImage* tmpImage = 0; - -- if (newTexture) -- { -- if (IsCompressed) -+ if (convert) - { -- glCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width, -- image->getDimension().Height, 0, compressedImageSize, source); -+ tmpImage = new CImage(image->getColorFormat(), image->getDimension()); -+ void* dest = tmpImage->lock(); -+ convert(source, image->getDimension().getArea(), dest); -+ image->unlock(); -+ source = dest; - } -- else -- glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width, -- image->getDimension().Height, 0, PixelFormat, PixelType, source); -- } -- else -- { -- if (IsCompressed) -+ -+ if (newTexture) - { -- glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, -- image->getDimension().Height, PixelFormat, compressedImageSize, source); -+ if (IsCompressed) -+ { -+ glCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width, -+ image->getDimension().Height, 0, compressedImageSize, source); -+ } -+ else -+ glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width, -+ image->getDimension().Height, 0, PixelFormat, PixelType, source); - } - else -- glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, -- image->getDimension().Height, PixelFormat, PixelType, source); -- } -- -- if (convert) -- { -- tmpImage->unlock(); -- tmpImage->drop(); -- } -- else -- image->unlock(); -- -- if (!level && newTexture) -- { -- if (IsCompressed && !mipmapData) - { -- if (image->hasMipMaps()) -- mipmapData = static_cast(image->lock())+compressedImageSize; -+ if (IsCompressed) -+ { -+ glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, -+ image->getDimension().Height, PixelFormat, compressedImageSize, source); -+ } - else -- HasMipMaps = false; -+ glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, -+ image->getDimension().Height, PixelFormat, PixelType, source); - } - -- regenerateMipMapLevels(mipmapData); -- -- if (HasMipMaps) // might have changed in regenerateMipMapLevels -+ if (convert) - { -- // enable bilinear mipmap filter -- GLint filteringMipMaps = GL_LINEAR_MIPMAP_NEAREST; -- -- if (filtering != GL_LINEAR) -- filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST; -+ tmpImage->unlock(); -+ tmpImage->drop(); -+ } -+ else -+ image->unlock(); -+ -+ if (glGetError() != GL_NO_ERROR) { -+ static bool warned = false; -+ if ((!retry) && (ColorFormat == ECF_A8R8G8B8)) { -+ -+ if (!warned) { -+ os::Printer::log("Your driver claims to support GL_BGRA but fails on trying to upload a texture, converting to GL_RGBA and trying again", ELL_ERROR); -+ warned = true; -+ } -+ } -+ else if (retry) { -+ os::Printer::log("Neither uploading texture as GL_BGRA nor, converted one using GL_RGBA succeeded", ELL_ERROR); -+ } -+ retry = !retry; -+ continue; -+ } else { -+ retry = false; -+ } - -- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps); -- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); -+ if (!level && newTexture) -+ { -+ if (IsCompressed && !mipmapData) -+ { -+ if (image->hasMipMaps()) -+ mipmapData = static_cast(image->lock())+compressedImageSize; -+ else -+ HasMipMaps = false; -+ } -+ -+ regenerateMipMapLevels(mipmapData); -+ -+ if (HasMipMaps) // might have changed in regenerateMipMapLevels -+ { -+ // enable bilinear mipmap filter -+ GLint filteringMipMaps = GL_LINEAR_MIPMAP_NEAREST; -+ -+ if (filtering != GL_LINEAR) -+ filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST; -+ -+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps); -+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); -+ } - } -- } - -- if (Driver->testGLError()) -- os::Printer::log("Could not glTexImage2D", ELL_ERROR); -+ if (Driver->testGLError()) -+ os::Printer::log("Could not glTexImage2D", ELL_ERROR); -+ } -+ while(retry); - } - - ---- irrlicht/source/Irrlicht/COGLESTexture.cpp.orig 2014-06-25 00:28:50.820501856 +0200 -+++ irrlicht/source/Irrlicht/COGLESTexture.cpp 2014-06-25 00:08:37.712544692 +0200 -@@ -422,6 +422,9 @@ - source = dest; - } - -+ //clear old error -+ glGetError(); -+ - if (newTexture) - { - if (IsCompressed) diff --git a/build/android/patches/irrlicht-touchcount.patch b/build/android/patches/irrlicht-touchcount.patch deleted file mode 100644 index d4e4b9c3e..000000000 --- a/build/android/patches/irrlicht-touchcount.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- irrlicht.orig/include/IEventReceiver.h 2014-06-03 19:43:50.433713133 +0200 -+++ irrlicht/include/IEventReceiver.h 2014-06-03 19:44:36.993711489 +0200 -@@ -375,6 +375,9 @@ - // Y position of simple touch. - s32 Y; - -+ // number of current touches -+ s32 touchedCount; -+ - //! Type of touch event. - ETOUCH_INPUT_EVENT Event; - }; ---- irrlicht.orig/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2014-06-03 19:43:50.505713130 +0200 -+++ irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2014-06-03 19:45:37.265709359 +0200 -@@ -315,6 +315,7 @@ - event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, i); - event.TouchInput.X = AMotionEvent_getX(androidEvent, i); - event.TouchInput.Y = AMotionEvent_getY(androidEvent, i); -+ event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent); - - device->postEventFromUser(event); - } -@@ -326,6 +327,7 @@ - event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, pointerIndex); - event.TouchInput.X = AMotionEvent_getX(androidEvent, pointerIndex); - event.TouchInput.Y = AMotionEvent_getY(androidEvent, pointerIndex); -+ event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent); - - device->postEventFromUser(event); - } diff --git a/build/android/patches/libvorbis-libogg-fpu.patch b/build/android/patches/libvorbis-libogg-fpu.patch deleted file mode 100644 index 52ab397ac..000000000 --- a/build/android/patches/libvorbis-libogg-fpu.patch +++ /dev/null @@ -1,37 +0,0 @@ ---- libvorbis-libogg-android/jni/libvorbis-jni/Android.mk.orig 2014-06-17 19:22:50.621559073 +0200 -+++ libvorbis-libogg-android/jni/libvorbis-jni/Android.mk 2014-06-17 19:38:20.641581140 +0200 -@@ -4,9 +4,6 @@ - - LOCAL_MODULE := vorbis-jni - LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -fsigned-char --ifeq ($(TARGET_ARCH),arm) -- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp --endif - - LOCAL_SHARED_LIBRARIES := libogg libvorbis - ---- libvorbis-libogg-android/jni/libvorbis/Android.mk.orig 2014-06-17 19:22:39.077558797 +0200 -+++ libvorbis-libogg-android/jni/libvorbis/Android.mk 2014-06-17 19:38:52.121581887 +0200 -@@ -4,9 +4,6 @@ - - LOCAL_MODULE := libvorbis - LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -ffast-math -fsigned-char --ifeq ($(TARGET_ARCH),arm) -- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp --endif - LOCAL_SHARED_LIBRARIES := libogg - - LOCAL_SRC_FILES := \ ---- libvorbis-libogg-android/jni/libogg/Android.mk.orig 2014-06-17 19:22:33.965558675 +0200 -+++ libvorbis-libogg-android/jni/libogg/Android.mk 2014-06-17 19:38:25.337581252 +0200 -@@ -4,10 +4,6 @@ - - LOCAL_MODULE := libogg - LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -ffast-math -fsigned-char --ifeq ($(TARGET_ARCH),arm) -- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp --endif -- - - LOCAL_SRC_FILES := \ - bitwise.c \ diff --git a/build/android/patches/openssl_arch.patch b/build/android/patches/openssl_arch.patch deleted file mode 100644 index d15e2b137..000000000 --- a/build/android/patches/openssl_arch.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- openssl-1.0.2e.orig/Configure 2015-12-03 15:04:23.000000000 +0100 -+++ openssl-1.0.2e/Configure 2015-12-14 21:01:40.351265968 +0100 -@@ -464,8 +464,10 @@ - # Android: linux-* but without pointers to headers and libs. - "android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - "android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", -+"android-arm","gcc:-march=armv4 -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - "android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - "android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", -+"android-mips32","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - - #### *BSD [do see comment about ${BSDthreads} above!] - "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", diff --git a/build/android/settings.gradle b/build/android/settings.gradle index b0cee5dad..b048fca7c 100644 --- a/build/android/settings.gradle +++ b/build/android/settings.gradle @@ -1 +1,2 @@ rootProject.name = "Minetest" +include ':app', ':native' diff --git a/build/android/src/main/java/net.minetest.minetest/MainActivity.java b/build/android/src/main/java/net.minetest.minetest/MainActivity.java deleted file mode 100644 index 71b0ce144..000000000 --- a/build/android/src/main/java/net.minetest.minetest/MainActivity.java +++ /dev/null @@ -1,77 +0,0 @@ -package net.minetest.minetest; - -import android.Manifest; -import android.app.Activity; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class MainActivity extends Activity { - private final static int PERMISSIONS = 1; - private static final String[] REQUIRED_SDK_PERMISSIONS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - checkPermission(); - } else { - next(); - } - } - - private void checkPermission() { - final List missingPermissions = new ArrayList<>(); - // check required permission - for (final String permission : REQUIRED_SDK_PERMISSIONS) { - final int result = ContextCompat.checkSelfPermission(this, permission); - if (result != PackageManager.PERMISSION_GRANTED) { - missingPermissions.add(permission); - } - } - if (!missingPermissions.isEmpty()) { - // request permission - final String[] permissions = missingPermissions - .toArray(new String[0]); - ActivityCompat.requestPermissions(this, permissions, PERMISSIONS); - } else { - final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length]; - Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED); - onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS, - grantResults); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - if (requestCode == PERMISSIONS) { - for (int index = 0; index < permissions.length; index++) { - if (grantResults[index] != PackageManager.PERMISSION_GRANTED) { - // permission not granted - toast and exit - Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show(); - finish(); - return; - } - } - // permission were granted - run - next(); - } - } - - private void next() { - Intent intent = new Intent(this, MtNativeActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent); - } -} diff --git a/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java b/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java deleted file mode 100644 index 84cfca796..000000000 --- a/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java +++ /dev/null @@ -1,371 +0,0 @@ -package net.minetest.minetest; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.res.AssetFileDescriptor; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.util.Log; -import android.view.Display; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.util.Vector; - -public class MinetestAssetCopy extends Activity { - private ProgressBar m_ProgressBar; - private TextView m_Filename; - private copyAssetTask m_AssetCopy; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.assetcopy); - m_ProgressBar = findViewById(R.id.progressBar1); - m_Filename = findViewById(R.id.textView1); - Display display = getWindowManager().getDefaultDisplay(); - m_ProgressBar.getLayoutParams().width = (int) (display.getWidth() * 0.8); - m_ProgressBar.invalidate(); - - /* check if there's already a copy in progress and reuse in case it is*/ - MinetestAssetCopy prevActivity = - (MinetestAssetCopy) getLastNonConfigurationInstance(); - if (prevActivity != null) { - m_AssetCopy = prevActivity.m_AssetCopy; - } else { - m_AssetCopy = new copyAssetTask(); - m_AssetCopy.execute(); - } - } - - @Override - protected void onResume() { - super.onResume(); - makeFullScreen(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (m_AssetCopy != null) { - m_AssetCopy.cancel(true); - } - } - - private void makeFullScreen() { - if (Build.VERSION.SDK_INT >= 19) - this.getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus) - makeFullScreen(); - } - - /* preserve asset copy background task to prevent restart of copying */ - /* this way of doing it is not recommended for latest android version */ - /* but the recommended way isn't available on android 2.x */ - public Object onRetainNonConfigurationInstance() { - return this; - } - - @SuppressLint("StaticFieldLeak") - private class copyAssetTask extends AsyncTask { - boolean m_copy_started = false; - String m_Foldername = "media"; - Vector m_foldernames; - Vector m_filenames; - Vector m_tocopy; - Vector m_asset_size_unknown; - - private long getFullSize(String filename) { - long size = 0; - try { - InputStream src = getAssets().open(filename); - byte[] buf = new byte[4096]; - - int len; - while ((len = src.read(buf)) > 0) { - size += len; - } - } catch (IOException e) { - e.printStackTrace(); - } - return size; - } - - @Override - protected String doInBackground(String... files) { - m_foldernames = new Vector<>(); - m_filenames = new Vector<>(); - m_tocopy = new Vector<>(); - m_asset_size_unknown = new Vector<>(); - String baseDir = - Environment.getExternalStorageDirectory().getAbsolutePath() - + "/"; - - - // prepare temp folder - File TempFolder = new File(baseDir + "Minetest/tmp/"); - - if (!TempFolder.exists()) { - TempFolder.mkdir(); - } else { - File[] todel = TempFolder.listFiles(); - - for (File file : todel) { - Log.v("MinetestAssetCopy", "deleting: " + file.getAbsolutePath()); - file.delete(); - } - } - - // add a .nomedia file - try { - OutputStream dst = new FileOutputStream(baseDir + "Minetest/.nomedia"); - dst.close(); - } catch (IOException e) { - Log.e("MinetestAssetCopy", "Failed to create .nomedia file"); - e.printStackTrace(); - } - - - // build lists from prepared data - BuildFolderList(); - BuildFileList(); - - // scan filelist - ProcessFileList(); - - // doing work - m_copy_started = true; - m_ProgressBar.setMax(m_tocopy.size()); - - for (int i = 0; i < m_tocopy.size(); i++) { - try { - String filename = m_tocopy.get(i); - publishProgress(i); - - boolean asset_size_unknown = false; - long filesize = -1; - - if (m_asset_size_unknown.contains(filename)) { - File testme = new File(baseDir + "/" + filename); - - if (testme.exists()) - filesize = testme.length(); - - asset_size_unknown = true; - } - - InputStream src; - try { - src = getAssets().open(filename); - } catch (IOException e) { - Log.e("MinetestAssetCopy", "Copying file: " + filename + " FAILED (not in assets)"); - e.printStackTrace(); - continue; - } - - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len = src.read(buf, 0, 1024); - - /* following handling is crazy but we need to deal with */ - /* compressed assets.Flash chips limited livetime due to */ - /* write operations, we can't allow large files to destroy */ - /* users flash. */ - if (asset_size_unknown) { - if ((len > 0) && (len < buf.length) && (len == filesize)) { - src.close(); - continue; - } - - if (len == buf.length) { - src.close(); - long size = getFullSize(filename); - if (size == filesize) { - continue; - } - src = getAssets().open(filename); - len = src.read(buf, 0, 1024); - } - } - if (len > 0) { - int total_filesize = 0; - OutputStream dst; - try { - dst = new FileOutputStream(baseDir + "/" + filename); - } catch (IOException e) { - Log.e("MinetestAssetCopy", "Copying file: " + baseDir + - "/" + filename + " FAILED (couldn't open output file)"); - e.printStackTrace(); - src.close(); - continue; - } - dst.write(buf, 0, len); - total_filesize += len; - - while ((len = src.read(buf)) > 0) { - dst.write(buf, 0, len); - total_filesize += len; - } - - dst.close(); - Log.v("MinetestAssetCopy", "Copied file: " + - m_tocopy.get(i) + " (" + total_filesize + - " bytes)"); - } else if (len < 0) { - Log.e("MinetestAssetCopy", "Copying file: " + - m_tocopy.get(i) + " failed, size < 0"); - } - src.close(); - } catch (IOException e) { - Log.e("MinetestAssetCopy", "Copying file: " + - m_tocopy.get(i) + " failed"); - e.printStackTrace(); - } - } - return ""; - } - - /** - * update progress bar - */ - protected void onProgressUpdate(Integer... progress) { - - if (m_copy_started) { - String todisplay = m_tocopy.get(progress[0]); - m_ProgressBar.setProgress(progress[0]); - m_Filename.setText(todisplay); - } else { - String todisplay = m_Foldername; - String full_text = "scanning " + todisplay + " ..."; - m_Filename.setText(full_text); - } - } - - /** - * check all files and folders in filelist - */ - void ProcessFileList() { - String FlashBaseDir = - Environment.getExternalStorageDirectory().getAbsolutePath(); - - for (String current_path : m_filenames) { - String FlashPath = FlashBaseDir + "/" + current_path; - - if (isAssetFolder(current_path)) { - /* store information and update gui */ - m_Foldername = current_path; - publishProgress(0); - - /* open file in order to check if it's a folder */ - File current_folder = new File(FlashPath); - if (!current_folder.exists()) { - if (!current_folder.mkdirs()) { - Log.e("MinetestAssetCopy", "\t failed create folder: " + - FlashPath); - } else { - Log.v("MinetestAssetCopy", "\t created folder: " + - FlashPath); - } - } - - continue; - } - - /* if it's not a folder it's most likely a file */ - boolean refresh = true; - - File testme = new File(FlashPath); - - long asset_filesize = -1; - long stored_filesize; - - if (testme.exists()) { - try { - AssetFileDescriptor fd = getAssets().openFd(current_path); - asset_filesize = fd.getLength(); - fd.close(); - } catch (IOException e) { - m_asset_size_unknown.add(current_path); - Log.e("MinetestAssetCopy", "Failed to open asset file \"" + - FlashPath + "\" for size check"); - } - - stored_filesize = testme.length(); - - if (asset_filesize == stored_filesize) - refresh = false; - - } - - if (refresh) - m_tocopy.add(current_path); - } - } - - /** - * read list of folders prepared on package build - */ - void BuildFolderList() { - try { - InputStream is = getAssets().open("index.txt"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - String line = reader.readLine(); - while (line != null) { - m_foldernames.add(line); - line = reader.readLine(); - } - is.close(); - } catch (IOException e1) { - Log.e("MinetestAssetCopy", "Error on processing index.txt"); - e1.printStackTrace(); - } - } - - /** - * read list of asset files prepared on package build - */ - void BuildFileList() { - long entrycount = 0; - try { - InputStream is = getAssets().open("filelist.txt"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - String line = reader.readLine(); - while (line != null) { - m_filenames.add(line); - line = reader.readLine(); - entrycount++; - } - is.close(); - } catch (IOException e1) { - Log.e("MinetestAssetCopy", "Error on processing filelist.txt"); - e1.printStackTrace(); - } - } - - protected void onPostExecute(String result) { - finish(); - } - - boolean isAssetFolder(String path) { - return m_foldernames.contains(path); - } - } -} diff --git a/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java b/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java deleted file mode 100644 index cb7ba8d3b..000000000 --- a/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java +++ /dev/null @@ -1,87 +0,0 @@ -package net.minetest.minetest; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.text.InputType; -import android.view.KeyEvent; -import android.view.View; -import android.view.View.OnKeyListener; -import android.widget.EditText; - -public class MinetestTextEntry extends Activity { - private final int MultiLineTextInput = 1; - private final int SingleLineTextInput = 2; - private final int SingleLinePasswordInput = 3; - private AlertDialog mTextInputDialog; - private EditText mTextInputWidget; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Bundle b = getIntent().getExtras(); - String acceptButton = b.getString("EnterButton"); - String hint = b.getString("hint"); - String current = b.getString("current"); - int editType = b.getInt("editType"); - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - mTextInputWidget = new EditText(this); - mTextInputWidget.setHint(hint); - mTextInputWidget.setText(current); - mTextInputWidget.setMinWidth(300); - if (editType == SingleLinePasswordInput) { - mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_VARIATION_PASSWORD); - } else { - mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT); - } - - builder.setView(mTextInputWidget); - - if (editType == MultiLineTextInput) { - builder.setPositiveButton(acceptButton, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - pushResult(mTextInputWidget.getText().toString()); - } - }); - } - - builder.setOnCancelListener(new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - cancelDialog(); - } - }); - - mTextInputWidget.setOnKeyListener(new OnKeyListener() { - @Override - public boolean onKey(View view, int KeyCode, KeyEvent event) { - if (KeyCode == KeyEvent.KEYCODE_ENTER) { - - pushResult(mTextInputWidget.getText().toString()); - return true; - } - return false; - } - }); - - mTextInputDialog = builder.create(); - mTextInputDialog.show(); - } - - private void pushResult(String text) { - Intent resultData = new Intent(); - resultData.putExtra("text", text); - setResult(Activity.RESULT_OK, resultData); - mTextInputDialog.dismiss(); - finish(); - } - - private void cancelDialog() { - setResult(Activity.RESULT_CANCELED); - mTextInputDialog.dismiss(); - finish(); - } -} diff --git a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java b/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java deleted file mode 100644 index f49d078fe..000000000 --- a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java +++ /dev/null @@ -1,108 +0,0 @@ -package net.minetest.minetest; - -import android.app.NativeActivity; -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.view.View; -import android.view.WindowManager; - -public class MtNativeActivity extends NativeActivity { - - static { - System.loadLibrary("c++_shared"); - System.loadLibrary("openal"); - System.loadLibrary("ogg"); - System.loadLibrary("vorbis"); - System.loadLibrary("iconv"); - System.loadLibrary("minetest"); - } - - private int m_MessagReturnCode; - private String m_MessageReturnValue; - - public static native void putMessageBoxResult(String text); - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - m_MessagReturnCode = -1; - m_MessageReturnValue = ""; - } - - @Override - protected void onResume() { - super.onResume(); - makeFullScreen(); - } - - private void makeFullScreen() { - if (Build.VERSION.SDK_INT >= 19) - this.getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus) - makeFullScreen(); - } - - public void copyAssets() { - Intent intent = new Intent(this, MinetestAssetCopy.class); - startActivity(intent); - } - - public void showDialog(String acceptButton, String hint, String current, - int editType) { - - Intent intent = new Intent(this, MinetestTextEntry.class); - Bundle params = new Bundle(); - params.putString("acceptButton", acceptButton); - params.putString("hint", hint); - params.putString("current", current); - params.putInt("editType", editType); - intent.putExtras(params); - startActivityForResult(intent, 101); - m_MessageReturnValue = ""; - m_MessagReturnCode = -1; - } - - /* ugly code to workaround putMessageBoxResult not beeing found */ - public int getDialogState() { - return m_MessagReturnCode; - } - - public String getDialogValue() { - m_MessagReturnCode = -1; - return m_MessageReturnValue; - } - - public float getDensity() { - return getResources().getDisplayMetrics().density; - } - - public int getDisplayWidth() { - return getResources().getDisplayMetrics().widthPixels; - } - - public int getDisplayHeight() { - return getResources().getDisplayMetrics().heightPixels; - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, - Intent data) { - if (requestCode == 101) { - if (resultCode == RESULT_OK) { - String text = data.getStringExtra("text"); - m_MessagReturnCode = 0; - m_MessageReturnValue = text; - } else { - m_MessagReturnCode = 1; - } - } - } -} diff --git a/build/android/src/main/res/layout/assetcopy.xml b/build/android/src/main/res/layout/assetcopy.xml deleted file mode 100644 index b3da2f027..000000000 --- a/build/android/src/main/res/layout/assetcopy.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/build/android/src/main/res/values-v21/styles.xml b/build/android/src/main/res/values-v21/styles.xml deleted file mode 100644 index 8b0777467..000000000 --- a/build/android/src/main/res/values-v21/styles.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - diff --git a/build/android/src/main/res/values/strings.xml b/build/android/src/main/res/values/strings.xml deleted file mode 100644 index a5eaef5d1..000000000 --- a/build/android/src/main/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Preparing media… - Required permission wasn\'t granted, Minetest can\'t run without it - \ No newline at end of file diff --git a/build/android/src/main/res/values/styles.xml b/build/android/src/main/res/values/styles.xml deleted file mode 100644 index 1bd41ae76..000000000 --- a/build/android/src/main/res/values/styles.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index c904bccfc..bd27a01dc 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -20,6 +20,8 @@ local function basic_dump(o) -- dump's output is intended for humans. --elseif tp == "function" then -- return string.format("loadstring(%q)", string.dump(o)) + elseif tp == "userdata" then + return tostring(o) else return string.format("<%s>", tp) end @@ -290,7 +292,8 @@ if INIT == "game" then return end local undef = core.registered_nodes[unode.name] - if undef and undef.on_rightclick then + local sneaking = placer and placer:get_player_control().sneak + if undef and undef.on_rightclick and not sneaking then return undef.on_rightclick(pointed_thing.under, unode, placer, itemstack, pointed_thing) end @@ -344,18 +347,12 @@ if INIT == "game" then --Wrapper for rotate_and_place() to check for sneak and assume Creative mode --implies infinite stacks when performing a 6d rotation. -------------------------------------------------------------------------------- - local creative_mode_cache = core.settings:get_bool("creative_mode") - local function is_creative(name) - return creative_mode_cache or - core.check_player_privs(name, {creative = true}) - end - core.rotate_node = function(itemstack, placer, pointed_thing) local name = placer and placer:get_player_name() or "" local invert_wall = placer and placer:get_player_control().sneak or false return core.rotate_and_place(itemstack, placer, pointed_thing, - is_creative(name), - {invert_wall = invert_wall}, true) + core.is_creative_enabled(name), + {invert_wall = invert_wall}, true) end end diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua index 163aa67ad..300b394c6 100644 --- a/builtin/common/serialize.lua +++ b/builtin/common/serialize.lua @@ -120,15 +120,8 @@ function core.serialize(x) elseif tp == "function" then return string.format("loadstring(%q)", string.dump(x)) elseif tp == "number" then - -- Serialize integers with string.format to prevent - -- scientific notation, which doesn't preserve - -- precision and breaks things like node position - -- hashes. Serialize floats normally. - if math.floor(x) == x then - return string.format("%d", x) - else - return tostring(x) - end + -- Serialize numbers reversibly with string.format + return string.format("%.17g", x) elseif tp == "table" then local vals = {} local idx_dumped = {} diff --git a/builtin/common/tests/serialize_spec.lua b/builtin/common/tests/serialize_spec.lua index c41b0a372..17c6a60f7 100644 --- a/builtin/common/tests/serialize_spec.lua +++ b/builtin/common/tests/serialize_spec.lua @@ -18,6 +18,18 @@ describe("serialize", function() assert.same(test_in, test_out) end) + it("handles precise numbers", function() + local test_in = 0.2695949158945771 + local test_out = core.deserialize(core.serialize(test_in)) + assert.same(test_in, test_out) + end) + + it("handles big integers", function() + local test_in = 269594915894577 + local test_out = core.deserialize(core.serialize(test_in)) + assert.same(test_in, test_out) + end) + it("handles recursive structures", function() local test_in = { hello = "world" } test_in.foo = test_in diff --git a/builtin/common/tests/vector_spec.lua b/builtin/common/tests/vector_spec.lua index 79f032f28..6f308a4a8 100644 --- a/builtin/common/tests/vector_spec.lua +++ b/builtin/common/tests/vector_spec.lua @@ -43,4 +43,146 @@ describe("vector", function() it("add()", function() assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 })) end) + + -- This function is needed because of floating point imprecision. + local function almost_equal(a, b) + if type(a) == "number" then + return math.abs(a - b) < 0.00000000001 + end + return vector.distance(a, b) < 0.000000000001 + end + + describe("rotate_around_axis()", function() + it("rotates", function() + assert.True(almost_equal({x = -1, y = 0, z = 0}, + vector.rotate_around_axis({x = 1, y = 0, z = 0}, {x = 0, y = 1, z = 0}, math.pi))) + assert.True(almost_equal({x = 0, y = 1, z = 0}, + vector.rotate_around_axis({x = 0, y = 0, z = 1}, {x = 1, y = 0, z = 0}, math.pi / 2))) + assert.True(almost_equal({x = 4, y = 1, z = 1}, + vector.rotate_around_axis({x = 4, y = 1, z = 1}, {x = 4, y = 1, z = 1}, math.pi / 6))) + end) + it("keeps distance to axis", function() + local rotate1 = {x = 1, y = 3, z = 1} + local axis1 = {x = 1, y = 3, z = 2} + local rotated1 = vector.rotate_around_axis(rotate1, axis1, math.pi / 13) + assert.True(almost_equal(vector.distance(axis1, rotate1), vector.distance(axis1, rotated1))) + local rotate2 = {x = 1, y = 1, z = 3} + local axis2 = {x = 2, y = 6, z = 100} + local rotated2 = vector.rotate_around_axis(rotate2, axis2, math.pi / 23) + assert.True(almost_equal(vector.distance(axis2, rotate2), vector.distance(axis2, rotated2))) + local rotate3 = {x = 1, y = -1, z = 3} + local axis3 = {x = 2, y = 6, z = 100} + local rotated3 = vector.rotate_around_axis(rotate3, axis3, math.pi / 2) + assert.True(almost_equal(vector.distance(axis3, rotate3), vector.distance(axis3, rotated3))) + end) + it("rotates back", function() + local rotate1 = {x = 1, y = 3, z = 1} + local axis1 = {x = 1, y = 3, z = 2} + local rotated1 = vector.rotate_around_axis(rotate1, axis1, math.pi / 13) + rotated1 = vector.rotate_around_axis(rotated1, axis1, -math.pi / 13) + assert.True(almost_equal(rotate1, rotated1)) + local rotate2 = {x = 1, y = 1, z = 3} + local axis2 = {x = 2, y = 6, z = 100} + local rotated2 = vector.rotate_around_axis(rotate2, axis2, math.pi / 23) + rotated2 = vector.rotate_around_axis(rotated2, axis2, -math.pi / 23) + assert.True(almost_equal(rotate2, rotated2)) + local rotate3 = {x = 1, y = -1, z = 3} + local axis3 = {x = 2, y = 6, z = 100} + local rotated3 = vector.rotate_around_axis(rotate3, axis3, math.pi / 2) + rotated3 = vector.rotate_around_axis(rotated3, axis3, -math.pi / 2) + assert.True(almost_equal(rotate3, rotated3)) + end) + it("is right handed", function() + local v_before1 = {x = 0, y = 1, z = -1} + local v_after1 = vector.rotate_around_axis(v_before1, {x = 1, y = 0, z = 0}, math.pi / 4) + assert.True(almost_equal(vector.normalize(vector.cross(v_after1, v_before1)), {x = 1, y = 0, z = 0})) + + local v_before2 = {x = 0, y = 3, z = 4} + local v_after2 = vector.rotate_around_axis(v_before2, {x = 1, y = 0, z = 0}, 2 * math.pi / 5) + assert.True(almost_equal(vector.normalize(vector.cross(v_after2, v_before2)), {x = 1, y = 0, z = 0})) + + local v_before3 = {x = 1, y = 0, z = -1} + local v_after3 = vector.rotate_around_axis(v_before3, {x = 0, y = 1, z = 0}, math.pi / 4) + assert.True(almost_equal(vector.normalize(vector.cross(v_after3, v_before3)), {x = 0, y = 1, z = 0})) + + local v_before4 = {x = 3, y = 0, z = 4} + local v_after4 = vector.rotate_around_axis(v_before4, {x = 0, y = 1, z = 0}, 2 * math.pi / 5) + assert.True(almost_equal(vector.normalize(vector.cross(v_after4, v_before4)), {x = 0, y = 1, z = 0})) + + local v_before5 = {x = 1, y = -1, z = 0} + local v_after5 = vector.rotate_around_axis(v_before5, {x = 0, y = 0, z = 1}, math.pi / 4) + assert.True(almost_equal(vector.normalize(vector.cross(v_after5, v_before5)), {x = 0, y = 0, z = 1})) + + local v_before6 = {x = 3, y = 4, z = 0} + local v_after6 = vector.rotate_around_axis(v_before6, {x = 0, y = 0, z = 1}, 2 * math.pi / 5) + assert.True(almost_equal(vector.normalize(vector.cross(v_after6, v_before6)), {x = 0, y = 0, z = 1})) + end) + end) + + describe("rotate()", function() + it("rotates", function() + assert.True(almost_equal({x = -1, y = 0, z = 0}, + vector.rotate({x = 1, y = 0, z = 0}, {x = 0, y = math.pi, z = 0}))) + assert.True(almost_equal({x = 0, y = -1, z = 0}, + vector.rotate({x = 1, y = 0, z = 0}, {x = 0, y = 0, z = math.pi / 2}))) + assert.True(almost_equal({x = 1, y = 0, z = 0}, + vector.rotate({x = 1, y = 0, z = 0}, {x = math.pi / 123, y = 0, z = 0}))) + end) + it("is counterclockwise", function() + local v_before1 = {x = 0, y = 1, z = -1} + local v_after1 = vector.rotate(v_before1, {x = math.pi / 4, y = 0, z = 0}) + assert.True(almost_equal(vector.normalize(vector.cross(v_after1, v_before1)), {x = 1, y = 0, z = 0})) + + local v_before2 = {x = 0, y = 3, z = 4} + local v_after2 = vector.rotate(v_before2, {x = 2 * math.pi / 5, y = 0, z = 0}) + assert.True(almost_equal(vector.normalize(vector.cross(v_after2, v_before2)), {x = 1, y = 0, z = 0})) + + local v_before3 = {x = 1, y = 0, z = -1} + local v_after3 = vector.rotate(v_before3, {x = 0, y = math.pi / 4, z = 0}) + assert.True(almost_equal(vector.normalize(vector.cross(v_after3, v_before3)), {x = 0, y = 1, z = 0})) + + local v_before4 = {x = 3, y = 0, z = 4} + local v_after4 = vector.rotate(v_before4, {x = 0, y = 2 * math.pi / 5, z = 0}) + assert.True(almost_equal(vector.normalize(vector.cross(v_after4, v_before4)), {x = 0, y = 1, z = 0})) + + local v_before5 = {x = 1, y = -1, z = 0} + local v_after5 = vector.rotate(v_before5, {x = 0, y = 0, z = math.pi / 4}) + assert.True(almost_equal(vector.normalize(vector.cross(v_after5, v_before5)), {x = 0, y = 0, z = 1})) + + local v_before6 = {x = 3, y = 4, z = 0} + local v_after6 = vector.rotate(v_before6, {x = 0, y = 0, z = 2 * math.pi / 5}) + assert.True(almost_equal(vector.normalize(vector.cross(v_after6, v_before6)), {x = 0, y = 0, z = 1})) + end) + end) + + it("dir_to_rotation()", function() + -- Comparing rotations (pitch, yaw, roll) is hard because of certain ambiguities, + -- e.g. (pi, 0, pi) looks exactly the same as (0, pi, 0) + -- So instead we convert the rotation back to vectors and compare these. + local function forward_at_rot(rot) + return vector.rotate(vector.new(0, 0, 1), rot) + end + local function up_at_rot(rot) + return vector.rotate(vector.new(0, 1, 0), rot) + end + local rot1 = vector.dir_to_rotation({x = 1, y = 0, z = 0}, {x = 0, y = 1, z = 0}) + assert.True(almost_equal({x = 1, y = 0, z = 0}, forward_at_rot(rot1))) + assert.True(almost_equal({x = 0, y = 1, z = 0}, up_at_rot(rot1))) + local rot2 = vector.dir_to_rotation({x = 1, y = 1, z = 0}, {x = 0, y = 0, z = 1}) + assert.True(almost_equal({x = 1/math.sqrt(2), y = 1/math.sqrt(2), z = 0}, forward_at_rot(rot2))) + assert.True(almost_equal({x = 0, y = 0, z = 1}, up_at_rot(rot2))) + for i = 1, 1000 do + local rand_vec = vector.new(math.random(), math.random(), math.random()) + if vector.length(rand_vec) ~= 0 then + local rot_1 = vector.dir_to_rotation(rand_vec) + local rot_2 = { + x = math.atan2(rand_vec.y, math.sqrt(rand_vec.z * rand_vec.z + rand_vec.x * rand_vec.x)), + y = -math.atan2(rand_vec.x, rand_vec.z), + z = 0 + } + assert.True(almost_equal(rot_1, rot_2)) + end + end + + end) end) diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index ca6541eb4..1fd784ce2 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -141,3 +141,96 @@ 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 + +local function sin(x) + if x % math.pi == 0 then + return 0 + else + return math.sin(x) + end +end + +local function cos(x) + if x % math.pi == math.pi / 2 then + return 0 + else + return math.cos(x) + end +end + +function vector.rotate_around_axis(v, axis, angle) + local cosangle = cos(angle) + local sinangle = sin(angle) + axis = vector.normalize(axis) + -- https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula + local dot_axis = vector.multiply(axis, vector.dot(axis, v)) + local cross = vector.cross(v, axis) + return vector.new( + cross.x * sinangle + (v.x - dot_axis.x) * cosangle + dot_axis.x, + cross.y * sinangle + (v.y - dot_axis.y) * cosangle + dot_axis.y, + cross.z * sinangle + (v.z - dot_axis.z) * cosangle + dot_axis.z + ) +end + +function vector.rotate(v, rot) + local sinpitch = sin(-rot.x) + local sinyaw = sin(-rot.y) + local sinroll = sin(-rot.z) + local cospitch = cos(rot.x) + local cosyaw = cos(rot.y) + local cosroll = math.cos(rot.z) + -- Rotation matrix that applies yaw, pitch and roll + local matrix = { + { + sinyaw * sinpitch * sinroll + cosyaw * cosroll, + sinyaw * sinpitch * cosroll - cosyaw * sinroll, + sinyaw * cospitch, + }, + { + cospitch * sinroll, + cospitch * cosroll, + -sinpitch, + }, + { + cosyaw * sinpitch * sinroll - sinyaw * cosroll, + cosyaw * sinpitch * cosroll + sinyaw * sinroll, + cosyaw * cospitch, + }, + } + -- Compute matrix multiplication: `matrix` * `v` + return vector.new( + matrix[1][1] * v.x + matrix[1][2] * v.y + matrix[1][3] * v.z, + matrix[2][1] * v.x + matrix[2][2] * v.y + matrix[2][3] * v.z, + matrix[3][1] * v.x + matrix[3][2] * v.y + matrix[3][3] * v.z + ) +end + +function vector.dir_to_rotation(forward, up) + forward = vector.normalize(forward) + local rot = {x = math.asin(forward.y), y = -math.atan2(forward.x, forward.z), z = 0} + if not up then + return rot + end + assert(vector.dot(forward, up) < 0.000001, + "Invalid vectors passed to vector.dir_to_rotation().") + up = vector.normalize(up) + -- Calculate vector pointing up with roll = 0, just based on forward vector. + local forwup = vector.rotate({x = 0, y = 1, z = 0}, rot) + -- 'forwup' and 'up' are now in a plane with 'forward' as normal. + -- The angle between them is the absolute of the roll value we're looking for. + rot.z = vector.angle(forwup, up) + + -- Since vector.angle never returns a negative value or a value greater + -- than math.pi, rot.z has to be inverted sometimes. + -- To determine wether this is the case, we rotate the up vector back around + -- the forward vector and check if it worked out. + local back = vector.rotate_around_axis(up, forward, -rot.z) + + -- We don't use vector.equals for this because of floating point imprecision. + if (back.x - forwup.x) * (back.x - forwup.x) + + (back.y - forwup.y) * (back.y - forwup.y) + + (back.z - forwup.z) * (back.z - forwup.z) > 0.0000001 then + rot.z = -rot.z + end + return rot +end diff --git a/builtin/fstk/dialog.lua b/builtin/fstk/dialog.lua index df887f413..ea57df1d2 100644 --- a/builtin/fstk/dialog.lua +++ b/builtin/fstk/dialog.lua @@ -67,3 +67,22 @@ function dialog_create(name,get_formspec,buttonhandler,eventhandler) ui.add(self) return self end + +function messagebox(name, message) + return dialog_create(name, + function() + return ([[ + formspec_version[3] + size[8,3] + textarea[0.375,0.375;7.25,1.2;;;%s] + button[3,1.825;2,0.8;ok;%s] + ]]):format(message, fgettext("OK")) + end, + function(this, fields) + if fields.ok then + this:delete() + return true + end + end, + nil) +end diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua index 884100543..6d26aabf0 100644 --- a/builtin/fstk/ui.lua +++ b/builtin/fstk/ui.lua @@ -85,7 +85,7 @@ function ui.update() "box[0.5,1.2;13,5;#000]", ("textarea[0.5,1.2;13,5;;%s;%s]"):format( error_title, error_message), - "button[5,6.6;4,1;btn_error_confirm;" .. fgettext("Ok") .. "]" + "button[5,6.6;4,1;btn_error_confirm;" .. fgettext("OK") .. "]" } else local active_toplevel_ui_elements = 0 diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index 7aedfc82e..fc061666c 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -41,7 +41,6 @@ core.builtin_auth_handler = { return { password = auth_entry.password, privileges = privileges, - -- Is set to nil if unknown last_login = auth_entry.last_login, } end, @@ -53,7 +52,7 @@ core.builtin_auth_handler = { name = name, password = password, privileges = core.string_to_privs(core.settings:get("default_privs")), - last_login = os.time(), + last_login = -1, -- Defer login time calculation until record_login (called by on_joinplayer) }) end, delete_auth = function(name) diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index fd1379162..aae811794 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -239,57 +239,76 @@ core.register_chatcommand("grantme", { end, }) +local function handle_revoke_command(caller, revokename, revokeprivstr) + local caller_privs = core.get_player_privs(caller) + if not (caller_privs.privs or caller_privs.basic_privs) then + return false, "Your privileges are insufficient." + end + + if not core.get_auth_handler().get_auth(revokename) then + return false, "Player " .. revokename .. " does not exist." + end + + local revokeprivs = core.string_to_privs(revokeprivstr) + local privs = core.get_player_privs(revokename) + local basic_privs = + core.string_to_privs(core.settings:get("basic_privs") or "interact,shout") + for priv, _ in pairs(revokeprivs) do + if not basic_privs[priv] and not caller_privs.privs then + return false, "Your privileges are insufficient." + end + end + + if revokeprivstr == "all" then + revokeprivs = privs + privs = {} + else + for priv, _ in pairs(revokeprivs) do + privs[priv] = nil + end + end + + for priv, _ in pairs(revokeprivs) do + -- call the on_revoke callbacks + core.run_priv_callbacks(revokename, priv, caller, "revoke") + end + + core.set_player_privs(revokename, privs) + core.log("action", caller..' revoked (' + ..core.privs_to_string(revokeprivs, ', ') + ..') privileges from '..revokename) + if revokename ~= caller then + core.chat_send_player(revokename, caller + .. " revoked privileges from you: " + .. core.privs_to_string(revokeprivs, ' ')) + end + return true, "Privileges of " .. revokename .. ": " + .. core.privs_to_string( + core.get_player_privs(revokename), ' ') +end + core.register_chatcommand("revoke", { params = " ( | all)", description = "Remove privileges from player", privs = {}, func = function(name, param) - if not core.check_player_privs(name, {privs=true}) and - not core.check_player_privs(name, {basic_privs=true}) then - return false, "Your privileges are insufficient." - end - local revoke_name, revoke_priv_str = string.match(param, "([^ ]+) (.+)") - if not revoke_name or not revoke_priv_str then + local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)") + if not revokename or not revokeprivstr then return false, "Invalid parameters (see /help revoke)" - 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.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 - return false, "Your privileges are insufficient." - end - end - if revoke_priv_str == "all" then - revoke_privs = privs - privs = {} - else - for priv, _ in pairs(revoke_privs) do - privs[priv] = nil - end end + return handle_revoke_command(name, revokename, revokeprivstr) + end, +}) - for priv, _ in pairs(revoke_privs) do - -- call the on_revoke callbacks - core.run_priv_callbacks(revoke_name, priv, name, "revoke") +core.register_chatcommand("revokeme", { + params = " | all", + description = "Revoke privileges from yourself", + privs = {}, + func = function(name, param) + if param == "" then + return false, "Invalid parameters (see /help revokeme)" end - - core.set_player_privs(revoke_name, privs) - core.log("action", name..' revoked (' - ..core.privs_to_string(revoke_privs, ', ') - ..') privileges from '..revoke_name) - if revoke_name ~= name then - core.chat_send_player(revoke_name, name - .. " revoked privileges from you: " - .. core.privs_to_string(revoke_privs, ' ')) - end - return true, "Privileges of " .. revoke_name .. ": " - .. core.privs_to_string( - core.get_player_privs(revoke_name), ' ') + return handle_revoke_command(name, name, param) end, }) @@ -424,6 +443,9 @@ core.register_chatcommand("teleport", { end local teleportee = core.get_player_by_name(name) if teleportee then + if teleportee:get_attach() then + return false, "Can't teleport, you're attached to an object!" + end teleportee:set_pos(p) return true, "Teleporting to "..core.pos_to_string(p) end @@ -441,6 +463,9 @@ core.register_chatcommand("teleport", { end if teleportee and p then + if teleportee:get_attach() then + return false, "Can't teleport, you're attached to an object!" + end p = find_free_position_near(p) teleportee:set_pos(p) return true, "Teleporting to " .. target_name @@ -461,6 +486,9 @@ core.register_chatcommand("teleport", { teleportee = core.get_player_by_name(teleportee_name) end if teleportee and p.x and p.y and p.z then + if teleportee:get_attach() then + return false, "Can't teleport, player is attached to an object!" + end teleportee:set_pos(p) return true, "Teleporting " .. teleportee_name .. " to " .. core.pos_to_string(p) @@ -479,6 +507,9 @@ core.register_chatcommand("teleport", { end end if teleportee and p then + if teleportee:get_attach() then + return false, "Can't teleport, player is attached to an object!" + end p = find_free_position_near(p) teleportee:set_pos(p) return true, "Teleporting " .. teleportee_name @@ -717,8 +748,9 @@ core.register_chatcommand("spawnentity", { end end p.y = p.y + 1 - core.add_entity(p, entityname) - return true, ("%q spawned."):format(entityname) + local obj = core.add_entity(p, entityname) + local msg = obj and "%q spawned." or "%q failed to spawn." + return true, msg:format(entityname) end, }) @@ -757,7 +789,7 @@ core.register_chatcommand("rollback_check", { params = "[] [] []", 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", + .. " seconds = 86400 = 24h, limit = 5. Set to inf for no time limit", privs = {rollback=true}, func = function(name, param) if not core.settings:get_bool("enable_rollback_recording") then @@ -808,7 +840,7 @@ 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. Set to inf for no time limit", privs = {rollback=true}, func = function(name, param) if not core.settings:get_bool("enable_rollback_recording") then @@ -1036,7 +1068,7 @@ core.register_chatcommand("last-login", { param = name end local pauth = core.get_auth_handler().get_auth(param) - if pauth and pauth.last_login then + if pauth and pauth.last_login and pauth.last_login ~= -1 then -- Time in UTC, ISO 8601 format return true, "Last login time was " .. os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login) diff --git a/builtin/game/constants.lua b/builtin/game/constants.lua index 0ee2a7237..54eeea50f 100644 --- a/builtin/game/constants.lua +++ b/builtin/game/constants.lua @@ -24,7 +24,7 @@ core.MAP_BLOCKSIZE = 16 -- Default maximal HP of a player core.PLAYER_MAX_HP_DEFAULT = 20 -- Default maximal breath of a player -core.PLAYER_MAX_BREATH_DEFAULT = 11 +core.PLAYER_MAX_BREATH_DEFAULT = 10 -- light.h -- Maximum value for node 'light_source' parameter diff --git a/builtin/game/deprecated.lua b/builtin/game/deprecated.lua index 73e105eb8..20f0482eb 100644 --- a/builtin/game/deprecated.lua +++ b/builtin/game/deprecated.lua @@ -70,3 +70,19 @@ 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") + +-- +-- core.register_on_auth_fail +-- + +function core.register_on_auth_fail(func) + core.log("deprecated", "core.register_on_auth_fail " .. + "is obsolete and should be replaced by " .. + "core.register_on_authplayer instead.") + + core.register_on_authplayer(function (player_name, ip, is_success) + if not is_success then + func(player_name, ip) + end + end) +end diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index ea02e3694..714506a5f 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -30,6 +30,8 @@ local facedir_to_euler = { {y = math.pi/2, x = math.pi, z = 0} } +local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81 + -- -- Falling stuff -- @@ -41,12 +43,13 @@ core.register_entity(":__builtin:falling_node", { textures = {}, physical = true, is_visible = false, - collide_with_objects = false, + collide_with_objects = true, collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, }, node = {}, meta = {}, + floats = false, set_node = function(self, node, meta) self.node = node @@ -71,6 +74,11 @@ core.register_entity(":__builtin:falling_node", { return end self.meta = meta + + -- Cache whether we're supposed to float on water + self.floats = core.get_item_group(node.name, "float") ~= 0 + + -- Set entity visuals if def.drawtype == "torchlike" or def.drawtype == "signlike" then local textures if def.tiles and def.tiles[1] then @@ -101,6 +109,7 @@ core.register_entity(":__builtin:falling_node", { if core.is_colored_paramtype(def.paramtype2) then itemstring = core.itemstring_with_palette(itemstring, node.param2) end + -- FIXME: solution needed for paramtype2 == "leveled" local vsize if def.visual_scale then local s = def.visual_scale * SCALE @@ -113,6 +122,25 @@ core.register_entity(":__builtin:falling_node", { glow = def.light_source, }) end + + -- Set collision box (certain nodeboxes only for now) + local nb_types = {fixed=true, leveled=true, connected=true} + if def.drawtype == "nodebox" and def.node_box and + nb_types[def.node_box.type] then + local box = table.copy(def.node_box.fixed) + if type(box[1]) == "table" then + box = #box == 1 and box[1] or nil -- We can only use a single box + end + if box then + if def.paramtype2 == "leveled" and (self.node.level or 0) > 0 then + box[5] = -0.5 + self.node.level / 64 + end + self.object:set_properties({ + collisionbox = box + }) + end + end + -- Rotate entity if def.drawtype == "torchlike" then self.object:set_yaw(math.pi*0.25) @@ -172,6 +200,7 @@ core.register_entity(":__builtin:falling_node", { on_activate = function(self, staticdata) self.object:set_armor_groups({immortal = 1}) + self.object:set_acceleration({x = 0, y = -gravity, z = 0}) local ds = core.deserialize(staticdata) if ds and ds.node then @@ -183,85 +212,159 @@ core.register_entity(":__builtin:falling_node", { end end, - on_step = function(self, dtime) - -- Set gravity - local acceleration = self.object:get_acceleration() - if not vector.equals(acceleration, {x = 0, y = -10, z = 0}) then - self.object:set_acceleration({x = 0, y = -10, z = 0}) + try_place = function(self, bcp, bcn) + local bcd = core.registered_nodes[bcn.name] + -- Add levels if dropped on same leveled node + if bcd and bcd.paramtype2 == "leveled" and + bcn.name == self.node.name then + local addlevel = self.node.level + if (addlevel or 0) <= 0 then + addlevel = bcd.leveled + end + if core.add_node_level(bcp, addlevel) < addlevel then + return true + elseif bcd.buildable_to then + -- Node level has already reached max, don't place anything + return true + end end - -- Turn to actual node when colliding with ground, or continue to move - local pos = self.object:get_pos() - -- Position of bottom center point - local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z} - -- 'bcn' is nil for unloaded nodes - local bcn = core.get_node_or_nil(bcp) - -- Delete on contact with ignore at world edges - if bcn and bcn.name == "ignore" then - self.object:remove() - return + + -- Decide if we're replacing the node or placing on top + local np = vector.new(bcp) + if bcd and bcd.buildable_to and + (not self.floats or bcd.liquidtype == "none") then + core.remove_node(bcp) + else + np.y = np.y + 1 end - local bcd = bcn and core.registered_nodes[bcn.name] - if bcn and - (not bcd or bcd.walkable or - (core.get_item_group(self.node.name, "float") ~= 0 and - bcd.liquidtype ~= "none")) then - if bcd and bcd.leveled and - bcn.name == self.node.name then - local addlevel = self.node.level - if not addlevel or addlevel <= 0 then - addlevel = bcd.leveled + + -- Check what's here + local n2 = core.get_node(np) + local nd = core.registered_nodes[n2.name] + -- If it's not air or liquid, remove node and replace it with + -- it's drops + if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then + if nd and nd.buildable_to == false then + nd.on_dig(np, n2, nil) + -- If it's still there, it might be protected + if core.get_node(np).name == n2.name then + return false end - if core.add_node_level(bcp, addlevel) == 0 then + else + core.remove_node(np) + end + end + + -- Create node + local def = core.registered_nodes[self.node.name] + if def then + core.add_node(np, self.node) + if self.meta then + core.get_meta(np):from_table(self.meta) + end + if def.sounds and def.sounds.place then + core.sound_play(def.sounds.place, {pos = np}, true) + end + end + core.check_for_falling(np) + return true + end, + + on_step = function(self, dtime, moveresult) + -- Fallback code since collision detection can't tell us + -- about liquids (which do not collide) + if self.floats then + local pos = self.object:get_pos() + + local bcp = vector.round({x = pos.x, y = pos.y - 0.7, z = pos.z}) + local bcn = core.get_node(bcp) + + local bcd = core.registered_nodes[bcn.name] + if bcd and bcd.liquidtype ~= "none" then + if self:try_place(bcp, bcn) then self.object:remove() return end - elseif bcd and bcd.buildable_to and - (core.get_item_group(self.node.name, "float") == 0 or - bcd.liquidtype == "none") then - core.remove_node(bcp) - return end - local np = {x = bcp.x, y = bcp.y + 1, z = bcp.z} - -- Check what's here - local n2 = core.get_node(np) - local nd = core.registered_nodes[n2.name] - -- If it's not air or liquid, remove node and replace it with - -- it's drops - if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then - core.remove_node(np) - if nd and nd.buildable_to == false then - -- Add dropped items - local drops = core.get_node_drops(n2, "") - for _, dropped_item in pairs(drops) do - core.add_item(np, dropped_item) + end + + assert(moveresult) + if not moveresult.collides then + return -- Nothing to do :) + end + + local bcp, bcn + local player_collision + if moveresult.touching_ground then + for _, info in ipairs(moveresult.collisions) do + if info.type == "object" then + if info.axis == "y" and info.object:is_player() then + player_collision = info end - end - -- Run script hook - for _, callback in pairs(core.registered_on_dignodes) do - callback(np, n2) + elseif info.axis == "y" then + bcp = info.node_pos + bcn = core.get_node(bcp) + break end end - -- Create node and remove entity - local def = core.registered_nodes[self.node.name] - if def then - core.add_node(np, self.node) - if self.meta then - local meta = core.get_meta(np) - meta:from_table(self.meta) - end - if def.sounds and def.sounds.place then - core.sound_play(def.sounds.place, {pos = np}, true) - end + end + + if not bcp then + -- We're colliding with something, but not the ground. Irrelevant to us. + if player_collision then + -- Continue falling through players by moving a little into + -- their collision box + -- TODO: this hack could be avoided in the future if objects + -- could choose who to collide with + local vel = self.object:get_velocity() + self.object:set_velocity({ + x = vel.x, + y = player_collision.old_velocity.y, + z = vel.z + }) + self.object:set_pos(vector.add(self.object:get_pos(), + {x = 0, y = -0.5, z = 0})) end + return + elseif bcn.name == "ignore" then + -- Delete on contact with ignore at world edges self.object:remove() - core.check_for_falling(np) return end - local vel = self.object:get_velocity() - if vector.equals(vel, {x = 0, y = 0, z = 0}) then - local npos = self.object:get_pos() - self.object:set_pos(vector.round(npos)) + + local failure = false + + local pos = self.object:get_pos() + local distance = vector.apply(vector.subtract(pos, bcp), math.abs) + if distance.x >= 1 or distance.z >= 1 then + -- We're colliding with some part of a node that's sticking out + -- Since we don't want to visually teleport, drop as item + failure = true + elseif distance.y >= 2 then + -- Doors consist of a hidden top node and a bottom node that is + -- the actual door. Despite the top node being solid, the moveresult + -- almost always indicates collision with the bottom node. + -- Compensate for this by checking the top node + bcp.y = bcp.y + 1 + bcn = core.get_node(bcp) + local def = core.registered_nodes[bcn.name] + if not (def and def.walkable) then + failure = true -- This is unexpected, fail + end end + + -- Try to actually place ourselves + if not failure then + failure = not self:try_place(bcp, bcn) + end + + if failure then + local drops = core.get_node_drops(self.node, "") + for _, item in pairs(drops) do + core.add_item(pos, item) + end + end + self.object:remove() end }) @@ -270,6 +373,7 @@ local function convert_to_falling_node(pos, node) if not obj then return false end + -- remember node level, the entities' set_node() uses this node.level = core.get_node_level(pos) local meta = core.get_meta(pos) local metatable = meta and meta:to_table() or {} @@ -355,18 +459,23 @@ function core.check_single_for_falling(p) -- Only spawn falling node if node below is loaded local n_bottom = core.get_node_or_nil(p_bottom) local d_bottom = n_bottom and core.registered_nodes[n_bottom.name] - if d_bottom and - - (core.get_item_group(n.name, "float") == 0 or - d_bottom.liquidtype == "none") and - - (n.name ~= n_bottom.name or (d_bottom.leveled and - core.get_node_level(p_bottom) < - core.get_node_max_level(p_bottom))) and - - (not d_bottom.walkable or d_bottom.buildable_to) then - convert_to_falling_node(p, n) - return true + if d_bottom then + local same = n.name == n_bottom.name + -- Let leveled nodes fall if it can merge with the bottom node + if same and d_bottom.paramtype2 == "leveled" and + core.get_node_level(p_bottom) < + core.get_node_max_level(p_bottom) then + convert_to_falling_node(p, n) + return true + end + -- Otherwise only if the bottom node is considered "fall through" + if not same and + (not d_bottom.walkable or d_bottom.buildable_to) and + (core.get_item_group(n.name, "float") == 0 or + d_bottom.liquidtype == "none") then + convert_to_falling_node(p, n) + return true + end end end diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 623f8183b..a15475333 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -16,6 +16,7 @@ core.features = { formspec_version_element = true, area_store_persistent_ids = true, pathfinder_works = true, + object_step_has_moveresult = true, } function core.has_feature(arg) diff --git a/builtin/game/item.lua b/builtin/game/item.lua index 513c3a5e1..f680ce0d4 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -582,7 +582,7 @@ function core.node_dig(pos, node, digger) wielded = wdef.after_use(wielded, digger, node, dp) or wielded else -- Wear out tool - if not core.settings:get_bool("creative_mode") then + if not core.is_creative_enabled(diggername) 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, { @@ -675,6 +675,8 @@ end -- Item definition defaults -- +local default_stack_max = tonumber(minetest.settings:get("default_stack_max")) or 99 + core.nodedef_default = { -- Item properties type="node", @@ -684,7 +686,7 @@ core.nodedef_default = { inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, - stack_max = 99, + stack_max = default_stack_max, usable = false, liquids_pointable = false, tool_capabilities = nil, @@ -748,7 +750,7 @@ core.craftitemdef_default = { inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, - stack_max = 99, + stack_max = default_stack_max, liquids_pointable = false, tool_capabilities = nil, @@ -786,7 +788,7 @@ core.noneitemdef_default = { -- This is used for the hand and unknown items inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, - stack_max = 99, + stack_max = default_stack_max, liquids_pointable = false, tool_capabilities = nil, diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index 1d66799f6..20dd18044 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -27,14 +27,11 @@ core.register_entity(":__builtin:item", { visual = "wielditem", visual_size = {x = 0.4, y = 0.4}, textures = {""}, - spritediv = {x = 1, y = 1}, - initial_sprite_basepos = {x = 0, y = 0}, is_visible = false, }, itemstring = "", moving_state = true, - slippery_state = false, physical_state = true, -- Item expiry age = 0, @@ -57,18 +54,15 @@ core.register_entity(":__builtin:item", { local max_count = stack:get_stack_max() local count = math.min(stack:get_count(), max_count) local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3) - local coll_height = size * 0.75 local def = core.registered_nodes[itemname] - local glow = def and def.light_source + local glow = def and math.floor(def.light_source / 2 + 0.5) self.object:set_properties({ is_visible = true, visual = "wielditem", textures = {itemname}, visual_size = {x = size, y = size}, - collisionbox = {-size, -coll_height, -size, - size, coll_height, size}, - selectionbox = {-size, -size, -size, size, size, size}, + collisionbox = {-size, -size, -size, size, size, size}, automatic_rotate = math.pi * 0.5 * 0.2 / size, wield_item = self.itemstring, glow = glow, @@ -157,7 +151,7 @@ core.register_entity(":__builtin:item", { end end, - on_step = function(self, dtime) + on_step = function(self, dtime, moveresult) self.age = self.age + dtime if time_to_live > 0 and self.age > time_to_live then self.itemstring = "" @@ -178,6 +172,38 @@ core.register_entity(":__builtin:item", { return end + if self.force_out then + -- This code runs after the entity got a push from the is_stuck code. + -- It makes sure the entity is entirely outside the solid node + local c = self.object:get_properties().collisionbox + local s = self.force_out_start + local f = self.force_out + local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or + (f.y > 0 and pos.y + c[2] > s.y + 0.5) or + (f.z > 0 and pos.z + c[3] > s.z + 0.5) or + (f.x < 0 and pos.x + c[4] < s.x - 0.5) or + (f.z < 0 and pos.z + c[6] < s.z - 0.5) + if ok then + -- Item was successfully forced out + self.force_out = nil + self:enable_physics() + return + end + end + + if not self.physical_state then + return -- Don't do anything + end + + assert(moveresult, + "Collision info missing, this is caused by an out-of-date/buggy mod or game") + + if not moveresult.collides then + -- future TODO: items should probably decelerate in air + return + end + + -- Push item out when stuck inside solid node local is_stuck = false local snode = core.get_node_or_nil(pos) if snode then @@ -187,7 +213,6 @@ core.register_entity(":__builtin:item", { and (sdef.node_box == nil or sdef.node_box.type == "regular") end - -- Push item out when stuck inside solid node if is_stuck then local shootdir local order = { @@ -223,69 +248,49 @@ core.register_entity(":__builtin:item", { self.force_out_start = vector.round(pos) return end - elseif self.force_out then - -- This code runs after the entity got a push from the above code. - -- It makes sure the entity is entirely outside the solid node - local c = self.object:get_properties().collisionbox - local s = self.force_out_start - local f = self.force_out - local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or - (f.y > 0 and pos.y + c[2] > s.y + 0.5) or - (f.z > 0 and pos.z + c[3] > s.z + 0.5) or - (f.x < 0 and pos.x + c[4] < s.x - 0.5) or - (f.z < 0 and pos.z + c[6] < s.z - 0.5) - if ok then - -- Item was successfully forced out - self.force_out = nil - self:enable_physics() - end end - if not self.physical_state then - return -- Don't do anything + node = nil -- ground node we're colliding with + if moveresult.touching_ground then + for _, info in ipairs(moveresult.collisions) do + if info.axis == "y" then + node = core.get_node(info.node_pos) + break + end + end end -- Slide on slippery nodes - local vel = self.object:get_velocity() local def = node and core.registered_nodes[node.name] - local is_moving = (def and not def.walkable) or - vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0 - local is_slippery = false + local keep_movement = false - if def and def.walkable then + if def then local slippery = core.get_item_group(node.name, "slippery") - is_slippery = slippery ~= 0 - if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then + local vel = self.object:get_velocity() + if slippery ~= 0 and (math.abs(vel.x) > 0.1 or math.abs(vel.z) > 0.1) then -- Horizontal deceleration - local slip_factor = 4.0 / (slippery + 4) - self.object:set_acceleration({ - x = -vel.x * slip_factor, + local factor = math.min(4 / (slippery + 4) * dtime, 1) + self.object:set_velocity({ + x = vel.x * (1 - factor), y = 0, - z = -vel.z * slip_factor + z = vel.z * (1 - factor) }) - elseif vel.y == 0 then - is_moving = false + keep_movement = true end end - if self.moving_state == is_moving and - self.slippery_state == is_slippery then + if not keep_movement then + self.object:set_velocity({x=0, y=0, z=0}) + end + + if self.moving_state == keep_movement then -- Do not update anything until the moving state changes return end + self.moving_state = keep_movement - self.moving_state = is_moving - self.slippery_state = is_slippery - - if is_moving then - self.object:set_acceleration({x = 0, y = -gravity, z = 0}) - else - self.object:set_acceleration({x = 0, y = 0, z = 0}) - self.object:set_velocity({x = 0, y = 0, z = 0}) - end - - --Only collect items if not moving - if is_moving then + -- Only collect items if not moving + if self.moving_state then return end -- Collect the items around to merge with diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index 0ed11ada0..341e613c2 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -164,6 +164,12 @@ function core.record_protection_violation(pos, name) end end +-- To be overridden by Creative mods + +local creative_mode_cache = core.settings:get_bool("creative_mode") +function core.is_creative_enabled(name) + return creative_mode_cache +end -- Checks if specified volume intersects a protected volume diff --git a/builtin/game/register.lua b/builtin/game/register.lua index eb6c2897c..1034d4f2b 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -607,9 +607,9 @@ core.registered_on_item_eats, core.register_on_item_eat = make_registration() core.registered_on_punchplayers, core.register_on_punchplayer = make_registration() core.registered_on_priv_grant, core.register_on_priv_grant = make_registration() core.registered_on_priv_revoke, core.register_on_priv_revoke = make_registration() +core.registered_on_authplayers, core.register_on_authplayer = make_registration() core.registered_can_bypass_userlimit, core.register_can_bypass_userlimit = make_registration() core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration() -core.registered_on_auth_fail, core.register_on_auth_fail = make_registration() core.registered_on_player_inventory_actions, core.register_on_player_inventory_action = make_registration() core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration() diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 46c947b60..d192029c5 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -3,22 +3,26 @@ local enable_damage = core.settings:get_bool("enable_damage") local health_bar_definition = { hud_elem_type = "statbar", - position = { x=0.5, y=1 }, + position = {x = 0.5, y = 1}, text = "heart.png", + text2 = "heart_gone.png", number = core.PLAYER_MAX_HP_DEFAULT, + item = core.PLAYER_MAX_HP_DEFAULT, direction = 0, - size = { x=24, y=24 }, - offset = { x=(-10*24)-25, y=-(48+24+16)}, + size = {x = 24, y = 24}, + offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)}, } local breath_bar_definition = { hud_elem_type = "statbar", - position = { x=0.5, y=1 }, + position = {x = 0.5, y = 1}, text = "bubble.png", + text2 = "bubble_gone.png", number = core.PLAYER_MAX_BREATH_DEFAULT, + item = core.PLAYER_MAX_BREATH_DEFAULT * 2, direction = 0, - size = { x=24, y=24 }, - offset = {x=25,y=-(48+24+16)}, + size = {x = 24, y = 24}, + offset = {x = 25, y= -(48 + 24 + 16)}, } local hud_ids = {} @@ -26,7 +30,7 @@ local hud_ids = {} local function scaleToDefault(player, field) -- Scale "hp" or "breath" to the default dimensions local current = player["get_" .. field](player) - local nominal = core["PLAYER_MAX_".. field:upper() .. "_DEFAULT"] + local nominal = core["PLAYER_MAX_" .. field:upper() .. "_DEFAULT"] local max_display = math.max(nominal, math.max(player:get_properties()[field .. "_max"], current)) return current / max_display * nominal @@ -49,6 +53,7 @@ local function update_builtin_statbars(player) local hud = hud_ids[name] local immortal = player:get_armor_groups().immortal == 1 + if flags.healthbar and enable_damage and not immortal then local number = scaleToDefault(player, "hp") if hud.id_healthbar == nil then @@ -63,19 +68,28 @@ local function update_builtin_statbars(player) hud.id_healthbar = nil end + local show_breathbar = flags.breathbar and enable_damage and not immortal + + local breath = player:get_breath() local breath_max = player:get_properties().breath_max - if flags.breathbar and enable_damage and not immortal and - player:get_breath() < breath_max then + if show_breathbar and breath <= breath_max then local number = 2 * scaleToDefault(player, "breath") - if hud.id_breathbar == nil then + if not hud.id_breathbar and breath < breath_max then local hud_def = table.copy(breath_bar_definition) hud_def.number = number hud.id_breathbar = player:hud_add(hud_def) - else + elseif hud.id_breathbar then player:hud_change(hud.id_breathbar, "number", number) end - elseif hud.id_breathbar then - player:hud_remove(hud.id_breathbar) + end + + if hud.id_breathbar and (not show_breathbar or breath == breath_max) then + minetest.after(1, function(player_name, breath_bar) + local player = minetest.get_player_by_name(player_name) + if player then + player:hud_remove(breath_bar) + end + end, name, hud.id_breathbar) hud.id_breathbar = nil end end diff --git a/builtin/init.lua b/builtin/init.lua index f76174be7..75bb3db85 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -36,6 +36,7 @@ dofile(commonpath .. "misc_helpers.lua") if INIT == "game" then dofile(gamepath .. "init.lua") + assert(not core.get_http_api) elseif INIT == "mainmenu" then local mm_script = core.settings:get("main_menu_script") if mm_script and mm_script ~= "" then diff --git a/builtin/common/async_event.lua b/builtin/mainmenu/async_event.lua similarity index 74% rename from builtin/common/async_event.lua rename to builtin/mainmenu/async_event.lua index 988af79b9..04bfb78d6 100644 --- a/builtin/common/async_event.lua +++ b/builtin/mainmenu/async_event.lua @@ -8,15 +8,7 @@ local function handle_job(jobid, serialized_retval) core.async_jobs[jobid] = nil end -if core.register_globalstep then - core.register_globalstep(function(dtime) - for i, job in ipairs(core.get_finished_jobs()) do - handle_job(job.jobid, job.retval) - end - end) -else - core.async_event_handler = handle_job -end +core.async_event_handler = handle_job function core.handle_async(func, parameter, callback) -- Serialize function diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua index 97218df9c..2cf70c9c9 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/dlg_config_world.lua @@ -23,7 +23,49 @@ local function modname_valid(name) return not name:find("[^a-z0-9_]") end +local function init_data(data) + data.list = filterlist.create( + pkgmgr.preparemodlist, + pkgmgr.comparemod, + function(element, uid) + if element.name == uid then + return true + end + end, + function(element, criteria) + if criteria.hide_game and + element.is_game_content then + return false + end + + if criteria.hide_modpackcontents and + element.modpack ~= nil then + return false + end + return true + end, + { + worldpath = data.worldspec.path, + gameid = data.worldspec.gameid + }) + + if data.selected_mod > data.list:size() then + data.selected_mod = 0 + end + + data.list:set_filtercriteria({ + hide_game = data.hide_gamemods, + hide_modpackcontents = data.hide_modpackcontents + }) + data.list:add_sort_mechanism("alphabetic", sort_mod_list) + data.list:set_sortmode("alphabetic") +end + local function get_formspec(data) + if not data.list then + init_data(data) + end + local mod = data.list:get_list()[data.selected_mod] or {name = ""} local retval = @@ -85,11 +127,14 @@ local function get_formspec(data) end end end + retval = retval .. "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") .. "]" + fgettext("Cancel") .. "]" .. + "button[9,7;2.5,0.5;btn_config_world_cdb;" .. + fgettext("Find More Mods") .. "]" if mod.name ~= "" and not mod.is_game_content then if mod.is_modpack then @@ -198,6 +243,16 @@ local function handle_buttons(this, fields) return true end + if fields.btn_config_world_cdb then + this.data.list = nil + + local dlg = create_store_dlg("mod") + dlg:set_parent(this) + this:hide() + dlg:show() + return true + end + if fields.btn_enable_all_mods then local list = this.data.list:get_raw_list() @@ -247,43 +302,5 @@ function create_configure_world_dlg(worldidx) return end - dlg.data.list = filterlist.create( - pkgmgr.preparemodlist, - pkgmgr.comparemod, - function(element, uid) - if element.name == uid then - return true - end - end, - function(element, criteria) - if criteria.hide_game and - element.is_game_content then - return false - end - - if criteria.hide_modpackcontents and - element.modpack ~= nil then - return false - end - return true - end, - { - worldpath = dlg.data.worldspec.path, - gameid = dlg.data.worldspec.gameid - } - ) - - - if dlg.data.selected_mod > dlg.data.list:size() then - dlg.data.selected_mod = 0 - end - - dlg.data.list:set_filtercriteria({ - hide_game = dlg.data.hide_gamemods, - hide_modpackcontents = dlg.data.hide_modpackcontents - }) - dlg.data.list:add_sort_mechanism("alphabetic", sort_mod_list) - dlg.data.list:set_sortmode("alphabetic") - return dlg end diff --git a/builtin/mainmenu/dlg_contentstore.lua b/builtin/mainmenu/dlg_contentstore.lua index 3bc5f60bb..01c42be0b 100644 --- a/builtin/mainmenu/dlg_contentstore.lua +++ b/builtin/mainmenu/dlg_contentstore.lua @@ -1,5 +1,5 @@ --Minetest ---Copyright (C) 2018 rubenwardy +--Copyright (C) 2018-20 rubenwardy -- --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 @@ -15,8 +15,17 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +if not minetest.get_http_api then + function create_store_dlg() + return messagebox("store", + fgettext("ContentDB is not available when Minetest was compiled without cURL")) + end + return +end + local store = { packages = {}, packages_full = {} } -local package_dialog = {} + +local http = minetest.get_http_api() -- Screenshot local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb" @@ -44,19 +53,15 @@ local filter_types_type = { } - - local function download_package(param) if core.download_file(param.package.url, param.filename) then return { - package = param.package, filename = param.filename, successful = true, } else core.log("error", "downloading " .. dump(param.package.url) .. " failed") return { - package = param.package, successful = false, } end @@ -70,9 +75,9 @@ local function start_install(calling_dialog, package) local function callback(result) if result.successful then - local path, msg = pkgmgr.install(result.package.type, - result.filename, result.package.name, - result.package.path) + local path, msg = pkgmgr.install(package.type, + result.filename, package.name, + package.path) if not path then gamedata.errormessage = msg else @@ -80,33 +85,33 @@ local function start_install(calling_dialog, package) local conf_path local name_is_title = false - if result.package.type == "mod" then + if package.type == "mod" then local actual_type = pkgmgr.get_folder_type(path) if actual_type.type == "modpack" then conf_path = path .. DIR_DELIM .. "modpack.conf" else conf_path = path .. DIR_DELIM .. "mod.conf" end - elseif result.package.type == "game" then + elseif package.type == "game" then conf_path = path .. DIR_DELIM .. "game.conf" name_is_title = true - elseif result.package.type == "txp" then + elseif package.type == "txp" then conf_path = path .. DIR_DELIM .. "texture_pack.conf" end if conf_path then local conf = Settings(conf_path) if name_is_title then - conf:set("name", result.package.title) + conf:set("name", package.title) else - conf:set("title", result.package.title) - conf:set("name", result.package.name) + conf:set("title", package.title) + conf:set("name", package.name) end if not conf:get("description") then - conf:set("description", result.package.short_description) + conf:set("description", package.short_description) end - conf:set("author", result.package.author) - conf:set("release", result.package.release) + conf:set("author", package.author) + conf:set("release", package.release) conf:write() end end @@ -115,37 +120,22 @@ local function start_install(calling_dialog, package) gamedata.errormessage = fgettext("Failed to download $1", package.name) end - if gamedata.errormessage == nil then - core.button_handler({btn_hidden_close_download=result}) - else - core.button_handler({btn_hidden_close_download={successful=false}}) - end + package.downloading = false + ui.update() end + package.downloading = true + if not core.handle_async(download_package, params, callback) then core.log("error", "ERROR: async event failed") gamedata.errormessage = fgettext("Failed to download $1", package.name) + return end +end - local new_dlg = dialog_create("store_downloading", - function(data) - return "size[7,2]label[0.25,0.75;" .. - fgettext("Downloading and installing $1, please wait...", data.title) .. "]" - end, - function(this,fields) - if fields["btn_hidden_close_download"] ~= nil then - this:delete() - return true - end - - return false - end, - nil) - - new_dlg:set_parent(calling_dialog) - new_dlg.data.title = package.title - calling_dialog:hide() - new_dlg:show() +local function get_file_extension(path) + local parts = path:split(".") + return parts[#parts] end local function get_screenshot(package) @@ -156,8 +146,9 @@ local function get_screenshot(package) end -- Get tmp screenshot path + local ext = get_file_extension(package.thumbnail) local filepath = screenshot_dir .. DIR_DELIM .. - package.type .. "-" .. package.author .. "-" .. package.name .. ".png" + ("%s-%s-%s.%s"):format(package.type, package.author, package.name, ext) -- Return if already downloaded local file = io.open(filepath, "r") @@ -195,84 +186,12 @@ local function get_screenshot(package) return defaulttexturedir .. "loading_screenshot.png" end - - -function package_dialog.get_formspec() - local package = package_dialog.package - - store.update_paths() - - local formspec = { - "size[9,4;true]", - "image[0,1;4.5,3;", core.formspec_escape(get_screenshot(package)), ']', - "label[3.8,1;", - minetest.colorize(mt_color_green, core.formspec_escape(package.title)), "\n", - minetest.colorize('#BFBFBF', "by " .. core.formspec_escape(package.author)), "]", - "textarea[4,2;5.3,2;;;", core.formspec_escape(package.short_description), "]", - "button[0,0;2,1;back;", fgettext("Back"), "]", - } - - if not package.path then - formspec[#formspec + 1] = "button[7,0;2,1;install;" - formspec[#formspec + 1] = fgettext("Install") - formspec[#formspec + 1] = "]" - elseif package.installed_release < package.release then - -- The install_ action also handles updating - formspec[#formspec + 1] = "button[7,0;2,1;install;" - formspec[#formspec + 1] = fgettext("Update") - formspec[#formspec + 1] = "]" - formspec[#formspec + 1] = "button[5,0;2,1;uninstall;" - formspec[#formspec + 1] = fgettext("Uninstall") - formspec[#formspec + 1] = "]" - else - formspec[#formspec + 1] = "button[7,0;2,1;uninstall;" - formspec[#formspec + 1] = fgettext("Uninstall") - formspec[#formspec + 1] = "]" - end - - return table.concat(formspec, "") -end - -function package_dialog.handle_submit(this, fields) - if fields.back then - this:delete() - return true - end - - if fields.install then - start_install(this, package_dialog.package) - return true - end - - if fields.uninstall then - local dlg_delmod = create_delete_content_dlg(package_dialog.package) - dlg_delmod:set_parent(this) - this:hide() - dlg_delmod:show() - return true - end - - return false -end - -function package_dialog.create(package) - package_dialog.package = package - return dialog_create("package_view", - package_dialog.get_formspec, - package_dialog.handle_submit, - nil) -end - function store.load() - local tmpdir = os.tempfolder() - local target = tmpdir .. DIR_DELIM .. "packages.json" - - assert(core.create_dir(tmpdir)) - - local base_url = core.settings:get("contentdb_url") + local version = core.get_version() + local base_url = core.settings:get("contentdb_url") local url = base_url .. "/api/packages/?type=mod&type=game&type=txp&protocol_version=" .. - core.get_max_supp_proto() + core.get_max_supp_proto() .. "&engine_version=" .. version.string for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do item = item:trim() @@ -281,31 +200,29 @@ function store.load() end end - core.download_file(url, target) + local timeout = tonumber(minetest.settings:get("curl_file_download_timeout")) + local response = http.fetch_sync({ url = url, timeout = timeout }) + if not response.succeeded then + return + end - local file = io.open(target, "r") - if file then - store.packages_full = core.parse_json(file:read("*all")) or {} - file:close() + store.packages_full = core.parse_json(response.data) or {} - for _, package in pairs(store.packages_full) do - package.url = base_url .. "/packages/" .. + for _, package in pairs(store.packages_full) do + package.url = base_url .. "/packages/" .. package.author .. "/" .. package.name .. "/releases/" .. package.release .. "/download/" - local name_len = #package.name - if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then - package.id = package.author:lower() .. "/" .. package.name:sub(1, name_len - 5) - else - package.id = package.author:lower() .. "/" .. package.name - end + local name_len = #package.name + if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then + package.id = package.author:lower() .. "/" .. package.name:sub(1, name_len - 5) + else + package.id = package.author:lower() .. "/" .. package.name end - - store.packages = store.packages_full - store.loaded = true end - core.delete_dir(tmpdir) + store.packages = store.packages_full + store.loaded = true end function store.update_paths() @@ -395,34 +312,35 @@ function store.get_formspec(dlgdata) cur_page = 1 end + local W = 15.75 + local H = 9.5 + local formspec if #store.packages_full > 0 then formspec = { - "size[12,7;true]", + "formspec_version[3]", + "size[15.75,9.5]", "position[0.5,0.55]", - "field[0.2,0.1;7.8,1;search_string;;", - core.formspec_escape(search_string), "]", + "container[0.375,0.375]", + "field[0,0;10.225,0.8;search_string;;", core.formspec_escape(search_string), "]", "field_close_on_enter[search_string;false]", - "button[7.7,-0.2;2,1;search;", - fgettext("Search"), "]", - "dropdown[9.7,-0.1;2.4;type;", - table.concat(filter_types_titles, ","), - ";", filter_type, "]", - -- "textlist[0,1;2.4,5.6;a;", - -- table.concat(taglist, ","), "]", + "button[10.225,0;2,0.8;search;", fgettext("Search"), "]", + "dropdown[12.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]", + "container_end[]", -- Page nav buttons - "container[0,", - num_per_page + 1.5, "]", - "button[-0.1,0;3,1;back;", - fgettext("Back to Main Menu"), "]", - "button[7.1,0;1,1;pstart;<<]", - "button[8.1,0;1,1;pback;<]", - "label[9.2,0.2;", - tonumber(cur_page), " / ", - tonumber(dlgdata.pagemax), "]", - "button[10.1,0;1,1;pnext;>]", - "button[11.1,0;1,1;pend;>>]", + "container[0,", H - 0.8 - 0.375, "]", + "button[0.375,0;4,0.8;back;", fgettext("Back to Main Menu"), "]", + + "container[", W - 0.375 - 0.8*4 - 2, ",0]", + "image_button[0,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "start_icon.png;pstart;]", + "image_button[0.8,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "prev_icon.png;pback;]", + "style[pagenum;border=false]", + "button[1.6,0;2,0.8;pagenum;", tonumber(cur_page), " / ", tonumber(dlgdata.pagemax), "]", + "image_button[3.6,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "next_icon.png;pnext;]", + "image_button[4.4,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "end_icon.png;pend;]", + "container_end[]", + "container_end[]", } @@ -433,73 +351,84 @@ function store.get_formspec(dlgdata) end else formspec = { - "size[12,7;true]", + "size[12,7]", "position[0.5,0.55]", "label[4,3;", fgettext("No packages could be retrieved"), "]", - "button[-0.1,", - num_per_page + 1.5, - ";3,1;back;", - fgettext("Back to Main Menu"), "]", + "container[0,", H - 0.8 - 0.375, "]", + "button[0,0;4,0.8;back;", fgettext("Back to Main Menu"), "]", + "container_end[]", } end local start_idx = (cur_page - 1) * num_per_page + 1 for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do local package = store.packages[i] - formspec[#formspec + 1] = "container[0.5," - formspec[#formspec + 1] = (i - start_idx) * 1.1 + 1 + formspec[#formspec + 1] = "container[0.375," + formspec[#formspec + 1] = (i - start_idx) * 1.375 + (2*0.375 + 0.8) formspec[#formspec + 1] = "]" -- image - formspec[#formspec + 1] = "image[-0.4,0;1.5,1;" + formspec[#formspec + 1] = "image[0,0;1.5,1;" formspec[#formspec + 1] = core.formspec_escape(get_screenshot(package)) formspec[#formspec + 1] = "]" -- title - formspec[#formspec + 1] = "label[1,-0.1;" + formspec[#formspec + 1] = "label[1.875,0.1;" formspec[#formspec + 1] = core.formspec_escape( minetest.colorize(mt_color_green, package.title) .. minetest.colorize("#BFBFBF", " by " .. package.author)) formspec[#formspec + 1] = "]" - -- description - if package.path and package.installed_release < package.release then - formspec[#formspec + 1] = "textarea[1.25,0.3;7.5,1;;;" - else - formspec[#formspec + 1] = "textarea[1.25,0.3;9,1;;;" - end - formspec[#formspec + 1] = core.formspec_escape(package.short_description) - formspec[#formspec + 1] = "]" - -- buttons - if not package.path then - formspec[#formspec + 1] = "button[9.9,0;1.5,1;install_" + local description_width = W - 0.375*5 - 1 - 2*1.5 + formspec[#formspec + 1] = "container[" + formspec[#formspec + 1] = W - 0.375*2 + formspec[#formspec + 1] = ",0.1]" + + if package.downloading then + formspec[#formspec + 1] = "style[download;border=false]" + + formspec[#formspec + 1] = "button[-3.5,0;2,0.8;download;" + formspec[#formspec + 1] = fgettext("Downloading...") + formspec[#formspec + 1] = "]" + elseif not package.path then + formspec[#formspec + 1] = "button[-3,0;1.5,0.8;install_" formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = ";" formspec[#formspec + 1] = fgettext("Install") formspec[#formspec + 1] = "]" else if package.installed_release < package.release then + description_width = description_width - 1.5 + -- The install_ action also handles updating - formspec[#formspec + 1] = "button[8.4,0;1.5,1;install_" + formspec[#formspec + 1] = "button[-4.5,0;1.5,0.8;install_" formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = ";" formspec[#formspec + 1] = fgettext("Update") formspec[#formspec + 1] = "]" end - formspec[#formspec + 1] = "button[9.9,0;1.5,1;uninstall_" + formspec[#formspec + 1] = "button[-3,0;1.5,0.8;uninstall_" formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = ";" formspec[#formspec + 1] = fgettext("Uninstall") formspec[#formspec + 1] = "]" end - --formspec[#formspec + 1] = "button[9.9,0;1.5,1;view_" - --formspec[#formspec + 1] = tostring(i) - --formspec[#formspec + 1] = ";" - --formspec[#formspec + 1] = fgettext("View") - --formspec[#formspec + 1] = "]" + formspec[#formspec + 1] = "button[-1.5,0;1.5,0.8;view_" + formspec[#formspec + 1] = tostring(i) + formspec[#formspec + 1] = ";" + formspec[#formspec + 1] = fgettext("View") + formspec[#formspec + 1] = "]" + formspec[#formspec + 1] = "container_end[]" + + -- description + formspec[#formspec + 1] = "textarea[1.855,0.3;" + formspec[#formspec + 1] = tostring(description_width) + formspec[#formspec + 1] = ",0.8;;;" + formspec[#formspec + 1] = core.formspec_escape(package.short_description) + formspec[#formspec + 1] = "]" formspec[#formspec + 1] = "container_end[]" end @@ -576,10 +505,9 @@ function store.handle_submit(this, fields) end if fields["view_" .. i] then - local dlg = package_dialog.create(package) - dlg:set_parent(this) - this:hide() - dlg:show() + local url = ("%s/packages/%s?protocol_version=%d"):format( + core.settings:get("contentdb_url"), package.id, core.get_max_supp_proto()) + core.open_url(url) return true end end @@ -594,6 +522,17 @@ function create_store_dlg(type) search_string = "" cur_page = 1 + + if type then + -- table.indexof does not work on tables that contain `nil` + for i, v in pairs(filter_types_type) do + if v == type then + filter_type = i + break + end + end + end + store.filter_packages(search_string) return dialog_create("store", diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua index 31d41d693..36df23cce 100644 --- a/builtin/mainmenu/dlg_create_world.lua +++ b/builtin/mainmenu/dlg_create_world.lua @@ -17,13 +17,110 @@ local worldname = "" +local function table_to_flags(ftable) + -- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves" + local str = {} + for flag, is_set in pairs(ftable) do + str[#str + 1] = is_set and flag or ("no" .. flag) + end + return table.concat(str, ",") +end + +-- Same as check_flag but returns a string +local function strflag(flags, flag) + return (flags[flag] == true) and "true" or "false" +end + +local cb_caverns = { "caverns", fgettext("Caverns"), "caverns", + fgettext("Very large caverns deep in the underground") } +local tt_sea_rivers = fgettext("Sea level rivers") + +local flag_checkboxes = { + v5 = { + cb_caverns, + }, + v7 = { + cb_caverns, + { "ridges", fgettext("Rivers"), "ridges", tt_sea_rivers }, + { "mountains", fgettext("Mountains"), "mountains" }, + { "floatlands", fgettext("Floatlands (experimental)"), "floatlands", + fgettext("Floating landmasses in the sky") }, + }, + carpathian = { + cb_caverns, + { "rivers", fgettext("Rivers"), "rivers", tt_sea_rivers }, + }, + valleys = { + { "altitude-chill", fgettext("Altitude chill"), "altitude_chill", + fgettext("Reduces heat with altitude") }, + { "altitude-dry", fgettext("Altitude dry"), "altitude_dry", + fgettext("Reduces humidity with altitude") }, + { "humid-rivers", fgettext("Humid rivers"), "humid_rivers", + fgettext("Increases humidity around rivers") }, + { "vary-river-depth", fgettext("Vary river depth"), "vary_river_depth", + fgettext("Low humidity and high heat causes shallow or dry rivers") }, + }, + flat = { + { "hills", fgettext("Hills"), "hills" }, + { "lakes", fgettext("Lakes"), "lakes" }, + }, + fractal = { + { "terrain", fgettext("Additional terrain"), "terrain", + fgettext("Generate non-fractal terrain: Oceans and underground") }, + }, + v6 = { + { "trees", fgettext("Trees and jungle grass"), "trees" }, + { "flat", fgettext("Flat terrain"), "flat" }, + { "mudflow", fgettext("Mud flow"), "mudflow", + fgettext("Terrain surface erosion") }, + -- Biome settings are in mgv6_biomes below + }, +} + +local mgv6_biomes = { + { + fgettext("Temperate, Desert, Jungle, Tundra, Taiga"), + {jungles = true, snowbiomes = true} + }, + { + fgettext("Temperate, Desert, Jungle"), + {jungles = true, snowbiomes = false} + }, + { + fgettext("Temperate, Desert"), + {jungles = false, snowbiomes = false} + }, +} + local function create_world_formspec(dialogdata) + + -- Error out when no games found + if #pkgmgr.games == 0 then + return "size[12.25,3,true]" .. + "box[0,0;12,2;#ff8800]" .. + "textarea[0.3,0;11.7,2;;;".. + fgettext("You have no games installed.") .. "\n" .. + fgettext("Download one from minetest.net") .. "]" .. + "button[4.75,2.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" + end + local mapgens = core.get_mapgen_names() local current_seed = core.settings:get("fixed_map_seed") or "" local current_mg = core.settings:get("mg_name") local gameid = core.settings:get("menu_last_game") + local flags = { + main = core.settings:get_flags("mg_flags"), + v5 = core.settings:get_flags("mgv5_spflags"), + v6 = core.settings:get_flags("mgv6_spflags"), + v7 = core.settings:get_flags("mgv7_spflags"), + fractal = core.settings:get_flags("mgfractal_spflags"), + carpathian = core.settings:get_flags("mgcarpathian_spflags"), + valleys = core.settings:get_flags("mgvalleys_spflags"), + flat = core.settings:get_flags("mgflat_spflags"), + } + local gameidx = 0 if gameid ~= nil then local _ @@ -35,15 +132,29 @@ local function create_world_formspec(dialogdata) end local game_by_gameidx = core.get_game(gameidx) + local disallowed_mapgen_settings = {} if game_by_gameidx ~= nil then local gamepath = game_by_gameidx.path local gameconfig = Settings(gamepath.."/game.conf") + local allowed_mapgens = (gameconfig:get("allowed_mapgens") or ""):split() + for key, value in pairs(allowed_mapgens) do + allowed_mapgens[key] = value:trim() + end + local disallowed_mapgens = (gameconfig:get("disallowed_mapgens") or ""):split() for key, value in pairs(disallowed_mapgens) do disallowed_mapgens[key] = value:trim() end + if #allowed_mapgens > 0 then + for i = #mapgens, 1, -1 do + if table.indexof(allowed_mapgens, mapgens[i]) == -1 then + table.remove(mapgens, i) + end + end + end + if disallowed_mapgens then for i = #mapgens, 1, -1 do if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then @@ -51,49 +162,193 @@ local function create_world_formspec(dialogdata) end end end + + local ds = (gameconfig:get("disallowed_mapgen_settings") or ""):split() + for _, value in pairs(ds) do + disallowed_mapgen_settings[value:trim()] = true + end end local mglist = "" - local selindex = 1 + local selindex local i = 1 + local first_mg for k,v in pairs(mapgens) do + if not first_mg then + first_mg = v + end if current_mg == v then selindex = i end i = i + 1 mglist = mglist .. v .. "," end + if not selindex then + selindex = 1 + current_mg = first_mg + end mglist = mglist:sub(1, -2) - current_seed = core.formspec_escape(current_seed) - local retval = - "size[11.5,6.5,true]" .. - "label[2,0;" .. fgettext("World name") .. "]".. - "field[4.5,0.4;6,0.5;te_world_name;;" .. minetest.formspec_escape(worldname) .. "]" .. + local mg_main_flags = function(mapgen, y) + if mapgen == "singlenode" then + return "", y + end + if disallowed_mapgen_settings["mg_flags"] then + return "", y + end - "label[2,1;" .. fgettext("Seed") .. "]".. - "field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" .. + local form = "checkbox[0," .. y .. ";flag_mg_caves;" .. + fgettext("Caves") .. ";"..strflag(flags.main, "caves").."]" + y = y + 0.5 - "label[2,2;" .. fgettext("Mapgen") .. "]".. - "dropdown[4.2,2;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" .. + form = form .. "checkbox[0,"..y..";flag_mg_dungeons;" .. + fgettext("Dungeons") .. ";"..strflag(flags.main, "dungeons").."]" + y = y + 0.5 - "label[2,3;" .. fgettext("Game") .. "]".. - "textlist[4.2,3;5.8,2.3;games;" .. pkgmgr.gamelist() .. - ";" .. gameidx .. ";true]" .. + local d_name = fgettext("Decorations") + local d_tt + if mapgen == "v6" then + d_tt = fgettext("Structures appearing on the terrain (no effect on trees and jungle grass created by v6)") + else + d_tt = fgettext("Structures appearing on the terrain, typically trees and plants") + end + form = form .. "checkbox[0,"..y..";flag_mg_decorations;" .. + d_name .. ";" .. + strflag(flags.main, "decorations").."]" .. + "tooltip[flag_mg_decorations;" .. + d_tt .. + "]" + y = y + 0.5 - "button[3.25,6;2.5,0.5;world_create_confirm;" .. fgettext("Create") .. "]" .. - "button[5.75,6;2.5,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" - - if #pkgmgr.games == 0 then - retval = retval .. "box[2,4;8,1;#ff8800]label[2.25,4;" .. - fgettext("You have no games installed.") .. "]label[2.25,4.4;" .. - fgettext("Download one from minetest.net") .. "]" - elseif #pkgmgr.games == 1 and pkgmgr.games[1].id == "minimal" then - retval = retval .. "box[1.75,4;8.7,1;#ff8800]label[2,4;" .. - fgettext("Warning: The minimal development test is meant for developers.") .. "]label[2,4.4;" .. - fgettext("Download a game, such as Minetest Game, from minetest.net") .. "]" + form = form .. "tooltip[flag_mg_caves;" .. + fgettext("Network of tunnels and caves") + .. "]" + return form, y end + local mg_specific_flags = function(mapgen, y) + if not flag_checkboxes[mapgen] then + return "", y + end + if disallowed_mapgen_settings["mg"..mapgen.."_spflags"] then + return "", y + end + local form = "" + for _,tab in pairs(flag_checkboxes[mapgen]) do + local id = "flag_mg"..mapgen.."_"..tab[1] + form = form .. ("checkbox[0,%f;%s;%s;%s]"): + format(y, id, tab[2], strflag(flags[mapgen], tab[3])) + + if tab[4] then + form = form .. "tooltip["..id..";"..tab[4].."]" + end + y = y + 0.5 + end + + if mapgen ~= "v6" then + -- No special treatment + return form, y + end + -- Special treatment for v6 (add biome widgets) + + -- Biome type (jungles, snowbiomes) + local biometype + if flags.v6.snowbiomes == true then + biometype = 1 + elseif flags.v6.jungles == true then + biometype = 2 + else + biometype = 3 + end + y = y + 0.3 + + form = form .. "label[0,"..(y+0.1)..";" .. fgettext("Biomes") .. "]" + y = y + 0.6 + + form = form .. "dropdown[0,"..y..";6.3;mgv6_biomes;" + for b=1, #mgv6_biomes do + form = form .. mgv6_biomes[b][1] + if b < #mgv6_biomes then + form = form .. "," + end + end + form = form .. ";" .. biometype.. "]" + + -- biomeblend + y = y + 0.55 + form = form .. "checkbox[0,"..y..";flag_mgv6_biomeblend;" .. + fgettext("Biome blending") .. ";"..strflag(flags.v6, "biomeblend").."]" .. + "tooltip[flag_mgv6_biomeblend;" .. + fgettext("Smooth transition between biomes") .. "]" + + return form, y + end + + current_seed = core.formspec_escape(current_seed) + + local y_start = 0.0 + local y = y_start + local str_flags, str_spflags + local label_flags, label_spflags = "", "" + y = y + 0.3 + str_flags, y = mg_main_flags(current_mg, y) + if str_flags ~= "" then + label_flags = "label[0,"..y_start..";" .. fgettext("Mapgen flags") .. "]" + y_start = y + 0.4 + else + y_start = 0.0 + end + y = y_start + 0.3 + str_spflags = mg_specific_flags(current_mg, y) + if str_spflags ~= "" then + label_spflags = "label[0,"..y_start..";" .. fgettext("Mapgen-specific flags") .. "]" + end + + -- Warning if only devtest is installed + local devtest_only = "" + local gamelist_height = 2.3 + if #pkgmgr.games == 1 and pkgmgr.games[1].id == "devtest" then + devtest_only = "box[0,0;5.8,1.7;#ff8800]" .. + "textarea[0.3,0;6,1.8;;;".. + fgettext("Warning: The Development Test is meant for developers.") .. "\n" .. + fgettext("Download a game, such as Minetest Game, from minetest.net") .. "]" + gamelist_height = 0.5 + end + + local retval = + "size[12.25,7,true]" .. + + -- Left side + "container[0,0]".. + "field[0.3,0.6;6,0.5;te_world_name;" .. + fgettext("World name") .. + ";" .. core.formspec_escape(worldname) .. "]" .. + + "field[0.3,1.7;6,0.5;te_seed;" .. + fgettext("Seed") .. + ";".. current_seed .. "]" .. + + "label[0,2;" .. fgettext("Mapgen") .. "]".. + "dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" .. + + "label[0,3.35;" .. fgettext("Game") .. "]".. + "textlist[0,3.85;5.8,"..gamelist_height..";games;" .. + pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" .. + "container[0,4.5]" .. + devtest_only .. + "container_end[]" .. + "container_end[]" .. + + -- Right side + "container[6.2,0]".. + label_flags .. str_flags .. + label_spflags .. str_spflags .. + "container_end[]".. + + -- Menu buttons + "button[3.25,6.5;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" .. + "button[6.25,6.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" + return retval end @@ -150,11 +405,53 @@ local function create_world_buttonhandler(this, fields) return true end + for k,v in pairs(fields) do + local split = string.split(k, "_", nil, 3) + if split and split[1] == "flag" then + local setting + if split[2] == "mg" then + setting = "mg_flags" + else + setting = split[2].."_spflags" + end + -- We replaced the underscore of flag names with a dash. + local flag = string.gsub(split[3], "-", "_") + local ftable = core.settings:get_flags(setting) + if v == "true" then + ftable[flag] = true + else + ftable[flag] = false + end + local flags = table_to_flags(ftable) + core.settings:set(setting, flags) + return true + end + end + if fields["world_create_cancel"] then this:delete() return true end + if fields["mgv6_biomes"] then + local entry = minetest.formspec_escape(fields["mgv6_biomes"]) + for b=1, #mgv6_biomes do + if entry == mgv6_biomes[b][1] then + local ftable = core.settings:get_flags("mgv6_spflags") + ftable.jungles = mgv6_biomes[b][2].jungles + ftable.snowbiomes = mgv6_biomes[b][2].snowbiomes + local flags = table_to_flags(ftable) + core.settings:set("mgv6_spflags", flags) + return true + end + end + end + + if fields["dd_mapgen"] then + core.settings:set("mg_name", fields["dd_mapgen"]) + return true + end + return false end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 21b74f34a..ae9ae8853 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -20,20 +20,18 @@ mt_color_blue = "#6389FF" mt_color_green = "#72FF63" mt_color_dark_green = "#25C191" ---for all other colors ask sfan5 to complete his work! - local menupath = core.get_mainmenu_path() local basepath = core.get_builtin_path() local menustyle = core.settings:get("main_menu_style") defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" .. DIR_DELIM .. "pack" .. DIR_DELIM -dofile(basepath .. "common" .. DIR_DELIM .. "async_event.lua") dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua") dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua") dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua") dofile(basepath .. "fstk" .. DIR_DELIM .. "tabview.lua") dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua") +dofile(menupath .. DIR_DELIM .. "async_event.lua") dofile(menupath .. DIR_DELIM .. "common.lua") dofile(menupath .. DIR_DELIM .. "pkgmgr.lua") dofile(menupath .. DIR_DELIM .. "textures.lua") diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua index dbea12669..14fcda0da 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_credits.lua @@ -23,51 +23,31 @@ local hackers = { local core_developers = { "Perttu Ahola (celeron55) ", "sfan5 ", - "ShadowNinja ", "Nathanaël Courant (Nore/Ekdohibs) ", "Loic Blot (nerzhul/nrz) ", "paramat", "Auke Kok (sofar) ", - "rubenwardy ", + "Andrew Ward (rubenwardy) ", "Krock/SmallJoker ", "Lars Hofhansl ", } local active_contributors = { - "numberZero [Audiovisuals: meshgen]", - "stujones11 [Android UX improvements]", - "red-001 [CSM & Menu fixes]", - "Paul Ouellette (pauloue) [Docs, fixes]", - "Dániel Juhász (juhdanad) [Audiovisuals: lighting]", - "Hybrid Dog [API]", - "srifqi [Android]", - "Vincent Glize (Dumbeldor) [Cleanups, CSM APIs]", - "Ben Deutsch [Rendering, Fixes, SQLite auth]", - "Wuzzy [Translation, Slippery]", - "ANAND (ClobberXD) [Docs, Fixes]", - "Shara/Ezhh [Docs, Game API]", - "DTA7 [Fixes, mute key]", - "Thomas-S [Disconnected, Formspecs]", - "Raymoo [Tool Capabilities]", - "Elijah Duffy (octacian) [Mainmenu]", - "noob3167 [Fixes]", - "adelcoding1 [Formspecs]", - "adrido [Windows Installer, Formspecs]", - "Rui [Sound Pitch]", - "Jean-Patrick G (kilbith) [Audiovisuals]", - "Esteban (EXio4) [Cleanups]", - "Vaughan Lapsley (vlapsley) [Carpathian mapgen]", - "CoderForTheBetter [Add set_rotation]", - "Quentin Bazin (Unarelith) [Cleanups]", + "Hugues Ross [Formspecs]", "Maksim (MoNTE48) [Android]", - "Gaël-de-Sailly [Mapgen, pitch fly]", - "zeuner [Docs, Fixes]", - "ThomasMonroe314 (tre) [Fixes]", - "Rob Blanckaert (basicer) [Fixes]", - "Jozef Behran (osjc) [Fixes]", - "random-geek [Fixes]", - "Pedro Gimeno (pgimeno) [Fixes]", - "lisacvuk [Fixes]", + "DS [Formspecs]", + "pyrollo [Formspecs: Hypertext]", + "v-rob [Formspecs]", + "Jordach [set_sky]", + "random-geek [Formspecs]", + "Wuzzy [Pathfinder, builtin, translations]", + "ANAND (ClobberXD) [Fixes, per-player FOV]", + "Warr1024 [Fixes]", + "Paul Ouellette (pauloue) [Fixes, Script API]", + "Jean-Patrick G (kilbith) [Audiovisuals]", + "HybridDog [Script API]", + "dcbrwn [Object shading]", + "srifqi [Fixes]", } local previous_core_developers = { @@ -82,24 +62,31 @@ local previous_core_developers = { "Ryan Kwolek (kwolekr) ", "sapier", "Zeno", + "ShadowNinja ", } local previous_contributors = { - "Gregory Currie (gregorycu) [optimisation]", - "Diego Martínez (kaeza) ", - "T4im [Profiler]", - "TeTpaAka [Hand overriding, nametag colors]", - "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]", + "Dániel Juhász (juhdanad) ", + "red-001 ", + "numberZero [Audiovisuals: meshgen]", + "Giuseppe Bilotta", + "MirceaKitsune ", + "Constantin Wenger (SpeedProg)", + "Ciaran Gultnieks (CiaranG)", + "stujones11 [Android UX improvements]", "Jeija [HTTP, particles]", - "bigfoot547 [CSM]", + "Vincent Glize (Dumbeldor) [Cleanups, CSM APIs]", + "Ben Deutsch [Rendering, Fixes, SQLite auth]", + "TeTpaAka [Hand overriding, nametag colors]", + "Rui [Sound Pitch]", + "Duane Robertson [MGValleys]", + "Raymoo [Tool Capabilities]", "Rogier [Fixes]", + "Gregory Currie (gregorycu) [optimisation]", + "TriBlade9 [Audiovisuals]", + "T4im [Profiler]", + "Jurgen Doser (doserj) ", } local function buildCreditList(source) @@ -117,8 +104,8 @@ return { local logofile = defaulttexturedir .. "logo.png" local version = core.get_version() return "image[0.5,1;" .. core.formspec_escape(logofile) .. "]" .. - "label[0.5,3.2;" .. version.project .. " " .. version.string .. "]" .. - "label[0.5,3.5;http://minetest.net]" .. + "label[0.5,2.8;" .. version.project .. " " .. version.string .. "]" .. + "button[0.5,3;2,2;homepage;minetest.net]" .. "tablecolumns[color;text]" .. "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. "table[3.5,-0.25;8.5,6.05;list_credits;" .. @@ -133,5 +120,10 @@ return { "#FFFF00," .. fgettext("Previous Contributors") .. ",," .. buildCreditList(previous_contributors) .. "," .. ";1]" - end + end, + cbf_button_handler = function(this, fields, name, tabdata) + if fields.homepage then + core.open_url("https://www.minetest.net") + end + end, } diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 0969bccfb..a21cf12b1 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -35,6 +35,15 @@ if enable_gamebar then end local function game_buttonbar_button_handler(fields) + if fields.game_open_cdb then + local maintab = ui.find_by_name("maintab") + local dlg = create_store_dlg("game") + dlg:set_parent(maintab) + maintab:hide() + dlg:show() + return true + end + for key,value in pairs(fields) do for j=1,#pkgmgr.games,1 do if ("game_btnbar_" .. pkgmgr.games[j].id == key) then @@ -87,6 +96,9 @@ if enable_gamebar then end btnbar:add_button(btn_name, text, image, tooltip) end + + local plus_image = core.formspec_escape(defaulttexturedir .. "plus.png") + btnbar:add_button("game_open_cdb", "", plus_image, fgettext("Install games from ContentDB")) end else function current_game() @@ -207,40 +219,35 @@ local function main_button_handler(this, fields, name, tabdata) local selected = core.get_textlist_index("sp_worlds") gamedata.selected_world = menudata.worldlist:get_raw_index(selected) - 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 = pkgmgr.find_by_gameid(world.gameid) - core.settings:set("menu_last_game", game.id) - end - - core.start() - else - gamedata.errormessage = + if selected == nil or gamedata.selected_world == 0 then + gamedata.errormessage = fgettext("No world created or selected!") - end - else - 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 + + -- Update last game + local world = menudata.worldlist:get_raw_element(gamedata.selected_world) + if world then + local game = pkgmgr.find_by_gameid(world.gameid) + core.settings:set("menu_last_game", game.id) + end + + if core.settings:get_bool("enable_server") 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 + else + gamedata.singleplayer = true + end + + core.start() + return true end if fields["world_create"] ~= nil then diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 1f48a7a13..2a53a3315 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -42,10 +42,10 @@ # Flags are always separated by comma without spaces. # - default possible_flags # * noise_params_2d: -# Format is , , (, , ), , , , [, ] +# Format is , , (, , ), , , , [, ] # - default # * noise_params_3d: -# Format is , , (, , ), , , , [, ] +# Format is , , (, , ), , , , [, ] # - default # * v3f: # Format is (, , ) @@ -252,7 +252,7 @@ keymap_cinematic (Cinematic mode key) key # Key for toggling display of minimap. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_minimap (Minimap key) key KEY_F9 +keymap_minimap (Minimap key) key KEY_KEY_V # Key for taking screenshots. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 @@ -424,7 +424,7 @@ keymap_toggle_profiler (Profiler toggle key) key KEY_F6 # Key for switching between first- and third-person camera. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_camera_mode (Toggle camera mode key) key KEY_F7 +keymap_camera_mode (Toggle camera mode key) key KEY_KEY_C # Key for increasing the viewing range. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 @@ -561,9 +561,6 @@ enable_parallax_occlusion (Parallax occlusion) bool false # 1 = relief mapping (slower, more accurate). parallax_occlusion_mode (Parallax occlusion mode) int 1 0 1 -# Strength of parallax. -3d_paralax_strength (Parallax occlusion strength) float 0.025 - # Number of parallax occlusion iterations. parallax_occlusion_iterations (Parallax occlusion iterations) int 4 @@ -713,6 +710,9 @@ fall_bobbing_amount (Fall bobbing factor) float 0.03 # Note that the interlaced mode requires shaders to be enabled. 3d_mode (3D mode) enum none none,anaglyph,interlaced,topbottom,sidebyside,crossview,pageflip +# Strength of 3D mode parallax. +3d_paralax_strength (3D mode parallax strength) float 0.025 + # In-game chat console height, between 0.1 (10%) and 1.0 (100%). console_height (Console height) float 0.6 0.1 1.0 @@ -741,9 +741,11 @@ selectionbox_color (Selection box color) string (0,0,0) selectionbox_width (Selection box width) int 2 1 5 # Crosshair color (R,G,B). +# Also controls the object crosshair color crosshair_color (Crosshair color) string (255,255,255) # Crosshair alpha (opaqueness, between 0 and 255). +# Also controls the object crosshair color crosshair_alpha (Crosshair alpha) int 255 0 255 # Maximum number of recent chat messages to show @@ -817,7 +819,8 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc autoscale_mode (Autoscaling mode) enum disable disable,enable,force # Show entity selection boxes -show_entity_selectionbox (Show entity selection boxes) bool true +# A restart is required after changing this. +show_entity_selectionbox (Show entity selection boxes) bool false [*Menus] @@ -903,8 +906,13 @@ fallback_font_shadow_alpha (Fallback font shadow alpha) int 128 0 255 # This font will be used for certain languages or if the default font is unavailable. fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf -# Path to save screenshots at. -screenshot_path (Screenshot folder) path +# Font size of the recent chat text and chat prompt in point (pt). +# Value 0 will use the default font size. +chat_font_size (Chat font size) int 0 + +# Path to save screenshots at. Can be an absolute or relative path. +# The folder will be created if it doesn't already exist. +screenshot_path (Screenshot folder) path screenshots # Format of screenshots. screenshot_format (Screenshot format) enum png png,jpg,bmp,pcx,ppm,tga @@ -954,6 +962,12 @@ address (Server address) string # Note that the port field in the main menu overrides this setting. remote_port (Remote port) int 30000 1 65535 +# Prometheus listener address. +# If minetest is compiled with ENABLE_PROMETHEUS option enabled, +# enable metrics listener for Prometheus on that address. +# Metrics can be fetch on http://127.0.0.1:30000/metrics +prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000 + # Save the map received by the client on disk. enable_local_map_saving (Saving map received from server) bool false @@ -1078,6 +1092,10 @@ map-dir (Map directory) path # Setting it to -1 disables the feature. item_entity_ttl (Item entity TTL) int 900 +# Specifies the default stack size of nodes, items and tools. +# Note that mods or games may explicitly set a stack for certain (or all) items. +default_stack_max (Default stack size) int 99 + # Enable players getting damage and dying. enable_damage (Damage) bool false @@ -1149,7 +1167,7 @@ active_object_send_range_blocks (Active object send range) int 4 # active block stuff, stated in mapblocks (16 nodes). # In active blocks objects are loaded and ABMs run. # This is also the minimum range in which active objects (mobs) are maintained. -# This should be configured together with active_object_range. +# This should be configured together with active_object_send_range_blocks. active_block_range (Active block range) int 3 # From how far blocks are sent to clients, stated in mapblocks (16 nodes). @@ -1372,7 +1390,7 @@ name (Player name) string # Set the language. Leave empty to use the system language. # A restart is required after changing this. -language (Language) enum ,ar,ca,cs,da,de,dv,el,eo,es,et,eu,fil,fr,hu,id,it,ja,ja_KS,jbo,kk,kn,lo,lt,ms,my,nb,nl,nn,pl,pt,pt_BR,ro,ru,sl,sr_Cyrl,sv,sw,th,tr,uk,vi +language (Language) enum ,ar,ca,cs,da,de,dv,el,en,eo,es,et,eu,fil,fr,hu,id,it,ja,ja_KS,jbo,kk,kn,lo,lt,ms,my,nb,nl,nn,pl,pt,pt_BR,ro,ru,sl,sr_Cyrl,sv,sw,th,tr,uk,vi # Level of logging to be written to debug.txt: # - (no logging) @@ -1390,6 +1408,9 @@ debug_log_level (Debug log level) enum action ,none,error,warning,action,info,ve # debug.txt is only moved if this setting is positive. debug_log_size_max (Debug log file size threshold) int 50 +# Minimal level of logging to be written to chat. +chat_log_level (Chat log level) enum error ,none,error,warning,action,info,verbose + # Enable IPv6 support (for both client and server). # Required for IPv6 connections to work at all. enable_ipv6 (IPv6) bool true @@ -1593,12 +1614,53 @@ mgv6_np_apple_trees (Apple trees noise) noise_params_2d 0, 1, (100, 100, 100), 3 [*Mapgen V7] # Map generation attributes specific to Mapgen v7. -# 'ridges' enables the rivers. +# 'ridges': Rivers. +# 'floatlands': Floating land masses in the atmosphere. +# 'caverns': Giant caves deep underground. mgv7_spflags (Mapgen V7 specific flags) flags mountains,ridges,nofloatlands,caverns mountains,ridges,floatlands,caverns,nomountains,noridges,nofloatlands,nocaverns # Y of mountain density gradient zero level. Used to shift mountains vertically. mgv7_mount_zero_level (Mountain zero level) int 0 +# Lower Y limit of floatlands. +mgv7_floatland_ymin (Floatland minimum Y) int 1024 + +# Upper Y limit of floatlands. +mgv7_floatland_ymax (Floatland maximum Y) int 4096 + +# Y-distance over which floatlands taper from full density to nothing. +# Tapering starts at this distance from the Y limit. +# For a solid floatland layer, this controls the height of hills/mountains. +# Must be less than or equal to half the distance between the Y limits. +mgv7_floatland_taper (Floatland tapering distance) int 256 + +# Exponent of the floatland tapering. Alters the tapering behaviour. +# Value = 1.0 creates a uniform, linear tapering. +# Values > 1.0 create a smooth tapering suitable for the default separated +# floatlands. +# Values < 1.0 (for example 0.25) create a more defined surface level with +# flatter lowlands, suitable for a solid floatland layer. +mgv7_float_taper_exp (Floatland taper exponent) float 2.0 + +# Adjusts the density of the floatland layer. +# Increase value to increase density. Can be positive or negative. +# Value = 0.0: 50% of volume is floatland. +# Value = 2.0 (can be higher depending on 'mgv7_np_floatland', always test +# to be sure) creates a solid floatland layer. +mgv7_floatland_density (Floatland density) float -0.6 + +# Surface level of optional water placed on a solid floatland layer. +# Water is disabled by default and will only be placed if this value is set +# to above 'mgv7_floatland_ymax' - 'mgv7_floatland_taper' (the start of the +# upper tapering). +# ***WARNING, POTENTIAL DANGER TO WORLDS AND SERVER PERFORMANCE***: +# When enabling water placement the floatlands must be configured and tested +# to be a solid layer by setting 'mgv7_floatland_density' to 2.0 (or other +# required value depending on 'mgv7_np_floatland'), to avoid +# server-intensive extreme water flow and to avoid vast flooding of the +# world surface below. +mgv7_floatland_ywater (Floatland water level) int -31000 + # Controls width of tunnels, a smaller value creates wider tunnels. # Value >= 10.0 completely disables generation of tunnels and avoids the # intensive noise calculations. @@ -1668,6 +1730,12 @@ mgv7_np_mountain (Mountain noise) noise_params_3d -0.6, 1, (250, 350, 250), 5333 # 3D noise defining structure of river canyon walls. mgv7_np_ridge (Ridge noise) noise_params_3d 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0 +# 3D noise defining structure of floatlands. +# If altered from the default, the noise 'scale' (0.7 by default) may need +# to be adjusted, as floatland tapering functions best when this noise has +# a value range of approximately -2.0 to 2.0. +mgv7_np_floatland (Floatland noise) noise_params_3d 0, 0.7, (384, 96, 384), 1009, 4, 0.75, 1.618 + # 3D noise defining giant caverns. mgv7_np_cavern (Cavern noise) noise_params_3d 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0 @@ -2098,20 +2166,17 @@ chunksize (Chunk size) int 5 enable_mapgen_debug_info (Mapgen debug) bool false # Maximum number of blocks that can be queued for loading. -emergequeue_limit_total (Absolute limit of emerge queues) int 512 +emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 512 # Maximum number of blocks to be queued that are to be loaded from file. -# Set to blank for an appropriate amount to be chosen automatically. -emergequeue_limit_diskonly (Limit of emerge queues on disk) int 64 +# This limit is enforced per player. +emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 64 # Maximum number of blocks to be queued that are to be generated. -# Set to blank for an appropriate amount to be chosen automatically. -emergequeue_limit_generate (Limit of emerge queues to generate) int 64 +# This limit is enforced per player. +emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 64 # Number of emerge threads to use. -# WARNING: Currently there are multiple bugs that may cause crashes when -# 'num_emerge_threads' is larger than 1. Until this warning is removed it is -# strongly recommended this value is set to the default '1'. # Value 0: # - Automatic selection. The number of emerge threads will be # - 'number of processors - 2', with a lower limit of 1. diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index ccff1260d..0d8d0a2a5 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -101,8 +101,8 @@ void main(void) float disp_x; float disp_z; -#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES) || \ - (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS) +// OpenGL < 4.3 does not support continued preprocessor lines +#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES) || (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS) vec4 pos2 = mWorld * gl_Vertex; float tOffset = (pos2.x + pos2.y) * 0.001 + pos2.z * 0.002; disp_x = (smoothTriangleWave(animationTimer * 23.0 + tOffset) + diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index bb9e40637..0534dc049 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -25,6 +25,38 @@ 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.03333; +} + +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)); @@ -113,7 +145,14 @@ void main(void) vec4 col = vec4(color.rgb, base.a); + col.rgb *= gl_Color.rgb; + col.rgb *= emissiveColor.rgb * vIDiff; + +#ifdef ENABLE_TONE_MAPPING + col = applyToneMapping(col); +#endif + // 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. diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index 488089392..968a07e22 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -38,7 +38,16 @@ void main(void) lightVec = sunPosition - worldPosition; eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; - vIDiff = directional_ambient(normalize(gl_Normal)); + +#if (MATERIAL_TYPE == TILE_MATERIAL_PLAIN) || (MATERIAL_TYPE == TILE_MATERIAL_PLAIN_ALPHA) + vIDiff = 1.0; +#else + // This is intentional comparison with zero without any margin. + // If normal is not equal to zero exactly, then we assume it's a valid, just not normalized vector + vIDiff = length(gl_Normal) == 0.0 + ? 1.0 + : directional_ambient(normalize(gl_Normal)); +#endif gl_FrontColor = gl_BackColor = gl_Color; } diff --git a/client/shaders/wielded_shader/opengl_fragment.glsl b/client/shaders/wielded_shader/opengl_fragment.glsl deleted file mode 100644 index 546aef71d..000000000 --- a/client/shaders/wielded_shader/opengl_fragment.glsl +++ /dev/null @@ -1,127 +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 lightVec; - -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); - -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) -{ - if (texSeamless) { - return intensity(texture2D(baseTexture, uv).rgb); - } else { - return intensity(texture2D(baseTexture, clamp(uv, 0.0, 0.999)).rgb); - } -} - -vec4 get_normal_map(vec2 uv) -{ - vec4 bump = texture2D(normalTexture, uv).rgba; - bump.xyz = normalize(bump.xyz * 2.0 - 1.0); - return bump; -} - -void main(void) -{ - vec3 color; - vec4 bump; - vec2 uv = gl_TexCoord[0].st; - bool use_normalmap = false; - get_texture_flags(); - -#if USE_NORMALMAPS == 1 - if (normalTexturePresent) { - bump = get_normal_map(uv); - use_normalmap = true; - } -#endif - -#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)); - 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; - } -#endif - - 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), 1.0); - float diffuse = dot(-E,bump.xyz); - color = (diffuse + 0.1 * specular) * base.rgb; - } else { - color = base.rgb; - } -#else - color = base.rgb; -#endif - - vec4 col = vec4(color.rgb, base.a); - col *= gl_Color; - // 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 deleted file mode 100644 index 9f05b833a..000000000 --- a/client/shaders/wielded_shader/opengl_vertex.glsl +++ /dev/null @@ -1,32 +0,0 @@ -uniform mat4 mWorldViewProj; -uniform mat4 mWorld; - -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; - -void main(void) -{ - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = mWorldViewProj * gl_Vertex; - - vPosition = gl_Position.xyz; - worldPosition = (mWorld * gl_Vertex).xyz; - - vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0); - - lightVec = sunPosition - worldPosition; - eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; - - gl_FrontColor = gl_BackColor = gl_Color; -} diff --git a/cmake/Modules/FindGettextLib.cmake b/cmake/Modules/FindGettextLib.cmake index 11de12177..529452a4a 100644 --- a/cmake/Modules/FindGettextLib.cmake +++ b/cmake/Modules/FindGettextLib.cmake @@ -55,10 +55,10 @@ endif(WIN32) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GetText DEFAULT_MSG ${GETTEXT_REQUIRED_VARS}) +find_package_handle_standard_args(GettextLib DEFAULT_MSG ${GETTEXT_REQUIRED_VARS}) -if(GETTEXT_FOUND) +if(GETTEXTLIB_FOUND) # BSD variants require special linkage as they don't use glibc if(${CMAKE_SYSTEM_NAME} MATCHES "BSD|DragonFly") set(GETTEXT_LIBRARY "intl") diff --git a/cmake/Modules/FindJson.cmake b/cmake/Modules/FindJson.cmake index 53ddf4599..a5e9098f8 100644 --- a/cmake/Modules/FindJson.cmake +++ b/cmake/Modules/FindJson.cmake @@ -11,14 +11,14 @@ if(ENABLE_SYSTEM_JSONCPP) find_path(JSON_INCLUDE_DIR json/allocator.h PATH_SUFFIXES jsoncpp) include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(JSONCPP DEFAULT_MSG JSON_LIBRARY JSON_INCLUDE_DIR) + find_package_handle_standard_args(Json DEFAULT_MSG JSON_LIBRARY JSON_INCLUDE_DIR) - if(JSONCPP_FOUND) + if(JSON_FOUND) message(STATUS "Using system JSONCPP library.") endif() endif() -if(NOT JSONCPP_FOUND) +if(NOT JSON_FOUND) message(STATUS "Using bundled JSONCPP library.") set(JSON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/jsoncpp) set(JSON_LIBRARY jsoncpp) diff --git a/cmake/Modules/FindLuaJIT.cmake b/cmake/Modules/FindLuaJIT.cmake index 4bc88b6b1..97b0b7c64 100644 --- a/cmake/Modules/FindLuaJIT.cmake +++ b/cmake/Modules/FindLuaJIT.cmake @@ -55,7 +55,7 @@ ENDIF() INCLUDE(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LUAJIT_FOUND to TRUE if # all listed variables are TRUE -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJit +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJIT REQUIRED_VARS LUA_LIBRARY LUA_INCLUDE_DIR VERSION_VAR LUA_VERSION_STRING) diff --git a/cmake/Modules/FindVorbis.cmake b/cmake/Modules/FindVorbis.cmake index 8f3813694..e5fe7f25e 100644 --- a/cmake/Modules/FindVorbis.cmake +++ b/cmake/Modules/FindVorbis.cmake @@ -20,13 +20,13 @@ if(NOT GP2XWIZ) # Handle the QUIETLY and REQUIRED arguments and set VORBIS_FOUND # to TRUE if all listed variables are TRUE. include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(VORBIS DEFAULT_MSG + find_package_handle_standard_args(Vorbis DEFAULT_MSG OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY) else(NOT GP2XWIZ) find_path(VORBIS_INCLUDE_DIR tremor/ivorbisfile.h) find_library(VORBIS_LIBRARY NAMES vorbis_dec) - find_package_handle_standard_args(VORBIS DEFAULT_MSG + find_package_handle_standard_args(Vorbis DEFAULT_MSG VORBIS_INCLUDE_DIR VORBIS_LIBRARY) endif(NOT GP2XWIZ) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 3618b852d..d7816f0e4 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -20,18 +20,10 @@ PREDEFINED = "USE_SPATIAL=1" \ "USE_GETTEXT=1" # Input -RECURSIVE = NO +RECURSIVE = YES STRIP_FROM_PATH = @CMAKE_CURRENT_SOURCE_DIR@/src INPUT = @CMAKE_CURRENT_SOURCE_DIR@/doc/main_page.dox \ - @CMAKE_CURRENT_SOURCE_DIR@/src/ \ - @CMAKE_CURRENT_SOURCE_DIR@/src/client \ - @CMAKE_CURRENT_SOURCE_DIR@/src/network \ - @CMAKE_CURRENT_SOURCE_DIR@/src/util \ - @CMAKE_CURRENT_SOURCE_DIR@/src/script \ - @CMAKE_CURRENT_SOURCE_DIR@/src/script/common \ - @CMAKE_CURRENT_SOURCE_DIR@/src/script/cpp_api \ - @CMAKE_CURRENT_SOURCE_DIR@/src/script/lua_api \ - @CMAKE_CURRENT_SOURCE_DIR@/src/threading + @CMAKE_CURRENT_SOURCE_DIR@/src/ # Dot graphs HAVE_DOT = @DOXYGEN_DOT_FOUND@ diff --git a/doc/README.android b/doc/README.android index c21279583..f6b67978f 100644 --- a/doc/README.android +++ b/doc/README.android @@ -1,6 +1,5 @@ -Minetest Android port -===================== -Date: 2014 06 28 +Minetest: Android version +========================= Controls -------- @@ -40,25 +39,6 @@ file can usually be found at /mnt/sdcard/Minetest. main menu is too big or small on your device, try changing this value. -Known issues ------------- -Not all issues are fixed by now: - -* Unable to exit from volume menu -- don't use the volume menu, use Android's - volume controls instead. -* 512 MB RAM seems to be inadequate -- this depends on the server you join. - Try to play on more lightweight servers. - -Versioning ----------- -Android version numbers are 4 digits instead of Minetest's 3 digits. The last -number of Android's version represents the Android internal version code. This -version code is strictly incremental. It's incremented for each official -Minetest Android build. - -E.g. prerelease Minetest Android builds have been 0.4.9.3, while the first -official version most likely will be 0.4.10.4 - Requirements ------------ @@ -69,9 +49,9 @@ following software packages. The version number in parenthesis denotes the version that was tested at the time this README was drafted; newer/older versions may or may not work. -* android SDK (api-26) -* android NDK (r17c) -* wget (1.13.4) +* Android SDK 29 +* Android NDK r21 +* Android Studio 3 [optional] Additionally, you'll need to have an Internet connection available on the build system, as the Android build will download some source packages. @@ -79,16 +59,15 @@ build system, as the Android build will download some source packages. Build ----- -Debug build: -* Enter "build/android" subdirectory -* Execute "make" -* Answer the questions about where SDK and NDK are located on your filesystem -* Wait for build to finish +The new build system Minetest Android is fully functional and is designed to +speed up and simplify the work, as well as adding the possibility of +cross-platform build. +You can use `./gradlew assemblerelease` or `./gradlew assembledebug` from the +command line or use Android Studio and click the build button. -After the build is finished, the resulting apk can be fond in -build/android/bin/. It will be called Minetest-debug.apk - -Release build: +When using gradlew, the newest NDK will be downloaded and installed +automatically. Or you can create a `local.properties` file and specify +`sdk.dir` and `ndk.dir` yourself. * In order to make a release build you'll have to have a keystore setup to sign the resulting apk package. How this is done is not part of this README. There @@ -97,32 +76,6 @@ Release build: * Once your keystore is setup, enter build/android subdirectory and create a new file "ant.properties" there. Add following lines to that file: - + > key.store= > key.alias=Minetest - -* Execute "make release" -* Enter your keystore as well as your Mintest key password once asked. Be - careful it's shown on console in clear text! -* The result can be found at "bin/Minetest-release.apk" - -Other things that may be nice to know ------------- -* The environment for Android development tools is saved within Android build - build folder. If you want direct access to it do: - - > make envpaths - > . and_env - - After you've done this you'll have your path and path variables set correct - to use adb and all other Android development tools - -* You can build a single dependency by calling make and the dependency's name, - e.g.: - - > make irrlicht - -* You can completely cleanup a dependency by calling make and the "clean" target, - e.g.: - - > make clean_irrlicht diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index c24de8d85..3b0046b4f 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Client Modding API Reference 5.2.0 +Minetest Lua Client Modding API Reference 5.4.0 ================================================ * More information at * Developer Wiki: @@ -112,7 +112,7 @@ The main Lua script. Running this script should register everything it wants to register. Subsequent execution depends on minetest calling the registered callbacks. -**NOTE**: Client mods currently can't provide and textures, sounds or models by +**NOTE**: Client mods currently can't provide textures, sounds, or models by themselves. Any media referenced in function calls must already be loaded (provided by mods that exist on the server). @@ -734,6 +734,13 @@ Call these functions only at load time! * `spec` is a `SimpleSoundSpec` * `parameters` is a sound parameter table * `minetest.sound_stop(handle)` + * `handle` is a handle returned by `minetest.sound_play` +* `minetest.sound_fade(handle, step, gain)` + * `handle` is a handle returned by `minetest.sound_play` + * `step` determines how fast a sound will fade. + Negative step will lower the sound volume, positive step will increase + the sound volume. + * `gain` the target gain for the fade. ### Timing * `minetest.after(time, func, ...)` @@ -761,12 +768,15 @@ Call these functions only at load time! * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * `search_center` is an optional boolean (default: `false`) If true `pos` is also checked for the nodes -* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of - positions. +* `minetest.find_nodes_in_area(pos1, pos2, nodenames, [grouped])` + * `pos1` and `pos2` are the min and max positions of the area to search. * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` - * First return value: Table with all node positions - * Second return value: Table with the count of each node with the node name - as index. + * If `grouped` is true the return value is a table indexed by node name + which contains lists of positions. + * If `grouped` is false or absent the return values are as follows: + first value: Table with all node positions + second value: Table with the count of each node with the node name + as index * Area volume is limited to 4,096,000 nodes * `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a list of positions. @@ -797,8 +807,6 @@ Call these functions only at load time! * get max available level for leveled node ### Player -* `minetest.get_wielded_item()` - * Returns the itemstack the local player is holding * `minetest.send_chat_message(message)` * Act as if `message` was typed by the player into the terminal. * `minetest.run_server_chatcommand(cmd, param)` @@ -962,7 +970,7 @@ Please do not try to access the reference until the camera is initialized, other * `get_camera_mode()` * Returns 0, 1, or 2 as described above * `get_fov()` - * Returns: + * Returns a table with X, Y, maximum and actual FOV in degrees: ```lua { @@ -999,6 +1007,10 @@ Methods: * returns player HP * `get_name()` * returns player name +* `get_wield_index()` + * returns the index of the wielded item +* `get_wielded_item()` + * returns the itemstack the player is holding * `is_attached()` * returns true if player is attached * `is_touching_ground()` @@ -1022,7 +1034,8 @@ Methods: jump = float, gravity = float, sneak = boolean, - sneak_glitch = boolean + sneak_glitch = boolean, + new_move = boolean, } ``` @@ -1074,8 +1087,26 @@ Methods: * returns last look horizontal angle * `get_last_look_vertical()`: * returns last look vertical angle -* `get_key_pressed()`: - * returns last key typed by the player +* `get_control()`: + * returns pressed player controls + +```lua + { + up = boolean, + down = boolean, + left = boolean, + right = boolean, + jump = boolean, + aux1 = boolean, + sneak = boolean, + zoom = boolean, + LMB = boolean, + RMB = boolean, + } +``` + +* `get_armor_groups()` + * returns a table with the armor group ratings * `hud_add(definition)` * add a HUD element described by HUD def, returns ID number on success and `nil` on failure. * See [`HUD definition`](#hud-definition-hud_add-hud_get) @@ -1388,12 +1419,35 @@ Displays a horizontal bar made up of half-images. * `offset`: offset in pixels from position. ### `waypoint` + Displays distance to selected world position. * `name`: The name of the waypoint. * `text`: Distance suffix. Can be blank. -* `number:` An integer containing the RGB value of the color used to draw the text. +* `precision`: Waypoint precision, integer >= 0. Defaults to 10. + If set to 0, distance is not shown. Shown value is `floor(distance*precision)/precision`. + When the precision is an integer multiple of 10, there will be `log_10(precision)` digits after the decimal point. + `precision = 1000`, for example, will show 3 decimal places (eg: `0.999`). + `precision = 2` will show multiples of `0.5`; precision = 5 will show multiples of `0.2` and so on: + `precision = n` will show multiples of `1/n` +* `number:` An integer containing the RGB value of the color used to draw the + text. * `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. +* `alignment`: The alignment of the waypoint. + +### `image_waypoint` + +Same as `image`, but does not accept a `position`; the position is instead determined by `world_pos`, the world position of the waypoint. + +* `scale`: The scale of the image, with 1 being the original texture size. + Only the X coordinate scale is used (positive values). + Negative values represent that percentage of the screen it + should take; e.g. `x=-100` means 100% (width). +* `text`: The name of the texture that is displayed. +* `alignment`: The alignment of the image. +* `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. ### Particle definition (`add_particle`) diff --git a/doc/fst_api.txt b/doc/fst_api.txt index c8bcb1e91..6f9aa14b3 100644 --- a/doc/fst_api.txt +++ b/doc/fst_api.txt @@ -101,6 +101,9 @@ dialog_create(name, cbf_formspec, cbf_button_handler, cbf_events) ^ cbf_events: function to handle events function(dialog, event) +messagebox(name, message) +^ creates a message dialog + Class reference dialog: methods: @@ -113,13 +116,13 @@ methods: ^ hide dialog - delete() ^ delete dialog from ui - + members: - data ^ variable data attached to this dialog - parent ^ parent component to return to on exit - + File: fst/buttonbar.lua ----------------------- diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 86e8d6575..e0c895c97 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -64,9 +64,21 @@ The game directory can contain the following files: * `game.conf`, with the following keys: * `name`: Required, human readable name e.g. `name = Minetest` * `description`: Short description to be shown in the content tab + * `allowed_mapgens = ` + e.g. `allowed_mapgens = v5,v6,flat` + Mapgens not in this list are removed from the list of mapgens for + the game. + If not specified, all mapgens are allowed. * `disallowed_mapgens = ` e.g. `disallowed_mapgens = v5,v6,flat` These mapgens are removed from the list of mapgens for the game. + When both `allowed_mapgens` and `disallowed_mapgens` are + specified, `allowed_mapgens` is applied before + `disallowed_mapgens`. + * `disallowed_mapgen_settings= ` + e.g. `disallowed_mapgen_settings = mgv5_spflags` + These settings are hidden for this game in the world creation + dialog and game start menu. * `minetest.conf`: Used to set default settings when running this game. * `settingtypes.txt`: @@ -889,6 +901,7 @@ These sound files are played back by the engine if provided. * `player_damage`: Played when the local player takes damage (gain = 0.5) * `player_falling_damage`: Played when the local player takes damage by falling (gain = 0.5) + * `player_jump`: Played when the local player jumps * `default_dig_`: Default node digging sound (see node sound definition for details) @@ -1010,22 +1023,24 @@ The function of `param2` is determined by `paramtype2` in node definition. * Values range 0 - 179. The value stored in `param2` is multiplied by two to get the actual rotation in degrees of the node. * `paramtype2 = "meshoptions"` - * Only valid for "plantlike" drawtype. The value of `param2` becomes a - bitfield which can be used to change how the client draws plantlike nodes. - * Bits 0, 1 and 2 form a mesh selector. - Currently the following meshes are choosable: + * Only valid for "plantlike" drawtype. `param2` encodes the shape and + optional modifiers of the "plant". `param2` is a bitfield. + * Bits 0 to 2 select the shape. + Use only one of the values below: * 0 = a "x" shaped plant (ordinary plant) * 1 = a "+" shaped plant (just rotated 45 degrees) * 2 = a "*" shaped plant with 3 faces instead of 2 * 3 = a "#" shaped plant with 4 faces instead of 2 * 4 = a "#" shaped plant with 4 faces that lean outwards * 5-7 are unused and reserved for future meshes. - * Bits 3 through 7 are optional flags that can be combined and give these - effects: - * bit 3 (0x08) - Makes the plant slightly vary placement horizontally - * bit 4 (0x10) - Makes the plant mesh 1.4x larger - * bit 5 (0x20) - Moves each face randomly a small bit down (1/8 max) - * bits 6-7 are reserved for future use. + * Bits 3 to 7 are used to enable any number of optional modifiers. + Just add the corresponding value(s) below to `param2`: + * 8 - Makes the plant slightly vary placement horizontally + * 16 - Makes the plant mesh 1.4x larger + * 32 - Moves each face randomly a small bit down (1/8 max) + * values 64 and 128 (bits 6-7) are reserved for future use. + * Example: `param2 = 0` selects a normal "x" shaped plant + * Example: `param2 = 17` selects a "+" shaped plant, 1.4x larger (1+16) * `paramtype2 = "color"` * `param2` tells which color is picked from the palette. The palette should have 256 pixels. @@ -1054,7 +1069,7 @@ Node drawtypes There are a bunch of different looking node types. -Look for examples in `games/minimal` or `games/minetest_game`. +Look for examples in `games/devtest` or `games/minetest_game`. * `normal` * A node-sized cube. @@ -1278,9 +1293,9 @@ To account for differing resolutions, the position coordinates are the percentage of the screen, ranging in value from `0` to `1`. The name field is not yet used, but should contain a description of what the -HUD element represents. The direction field is the direction in which something -is drawn. +HUD element represents. +The `direction` field is the direction in which something is drawn. `0` draws from left to right, `1` draws from right to left, `2` draws from top to bottom, and `3` draws from bottom to top. @@ -1299,7 +1314,21 @@ factor! The `z_index` field specifies the order of HUD elements from back to front. Lower z-index elements are displayed behind higher z-index elements. Elements with same z-index are displayed in an arbitrary order. Default 0. -Supports negative values. +Supports negative values. By convention, the following values are recommended: + +* -400: Graphical effects, such as vignette +* -300: Name tags, waypoints +* -200: Wieldhand +* -100: Things that block the player's view, e.g. masks +* 0: Default. For standard in-game HUD elements like crosshair, hotbar, + minimap, builtin statbars, etc. +* 100: Temporary text messages or notification icons +* 1000: Full-screen effects such as full-black screen or credits. + This includes effects that cover the entire screen +* Other: If your HUD element doesn't fit into any category, pick a number + between the suggested values + + Below are the specific uses for fields in each type; fields not listed for that type are ignored. @@ -1327,15 +1356,21 @@ Displays text on the HUD. text. Specify `0xFFFFFF` for white text, `0xFF0000` for red, and so on. * `alignment`: The alignment of the text. * `offset`: offset in pixels from position. +* `size`: size of the text. + The player-set font size is multiplied by size.x (y value isn't used). ### `statbar` -Displays a horizontal bar made up of half-images. +Displays a horizontal bar made up of half-images with an optional background. -* `text`: The name of the texture that is used. +* `text`: The name of the texture to use. +* `text2`: Optional texture name to enable a background / "off state" + texture (useful to visualize the maximal value). Both textures + must have the same size. * `number`: The number of half-textures that are displayed. If odd, will end with a vertically center-split texture. -* `direction` +* `item`: Same as `number` but for the "off state" texture +* `direction`: To which direction the images will extend to * `offset`: offset in pixels from position. * `size`: If used, will force full-image size to this value (override texture pack image size) @@ -1354,10 +1389,30 @@ Displays distance to selected world position. * `name`: The name of the waypoint. * `text`: Distance suffix. Can be blank. +* `precision`: Waypoint precision, integer >= 0. Defaults to 10. + If set to 0, distance is not shown. Shown value is `floor(distance*precision)/precision`. + When the precision is an integer multiple of 10, there will be `log_10(precision)` digits after the decimal point. + `precision = 1000`, for example, will show 3 decimal places (eg: `0.999`). + `precision = 2` will show multiples of `0.5`; precision = 5 will show multiples of `0.2` and so on: + `precision = n` will show multiples of `1/n` * `number:` An integer containing the RGB value of the color used to draw the text. * `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. +* `alignment`: The alignment of the waypoint. +### `image_waypoint` + +Same as `image`, but does not accept a `position`; the position is instead determined by `world_pos`, the world position of the waypoint. + +* `scale`: The scale of the image, with 1 being the original texture size. + Only the X coordinate scale is used (positive values). + Negative values represent that percentage of the screen it + should take; e.g. `x=-100` means 100% (width). +* `text`: The name of the texture that is displayed. +* `alignment`: The alignment of the image. +* `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. @@ -1611,6 +1666,7 @@ to games. * `2`: the node always gets the digging time 0.5 seconds (rail, sign) * `3`: the node always gets the digging time 0 seconds (torch) * `disable_jump`: Player (and possibly other things) cannot jump from node + or if their feet are in the node. Note: not supported for `new_move = false` * `fall_damage_add_percent`: damage speed = `speed * (1 + value/100)` * `falling_node`: if there is no walkable block under the node it will fall * `float`: the node will not fall through liquids @@ -2071,6 +2127,26 @@ Elements * End of a container, following elements are no longer relative to this container. +### `scroll_container[,;,;;;]` + +* Start of a scroll_container block. All contained elements will ... + * take the scroll_container coordinate as position origin, + * be additionally moved by the current value of the scrollbar with the name + `scrollbar name` times `scroll factor` along the orientation `orientation` and + * be clipped to the rectangle defined by `X`, `Y`, `W` and `H`. +* `orientation`: possible values are `vertical` and `horizontal`. +* `scroll factor`: optional, defaults to `0.1`. +* Nesting is possible. +* Some elements might work a little different if they are in a scroll_container. +* Note: If you want the scroll_container to actually work, you also need to add a + scrollbar element with the specified name. Furthermore, it is highly recommended + to use a scrollbaroptions element on this scrollbar. + +### `scroll_container_end[]` + +* End of a scroll_container, following elements are no longer bound to this + container. + ### `list[;;,;,;]` * Show an inventory list if it has been sent to the client. Nothing will @@ -2178,12 +2254,12 @@ Elements * 9-sliced background. See https://en.wikipedia.org/wiki/9-slice_scaling * Middle is a rect which defines the middle of the 9-slice. - * `x` - The middle will be x pixels from all sides. - * `x,y` - The middle will be x pixels from the horizontal and y from the vertical. - * `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values - will be added to the width and height of the texture, allowing it to be used as the - distance from the far end. - * All numbers in middle are integers. + * `x` - The middle will be x pixels from all sides. + * `x,y` - The middle will be x pixels from the horizontal and y from the vertical. + * `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values + will be added to the width and height of the texture, allowing it to be used as the + distance from the far end. + * All numbers in middle are integers. * Example for formspec 8x4 in 16x resolution: image shall be sized 8 times 16px times 4 times 16px * If `auto_clip` is `true`, the background is clipped to the formspec size @@ -2332,8 +2408,8 @@ Elements * `name` fieldname data is transferred to Lua * `caption 1`...: name shown on top of tab * `current_tab`: index of selected tab 1... -* `transparent` (optional): show transparent -* `draw_border` (optional): draw border +* `transparent` (optional): if true, tabs are semi-transparent +* `draw_border` (optional): if true, draw a thin line at tab base ### `tabheader[,;;;,,...,;;;]` @@ -2367,7 +2443,7 @@ Elements * `color` is color specified as a `ColorString`. If the alpha component is left blank, the box will be semitransparent. -### `dropdown[,;;;,, ...,;]` +### `dropdown[,;;;,, ...,;;]` * Show a dropdown field * **Important note**: There are two different operation modes: @@ -2378,8 +2454,12 @@ Elements * Fieldname data is transferred to Lua * Items to be shown in dropdown * Index of currently selected dropdown item +* `index event` (optional, allowed parameter since formspec version 4): Specifies the + event field value for selected items. + * `true`: Selected item index + * `false` (default): Selected item value -### `dropdown[,;,;;,, ...,;]` +### `dropdown[,;,;;,, ...,;;]` * Show a dropdown field * **Important note**: This syntax for dropdowns can only be used with the @@ -2392,6 +2472,10 @@ Elements * Fieldname data is transferred to Lua * Items to be shown in dropdown * Index of currently selected dropdown item +* `index event` (optional, allowed parameter since formspec version 4): Specifies the + event field value for selected items. + * `true`: Selected item index + * `false` (default): Selected item value ### `checkbox[,;;