Merge branch 'master' of https://github.com/minetest/minetest
commit
21df26984d
|
@ -1,6 +1,7 @@
|
||||||
BasedOnStyle: LLVM
|
BasedOnStyle: LLVM
|
||||||
IndentWidth: 8
|
IndentWidth: 4
|
||||||
UseTab: Always
|
UseTab: Always
|
||||||
|
TabWidth: 4
|
||||||
BreakBeforeBraces: Custom
|
BreakBeforeBraces: Custom
|
||||||
Standard: Cpp11
|
Standard: Cpp11
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
|
@ -16,7 +17,7 @@ BraceWrapping:
|
||||||
FixNamespaceComments: false
|
FixNamespaceComments: false
|
||||||
AllowShortIfStatementsOnASingleLine: false
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
IndentCaseLabels: false
|
IndentCaseLabels: false
|
||||||
AccessModifierOffset: -8
|
AccessModifierOffset: -4
|
||||||
ColumnLimit: 90
|
ColumnLimit: 90
|
||||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||||
SortIncludes: false
|
SortIncludes: false
|
||||||
|
@ -26,7 +27,7 @@ IncludeCategories:
|
||||||
- Regex: '^<.*'
|
- Regex: '^<.*'
|
||||||
Priority: 1
|
Priority: 1
|
||||||
AlignAfterOpenBracket: DontAlign
|
AlignAfterOpenBracket: DontAlign
|
||||||
ContinuationIndentWidth: 16
|
ContinuationIndentWidth: 8
|
||||||
ConstructorInitializerIndentWidth: 16
|
ConstructorInitializerIndentWidth: 8
|
||||||
BreakConstructorInitializers: AfterColon
|
BreakConstructorInitializers: AfterColon
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
|
|
@ -21,24 +21,22 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up JDK 1.8
|
|
||||||
uses: actions/setup-java@v1
|
|
||||||
with:
|
|
||||||
java-version: 1.8
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: sudo apt-get update; sudo apt-get install -y --no-install-recommends gettext
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y --no-install-recommends gettext openjdk-11-jdk-headless
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: cd android; ./gradlew assemblerelease
|
run: cd android; ./gradlew assemblerelease
|
||||||
- name: Save armeabi artifact
|
- name: Save armeabi artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Minetest-armeabi-v7a.apk
|
name: Minetest-armeabi-v7a.apk
|
||||||
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
||||||
- name: Save arm64 artifact
|
- name: Save arm64 artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Minetest-arm64-v8a.apk
|
name: Minetest-arm64-v8a.apk
|
||||||
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
||||||
|
|
|
@ -30,53 +30,53 @@ on:
|
||||||
- '.dockerignore'
|
- '.dockerignore'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# This is our minor gcc compiler
|
# Older gcc version (should be close to our minimum supported version)
|
||||||
gcc_6:
|
gcc_5:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps g++-6
|
install_linux_deps g++-5
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./util/ci/build.sh
|
./util/ci/build.sh
|
||||||
env:
|
env:
|
||||||
CC: gcc-6
|
CC: gcc-5
|
||||||
CXX: g++-6
|
CXX: g++-5
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
./bin/minetest --run-unittests
|
./bin/minetest --run-unittests
|
||||||
|
|
||||||
# This is the current gcc compiler (available in bionic)
|
# Current gcc version
|
||||||
gcc_8:
|
gcc_10:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps g++-8
|
install_linux_deps g++-10
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./util/ci/build.sh
|
./util/ci/build.sh
|
||||||
env:
|
env:
|
||||||
CC: gcc-8
|
CC: gcc-10
|
||||||
CXX: g++-8
|
CXX: g++-10
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
./bin/minetest --run-unittests
|
./bin/minetest --run-unittests
|
||||||
|
|
||||||
# This is our minor clang compiler
|
# Older clang version (should be close to our minimum supported version)
|
||||||
clang_3_9:
|
clang_3_9:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
|
@ -93,26 +93,26 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./bin/minetest --run-unittests
|
./bin/minetest --run-unittests
|
||||||
|
|
||||||
- name: Integration test
|
- name: Integration test + devtest
|
||||||
run: |
|
run: |
|
||||||
./util/test_multiplayer.sh
|
./util/test_multiplayer.sh
|
||||||
|
|
||||||
# This is the current clang version
|
# Current clang version
|
||||||
clang_9:
|
clang_10:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps clang-9 valgrind libluajit-5.1-dev
|
install_linux_deps clang-10 valgrind libluajit-5.1-dev
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./util/ci/build.sh
|
./util/ci/build.sh
|
||||||
env:
|
env:
|
||||||
CC: clang-9
|
CC: clang-10
|
||||||
CXX: clang++-9
|
CXX: clang++-10
|
||||||
CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
|
CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
|
@ -126,9 +126,9 @@ jobs:
|
||||||
# Build with prometheus-cpp (server-only)
|
# Build with prometheus-cpp (server-only)
|
||||||
clang_9_prometheus:
|
clang_9_prometheus:
|
||||||
name: "clang_9 (PROMETHEUS=1)"
|
name: "clang_9 (PROMETHEUS=1)"
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
|
@ -150,34 +150,11 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./bin/minetestserver --run-unittests
|
./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: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps clang-9
|
|
||||||
|
|
||||||
- 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:
|
docker:
|
||||||
name: "Docker image"
|
name: "Docker image"
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Build docker image
|
- name: Build docker image
|
||||||
run: |
|
run: |
|
||||||
docker build . -t minetest:latest
|
docker build . -t minetest:latest
|
||||||
|
@ -185,13 +162,13 @@ jobs:
|
||||||
|
|
||||||
win32:
|
win32:
|
||||||
name: "MinGW cross-compiler (32-bit)"
|
name: "MinGW cross-compiler (32-bit)"
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install compiler
|
- name: Install compiler
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -q && sudo apt-get install gettext -qyy
|
sudo apt-get update && sudo apt-get install -y gettext
|
||||||
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
||||||
sudo tar -xaf mingw.tar.xz -C /usr
|
sudo tar -xaf mingw.tar.xz -C /usr
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@ -203,13 +180,13 @@ jobs:
|
||||||
|
|
||||||
win64:
|
win64:
|
||||||
name: "MinGW cross-compiler (64-bit)"
|
name: "MinGW cross-compiler (64-bit)"
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install compiler
|
- name: Install compiler
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -q && sudo apt-get install gettext -qyy
|
sudo apt-get update && sudo apt-get install -y gettext
|
||||||
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
||||||
sudo tar -xaf mingw.tar.xz -C /usr
|
sudo tar -xaf mingw.tar.xz -C /usr
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@ -222,13 +199,10 @@ jobs:
|
||||||
msvc:
|
msvc:
|
||||||
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
#### Disabled due to Irrlicht switch
|
|
||||||
if: false
|
|
||||||
#### Disabled due to Irrlicht switch
|
|
||||||
env:
|
env:
|
||||||
VCPKG_VERSION: 0bf3923f9fab4001c00f0f429682a0853b5749e0
|
VCPKG_VERSION: 5cf60186a241e84e8232641ee973395d4fde90e1
|
||||||
# 2020.11
|
# 2022.02
|
||||||
vcpkg_packages: irrlicht zlib zstd curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit
|
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -249,11 +223,17 @@ jobs:
|
||||||
# Enable it, when working on the installer.
|
# Enable it, when working on the installer.
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- uses: actions/checkout@v3
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
- name: Checkout IrrlichtMt
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: minetest/irrlicht
|
||||||
|
path: lib/irrlichtmt/
|
||||||
|
ref: "1.9.0mt5"
|
||||||
|
|
||||||
- name: Restore from cache and run vcpkg
|
- name: Restore from cache and run vcpkg
|
||||||
uses: lukka/run-vcpkg@v5
|
uses: lukka/run-vcpkg@v7
|
||||||
with:
|
with:
|
||||||
vcpkgArguments: ${{env.vcpkg_packages}}
|
vcpkgArguments: ${{env.vcpkg_packages}}
|
||||||
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
||||||
|
@ -261,7 +241,7 @@ jobs:
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
||||||
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
||||||
|
|
||||||
- name: CMake
|
- name: Minetest CMake
|
||||||
run: |
|
run: |
|
||||||
cmake ${{matrix.config.generator}} `
|
cmake ${{matrix.config.generator}} `
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
||||||
|
@ -269,7 +249,7 @@ jobs:
|
||||||
-DENABLE_POSTGRESQL=OFF `
|
-DENABLE_POSTGRESQL=OFF `
|
||||||
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
||||||
|
|
||||||
- name: Build
|
- name: Build Minetest
|
||||||
run: cmake --build . --config Release
|
run: cmake --build . --config Release
|
||||||
|
|
||||||
- name: CPack
|
- name: CPack
|
||||||
|
@ -288,7 +268,7 @@ jobs:
|
||||||
- name: Package Clean
|
- name: Package Clean
|
||||||
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v1
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
||||||
path: .\Package\
|
path: .\Package\
|
||||||
|
|
|
@ -26,12 +26,13 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
# clang_format:
|
# clang_format:
|
||||||
# runs-on: ubuntu-18.04
|
# runs-on: ubuntu-20.04
|
||||||
# steps:
|
# steps:
|
||||||
# - uses: actions/checkout@v2
|
# - uses: actions/checkout@v3
|
||||||
# - name: Install clang-format
|
# - name: Install clang-format
|
||||||
# run: |
|
# run: |
|
||||||
# sudo apt-get install clang-format-9 -qyy
|
# sudo apt-get update
|
||||||
|
# sudo apt-get install -y clang-format-9
|
||||||
#
|
#
|
||||||
# - name: Run clang-format
|
# - name: Run clang-format
|
||||||
# run: |
|
# run: |
|
||||||
|
@ -41,14 +42,13 @@ jobs:
|
||||||
# CLANG_FORMAT: clang-format-9
|
# CLANG_FORMAT: clang-format-9
|
||||||
|
|
||||||
clang_tidy:
|
clang_tidy:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install clang-tidy-9 -qyy
|
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps
|
install_linux_deps clang-tidy-9
|
||||||
|
|
||||||
- name: Run clang-tidy
|
- name: Run clang-tidy
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
name: lua_lint
|
||||||
|
|
||||||
|
# Lint on lua changes on builtin or if workflow changed
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'builtin/**.lua'
|
||||||
|
- 'games/devtest/**.lua'
|
||||||
|
- '.github/workflows/**.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'builtin/**.lua'
|
||||||
|
- 'games/devtest/**.lua'
|
||||||
|
- '.github/workflows/**.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Note that the integration tests are also run build.yml, but only when C++ code is changed.
|
||||||
|
integration_tests:
|
||||||
|
name: "Compile and run multiplayer tests"
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps clang-10 gdb libluajit-5.1-dev
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: clang-10
|
||||||
|
CXX: clang++-10
|
||||||
|
CMAKE_FLAGS: "-DENABLE_GETTEXT=0 -DBUILD_SERVER=0"
|
||||||
|
|
||||||
|
- name: Integration test + devtest
|
||||||
|
run: |
|
||||||
|
./util/test_multiplayer.sh
|
||||||
|
|
||||||
|
luacheck:
|
||||||
|
name: "Builtin Luacheck and Unit Tests"
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install luarocks
|
||||||
|
run: |
|
||||||
|
sudo apt-get update && sudo apt-get install -y luarocks
|
||||||
|
|
||||||
|
- name: Install luarocks tools
|
||||||
|
run: |
|
||||||
|
luarocks install --local luacheck
|
||||||
|
luarocks install --local busted
|
||||||
|
|
||||||
|
- name: Run checks (builtin)
|
||||||
|
run: |
|
||||||
|
$HOME/.luarocks/bin/luacheck builtin
|
||||||
|
$HOME/.luarocks/bin/busted builtin
|
||||||
|
|
||||||
|
- name: Run checks (devtest)
|
||||||
|
run: |
|
||||||
|
$HOME/.luarocks/bin/luacheck --config=games/devtest/.luacheckrc games/devtest
|
|
@ -1,32 +0,0 @@
|
||||||
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
|
|
|
@ -22,7 +22,7 @@ on:
|
||||||
- '.github/workflows/macos.yml'
|
- '.github/workflows/macos.yml'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IRRLICHT_TAG: 1.9.0mt3
|
IRRLICHT_TAG: 1.9.0mt5
|
||||||
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
|
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
|
||||||
MINETEST_GAME_BRANCH: master
|
MINETEST_GAME_BRANCH: master
|
||||||
MINETEST_GAME_NAME: minetest_game
|
MINETEST_GAME_NAME: minetest_game
|
||||||
|
@ -31,7 +31,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macos-10.15
|
runs-on: macos-10.15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
pkgs=(cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd)
|
pkgs=(cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd)
|
||||||
|
@ -45,8 +45,8 @@ jobs:
|
||||||
git clone -b $MINETEST_GAME_BRANCH $MINETEST_GAME_REPO games/$MINETEST_GAME_NAME
|
git clone -b $MINETEST_GAME_BRANCH $MINETEST_GAME_REPO games/$MINETEST_GAME_NAME
|
||||||
rm -rvf games/$MINETEST_GAME_NAME/.git
|
rm -rvf games/$MINETEST_GAME_NAME/.git
|
||||||
git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
|
git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
|
||||||
mkdir cmakebuild
|
mkdir build
|
||||||
cd cmakebuild
|
cd build
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
||||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||||
|
@ -60,7 +60,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./build/macos/minetest.app/Contents/MacOS/minetest --run-unittests
|
./build/macos/minetest.app/Contents/MacOS/minetest --run-unittests
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: minetest-macos
|
name: minetest-macos
|
||||||
path: ./build/macos/
|
path: ./build/macos/
|
||||||
|
|
|
@ -107,6 +107,13 @@ CMakeDoxy*
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
*.apk
|
*.apk
|
||||||
*.zip
|
*.zip
|
||||||
|
# Visual Studio
|
||||||
|
*.vcxproj*
|
||||||
|
*.sln
|
||||||
|
.vs/
|
||||||
|
|
||||||
# Optional user provided library folder
|
# Optional user provided library folder
|
||||||
lib/irrlichtmt
|
lib/irrlichtmt
|
||||||
|
|
||||||
|
# Generated mod storage database
|
||||||
|
client/mod_storage.sqlite
|
||||||
|
|
|
@ -9,7 +9,7 @@ stages:
|
||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
IRRLICHT_TAG: "1.9.0mt3"
|
IRRLICHT_TAG: "1.9.0mt5"
|
||||||
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
|
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
|
||||||
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
|
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
|
||||||
|
|
||||||
|
@ -20,11 +20,9 @@ variables:
|
||||||
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential git cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libleveldb-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev
|
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential git cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libleveldb-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev
|
||||||
script:
|
script:
|
||||||
- git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
|
- git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
|
||||||
- mkdir cmakebuild
|
- cmake -B build -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
||||||
- cd cmakebuild
|
- cmake --build build --parallel $(($(nproc) + 1))
|
||||||
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
- cmake --install build
|
||||||
- make -j2
|
|
||||||
- make install
|
|
||||||
artifacts:
|
artifacts:
|
||||||
when: on_success
|
when: on_success
|
||||||
expire_in: 1h
|
expire_in: 1h
|
||||||
|
@ -198,25 +196,12 @@ build:fedora-28:
|
||||||
before_script:
|
before_script:
|
||||||
- apt-get update
|
- apt-get update
|
||||||
- DEBIAN_FRONTEND=noninteractive apt-get install -y wget xz-utils unzip git cmake gettext
|
- DEBIAN_FRONTEND=noninteractive apt-get install -y wget xz-utils unzip git cmake gettext
|
||||||
- wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
- wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
||||||
- tar -xaf mingw.tar.xz -C /usr
|
- tar -xaf mingw.tar.xz -C /usr
|
||||||
|
|
||||||
.build_win_template:
|
.build_win_template:
|
||||||
extends: .generic_win_template
|
extends: .generic_win_template
|
||||||
stage: build
|
stage: build
|
||||||
artifacts:
|
|
||||||
expire_in: 1h
|
|
||||||
paths:
|
|
||||||
- build/build/*.zip
|
|
||||||
|
|
||||||
.package_win_template:
|
|
||||||
extends: .generic_win_template
|
|
||||||
stage: package
|
|
||||||
script:
|
|
||||||
- unzip build/build/*.zip
|
|
||||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-*-win*/bin/
|
|
||||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-*-win*/bin/
|
|
||||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-*-win*/bin/
|
|
||||||
artifacts:
|
artifacts:
|
||||||
expire_in: 90 day
|
expire_in: 90 day
|
||||||
paths:
|
paths:
|
||||||
|
@ -226,28 +211,15 @@ build:win32:
|
||||||
extends: .build_win_template
|
extends: .build_win_template
|
||||||
script:
|
script:
|
||||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh build
|
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh build
|
||||||
|
- unzip -q build/build/*.zip
|
||||||
variables:
|
variables:
|
||||||
WIN_ARCH: "i686"
|
WIN_ARCH: "i686"
|
||||||
|
|
||||||
package:win32:
|
|
||||||
extends: .package_win_template
|
|
||||||
needs:
|
|
||||||
- build:win32
|
|
||||||
variables:
|
|
||||||
WIN_ARCH: "i686"
|
|
||||||
|
|
||||||
|
|
||||||
build:win64:
|
build:win64:
|
||||||
extends: .build_win_template
|
extends: .build_win_template
|
||||||
script:
|
script:
|
||||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh build
|
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh build
|
||||||
variables:
|
- unzip -q build/build/*.zip
|
||||||
WIN_ARCH: "x86_64"
|
|
||||||
|
|
||||||
package:win64:
|
|
||||||
extends: .package_win_template
|
|
||||||
needs:
|
|
||||||
- build:win64
|
|
||||||
variables:
|
variables:
|
||||||
WIN_ARCH: "x86_64"
|
WIN_ARCH: "x86_64"
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,14 @@ endif()
|
||||||
project(minetest)
|
project(minetest)
|
||||||
set(PROJECT_NAME_CAPITALIZED "Dragonfire")
|
set(PROJECT_NAME_CAPITALIZED "Dragonfire")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
set(GCC_MINIMUM_VERSION "4.8")
|
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||||
set(CLANG_MINIMUM_VERSION "3.4")
|
set(GCC_MINIMUM_VERSION "5.1")
|
||||||
|
set(CLANG_MINIMUM_VERSION "3.5")
|
||||||
|
|
||||||
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
|
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
|
||||||
set(VERSION_MAJOR 5)
|
set(VERSION_MAJOR 5)
|
||||||
set(VERSION_MINOR 5)
|
set(VERSION_MINOR 6)
|
||||||
set(VERSION_PATCH 0)
|
set(VERSION_PATCH 0)
|
||||||
set(VERSION_EXTRA "dragonfire" CACHE STRING "Stuff to append to version string")
|
set(VERSION_EXTRA "dragonfire" CACHE STRING "Stuff to append to version string")
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ set(DEVELOPMENT_BUILD FALSE)
|
||||||
|
|
||||||
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||||
if(VERSION_EXTRA)
|
if(VERSION_EXTRA)
|
||||||
set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
|
set(VERSION_STRING "${VERSION_STRING}-${VERSION_EXTRA}")
|
||||||
elseif(DEVELOPMENT_BUILD)
|
elseif(DEVELOPMENT_BUILD)
|
||||||
set(VERSION_STRING "${VERSION_STRING}-dev")
|
set(VERSION_STRING "${VERSION_STRING}-dev")
|
||||||
endif()
|
endif()
|
||||||
|
@ -51,7 +52,7 @@ set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL
|
||||||
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
||||||
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
|
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
|
||||||
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
||||||
|
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
|
||||||
|
|
||||||
set(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build")
|
set(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build")
|
||||||
|
|
||||||
|
@ -64,8 +65,21 @@ endif()
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||||
|
|
||||||
|
|
||||||
|
set(IRRLICHTMT_BUILD_DIR "" CACHE PATH "Path to IrrlichtMt build directory.")
|
||||||
|
if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
|
||||||
|
find_package(IrrlichtMt QUIET
|
||||||
|
PATHS "${IRRLICHTMT_BUILD_DIR}"
|
||||||
|
NO_DEFAULT_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT TARGET IrrlichtMt::IrrlichtMt)
|
||||||
|
# find_package() searches certain subdirectories. ${PATH}/cmake is not
|
||||||
|
# the only one, but it is the one where IrrlichtMt is supposed to export
|
||||||
|
# IrrlichtMtConfig.cmake
|
||||||
|
message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.")
|
||||||
|
endif()
|
||||||
# This is done here so that relative search paths are more reasonable
|
# This is done here so that relative search paths are more reasonable
|
||||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
|
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
|
||||||
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
|
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
|
||||||
if(BUILD_CLIENT)
|
if(BUILD_CLIENT)
|
||||||
# tell IrrlichtMt to create a static library
|
# tell IrrlichtMt to create a static library
|
||||||
|
@ -101,11 +115,13 @@ else()
|
||||||
# Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11
|
# Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11
|
||||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${IRRLICHT_INCLUDE_DIR}")
|
INTERFACE_INCLUDE_DIRECTORIES "${IRRLICHT_INCLUDE_DIR}")
|
||||||
else()
|
|
||||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(TARGET IrrlichtMt::IrrlichtMt)
|
||||||
|
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
|
@ -135,15 +151,16 @@ elseif(UNIX) # Linux, BSD etc
|
||||||
set(ICONDIR "unix/icons")
|
set(ICONDIR "unix/icons")
|
||||||
set(LOCALEDIR "locale")
|
set(LOCALEDIR "locale")
|
||||||
else()
|
else()
|
||||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
|
include(GNUInstallDirs)
|
||||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}")
|
||||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}")
|
set(BINDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
|
||||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man")
|
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DOCDIR}")
|
||||||
|
set(MANDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_MANDIR}")
|
||||||
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
||||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications")
|
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications")
|
||||||
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/metainfo")
|
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/metainfo")
|
||||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons")
|
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons")
|
||||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/locale")
|
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -246,10 +263,10 @@ endif()
|
||||||
find_package(GMP REQUIRED)
|
find_package(GMP REQUIRED)
|
||||||
find_package(Json REQUIRED)
|
find_package(Json REQUIRED)
|
||||||
find_package(Lua REQUIRED)
|
find_package(Lua REQUIRED)
|
||||||
|
if(NOT USE_LUAJIT)
|
||||||
# JsonCpp doesn't compile well on GCC 4.8
|
set(LUA_BIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/bitop)
|
||||||
if(NOT USE_SYSTEM_JSONCPP)
|
set(LUA_BIT_LIBRARY bitop)
|
||||||
set(GCC_MINIMUM_VERSION "4.9")
|
add_subdirectory(lib/bitop)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
@ -264,9 +281,12 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_BENCHMARKS)
|
||||||
|
add_subdirectory(lib/catch2)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Subdirectories
|
# Subdirectories
|
||||||
# Be sure to add all relevant definitions above this
|
# Be sure to add all relevant definitions above this
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
|
||||||
|
|
17
Dockerfile
17
Dockerfile
|
@ -27,23 +27,20 @@ RUN apk add --no-cache git build-base cmake sqlite-dev curl-dev zlib-dev zstd-de
|
||||||
|
|
||||||
WORKDIR /usr/src/
|
WORKDIR /usr/src/
|
||||||
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
||||||
mkdir prometheus-cpp/build && \
|
cd prometheus-cpp && \
|
||||||
cd prometheus-cpp/build && \
|
cmake -B build \
|
||||||
cmake .. \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DENABLE_TESTING=0 \
|
-DENABLE_TESTING=0 \
|
||||||
-GNinja && \
|
-GNinja && \
|
||||||
ninja && \
|
cmake --build build && \
|
||||||
ninja install
|
cmake --install build
|
||||||
|
|
||||||
RUN git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
|
RUN git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
|
||||||
cp -r irrlicht/include /usr/include/irrlichtmt
|
cp -r irrlicht/include /usr/include/irrlichtmt
|
||||||
|
|
||||||
WORKDIR /usr/src/minetest
|
WORKDIR /usr/src/minetest
|
||||||
RUN mkdir build && \
|
RUN cmake -B build \
|
||||||
cd build && \
|
|
||||||
cmake .. \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DBUILD_SERVER=TRUE \
|
-DBUILD_SERVER=TRUE \
|
||||||
|
@ -51,8 +48,8 @@ RUN mkdir build && \
|
||||||
-DBUILD_UNITTESTS=FALSE \
|
-DBUILD_UNITTESTS=FALSE \
|
||||||
-DBUILD_CLIENT=FALSE \
|
-DBUILD_CLIENT=FALSE \
|
||||||
-GNinja && \
|
-GNinja && \
|
||||||
ninja && \
|
cmake --build build && \
|
||||||
ninja install
|
cmake --install build
|
||||||
|
|
||||||
ARG DOCKER_IMAGE=alpine:3.14
|
ARG DOCKER_IMAGE=alpine:3.14
|
||||||
FROM $DOCKER_IMAGE AS runtime
|
FROM $DOCKER_IMAGE AS runtime
|
||||||
|
|
97
README.md
97
README.md
|
@ -7,7 +7,7 @@ Minetest
|
||||||
|
|
||||||
Minetest is a free open-source voxel game engine with easy modding and game creation.
|
Minetest is a free open-source voxel game engine with easy modding and game creation.
|
||||||
|
|
||||||
Copyright (C) 2010-2020 Perttu Ahola <celeron55@gmail.com>
|
Copyright (C) 2010-2022 Perttu Ahola <celeron55@gmail.com>
|
||||||
and contributors (see source file comments and the version control log)
|
and contributors (see source file comments and the version control log)
|
||||||
|
|
||||||
In case you downloaded the source code
|
In case you downloaded the source code
|
||||||
|
@ -132,10 +132,11 @@ Compiling
|
||||||
|
|
||||||
| Dependency | Version | Commentary |
|
| Dependency | Version | Commentary |
|
||||||
|------------|---------|------------|
|
|------------|---------|------------|
|
||||||
| GCC | 4.9+ | Can be replaced with Clang 3.4+ |
|
| GCC | 5.1+ | or Clang 3.5+ |
|
||||||
| CMake | 3.5+ | |
|
| CMake | 3.5+ | |
|
||||||
| IrrlichtMt | - | Custom version of Irrlicht, see https://github.com/minetest/irrlicht |
|
| IrrlichtMt | - | Custom version of Irrlicht, see https://github.com/minetest/irrlicht |
|
||||||
| SQLite3 | 3.0+ | |
|
| Freetype | 2.0+ | |
|
||||||
|
| SQLite3 | 3+ | |
|
||||||
| Zstd | 1.0+ | |
|
| Zstd | 1.0+ | |
|
||||||
| LuaJIT | 2.0+ | Bundled Lua 5.1 is used if not present |
|
| LuaJIT | 2.0+ | Bundled Lua 5.1 is used if not present |
|
||||||
| GMP | 5.0.0+ | Bundled mini-GMP is used if not present |
|
| GMP | 5.0.0+ | Bundled mini-GMP is used if not present |
|
||||||
|
@ -143,12 +144,12 @@ Compiling
|
||||||
|
|
||||||
For Debian/Ubuntu users:
|
For Debian/Ubuntu users:
|
||||||
|
|
||||||
sudo apt install g++ make libc6-dev cmake 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 libzstd-dev
|
sudo apt install g++ make libc6-dev cmake 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 libzstd-dev libluajit-5.1-dev
|
||||||
|
|
||||||
For Fedora users:
|
For Fedora users:
|
||||||
|
|
||||||
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel
|
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel
|
||||||
|
|
||||||
For Arch users:
|
For Arch users:
|
||||||
|
|
||||||
sudo pacman -S base-devel libcurl-gnutls cmake libxxf86vm libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd
|
sudo pacman -S base-devel libcurl-gnutls cmake libxxf86vm libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd
|
||||||
|
@ -224,10 +225,13 @@ Run it:
|
||||||
- Debug build is slower, but gives much more useful output in a debugger.
|
- Debug build is slower, but gives much more useful output in a debugger.
|
||||||
- If you build a bare server you don't need to have the Irrlicht or IrrlichtMt library installed.
|
- If you build a bare server you don't need to have the Irrlicht or IrrlichtMt library installed.
|
||||||
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlicht/include`.
|
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlicht/include`.
|
||||||
- IrrlichtMt can also be installed somewhere that is not a standard install path.
|
|
||||||
- In that case use `-DCMAKE_PREFIX_PATH=/path/to/install_prefix`
|
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
|
||||||
- The path must be set so that `$(CMAKE_PREFIX_PATH)/lib/cmake/IrrlichtMt` exists
|
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
|
||||||
or that `$(CMAKE_PREFIX_PATH)` is the path of an IrrlichtMt build folder.
|
2. `${PROJECT_SOURCE_DIR}/lib/irrlichtmt` (if existent)
|
||||||
|
3. Installation of IrrlichtMt in the system-specific library paths
|
||||||
|
4. For server builds with disabled `BUILD_CLIENT` variable, the headers from `IRRLICHT_INCLUDE_DIR` will be used.
|
||||||
|
- NOTE: Changing the IrrlichtMt build directory (includes system installs) requires regenerating the CMake cache (`rm CMakeCache.txt`)
|
||||||
|
|
||||||
### CMake options
|
### CMake options
|
||||||
|
|
||||||
|
@ -236,6 +240,7 @@ General options and their default values:
|
||||||
BUILD_CLIENT=TRUE - Build Minetest client
|
BUILD_CLIENT=TRUE - Build Minetest client
|
||||||
BUILD_SERVER=FALSE - Build Minetest server
|
BUILD_SERVER=FALSE - Build Minetest server
|
||||||
BUILD_UNITTESTS=TRUE - Build unittest sources
|
BUILD_UNITTESTS=TRUE - Build unittest sources
|
||||||
|
BUILD_BENCHMARKS=FALSE - Build benchmark sources
|
||||||
CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug)
|
CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug)
|
||||||
Release - Release build
|
Release - Release build
|
||||||
Debug - Debug build
|
Debug - Debug build
|
||||||
|
@ -244,9 +249,8 @@ General options and their default values:
|
||||||
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
|
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
|
||||||
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
|
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
|
||||||
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
|
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
|
||||||
ENABLE_FREETYPE=ON - Build with FreeType2; Allows using TTF fonts
|
|
||||||
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
|
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
|
||||||
ENABLE_GLES=OFF - Build for OpenGL ES instead of OpenGL (requires support by IrrlichtMt)
|
ENABLE_GLES=OFF - Enable extra support code for OpenGL ES (requires support by IrrlichtMt)
|
||||||
ENABLE_LEVELDB=ON - Build with LevelDB; Enables use of LevelDB map backend
|
ENABLE_LEVELDB=ON - Build with LevelDB; Enables use of LevelDB map backend
|
||||||
ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended)
|
ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended)
|
||||||
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
|
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
|
||||||
|
@ -256,10 +260,10 @@ General options and their default values:
|
||||||
ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
|
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_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
|
||||||
ENABLE_SYSTEM_JSONCPP=ON - Use JsonCPP from system
|
ENABLE_SYSTEM_JSONCPP=ON - Use JsonCPP from system
|
||||||
OPENGL_GL_PREFERENCE=LEGACY - Linux client build only; See CMake Policy CMP0072 for reference
|
|
||||||
RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory)
|
RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory)
|
||||||
USE_GPROF=FALSE - Enable profiling using GProf
|
USE_GPROF=FALSE - Enable profiling using GProf
|
||||||
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
|
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
|
||||||
|
ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt)
|
||||||
|
|
||||||
Library specific options:
|
Library specific options:
|
||||||
|
|
||||||
|
@ -268,10 +272,11 @@ Library specific options:
|
||||||
CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib
|
CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib
|
||||||
EGL_INCLUDE_DIR - Only if building with GLES; directory that contains egl.h
|
EGL_INCLUDE_DIR - Only if building with GLES; directory that contains egl.h
|
||||||
EGL_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so
|
EGL_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so
|
||||||
FREETYPE_INCLUDE_DIR_freetype2 - Only if building with FreeType 2; directory that contains an freetype directory with files such as ftimage.h in it
|
EXTRA_DLL - Only on Windows; optional paths to additional DLLs that should be packaged
|
||||||
FREETYPE_INCLUDE_DIR_ft2build - Only if building with FreeType 2; directory that contains ft2build.h
|
FREETYPE_INCLUDE_DIR_freetype2 - Directory that contains files such as ftimage.h
|
||||||
FREETYPE_LIBRARY - Only if building with FreeType 2; path to libfreetype.a/libfreetype.so/freetype.lib
|
FREETYPE_INCLUDE_DIR_ft2build - Directory that contains ft2build.h
|
||||||
FREETYPE_DLL - Only if building with FreeType 2 on Windows; path to libfreetype.dll
|
FREETYPE_LIBRARY - Path to libfreetype.a/libfreetype.so/freetype.lib
|
||||||
|
FREETYPE_DLL - Only on Windows; path to libfreetype-6.dll
|
||||||
GETTEXT_DLL - Only when building with gettext on Windows; paths to libintl + libiconv DLLs
|
GETTEXT_DLL - Only when building with gettext on Windows; paths to libintl + libiconv DLLs
|
||||||
GETTEXT_INCLUDE_DIR - Only when building with gettext; directory that contains iconv.h
|
GETTEXT_INCLUDE_DIR - Only when building with gettext; directory that contains iconv.h
|
||||||
GETTEXT_LIBRARY - Only when building with gettext on Windows; path to libintl.dll.a
|
GETTEXT_LIBRARY - Only when building with gettext on Windows; path to libintl.dll.a
|
||||||
|
@ -295,15 +300,12 @@ Library specific options:
|
||||||
OPENAL_DLL - Only if building with sound on Windows; path to OpenAL32.dll
|
OPENAL_DLL - Only if building with sound on Windows; path to OpenAL32.dll
|
||||||
OPENAL_INCLUDE_DIR - Only if building with sound; directory where al.h is located
|
OPENAL_INCLUDE_DIR - Only if building with sound; directory where al.h is located
|
||||||
OPENAL_LIBRARY - Only if building with sound; path to libopenal.a/libopenal.so/OpenAL32.lib
|
OPENAL_LIBRARY - Only if building with sound; path to libopenal.a/libopenal.so/OpenAL32.lib
|
||||||
OPENGLES2_INCLUDE_DIR - Only if building with GLES; directory that contains gl2.h
|
|
||||||
OPENGLES2_LIBRARY - Only if building with GLES; path to libGLESv2.a/libGLESv2.so
|
|
||||||
SQLITE3_INCLUDE_DIR - Directory that contains sqlite3.h
|
SQLITE3_INCLUDE_DIR - Directory that contains sqlite3.h
|
||||||
SQLITE3_LIBRARY - Path to libsqlite3.a/libsqlite3.so/sqlite3.lib
|
SQLITE3_LIBRARY - Path to libsqlite3.a/libsqlite3.so/sqlite3.lib
|
||||||
VORBISFILE_LIBRARY - Only if building with sound; path to libvorbisfile.a/libvorbisfile.so/libvorbisfile.dll.a
|
VORBISFILE_LIBRARY - Only if building with sound; path to libvorbisfile.a/libvorbisfile.so/libvorbisfile.dll.a
|
||||||
VORBIS_DLL - Only if building with sound on Windows; paths to vorbis DLLs
|
VORBIS_DLL - Only if building with sound on Windows; paths to vorbis DLLs
|
||||||
VORBIS_INCLUDE_DIR - Only if building with sound; directory that contains a directory vorbis with vorbisenc.h inside
|
VORBIS_INCLUDE_DIR - Only if building with sound; directory that contains a directory vorbis with vorbisenc.h inside
|
||||||
VORBIS_LIBRARY - Only if building with sound; path to libvorbis.a/libvorbis.so/libvorbis.dll.a
|
VORBIS_LIBRARY - Only if building with sound; path to libvorbis.a/libvorbis.so/libvorbis.dll.a
|
||||||
XXF86VM_LIBRARY - Only on Linux; path to libXXf86vm.a/libXXf86vm.so
|
|
||||||
ZLIB_DLL - Only on Windows; path to zlib1.dll
|
ZLIB_DLL - Only on Windows; path to zlib1.dll
|
||||||
ZLIB_INCLUDE_DIR - Directory that contains zlib.h
|
ZLIB_INCLUDE_DIR - Directory that contains zlib.h
|
||||||
ZLIB_LIBRARY - Path to libz.a/libz.so/zlib.lib
|
ZLIB_LIBRARY - Path to libz.a/libz.so/zlib.lib
|
||||||
|
@ -326,13 +328,12 @@ It is highly recommended to use vcpkg as package manager.
|
||||||
|
|
||||||
After you successfully built vcpkg you can easily install the required libraries:
|
After you successfully built vcpkg you can easily install the required libraries:
|
||||||
```powershell
|
```powershell
|
||||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit gmp jsoncpp --triplet x64-windows
|
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry --triplet x64-windows
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt` as described in the Linux section.
|
- **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt` as described in the Linux section.
|
||||||
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
|
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
|
||||||
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
|
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
|
||||||
- `freetype` is optional, it allows true-type font rendering.
|
|
||||||
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
|
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
|
||||||
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
|
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
|
||||||
|
|
||||||
|
@ -381,6 +382,60 @@ Build the binaries as described above, but make sure you unselect `RUN_IN_PLACE`
|
||||||
Open the generated project file with Visual Studio. Right-click **Package** and choose **Generate**.
|
Open the generated project file with Visual Studio. Right-click **Package** and choose **Generate**.
|
||||||
It may take some minutes to generate the installer.
|
It may take some minutes to generate the installer.
|
||||||
|
|
||||||
|
### Compiling on MacOS
|
||||||
|
|
||||||
|
#### Requirements
|
||||||
|
- [Homebrew](https://brew.sh/)
|
||||||
|
- [Git](https://git-scm.com/downloads)
|
||||||
|
|
||||||
|
Install dependencies with homebrew:
|
||||||
|
|
||||||
|
```
|
||||||
|
brew install cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Download
|
||||||
|
|
||||||
|
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone --depth 1 https://github.com/minetest/minetest.git
|
||||||
|
cd minetest
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
Download Minetest's fork of Irrlicht:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
|
||||||
|
cmake .. \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
||||||
|
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||||
|
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE
|
||||||
|
|
||||||
|
make -j$(sysctl -n hw.logicalcpu)
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Run
|
||||||
|
|
||||||
|
```
|
||||||
|
open ./build/macos/minetest.app
|
||||||
|
```
|
||||||
|
|
||||||
Docker
|
Docker
|
||||||
------
|
------
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 30
|
||||||
buildToolsVersion '30.0.3'
|
buildToolsVersion '30.0.3'
|
||||||
ndkVersion '22.0.7026061'
|
ndkVersion "$ndk_version"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'net.minetest.minetest'
|
applicationId 'net.minetest.minetest'
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 30
|
||||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
|
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||||
versionCode project.versionCode
|
versionCode project.versionCode
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ task prepareAssets() {
|
||||||
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
|
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
|
||||||
}
|
}
|
||||||
copy {
|
copy {
|
||||||
from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht"
|
from "../native/deps/armeabi-v7a/Irrlicht/Shaders" into "${assetsFolder}/client/shaders/Irrlicht"
|
||||||
}
|
}
|
||||||
copy {
|
copy {
|
||||||
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
|
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
|
||||||
|
@ -112,5 +112,5 @@ android.applicationVariants.all { variant ->
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':native')
|
implementation project(':native')
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@
|
||||||
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
|
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
|
||||||
android:maxAspectRatio="3.0"
|
android:maxAspectRatio="3.0"
|
||||||
android:screenOrientation="sensorLandscape"
|
android:screenOrientation="sensorLandscape"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
@ -44,7 +45,8 @@
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:maxAspectRatio="3.0"
|
android:maxAspectRatio="3.0"
|
||||||
android:screenOrientation="sensorLandscape"
|
android:screenOrientation="sensorLandscape"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
Minetest
|
|
||||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
|
||||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
|
||||||
|
|
||||||
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<String, Void, String> {
|
|
||||||
|
|
||||||
private final WeakReference<AppCompatActivity> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -171,4 +171,12 @@ public class GameActivity extends NativeActivity {
|
||||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
||||||
startActivity(browserIntent);
|
startActivity(browserIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserDataPath() {
|
||||||
|
return Utils.getUserDataDirectory(this).getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCachePath() {
|
||||||
|
return Utils.getCacheDirectory(this).getAbsolutePath();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,14 @@ import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
@ -43,11 +45,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static net.minetest.minetest.UnzipService.ACTION_FAILURE;
|
import static net.minetest.minetest.UnzipService.*;
|
||||||
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 {
|
public class MainActivity extends AppCompatActivity {
|
||||||
private final static int versionCode = BuildConfig.VERSION_CODE;
|
private final static int versionCode = BuildConfig.VERSION_CODE;
|
||||||
|
@ -56,26 +54,40 @@ public class MainActivity extends AppCompatActivity {
|
||||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||||
private static final String SETTINGS = "MinetestSettings";
|
private static final String SETTINGS = "MinetestSettings";
|
||||||
private static final String TAG_VERSION_CODE = "versionCode";
|
private static final String TAG_VERSION_CODE = "versionCode";
|
||||||
|
|
||||||
private ProgressBar mProgressBar;
|
private ProgressBar mProgressBar;
|
||||||
private TextView mTextView;
|
private TextView mTextView;
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
int progress = 0;
|
int progress = 0;
|
||||||
if (intent != null)
|
@StringRes int message = 0;
|
||||||
|
if (intent != null) {
|
||||||
progress = intent.getIntExtra(ACTION_PROGRESS, 0);
|
progress = intent.getIntExtra(ACTION_PROGRESS, 0);
|
||||||
if (progress >= 0) {
|
message = intent.getIntExtra(ACTION_PROGRESS_MESSAGE, 0);
|
||||||
if (mProgressBar != null) {
|
}
|
||||||
mProgressBar.setVisibility(View.VISIBLE);
|
|
||||||
mProgressBar.setProgress(progress);
|
if (progress == FAILURE) {
|
||||||
}
|
|
||||||
mTextView.setVisibility(View.VISIBLE);
|
|
||||||
} else if (progress == FAILURE) {
|
|
||||||
Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show();
|
Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show();
|
||||||
finish();
|
finish();
|
||||||
} else if (progress == SUCCESS)
|
} else if (progress == SUCCESS) {
|
||||||
startNative();
|
startNative();
|
||||||
|
} else {
|
||||||
|
if (mProgressBar != null) {
|
||||||
|
mProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
if (progress == INDETERMINATE) {
|
||||||
|
mProgressBar.setIndeterminate(true);
|
||||||
|
} else {
|
||||||
|
mProgressBar.setIndeterminate(false);
|
||||||
|
mProgressBar.setProgress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTextView.setVisibility(View.VISIBLE);
|
||||||
|
if (message != 0)
|
||||||
|
mTextView.setText(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,7 +100,9 @@ public class MainActivity extends AppCompatActivity {
|
||||||
mProgressBar = findViewById(R.id.progressBar);
|
mProgressBar = findViewById(R.id.progressBar);
|
||||||
mTextView = findViewById(R.id.textView);
|
mTextView = findViewById(R.id.textView);
|
||||||
sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
|
sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||||
|
Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||||
checkPermission();
|
checkPermission();
|
||||||
else
|
else
|
||||||
checkAppVersion();
|
checkAppVersion();
|
||||||
|
@ -120,6 +134,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||||
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
|
||||||
finish();
|
finish();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkAppVersion();
|
checkAppVersion();
|
||||||
|
@ -127,10 +142,27 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAppVersion() {
|
private void checkAppVersion() {
|
||||||
if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode)
|
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||||
|
Toast.makeText(this, R.string.no_external_storage, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UnzipService.getIsRunning()) {
|
||||||
|
mProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
mProgressBar.setIndeterminate(true);
|
||||||
|
mTextView.setVisibility(View.VISIBLE);
|
||||||
|
} else if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode &&
|
||||||
|
Utils.isInstallValid(this)) {
|
||||||
startNative();
|
startNative();
|
||||||
else
|
} else {
|
||||||
new CopyZipTask(this).execute(getCacheDir() + "/Minetest.zip");
|
mProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
mProgressBar.setIndeterminate(true);
|
||||||
|
mTextView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
Intent intent = new Intent(this, UnzipService.class);
|
||||||
|
startService(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startNative() {
|
private void startNative() {
|
||||||
|
|
|
@ -24,16 +24,22 @@ import android.app.IntentService;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.widget.Toast;
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
@ -42,32 +48,61 @@ import java.util.zip.ZipInputStream;
|
||||||
public class UnzipService extends IntentService {
|
public class UnzipService extends IntentService {
|
||||||
public static final String ACTION_UPDATE = "net.minetest.minetest.UPDATE";
|
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_PROGRESS = "net.minetest.minetest.PROGRESS";
|
||||||
|
public static final String ACTION_PROGRESS_MESSAGE = "net.minetest.minetest.PROGRESS_MESSAGE";
|
||||||
public static final String ACTION_FAILURE = "net.minetest.minetest.FAILURE";
|
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 SUCCESS = -1;
|
||||||
public static final int FAILURE = -2;
|
public static final int FAILURE = -2;
|
||||||
|
public static final int INDETERMINATE = -3;
|
||||||
private final int id = 1;
|
private final int id = 1;
|
||||||
private NotificationManager mNotifyManager;
|
private NotificationManager mNotifyManager;
|
||||||
private boolean isSuccess = true;
|
private boolean isSuccess = true;
|
||||||
private String failureMessage;
|
private String failureMessage;
|
||||||
|
|
||||||
|
private static boolean isRunning = false;
|
||||||
|
public static synchronized boolean getIsRunning() {
|
||||||
|
return isRunning;
|
||||||
|
}
|
||||||
|
private static synchronized void setIsRunning(boolean v) {
|
||||||
|
isRunning = v;
|
||||||
|
}
|
||||||
|
|
||||||
public UnzipService() {
|
public UnzipService() {
|
||||||
super("net.minetest.minetest.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
|
@Override
|
||||||
protected void onHandleIntent(Intent intent) {
|
protected void onHandleIntent(Intent intent) {
|
||||||
createNotification();
|
Notification.Builder notificationBuilder = createNotification();
|
||||||
unzip(intent);
|
final File zipFile = new File(getCacheDir(), "Minetest.zip");
|
||||||
|
try {
|
||||||
|
setIsRunning(true);
|
||||||
|
File userDataDirectory = Utils.getUserDataDirectory(this);
|
||||||
|
if (userDataDirectory == null) {
|
||||||
|
throw new IOException("Unable to find user data directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream in = this.getAssets().open(zipFile.getName())) {
|
||||||
|
try (OutputStream out = new FileOutputStream(zipFile)) {
|
||||||
|
int readLen;
|
||||||
|
byte[] readBuffer = new byte[16384];
|
||||||
|
while ((readLen = in.read(readBuffer)) != -1) {
|
||||||
|
out.write(readBuffer, 0, readLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate(notificationBuilder, userDataDirectory);
|
||||||
|
unzip(notificationBuilder, zipFile, userDataDirectory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
isSuccess = false;
|
||||||
|
failureMessage = e.getLocalizedMessage();
|
||||||
|
} finally {
|
||||||
|
setIsRunning(false);
|
||||||
|
zipFile.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNotification() {
|
private Notification.Builder createNotification() {
|
||||||
String name = "net.minetest.minetest";
|
String name = "net.minetest.minetest";
|
||||||
String channelId = "Minetest channel";
|
String channelId = "Minetest channel";
|
||||||
String description = "notifications from Minetest";
|
String description = "notifications from Minetest";
|
||||||
|
@ -92,66 +127,133 @@ public class UnzipService extends IntentService {
|
||||||
} else {
|
} else {
|
||||||
builder = new Notification.Builder(this);
|
builder = new Notification.Builder(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||||
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
|
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
|
PendingIntent intent = PendingIntent.getActivity(this, 0,
|
||||||
|
notificationIntent, 0);
|
||||||
|
|
||||||
builder.setContentTitle(getString(R.string.notification_title))
|
builder.setContentTitle(getString(R.string.notification_title))
|
||||||
.setSmallIcon(R.mipmap.ic_launcher)
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
.setContentText(getString(R.string.notification_description));
|
.setContentText(getString(R.string.notification_description))
|
||||||
|
.setContentIntent(intent)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setProgress(0, 0, true);
|
||||||
|
|
||||||
mNotifyManager.notify(id, builder.build());
|
mNotifyManager.notify(id, builder.build());
|
||||||
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unzip(Intent intent) {
|
private void unzip(Notification.Builder notificationBuilder, File zipFile, File userDataDirectory) throws IOException {
|
||||||
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 per = 0;
|
||||||
int size = getSummarySize(zip);
|
|
||||||
File zipFile = new File(zip);
|
int size;
|
||||||
|
try (ZipFile zipSize = new ZipFile(zipFile)) {
|
||||||
|
size = zipSize.size();
|
||||||
|
}
|
||||||
|
|
||||||
int readLen;
|
int readLen;
|
||||||
byte[] readBuffer = new byte[8192];
|
byte[] readBuffer = new byte[16384];
|
||||||
try (FileInputStream fileInputStream = new FileInputStream(zipFile);
|
try (FileInputStream fileInputStream = new FileInputStream(zipFile);
|
||||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
|
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
|
||||||
ZipEntry ze;
|
ZipEntry ze;
|
||||||
while ((ze = zipInputStream.getNextEntry()) != null) {
|
while ((ze = zipInputStream.getNextEntry()) != null) {
|
||||||
if (ze.isDirectory()) {
|
if (ze.isDirectory()) {
|
||||||
++per;
|
++per;
|
||||||
isDir(ze.getName(), location);
|
Utils.createDirs(userDataDirectory, ze.getName());
|
||||||
} else {
|
continue;
|
||||||
publishProgress(100 * ++per / size);
|
}
|
||||||
try (OutputStream outputStream = new FileOutputStream(location + ze.getName())) {
|
publishProgress(notificationBuilder, R.string.loading, 100 * ++per / size);
|
||||||
while ((readLen = zipInputStream.read(readBuffer)) != -1) {
|
try (OutputStream outputStream = new FileOutputStream(
|
||||||
outputStream.write(readBuffer, 0, readLen);
|
new File(userDataDirectory, 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) {
|
void moveFileOrDir(@NonNull File src, @NonNull File dst) throws IOException {
|
||||||
|
try {
|
||||||
|
Process p = new ProcessBuilder("/system/bin/mv",
|
||||||
|
src.getAbsolutePath(), dst.getAbsolutePath()).start();
|
||||||
|
int exitcode = p.waitFor();
|
||||||
|
if (exitcode != 0)
|
||||||
|
throw new IOException("Move failed with exit code " + exitcode);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException("Move operation interrupted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean recursivelyDeleteDirectory(@NonNull File loc) {
|
||||||
|
try {
|
||||||
|
Process p = new ProcessBuilder("/system/bin/rm", "-rf",
|
||||||
|
loc.getAbsolutePath()).start();
|
||||||
|
return p.waitFor() == 0;
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrates user data from deprecated external storage to app scoped storage
|
||||||
|
*/
|
||||||
|
private void migrate(Notification.Builder notificationBuilder, File newLocation) throws IOException {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File oldLocation = new File(Environment.getExternalStorageDirectory(), "Minetest");
|
||||||
|
if (!oldLocation.isDirectory())
|
||||||
|
return;
|
||||||
|
|
||||||
|
publishProgress(notificationBuilder, R.string.migrating, 0);
|
||||||
|
newLocation.mkdir();
|
||||||
|
|
||||||
|
String[] dirs = new String[] { "worlds", "games", "mods", "textures", "client" };
|
||||||
|
for (int i = 0; i < dirs.length; i++) {
|
||||||
|
publishProgress(notificationBuilder, R.string.migrating, 100 * i / dirs.length);
|
||||||
|
File dir = new File(oldLocation, dirs[i]), dir2 = new File(newLocation, dirs[i]);
|
||||||
|
if (dir.isDirectory() && !dir2.isDirectory()) {
|
||||||
|
moveFileOrDir(dir, dir2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String filename : new String[] { "minetest.conf" }) {
|
||||||
|
File file = new File(oldLocation, filename), file2 = new File(newLocation, filename);
|
||||||
|
if (file.isFile() && !file2.isFile()) {
|
||||||
|
moveFileOrDir(file, file2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recursivelyDeleteDirectory(oldLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
|
||||||
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
||||||
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
||||||
if (!isSuccess) intentUpdate.putExtra(ACTION_FAILURE, failureMessage);
|
intentUpdate.putExtra(ACTION_PROGRESS_MESSAGE, message);
|
||||||
|
if (!isSuccess)
|
||||||
|
intentUpdate.putExtra(ACTION_FAILURE, failureMessage);
|
||||||
sendBroadcast(intentUpdate);
|
sendBroadcast(intentUpdate);
|
||||||
}
|
|
||||||
|
|
||||||
private int getSummarySize(String zip) {
|
if (notificationBuilder != null) {
|
||||||
int size = 0;
|
notificationBuilder.setContentText(getString(message));
|
||||||
try {
|
if (progress == INDETERMINATE) {
|
||||||
ZipFile zipSize = new ZipFile(zip);
|
notificationBuilder.setProgress(100, 50, true);
|
||||||
size += zipSize.size();
|
} else {
|
||||||
} catch (IOException e) {
|
notificationBuilder.setProgress(100, progress, false);
|
||||||
Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
}
|
||||||
|
mNotifyManager.notify(id, notificationBuilder.build());
|
||||||
}
|
}
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
mNotifyManager.cancel(id);
|
mNotifyManager.cancel(id);
|
||||||
publishProgress(isSuccess ? SUCCESS : FAILURE);
|
publishProgress(null, R.string.loading, isSuccess ? SUCCESS : FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package net.minetest.minetest;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class Utils {
|
||||||
|
public static @NonNull File createDirs(File root, String dir) {
|
||||||
|
File f = new File(root, dir);
|
||||||
|
if (!f.isDirectory())
|
||||||
|
f.mkdirs();
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable File getUserDataDirectory(Context context) {
|
||||||
|
File extDir = context.getExternalFilesDir(null);
|
||||||
|
if (extDir == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createDirs(extDir, "Minetest");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable File getCacheDirectory(Context context) {
|
||||||
|
return context.getCacheDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInstallValid(Context context) {
|
||||||
|
File userDataDirectory = getUserDataDirectory(context);
|
||||||
|
return userDataDirectory != null && userDataDirectory.isDirectory() &&
|
||||||
|
new File(userDataDirectory, "games").isDirectory() &&
|
||||||
|
new File(userDataDirectory, "builtin").isDirectory() &&
|
||||||
|
new File(userDataDirectory, "client").isDirectory() &&
|
||||||
|
new File(userDataDirectory, "textures").isDirectory();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/activity_main"
|
android:id="@+id/activity_main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -14,7 +15,8 @@
|
||||||
android:layout_marginRight="90dp"
|
android:layout_marginRight="90dp"
|
||||||
android:indeterminate="false"
|
android:indeterminate="false"
|
||||||
android:max="100"
|
android:max="100"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textView"
|
android:id="@+id/textView"
|
||||||
|
@ -25,6 +27,7 @@
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:text="@string/loading"
|
android:text="@string/loading"
|
||||||
android:textColor="#FEFEFE"
|
android:textColor="#FEFEFE"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
|
|
||||||
<string name="label">Minetest</string>
|
<string name="label">Minetest</string>
|
||||||
<string name="loading">Loading…</string>
|
<string name="loading">Loading…</string>
|
||||||
|
<string name="migrating">Migrating save data from old install… (this may take a while)</string>
|
||||||
<string name="not_granted">Required permission wasn\'t granted, Minetest can\'t run without it</string>
|
<string name="not_granted">Required permission wasn\'t granted, Minetest can\'t run without it</string>
|
||||||
<string name="notification_title">Loading Minetest</string>
|
<string name="notification_title">Loading Minetest</string>
|
||||||
<string name="notification_description">Less than 1 minute…</string>
|
<string name="notification_description">Less than 1 minute…</string>
|
||||||
<string name="ime_dialog_done">Done</string>
|
<string name="ime_dialog_done">Done</string>
|
||||||
|
<string name="no_external_storage">External storage isn\'t available. If you use an SDCard, please reinsert it. Otherwise, try restarting your phone or contacting the Minetest developers</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// 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("versionMajor", 5) // Version Major
|
||||||
project.ext.set("versionMinor", 5) // Version Minor
|
project.ext.set("versionMinor", 6) // Version Minor
|
||||||
project.ext.set("versionPatch", 0) // Version Patch
|
project.ext.set("versionPatch", 0) // Version Patch
|
||||||
project.ext.set("versionExtra", "-dev") // Version Extra
|
project.ext.set("versionExtra", "-dev") // Version Extra
|
||||||
project.ext.set("versionCode", 32) // Android Version Code
|
project.ext.set("versionCode", 38) // Android Version Code
|
||||||
// NOTE: +2 after each release!
|
// NOTE: +2 after each release!
|
||||||
// +1 for ARM and +1 for ARM64 APK's, because
|
// +1 for ARM and +1 for ARM64 APK's, because
|
||||||
// each APK must have a larger `versionCode` than the previous
|
// each APK must have a larger `versionCode` than the previous
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
ext.ndk_version = '23.0.7599858'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
classpath 'com.android.tools.build:gradle:7.0.3'
|
||||||
classpath 'de.undercouch:gradle-download-task:4.1.1'
|
classpath 'de.undercouch:gradle-download-task:4.1.1'
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#Fri Jan 08 17:52:00 UTC 2020
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
|
|
|
@ -98,7 +98,7 @@ location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD="java"
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
command -v java >/dev/null || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
|
|
@ -2,12 +2,12 @@ apply plugin: 'com.android.library'
|
||||||
apply plugin: 'de.undercouch.download'
|
apply plugin: 'de.undercouch.download'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 30
|
||||||
buildToolsVersion '30.0.3'
|
buildToolsVersion '30.0.3'
|
||||||
ndkVersion '22.0.7026061'
|
ndkVersion "$ndk_version"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 30
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
ndkBuild {
|
ndkBuild {
|
||||||
arguments '-j' + Runtime.getRuntime().availableProcessors(),
|
arguments '-j' + Runtime.getRuntime().availableProcessors(),
|
||||||
|
@ -41,58 +41,28 @@ android {
|
||||||
arguments 'NDEBUG=1'
|
arguments 'NDEBUG=1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ndk {
|
||||||
|
debugSymbolLevel 'SYMBOL_TABLE'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get precompiled deps
|
// get precompiled deps
|
||||||
def folder = 'minetest_android_deps_binaries'
|
|
||||||
|
|
||||||
task downloadDeps(type: Download) {
|
task downloadDeps(type: Download) {
|
||||||
src 'https://github.com/minetest/' + folder + '/archive/master.zip'
|
src 'https://github.com/minetest/minetest_android_deps/releases/download/latest/deps.zip'
|
||||||
dest new File(buildDir, 'deps.zip')
|
dest new File(buildDir, 'deps.zip')
|
||||||
overwrite false
|
overwrite false
|
||||||
}
|
}
|
||||||
|
|
||||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||||
def deps = file('deps')
|
def deps = new File(buildDir.parent, 'deps')
|
||||||
def f = file("$buildDir/" + folder + "-master")
|
if (!deps.exists()) {
|
||||||
|
deps.mkdir()
|
||||||
if (!deps.exists() && !f.exists()) {
|
|
||||||
from zipTree(downloadDeps.dest)
|
from zipTree(downloadDeps.dest)
|
||||||
into buildDir
|
into deps
|
||||||
}
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
if (!deps.exists()) {
|
|
||||||
file(f).renameTo(file(deps))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get sqlite
|
|
||||||
def sqlite_ver = '3340000'
|
|
||||||
task downloadSqlite(dependsOn: getDeps, type: Download) {
|
|
||||||
src 'https://www.sqlite.org/2020/sqlite-amalgamation-' + sqlite_ver + '.zip'
|
|
||||||
dest new File(buildDir, 'sqlite.zip')
|
|
||||||
overwrite false
|
|
||||||
}
|
|
||||||
|
|
||||||
task getSqlite(dependsOn: downloadSqlite, type: Copy) {
|
|
||||||
def sqlite = file('deps/Android/sqlite')
|
|
||||||
def f = file("$buildDir/sqlite-amalgamation-" + sqlite_ver)
|
|
||||||
|
|
||||||
if (!sqlite.exists() && !f.exists()) {
|
|
||||||
from zipTree(downloadSqlite.dest)
|
|
||||||
into buildDir
|
|
||||||
}
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
if (!sqlite.exists()) {
|
|
||||||
file(f).renameTo(file(sqlite))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preBuild.dependsOn getDeps
|
preBuild.dependsOn getDeps
|
||||||
preBuild.dependsOn getSqlite
|
|
||||||
|
|
|
@ -4,62 +4,82 @@ LOCAL_PATH := $(call my-dir)/..
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := Curl
|
LOCAL_MODULE := Curl
|
||||||
LOCAL_SRC_FILES := deps/Android/Curl/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcurl.a
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libcurl.a
|
||||||
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libmbedcrypto
|
||||||
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedcrypto.a
|
||||||
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libmbedtls
|
||||||
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedtls.a
|
||||||
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libmbedx509
|
||||||
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedx509.a
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := Freetype
|
LOCAL_MODULE := Freetype
|
||||||
LOCAL_SRC_FILES := deps/Android/Freetype/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libfreetype.a
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Freetype/libfreetype.a
|
||||||
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := Iconv
|
||||||
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Iconv/libiconv.a
|
||||||
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libcharset
|
||||||
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Iconv/libcharset.a
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := Irrlicht
|
LOCAL_MODULE := Irrlicht
|
||||||
LOCAL_SRC_FILES := deps/Android/Irrlicht/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libIrrlichtMt.a
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libIrrlichtMt.a
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
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)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := LuaJIT
|
LOCAL_MODULE := LuaJIT
|
||||||
LOCAL_SRC_FILES := deps/Android/LuaJIT/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libluajit.a
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/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 $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := OpenAL
|
LOCAL_MODULE := OpenAL
|
||||||
LOCAL_SRC_FILES := deps/Android/OpenAL-Soft/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libopenal.a
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := GetText
|
LOCAL_MODULE := Gettext
|
||||||
LOCAL_SRC_FILES := deps/Android/GetText/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libintl.a
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Gettext/libintl.a
|
||||||
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := SQLite3
|
||||||
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/SQLite/libsqlite3.a
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := Vorbis
|
LOCAL_MODULE := Vorbis
|
||||||
LOCAL_SRC_FILES := deps/Android/Vorbis/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libvorbis.a
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libvorbis.a
|
||||||
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libvorbisfile
|
||||||
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libvorbisfile.a
|
||||||
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libogg
|
||||||
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libogg.a
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := Zstd
|
LOCAL_MODULE := Zstd
|
||||||
LOCAL_SRC_FILES := deps/Android/Zstd/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libzstd.a
|
LOCAL_SRC_FILES := deps/$(APP_ABI)/Zstd/libzstd.a
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
@ -71,7 +91,6 @@ LOCAL_CFLAGS += \
|
||||||
-DENABLE_GLES=1 \
|
-DENABLE_GLES=1 \
|
||||||
-DUSE_CURL=1 \
|
-DUSE_CURL=1 \
|
||||||
-DUSE_SOUND=1 \
|
-DUSE_SOUND=1 \
|
||||||
-DUSE_FREETYPE=1 \
|
|
||||||
-DUSE_LEVELDB=0 \
|
-DUSE_LEVELDB=0 \
|
||||||
-DUSE_LUAJIT=1 \
|
-DUSE_LUAJIT=1 \
|
||||||
-DUSE_GETTEXT=1 \
|
-DUSE_GETTEXT=1 \
|
||||||
|
@ -96,18 +115,16 @@ LOCAL_C_INCLUDES := \
|
||||||
../../src/script \
|
../../src/script \
|
||||||
../../lib/gmp \
|
../../lib/gmp \
|
||||||
../../lib/jsoncpp \
|
../../lib/jsoncpp \
|
||||||
deps/Android/Curl/include \
|
deps/$(APP_ABI)/Curl/include \
|
||||||
deps/Android/Freetype/include \
|
deps/$(APP_ABI)/Freetype/include/freetype2 \
|
||||||
deps/Android/Irrlicht/include \
|
deps/$(APP_ABI)/Irrlicht/include \
|
||||||
deps/Android/LevelDB/include \
|
deps/$(APP_ABI)/Gettext/include \
|
||||||
deps/Android/GetText/include \
|
deps/$(APP_ABI)/Iconv/include \
|
||||||
deps/Android/libiconv/include \
|
deps/$(APP_ABI)/LuaJIT/include \
|
||||||
deps/Android/libiconv/libcharset/include \
|
deps/$(APP_ABI)/OpenAL-Soft/include \
|
||||||
deps/Android/LuaJIT/src \
|
deps/$(APP_ABI)/SQLite/include \
|
||||||
deps/Android/OpenAL-Soft/include \
|
deps/$(APP_ABI)/Vorbis/include \
|
||||||
deps/Android/sqlite \
|
deps/$(APP_ABI)/Zstd/include
|
||||||
deps/Android/Vorbis/include \
|
|
||||||
deps/Android/Zstd/include
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
$(wildcard ../../src/client/*.cpp) \
|
$(wildcard ../../src/client/*.cpp) \
|
||||||
|
@ -190,24 +207,24 @@ LOCAL_SRC_FILES := \
|
||||||
../../src/voxel.cpp \
|
../../src/voxel.cpp \
|
||||||
../../src/voxelalgorithms.cpp
|
../../src/voxelalgorithms.cpp
|
||||||
|
|
||||||
# LevelDB backend is disabled
|
|
||||||
# ../../src/database/database-leveldb.cpp
|
|
||||||
|
|
||||||
# GMP
|
# GMP
|
||||||
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
|
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
|
||||||
|
|
||||||
# JSONCPP
|
# JSONCPP
|
||||||
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
|
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
|
||||||
|
|
||||||
# iconv
|
LOCAL_STATIC_LIBRARIES += \
|
||||||
LOCAL_SRC_FILES += \
|
Curl libmbedcrypto libmbedtls libmbedx509 \
|
||||||
deps/Android/libiconv/lib/iconv.c \
|
Freetype \
|
||||||
deps/Android/libiconv/libcharset/lib/localcharset.c
|
Iconv libcharset \
|
||||||
|
Irrlicht \
|
||||||
# SQLite3
|
LuaJIT \
|
||||||
LOCAL_SRC_FILES += deps/Android/sqlite/sqlite3.c
|
OpenAL \
|
||||||
|
Gettext \
|
||||||
LOCAL_STATIC_LIBRARIES += Curl Freetype Irrlicht OpenAL mbedTLS mbedx509 mbedcrypto Vorbis LuaJIT GetText Zstd android_native_app_glue $(PROFILER_LIBS) #LevelDB
|
SQLite3 \
|
||||||
|
Vorbis libvorbisfile libogg \
|
||||||
|
Zstd
|
||||||
|
LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
|
||||||
|
|
||||||
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
|
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
|
||||||
|
|
||||||
|
|
|
@ -5,22 +5,22 @@ NDK_TOOLCHAIN_VERSION := clang
|
||||||
APP_SHORT_COMMANDS := true
|
APP_SHORT_COMMANDS := true
|
||||||
APP_MODULES := Minetest
|
APP_MODULES := Minetest
|
||||||
|
|
||||||
APP_CPPFLAGS := -Ofast -fvisibility=hidden -fexceptions -Wno-deprecated-declarations -Wno-extra-tokens
|
APP_CPPFLAGS := -O2 -fvisibility=hidden
|
||||||
|
|
||||||
ifeq ($(APP_ABI),armeabi-v7a)
|
ifeq ($(APP_ABI),armeabi-v7a)
|
||||||
APP_CPPFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
|
APP_CPPFLAGS += -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#ifeq ($(APP_ABI),x86)
|
ifeq ($(APP_ABI),x86)
|
||||||
#APP_CPPFLAGS += -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32 -funroll-loops
|
APP_CPPFLAGS += -mssse3 -mfpmath=sse -funroll-loops
|
||||||
#endif
|
endif
|
||||||
|
|
||||||
ifndef NDEBUG
|
ifndef NDEBUG
|
||||||
APP_CPPFLAGS := -g -D_DEBUG -O0 -fno-omit-frame-pointer -fexceptions
|
APP_CPPFLAGS := -g -Og -fno-omit-frame-pointer
|
||||||
endif
|
endif
|
||||||
|
|
||||||
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-parentheses-equality #-Werror=shorten-64-to-32
|
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-inconsistent-missing-override -Wno-parentheses-equality
|
||||||
APP_CXXFLAGS := $(APP_CPPFLAGS) -frtti -std=gnu++17
|
APP_CXXFLAGS := $(APP_CPPFLAGS) -fexceptions -frtti -std=gnu++14
|
||||||
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections,--icf=safe
|
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections,--icf=safe
|
||||||
|
|
||||||
ifeq ($(APP_ABI),arm64-v8a)
|
ifeq ($(APP_ABI),arm64-v8a)
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
core.log("info", "Initializing asynchronous environment (game)")
|
||||||
|
|
||||||
|
local function pack2(...)
|
||||||
|
return {n=select('#', ...), ...}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Entrypoint to run async jobs, called by C++
|
||||||
|
function core.job_processor(func, params)
|
||||||
|
local retval = pack2(func(unpack(params, 1, params.n)))
|
||||||
|
|
||||||
|
return retval
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Import a bunch of individual files from builtin/game/
|
||||||
|
local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM
|
||||||
|
local commonpath = core.get_builtin_path() .. "common" .. DIR_DELIM
|
||||||
|
|
||||||
|
dofile(gamepath .. "constants.lua")
|
||||||
|
dofile(gamepath .. "item_s.lua")
|
||||||
|
dofile(gamepath .. "misc_s.lua")
|
||||||
|
dofile(gamepath .. "features.lua")
|
||||||
|
dofile(commonpath .. "voxelarea.lua")
|
||||||
|
|
||||||
|
-- Transfer of globals
|
||||||
|
do
|
||||||
|
local all = assert(core.transferred_globals)
|
||||||
|
core.transferred_globals = nil
|
||||||
|
|
||||||
|
-- reassemble other tables
|
||||||
|
all.registered_nodes = {}
|
||||||
|
all.registered_craftitems = {}
|
||||||
|
all.registered_tools = {}
|
||||||
|
for k, v in pairs(all.registered_items) do
|
||||||
|
if v.type == "node" then
|
||||||
|
all.registered_nodes[k] = v
|
||||||
|
elseif v.type == "craftitem" then
|
||||||
|
all.registered_craftitems[k] = v
|
||||||
|
elseif v.type == "tool" then
|
||||||
|
all.registered_tools[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(all) do
|
||||||
|
core[k] = v
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,4 @@
|
||||||
|
core.log("info", "Initializing asynchronous environment")
|
||||||
core.log("info", "Initializing Asynchronous environment")
|
|
||||||
|
|
||||||
function core.job_processor(func, serialized_param)
|
function core.job_processor(func, serialized_param)
|
||||||
local param = core.deserialize(serialized_param)
|
local param = core.deserialize(serialized_param)
|
||||||
|
@ -8,4 +7,3 @@ function core.job_processor(func, serialized_param)
|
||||||
|
|
||||||
return retval or core.serialize(nil)
|
return retval or core.serialize(nil)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
core.callback_origins = {}
|
core.callback_origins = {}
|
||||||
|
|
||||||
local getinfo = debug.getinfo
|
local getinfo = debug.getinfo
|
||||||
|
|
|
@ -37,7 +37,14 @@ function core.after(after, func, ...)
|
||||||
arg = {...},
|
arg = {...},
|
||||||
mod_origin = core.get_last_run_mod(),
|
mod_origin = core.get_last_run_mod(),
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs[#jobs + 1] = new_job
|
jobs[#jobs + 1] = new_job
|
||||||
time_next = math.min(time_next, expire)
|
time_next = math.min(time_next, expire)
|
||||||
return { cancel = function() new_job.func = function() end end }
|
|
||||||
|
return {
|
||||||
|
cancel = function()
|
||||||
|
new_job.func = function() end
|
||||||
|
new_job.args = {}
|
||||||
|
end
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,42 @@ local S = core.get_translator("__builtin")
|
||||||
|
|
||||||
core.registered_chatcommands = {}
|
core.registered_chatcommands = {}
|
||||||
|
|
||||||
|
-- Interpret the parameters of a command, separating options and arguments.
|
||||||
|
-- Input: command, param
|
||||||
|
-- command: name of command
|
||||||
|
-- param: parameters of command
|
||||||
|
-- Returns: opts, args
|
||||||
|
-- opts is a string of option letters, or false on error
|
||||||
|
-- args is an array with the non-option arguments in order, or an error message
|
||||||
|
-- Example: for this command line:
|
||||||
|
-- /command a b -cd e f -g
|
||||||
|
-- the function would receive:
|
||||||
|
-- a b -cd e f -g
|
||||||
|
-- and it would return:
|
||||||
|
-- "cdg", {"a", "b", "e", "f"}
|
||||||
|
-- Negative numbers are taken as arguments. Long options (--option) are
|
||||||
|
-- currently rejected as reserved.
|
||||||
|
local function getopts(command, param)
|
||||||
|
local opts = ""
|
||||||
|
local args = {}
|
||||||
|
for match in param:gmatch("%S+") do
|
||||||
|
if match:byte(1) == 45 then -- 45 = '-'
|
||||||
|
local second = match:byte(2)
|
||||||
|
if second == 45 then
|
||||||
|
return false, S("Invalid parameters (see /help @1).", command)
|
||||||
|
elseif second and (second < 48 or second > 57) then -- 48 = '0', 57 = '9'
|
||||||
|
opts = opts .. match:sub(2)
|
||||||
|
else
|
||||||
|
-- numeric, add it to args
|
||||||
|
args[#args + 1] = match
|
||||||
|
end
|
||||||
|
else
|
||||||
|
args[#args + 1] = match
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return opts, args
|
||||||
|
end
|
||||||
|
|
||||||
function core.register_chatcommand(cmd, def)
|
function core.register_chatcommand(cmd, def)
|
||||||
def = def or {}
|
def = def or {}
|
||||||
def.params = def.params or ""
|
def.params = def.params or ""
|
||||||
|
@ -78,22 +114,30 @@ if INIT == "client" then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function do_help_cmd(name, param)
|
local function format_help_line(cmd, def)
|
||||||
local function format_help_line(cmd, def)
|
local cmd_marker = INIT == "client" and "." or "/"
|
||||||
local cmd_marker = "/"
|
local msg = core.colorize("#00ffff", cmd_marker .. cmd)
|
||||||
if INIT == "client" then
|
if def.params and def.params ~= "" then
|
||||||
cmd_marker = "."
|
msg = msg .. " " .. def.params
|
||||||
end
|
|
||||||
local msg = core.colorize("#00ffff", cmd_marker .. cmd)
|
|
||||||
if def.params and def.params ~= "" then
|
|
||||||
msg = msg .. " " .. def.params
|
|
||||||
end
|
|
||||||
if def.description and def.description ~= "" then
|
|
||||||
msg = msg .. ": " .. def.description
|
|
||||||
end
|
|
||||||
return msg
|
|
||||||
end
|
end
|
||||||
if param == "" then
|
if def.description and def.description ~= "" then
|
||||||
|
msg = msg .. ": " .. def.description
|
||||||
|
end
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
local function do_help_cmd(name, param)
|
||||||
|
local opts, args = getopts("help", param)
|
||||||
|
if not opts then
|
||||||
|
return false, args
|
||||||
|
end
|
||||||
|
if #args > 1 then
|
||||||
|
return false, S("Too many arguments, try using just /help <command>")
|
||||||
|
end
|
||||||
|
local use_gui = INIT ~= "client" and core.get_player_by_name(name)
|
||||||
|
use_gui = use_gui and not opts:find("t")
|
||||||
|
|
||||||
|
if #args == 0 and not use_gui then
|
||||||
local cmds = {}
|
local cmds = {}
|
||||||
for cmd, def in pairs(core.registered_chatcommands) do
|
for cmd, def in pairs(core.registered_chatcommands) do
|
||||||
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
||||||
|
@ -116,7 +160,10 @@ local function do_help_cmd(name, param)
|
||||||
.. "everything.")
|
.. "everything.")
|
||||||
end
|
end
|
||||||
return true, msg
|
return true, msg
|
||||||
elseif param == "all" then
|
elseif #args == 0 or (args[1] == "all" and use_gui) then
|
||||||
|
core.show_general_help_formspec(name)
|
||||||
|
return true
|
||||||
|
elseif args[1] == "all" then
|
||||||
local cmds = {}
|
local cmds = {}
|
||||||
for cmd, def in pairs(core.registered_chatcommands) do
|
for cmd, def in pairs(core.registered_chatcommands) do
|
||||||
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
||||||
|
@ -131,7 +178,11 @@ local function do_help_cmd(name, param)
|
||||||
msg = core.gettext("Available commands:")
|
msg = core.gettext("Available commands:")
|
||||||
end
|
end
|
||||||
return true, msg.."\n"..table.concat(cmds, "\n")
|
return true, msg.."\n"..table.concat(cmds, "\n")
|
||||||
elseif INIT == "game" and param == "privs" then
|
elseif INIT == "game" and args[1] == "privs" then
|
||||||
|
if use_gui then
|
||||||
|
core.show_privs_help_formspec(name)
|
||||||
|
return true
|
||||||
|
end
|
||||||
local privs = {}
|
local privs = {}
|
||||||
for priv, def in pairs(core.registered_privileges) do
|
for priv, def in pairs(core.registered_privileges) do
|
||||||
privs[#privs + 1] = priv .. ": " .. def.description
|
privs[#privs + 1] = priv .. ": " .. def.description
|
||||||
|
@ -139,7 +190,7 @@ local function do_help_cmd(name, param)
|
||||||
table.sort(privs)
|
table.sort(privs)
|
||||||
return true, S("Available privileges:").."\n"..table.concat(privs, "\n")
|
return true, S("Available privileges:").."\n"..table.concat(privs, "\n")
|
||||||
else
|
else
|
||||||
local cmd = param
|
local cmd = args[1]
|
||||||
local def = core.registered_chatcommands[cmd]
|
local def = core.registered_chatcommands[cmd]
|
||||||
if not def then
|
if not def then
|
||||||
local msg
|
local msg
|
||||||
|
@ -165,8 +216,8 @@ if INIT == "client" then
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
core.register_chatcommand("help", {
|
core.register_chatcommand("help", {
|
||||||
params = S("[all | privs | <cmd>]"),
|
params = S("[all | privs | <cmd>] [-t]"),
|
||||||
description = S("Get help for commands or list privileges"),
|
description = S("Get help for commands or list privileges (-t: output in chat)"),
|
||||||
func = do_help_cmd,
|
func = do_help_cmd,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
@ -125,30 +125,12 @@ core.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
function core.show_general_help_formspec(name)
|
||||||
local help_command = core.registered_chatcommands["help"]
|
core.show_formspec(name, "__builtin:help_cmds",
|
||||||
local old_help_func = help_command.func
|
build_chatcommands_formspec(name))
|
||||||
|
|
||||||
help_command.func = function(name, param)
|
|
||||||
local admin = core.settings:get("name")
|
|
||||||
|
|
||||||
-- If the admin ran help, put the output in the chat buffer as well to
|
|
||||||
-- work with the server terminal
|
|
||||||
if param == "privs" then
|
|
||||||
core.show_formspec(name, "__builtin:help_privs",
|
|
||||||
build_privs_formspec(name))
|
|
||||||
if name ~= admin then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if param == "" or param == "all" then
|
|
||||||
core.show_formspec(name, "__builtin:help_cmds",
|
|
||||||
build_chatcommands_formspec(name))
|
|
||||||
if name ~= admin then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return old_help_func(name, param)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function core.show_privs_help_formspec(name)
|
||||||
|
core.show_formspec(name, "__builtin:help_privs",
|
||||||
|
build_privs_formspec(name))
|
||||||
|
end
|
||||||
|
|
|
@ -543,7 +543,7 @@ if INIT == "mainmenu" then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if INIT == "client" or INIT == "mainmenu" then
|
if core.gettext then -- for client and mainmenu
|
||||||
function fgettext_ne(text, ...)
|
function fgettext_ne(text, ...)
|
||||||
text = core.gettext(text)
|
text = core.gettext(text)
|
||||||
local arg = {n=select('#', ...), ...}
|
local arg = {n=select('#', ...), ...}
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
|
|
||||||
-- Always warn when creating a global variable, even outside of a function.
|
|
||||||
-- This ignores mod namespaces (variables with the same name as the current mod).
|
|
||||||
local WARN_INIT = false
|
|
||||||
|
|
||||||
local getinfo = debug.getinfo
|
local getinfo = debug.getinfo
|
||||||
|
|
||||||
function core.global_exists(name)
|
function core.global_exists(name)
|
||||||
|
@ -33,11 +28,6 @@ function meta:__newindex(name, value)
|
||||||
end
|
end
|
||||||
declared[name] = true
|
declared[name] = true
|
||||||
end
|
end
|
||||||
-- Ignore mod namespaces
|
|
||||||
if WARN_INIT and name ~= core.get_current_modname() then
|
|
||||||
core.log("warning", ("Global variable %q created at %s.")
|
|
||||||
:format(name, desc))
|
|
||||||
end
|
|
||||||
rawset(self, name, value)
|
rawset(self, name, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -54,4 +44,3 @@ function meta:__index(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
setmetatable(_G, meta)
|
setmetatable(_G, meta)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
_G.core = {}
|
_G.core = {}
|
||||||
|
_G.vector = {metatable = {}}
|
||||||
dofile("builtin/common/vector.lua")
|
dofile("builtin/common/vector.lua")
|
||||||
dofile("builtin/common/misc_helpers.lua")
|
dofile("builtin/common/misc_helpers.lua")
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
_G.core = {}
|
_G.core = {}
|
||||||
|
_G.vector = {metatable = {}}
|
||||||
|
|
||||||
_G.setfenv = require 'busted.compatibility'.setfenv
|
_G.setfenv = require 'busted.compatibility'.setfenv
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
_G.vector = {}
|
_G.vector = {metatable = {}}
|
||||||
dofile("builtin/common/vector.lua")
|
dofile("builtin/common/vector.lua")
|
||||||
|
|
||||||
describe("vector", function()
|
describe("vector", function()
|
||||||
|
@ -128,6 +128,14 @@ describe("vector", function()
|
||||||
assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f))
|
assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("combine()", function()
|
||||||
|
local a = vector.new(1, 2, 3)
|
||||||
|
local b = vector.new(3, 2, 1)
|
||||||
|
assert.equal(vector.add(a, b), vector.combine(a, b, function(x, y) return x + y end))
|
||||||
|
assert.equal(vector.new(3, 2, 3), vector.combine(a, b, math.max))
|
||||||
|
assert.equal(vector.new(1, 2, 1), vector.combine(a, b, math.min))
|
||||||
|
end)
|
||||||
|
|
||||||
it("equals()", function()
|
it("equals()", function()
|
||||||
local function assertE(a, b)
|
local function assertE(a, b)
|
||||||
assert.is_true(vector.equals(a, b))
|
assert.is_true(vector.equals(a, b))
|
||||||
|
@ -300,6 +308,7 @@ describe("vector", function()
|
||||||
|
|
||||||
it("from_string()", function()
|
it("from_string()", function()
|
||||||
local v = vector.new(1, 2, 3.14)
|
local v = vector.new(1, 2, 3.14)
|
||||||
|
assert.is_true(vector.check(vector.from_string("(1, 2, 3.14)")))
|
||||||
assert.same({v, 13}, {vector.from_string("(1, 2, 3.14)")})
|
assert.same({v, 13}, {vector.from_string("(1, 2, 3.14)")})
|
||||||
assert.same({v, 12}, {vector.from_string("(1,2 ,3.14)")})
|
assert.same({v, 12}, {vector.from_string("(1,2 ,3.14)")})
|
||||||
assert.same({v, 12}, {vector.from_string("(1,2,3.14,)")})
|
assert.same({v, 12}, {vector.from_string("(1,2,3.14,)")})
|
||||||
|
|
|
@ -6,10 +6,8 @@ Note: The vector.*-functions must be able to accept old vectors that had no meta
|
||||||
-- localize functions
|
-- localize functions
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
|
||||||
vector = {}
|
-- vector.metatable is set by C++.
|
||||||
|
local metatable = vector.metatable
|
||||||
local metatable = {}
|
|
||||||
vector.metatable = metatable
|
|
||||||
|
|
||||||
local xyz = {"x", "y", "z"}
|
local xyz = {"x", "y", "z"}
|
||||||
|
|
||||||
|
@ -61,7 +59,7 @@ function vector.from_string(s, init)
|
||||||
if not (x and y and z) then
|
if not (x and y and z) then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return {x = x, y = y, z = z}, np
|
return fast_new(x, y, z), np
|
||||||
end
|
end
|
||||||
|
|
||||||
function vector.to_string(v)
|
function vector.to_string(v)
|
||||||
|
@ -112,6 +110,14 @@ function vector.apply(v, func)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function vector.combine(a, b, func)
|
||||||
|
return fast_new(
|
||||||
|
func(a.x, b.x),
|
||||||
|
func(a.y, b.y),
|
||||||
|
func(a.z, b.z)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
function vector.distance(a, b)
|
function vector.distance(a, b)
|
||||||
local x = a.x - b.x
|
local x = a.x - b.x
|
||||||
local y = a.y - b.y
|
local y = a.y - b.y
|
||||||
|
|
|
@ -63,7 +63,7 @@ function ui.update()
|
||||||
-- handle errors
|
-- handle errors
|
||||||
if gamedata ~= nil and gamedata.reconnect_requested then
|
if gamedata ~= nil and gamedata.reconnect_requested then
|
||||||
local error_message = core.formspec_escape(
|
local error_message = core.formspec_escape(
|
||||||
gamedata.errormessage or "<none available>")
|
gamedata.errormessage or fgettext("<none available>"))
|
||||||
formspec = {
|
formspec = {
|
||||||
"size[14,8]",
|
"size[14,8]",
|
||||||
"real_coordinates[true]",
|
"real_coordinates[true]",
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
core.async_jobs = {}
|
||||||
|
|
||||||
|
function core.async_event_handler(jobid, retval)
|
||||||
|
local callback = core.async_jobs[jobid]
|
||||||
|
assert(type(callback) == "function")
|
||||||
|
callback(unpack(retval, 1, retval.n))
|
||||||
|
core.async_jobs[jobid] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.handle_async(func, callback, ...)
|
||||||
|
assert(type(func) == "function" and type(callback) == "function",
|
||||||
|
"Invalid minetest.handle_async invocation")
|
||||||
|
local args = {n = select("#", ...), ...}
|
||||||
|
local mod_origin = core.get_last_run_mod()
|
||||||
|
|
||||||
|
local jobid = core.do_async_callback(func, args, mod_origin)
|
||||||
|
core.async_jobs[jobid] = callback
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
|
@ -310,12 +310,7 @@ local function handle_revoke_command(caller, revokename, revokeprivstr)
|
||||||
and revokename == core.settings:get("name")
|
and revokename == core.settings:get("name")
|
||||||
and revokename ~= ""
|
and revokename ~= ""
|
||||||
if revokeprivstr == "all" then
|
if revokeprivstr == "all" then
|
||||||
revokeprivs = privs
|
revokeprivs = table.copy(privs)
|
||||||
privs = {}
|
|
||||||
else
|
|
||||||
for priv, _ in pairs(revokeprivs) do
|
|
||||||
privs[priv] = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local privs_unknown = ""
|
local privs_unknown = ""
|
||||||
|
@ -332,7 +327,10 @@ local function handle_revoke_command(caller, revokename, revokeprivstr)
|
||||||
end
|
end
|
||||||
local def = core.registered_privileges[priv]
|
local def = core.registered_privileges[priv]
|
||||||
if not def then
|
if not def then
|
||||||
privs_unknown = privs_unknown .. S("Unknown privilege: @1", priv) .. "\n"
|
-- Old/removed privileges might still be granted to certain players
|
||||||
|
if not privs[priv] then
|
||||||
|
privs_unknown = privs_unknown .. S("Unknown privilege: @1", priv) .. "\n"
|
||||||
|
end
|
||||||
elseif is_singleplayer and def.give_to_singleplayer then
|
elseif is_singleplayer and def.give_to_singleplayer then
|
||||||
irrevokable[priv] = true
|
irrevokable[priv] = true
|
||||||
elseif is_admin and def.give_to_admin then
|
elseif is_admin and def.give_to_admin then
|
||||||
|
@ -359,19 +357,22 @@ local function handle_revoke_command(caller, revokename, revokeprivstr)
|
||||||
end
|
end
|
||||||
|
|
||||||
local revokecount = 0
|
local revokecount = 0
|
||||||
|
for priv, _ in pairs(revokeprivs) do
|
||||||
|
privs[priv] = nil
|
||||||
|
revokecount = revokecount + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if revokecount == 0 then
|
||||||
|
return false, S("No privileges were revoked.")
|
||||||
|
end
|
||||||
|
|
||||||
core.set_player_privs(revokename, privs)
|
core.set_player_privs(revokename, privs)
|
||||||
for priv, _ in pairs(revokeprivs) do
|
for priv, _ in pairs(revokeprivs) do
|
||||||
-- call the on_revoke callbacks
|
-- call the on_revoke callbacks
|
||||||
core.run_priv_callbacks(revokename, priv, caller, "revoke")
|
core.run_priv_callbacks(revokename, priv, caller, "revoke")
|
||||||
revokecount = revokecount + 1
|
|
||||||
end
|
end
|
||||||
local new_privs = core.get_player_privs(revokename)
|
local new_privs = core.get_player_privs(revokename)
|
||||||
|
|
||||||
if revokecount == 0 then
|
|
||||||
return false, S("No privileges were revoked.")
|
|
||||||
end
|
|
||||||
|
|
||||||
core.log("action", caller..' revoked ('
|
core.log("action", caller..' revoked ('
|
||||||
..core.privs_to_string(revokeprivs, ', ')
|
..core.privs_to_string(revokeprivs, ', ')
|
||||||
..') privileges from '..revokename)
|
..') privileges from '..revokename)
|
||||||
|
@ -524,7 +525,7 @@ end
|
||||||
|
|
||||||
-- Teleports player <name> to <p> if possible
|
-- Teleports player <name> to <p> if possible
|
||||||
local function teleport_to_pos(name, p)
|
local function teleport_to_pos(name, p)
|
||||||
local lm = 31000
|
local lm = 31007 -- equals MAX_MAP_GENERATION_LIMIT in C++
|
||||||
if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm
|
if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm
|
||||||
or p.z < -lm or p.z > lm then
|
or p.z < -lm or p.z > lm then
|
||||||
return false, S("Cannot teleport out of map bounds!")
|
return false, S("Cannot teleport out of map bounds!")
|
||||||
|
@ -621,6 +622,10 @@ core.register_chatcommand("set", {
|
||||||
|
|
||||||
setname, setvalue = string.match(param, "([^ ]+) (.+)")
|
setname, setvalue = string.match(param, "([^ ]+) (.+)")
|
||||||
if setname and setvalue then
|
if setname and setvalue then
|
||||||
|
if setname:sub(1, 7) == "secure." then
|
||||||
|
return false, S("Failed. Cannot modify secure settings. "
|
||||||
|
.. "Edit the settings file manually.")
|
||||||
|
end
|
||||||
if not core.settings:get(setname) then
|
if not core.settings:get(setname) then
|
||||||
return false, S("Failed. Use '/set -n <name> <value>' "
|
return false, S("Failed. Use '/set -n <name> <value>' "
|
||||||
.. "to create a new setting.")
|
.. "to create a new setting.")
|
||||||
|
@ -1034,12 +1039,11 @@ core.register_chatcommand("time", {
|
||||||
end
|
end
|
||||||
local hour, minute = param:match("^(%d+):(%d+)$")
|
local hour, minute = param:match("^(%d+):(%d+)$")
|
||||||
if not hour then
|
if not hour then
|
||||||
local new_time = tonumber(param)
|
local new_time = tonumber(param) or -1
|
||||||
if not new_time then
|
if new_time ~= new_time or new_time < 0 or new_time > 24000 then
|
||||||
return false, S("Invalid time.")
|
return false, S("Invalid time (must be between 0 and 24000).")
|
||||||
end
|
end
|
||||||
-- Backward compatibility.
|
core.set_timeofday(new_time / 24000)
|
||||||
core.set_timeofday((new_time % 24000) / 24000)
|
|
||||||
core.log("action", name .. " sets time to " .. new_time)
|
core.log("action", name .. " sets time to " .. new_time)
|
||||||
return true, S("Time of day changed.")
|
return true, S("Time of day changed.")
|
||||||
end
|
end
|
||||||
|
@ -1283,7 +1287,7 @@ local function handle_kill_command(killer, victim)
|
||||||
return false, S("@1 is already dead.", victim)
|
return false, S("@1 is already dead.", victim)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not killer == victim then
|
if killer ~= victim then
|
||||||
core.log("action", string.format("%s killed %s", killer, victim))
|
core.log("action", string.format("%s killed %s", killer, victim))
|
||||||
end
|
end
|
||||||
-- Kill victim
|
-- Kill victim
|
||||||
|
|
|
@ -22,6 +22,7 @@ core.features = {
|
||||||
degrotate_240_steps = true,
|
degrotate_240_steps = true,
|
||||||
abm_min_max_y = true,
|
abm_min_max_y = true,
|
||||||
dynamic_add_media_table = true,
|
dynamic_add_media_table = true,
|
||||||
|
get_sky_as_table = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function core.has_feature(arg)
|
function core.has_feature(arg)
|
||||||
|
|
|
@ -8,6 +8,7 @@ local gamepath = scriptpath .. "game".. DIR_DELIM
|
||||||
local builtin_shared = {}
|
local builtin_shared = {}
|
||||||
|
|
||||||
dofile(gamepath .. "constants.lua")
|
dofile(gamepath .. "constants.lua")
|
||||||
|
dofile(gamepath .. "item_s.lua")
|
||||||
assert(loadfile(gamepath .. "item.lua"))(builtin_shared)
|
assert(loadfile(gamepath .. "item.lua"))(builtin_shared)
|
||||||
dofile(gamepath .. "register.lua")
|
dofile(gamepath .. "register.lua")
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ dofile(commonpath .. "after.lua")
|
||||||
dofile(commonpath .. "voxelarea.lua")
|
dofile(commonpath .. "voxelarea.lua")
|
||||||
dofile(gamepath .. "item_entity.lua")
|
dofile(gamepath .. "item_entity.lua")
|
||||||
dofile(gamepath .. "deprecated.lua")
|
dofile(gamepath .. "deprecated.lua")
|
||||||
|
dofile(gamepath .. "misc_s.lua")
|
||||||
dofile(gamepath .. "misc.lua")
|
dofile(gamepath .. "misc.lua")
|
||||||
dofile(gamepath .. "privileges.lua")
|
dofile(gamepath .. "privileges.lua")
|
||||||
dofile(gamepath .. "auth.lua")
|
dofile(gamepath .. "auth.lua")
|
||||||
|
@ -32,5 +34,6 @@ dofile(gamepath .. "features.lua")
|
||||||
dofile(gamepath .. "forceloading.lua")
|
dofile(gamepath .. "forceloading.lua")
|
||||||
dofile(gamepath .. "statbars.lua")
|
dofile(gamepath .. "statbars.lua")
|
||||||
dofile(gamepath .. "knockback.lua")
|
dofile(gamepath .. "knockback.lua")
|
||||||
|
dofile(gamepath .. "async.lua")
|
||||||
|
|
||||||
profiler = nil
|
profiler = nil
|
||||||
|
|
|
@ -15,144 +15,19 @@ end
|
||||||
-- Item definition helpers
|
-- Item definition helpers
|
||||||
--
|
--
|
||||||
|
|
||||||
function core.dir_to_facedir(dir, is6d)
|
function core.get_pointed_thing_position(pointed_thing, above)
|
||||||
--account for y if requested
|
if pointed_thing.type == "node" then
|
||||||
if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then
|
if above then
|
||||||
|
-- The position where a node would be placed
|
||||||
--from above
|
return pointed_thing.above
|
||||||
if dir.y < 0 then
|
|
||||||
if math.abs(dir.x) > math.abs(dir.z) then
|
|
||||||
if dir.x < 0 then
|
|
||||||
return 19
|
|
||||||
else
|
|
||||||
return 13
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if dir.z < 0 then
|
|
||||||
return 10
|
|
||||||
else
|
|
||||||
return 4
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--from below
|
|
||||||
else
|
|
||||||
if math.abs(dir.x) > math.abs(dir.z) then
|
|
||||||
if dir.x < 0 then
|
|
||||||
return 15
|
|
||||||
else
|
|
||||||
return 17
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if dir.z < 0 then
|
|
||||||
return 6
|
|
||||||
else
|
|
||||||
return 8
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--otherwise, place horizontally
|
|
||||||
elseif math.abs(dir.x) > math.abs(dir.z) then
|
|
||||||
if dir.x < 0 then
|
|
||||||
return 3
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if dir.z < 0 then
|
|
||||||
return 2
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
end
|
||||||
|
-- The position where a node would be dug
|
||||||
|
return pointed_thing.under
|
||||||
|
elseif pointed_thing.type == "object" then
|
||||||
|
return pointed_thing.ref and pointed_thing.ref:get_pos()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Table of possible dirs
|
|
||||||
local facedir_to_dir = {
|
|
||||||
vector.new( 0, 0, 1),
|
|
||||||
vector.new( 1, 0, 0),
|
|
||||||
vector.new( 0, 0, -1),
|
|
||||||
vector.new(-1, 0, 0),
|
|
||||||
vector.new( 0, -1, 0),
|
|
||||||
vector.new( 0, 1, 0),
|
|
||||||
}
|
|
||||||
-- Mapping from facedir value to index in facedir_to_dir.
|
|
||||||
local facedir_to_dir_map = {
|
|
||||||
[0]=1, 2, 3, 4,
|
|
||||||
5, 2, 6, 4,
|
|
||||||
6, 2, 5, 4,
|
|
||||||
1, 5, 3, 6,
|
|
||||||
1, 6, 3, 5,
|
|
||||||
1, 4, 3, 2,
|
|
||||||
}
|
|
||||||
function core.facedir_to_dir(facedir)
|
|
||||||
return facedir_to_dir[facedir_to_dir_map[facedir % 32]]
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.dir_to_wallmounted(dir)
|
|
||||||
if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
|
|
||||||
if dir.y < 0 then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
elseif math.abs(dir.x) > math.abs(dir.z) then
|
|
||||||
if dir.x < 0 then
|
|
||||||
return 3
|
|
||||||
else
|
|
||||||
return 2
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if dir.z < 0 then
|
|
||||||
return 5
|
|
||||||
else
|
|
||||||
return 4
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- table of dirs in wallmounted order
|
|
||||||
local wallmounted_to_dir = {
|
|
||||||
[0] = vector.new( 0, 1, 0),
|
|
||||||
vector.new( 0, -1, 0),
|
|
||||||
vector.new( 1, 0, 0),
|
|
||||||
vector.new(-1, 0, 0),
|
|
||||||
vector.new( 0, 0, 1),
|
|
||||||
vector.new( 0, 0, -1),
|
|
||||||
}
|
|
||||||
function core.wallmounted_to_dir(wallmounted)
|
|
||||||
return wallmounted_to_dir[wallmounted % 8]
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.dir_to_yaw(dir)
|
|
||||||
return -math.atan2(dir.x, dir.z)
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.yaw_to_dir(yaw)
|
|
||||||
return vector.new(-math.sin(yaw), 0, math.cos(yaw))
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.is_colored_paramtype(ptype)
|
|
||||||
return (ptype == "color") or (ptype == "colorfacedir") or
|
|
||||||
(ptype == "colorwallmounted") or (ptype == "colordegrotate")
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.strip_param2_color(param2, paramtype2)
|
|
||||||
if not core.is_colored_paramtype(paramtype2) then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
if paramtype2 == "colorfacedir" then
|
|
||||||
param2 = math.floor(param2 / 32) * 32
|
|
||||||
elseif paramtype2 == "colorwallmounted" then
|
|
||||||
param2 = math.floor(param2 / 8) * 8
|
|
||||||
elseif paramtype2 == "colordegrotate" then
|
|
||||||
param2 = math.floor(param2 / 32) * 32
|
|
||||||
end
|
|
||||||
-- paramtype2 == "color" requires no modification.
|
|
||||||
return param2
|
|
||||||
end
|
|
||||||
|
|
||||||
local function has_all_groups(tbl, required_groups)
|
local function has_all_groups(tbl, required_groups)
|
||||||
if type(required_groups) == "string" then
|
if type(required_groups) == "string" then
|
||||||
return (tbl[required_groups] or 0) ~= 0
|
return (tbl[required_groups] or 0) ~= 0
|
||||||
|
@ -477,34 +352,41 @@ function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- read definition before potentially emptying the stack
|
||||||
local def = itemstack:get_definition()
|
local def = itemstack:get_definition()
|
||||||
if itemstack:take_item() ~= nil then
|
if itemstack:take_item():is_empty() then
|
||||||
user:set_hp(user:get_hp() + hp_change)
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
if def and def.sound and def.sound.eat then
|
if def and def.sound and def.sound.eat then
|
||||||
core.sound_play(def.sound.eat, {
|
core.sound_play(def.sound.eat, {
|
||||||
pos = user:get_pos(),
|
pos = user:get_pos(),
|
||||||
max_hear_distance = 16
|
max_hear_distance = 16
|
||||||
}, true)
|
}, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if replace_with_item then
|
-- Changing hp might kill the player causing mods to do who-knows-what to the
|
||||||
if itemstack:is_empty() then
|
-- inventory, so do this before set_hp().
|
||||||
itemstack:add_item(replace_with_item)
|
if replace_with_item then
|
||||||
|
if itemstack:is_empty() then
|
||||||
|
itemstack:add_item(replace_with_item)
|
||||||
|
else
|
||||||
|
local inv = user:get_inventory()
|
||||||
|
-- Check if inv is null, since non-players don't have one
|
||||||
|
if inv and inv:room_for_item("main", {name=replace_with_item}) then
|
||||||
|
inv:add_item("main", replace_with_item)
|
||||||
else
|
else
|
||||||
local inv = user:get_inventory()
|
local pos = user:get_pos()
|
||||||
-- Check if inv is null, since non-players don't have one
|
pos.y = math.floor(pos.y + 0.5)
|
||||||
if inv and inv:room_for_item("main", {name=replace_with_item}) then
|
core.add_item(pos, replace_with_item)
|
||||||
inv:add_item("main", replace_with_item)
|
|
||||||
else
|
|
||||||
local pos = user:get_pos()
|
|
||||||
pos.y = math.floor(pos.y + 0.5)
|
|
||||||
core.add_item(pos, replace_with_item)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return itemstack
|
user:set_wielded_item(itemstack)
|
||||||
|
|
||||||
|
user:set_hp(user:get_hp() + hp_change)
|
||||||
|
|
||||||
|
return nil -- don't overwrite wield item a second time
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.item_eat(hp_change, replace_with_item)
|
function core.item_eat(hp_change, replace_with_item)
|
||||||
|
@ -585,7 +467,7 @@ function core.node_dig(pos, node, digger)
|
||||||
if wielded then
|
if wielded then
|
||||||
local wdef = wielded:get_definition()
|
local wdef = wielded:get_definition()
|
||||||
local tp = wielded:get_tool_capabilities()
|
local tp = wielded:get_tool_capabilities()
|
||||||
local dp = core.get_dig_params(def and def.groups, tp)
|
local dp = core.get_dig_params(def and def.groups, tp, wielded:get_wear())
|
||||||
if wdef and wdef.after_use then
|
if wdef and wdef.after_use then
|
||||||
wielded = wdef.after_use(wielded, digger, node, dp) or wielded
|
wielded = wdef.after_use(wielded, digger, node, dp) or wielded
|
||||||
else
|
else
|
||||||
|
@ -647,9 +529,7 @@ function core.node_dig(pos, node, digger)
|
||||||
-- Run script hook
|
-- Run script hook
|
||||||
for _, callback in ipairs(core.registered_on_dignodes) do
|
for _, callback in ipairs(core.registered_on_dignodes) do
|
||||||
local origin = core.callback_origins[callback]
|
local origin = core.callback_origins[callback]
|
||||||
if origin then
|
core.set_last_run_mod(origin.mod)
|
||||||
core.set_last_run_mod(origin.mod)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Copy pos and node because callback can modify them
|
-- Copy pos and node because callback can modify them
|
||||||
local pos_copy = vector.new(pos)
|
local pos_copy = vector.new(pos)
|
||||||
|
|
|
@ -58,17 +58,21 @@ core.register_entity(":__builtin:item", {
|
||||||
local glow = def and def.light_source and
|
local glow = def and def.light_source and
|
||||||
math.floor(def.light_source / 2 + 0.5)
|
math.floor(def.light_source / 2 + 0.5)
|
||||||
|
|
||||||
|
local size_bias = 1e-3 * math.random() -- small random bias to counter Z-fighting
|
||||||
|
local c = {-size, -size, -size, size, size, size}
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
is_visible = true,
|
is_visible = true,
|
||||||
visual = "wielditem",
|
visual = "wielditem",
|
||||||
textures = {itemname},
|
textures = {itemname},
|
||||||
visual_size = {x = size, y = size},
|
visual_size = {x = size + size_bias, y = size + size_bias},
|
||||||
collisionbox = {-size, -size, -size, size, size, size},
|
collisionbox = c,
|
||||||
automatic_rotate = math.pi * 0.5 * 0.2 / size,
|
automatic_rotate = math.pi * 0.5 * 0.2 / size,
|
||||||
wield_item = self.itemstring,
|
wield_item = self.itemstring,
|
||||||
glow = glow,
|
glow = glow,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
-- cache for usage in on_step
|
||||||
|
self._collisionbox = c
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
|
@ -93,6 +97,7 @@ core.register_entity(":__builtin:item", {
|
||||||
self.object:set_armor_groups({immortal = 1})
|
self.object:set_armor_groups({immortal = 1})
|
||||||
self.object:set_velocity({x = 0, y = 2, z = 0})
|
self.object:set_velocity({x = 0, y = 2, z = 0})
|
||||||
self.object:set_acceleration({x = 0, y = -gravity, z = 0})
|
self.object:set_acceleration({x = 0, y = -gravity, z = 0})
|
||||||
|
self._collisionbox = self.initial_properties.collisionbox
|
||||||
self:set_item()
|
self:set_item()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
@ -163,7 +168,7 @@ core.register_entity(":__builtin:item", {
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
local node = core.get_node_or_nil({
|
local node = core.get_node_or_nil({
|
||||||
x = pos.x,
|
x = pos.x,
|
||||||
y = pos.y + self.object:get_properties().collisionbox[2] - 0.05,
|
y = pos.y + self._collisionbox[2] - 0.05,
|
||||||
z = pos.z
|
z = pos.z
|
||||||
})
|
})
|
||||||
-- Delete in 'ignore' nodes
|
-- Delete in 'ignore' nodes
|
||||||
|
@ -176,7 +181,7 @@ core.register_entity(":__builtin:item", {
|
||||||
if self.force_out then
|
if self.force_out then
|
||||||
-- This code runs after the entity got a push from the is_stuck code.
|
-- 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
|
-- It makes sure the entity is entirely outside the solid node
|
||||||
local c = self.object:get_properties().collisionbox
|
local c = self._collisionbox
|
||||||
local s = self.force_out_start
|
local s = self.force_out_start
|
||||||
local f = self.force_out
|
local f = self.force_out
|
||||||
local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or
|
local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
-- Minetest: builtin/item_s.lua
|
||||||
|
-- The distinction of what goes here is a bit tricky, basically it's everything
|
||||||
|
-- that does not (directly or indirectly) need access to ServerEnvironment,
|
||||||
|
-- Server or writable access to IGameDef on the engine side.
|
||||||
|
-- (The '_s' stands for standalone.)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Item definition helpers
|
||||||
|
--
|
||||||
|
|
||||||
|
function core.inventorycube(img1, img2, img3)
|
||||||
|
img2 = img2 or img1
|
||||||
|
img3 = img3 or img1
|
||||||
|
return "[inventorycube"
|
||||||
|
.. "{" .. img1:gsub("%^", "&")
|
||||||
|
.. "{" .. img2:gsub("%^", "&")
|
||||||
|
.. "{" .. img3:gsub("%^", "&")
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.dir_to_facedir(dir, is6d)
|
||||||
|
--account for y if requested
|
||||||
|
if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then
|
||||||
|
|
||||||
|
--from above
|
||||||
|
if dir.y < 0 then
|
||||||
|
if math.abs(dir.x) > math.abs(dir.z) then
|
||||||
|
if dir.x < 0 then
|
||||||
|
return 19
|
||||||
|
else
|
||||||
|
return 13
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if dir.z < 0 then
|
||||||
|
return 10
|
||||||
|
else
|
||||||
|
return 4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--from below
|
||||||
|
else
|
||||||
|
if math.abs(dir.x) > math.abs(dir.z) then
|
||||||
|
if dir.x < 0 then
|
||||||
|
return 15
|
||||||
|
else
|
||||||
|
return 17
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if dir.z < 0 then
|
||||||
|
return 6
|
||||||
|
else
|
||||||
|
return 8
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--otherwise, place horizontally
|
||||||
|
elseif math.abs(dir.x) > math.abs(dir.z) then
|
||||||
|
if dir.x < 0 then
|
||||||
|
return 3
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if dir.z < 0 then
|
||||||
|
return 2
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Table of possible dirs
|
||||||
|
local facedir_to_dir = {
|
||||||
|
vector.new( 0, 0, 1),
|
||||||
|
vector.new( 1, 0, 0),
|
||||||
|
vector.new( 0, 0, -1),
|
||||||
|
vector.new(-1, 0, 0),
|
||||||
|
vector.new( 0, -1, 0),
|
||||||
|
vector.new( 0, 1, 0),
|
||||||
|
}
|
||||||
|
-- Mapping from facedir value to index in facedir_to_dir.
|
||||||
|
local facedir_to_dir_map = {
|
||||||
|
[0]=1, 2, 3, 4,
|
||||||
|
5, 2, 6, 4,
|
||||||
|
6, 2, 5, 4,
|
||||||
|
1, 5, 3, 6,
|
||||||
|
1, 6, 3, 5,
|
||||||
|
1, 4, 3, 2,
|
||||||
|
}
|
||||||
|
function core.facedir_to_dir(facedir)
|
||||||
|
return facedir_to_dir[facedir_to_dir_map[facedir % 32]]
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.dir_to_wallmounted(dir)
|
||||||
|
if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
|
||||||
|
if dir.y < 0 then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
elseif math.abs(dir.x) > math.abs(dir.z) then
|
||||||
|
if dir.x < 0 then
|
||||||
|
return 3
|
||||||
|
else
|
||||||
|
return 2
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if dir.z < 0 then
|
||||||
|
return 5
|
||||||
|
else
|
||||||
|
return 4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- table of dirs in wallmounted order
|
||||||
|
local wallmounted_to_dir = {
|
||||||
|
[0] = vector.new( 0, 1, 0),
|
||||||
|
vector.new( 0, -1, 0),
|
||||||
|
vector.new( 1, 0, 0),
|
||||||
|
vector.new(-1, 0, 0),
|
||||||
|
vector.new( 0, 0, 1),
|
||||||
|
vector.new( 0, 0, -1),
|
||||||
|
}
|
||||||
|
function core.wallmounted_to_dir(wallmounted)
|
||||||
|
return wallmounted_to_dir[wallmounted % 8]
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.dir_to_yaw(dir)
|
||||||
|
return -math.atan2(dir.x, dir.z)
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.yaw_to_dir(yaw)
|
||||||
|
return vector.new(-math.sin(yaw), 0, math.cos(yaw))
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.is_colored_paramtype(ptype)
|
||||||
|
return (ptype == "color") or (ptype == "colorfacedir") or
|
||||||
|
(ptype == "colorwallmounted") or (ptype == "colordegrotate")
|
||||||
|
end
|
||||||
|
|
||||||
|
function core.strip_param2_color(param2, paramtype2)
|
||||||
|
if not core.is_colored_paramtype(paramtype2) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if paramtype2 == "colorfacedir" then
|
||||||
|
param2 = math.floor(param2 / 32) * 32
|
||||||
|
elseif paramtype2 == "colorwallmounted" then
|
||||||
|
param2 = math.floor(param2 / 8) * 8
|
||||||
|
elseif paramtype2 == "colordegrotate" then
|
||||||
|
param2 = math.floor(param2 / 32) * 32
|
||||||
|
end
|
||||||
|
-- paramtype2 == "color" requires no modification.
|
||||||
|
return param2
|
||||||
|
end
|
|
@ -6,6 +6,16 @@ local S = core.get_translator("__builtin")
|
||||||
-- Misc. API functions
|
-- Misc. API functions
|
||||||
--
|
--
|
||||||
|
|
||||||
|
-- @spec core.kick_player(String, String) :: Boolean
|
||||||
|
function core.kick_player(player_name, reason)
|
||||||
|
if type(reason) == "string" then
|
||||||
|
reason = "Kicked: " .. reason
|
||||||
|
else
|
||||||
|
reason = "Kicked."
|
||||||
|
end
|
||||||
|
return core.disconnect_player(player_name, reason)
|
||||||
|
end
|
||||||
|
|
||||||
function core.check_player_privs(name, ...)
|
function core.check_player_privs(name, ...)
|
||||||
if core.is_player(name) then
|
if core.is_player(name) then
|
||||||
name = name:get_player_name()
|
name = name:get_player_name()
|
||||||
|
@ -111,53 +121,6 @@ function core.get_player_radius_area(player_name, radius)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function core.hash_node_position(pos)
|
|
||||||
return (pos.z + 32768) * 65536 * 65536
|
|
||||||
+ (pos.y + 32768) * 65536
|
|
||||||
+ pos.x + 32768
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function core.get_position_from_hash(hash)
|
|
||||||
local x = (hash % 65536) - 32768
|
|
||||||
hash = math.floor(hash / 65536)
|
|
||||||
local y = (hash % 65536) - 32768
|
|
||||||
hash = math.floor(hash / 65536)
|
|
||||||
local z = (hash % 65536) - 32768
|
|
||||||
return vector.new(x, y, z)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function core.get_item_group(name, group)
|
|
||||||
if not core.registered_items[name] or not
|
|
||||||
core.registered_items[name].groups[group] then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
return core.registered_items[name].groups[group]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function core.get_node_group(name, group)
|
|
||||||
core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead")
|
|
||||||
return core.get_item_group(name, group)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function core.setting_get_pos(name)
|
|
||||||
local value = core.settings:get(name)
|
|
||||||
if not value then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return core.string_to_pos(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- See l_env.cpp for the other functions
|
|
||||||
function core.get_artificial_light(param1)
|
|
||||||
return math.floor(param1 / 16)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- To be overriden by protection mods
|
-- To be overriden by protection mods
|
||||||
|
|
||||||
function core.is_protected(pos, name)
|
function core.is_protected(pos, name)
|
||||||
|
@ -240,7 +203,7 @@ end
|
||||||
|
|
||||||
-- HTTP callback interface
|
-- HTTP callback interface
|
||||||
|
|
||||||
function core.http_add_fetch(httpenv)
|
core.set_http_api_lua(function(httpenv)
|
||||||
httpenv.fetch = function(req, callback)
|
httpenv.fetch = function(req, callback)
|
||||||
local handle = httpenv.fetch_async(req)
|
local handle = httpenv.fetch_async(req)
|
||||||
|
|
||||||
|
@ -256,7 +219,8 @@ function core.http_add_fetch(httpenv)
|
||||||
end
|
end
|
||||||
|
|
||||||
return httpenv
|
return httpenv
|
||||||
end
|
end)
|
||||||
|
core.set_http_api_lua = nil
|
||||||
|
|
||||||
|
|
||||||
function core.close_formspec(player_name, formname)
|
function core.close_formspec(player_name, formname)
|
||||||
|
@ -273,40 +237,30 @@ end
|
||||||
core.dynamic_media_callbacks = {}
|
core.dynamic_media_callbacks = {}
|
||||||
|
|
||||||
|
|
||||||
-- PNG encoder safety wrapper
|
-- Transfer of certain globals into async environment
|
||||||
|
-- see builtin/async/game.lua for the other side
|
||||||
|
|
||||||
local o_encode_png = core.encode_png
|
local function copy_filtering(t, seen)
|
||||||
function core.encode_png(width, height, data, compression)
|
if type(t) == "userdata" or type(t) == "function" then
|
||||||
if type(width) ~= "number" then
|
return true -- don't use nil so presence can still be detected
|
||||||
error("Incorrect type for 'width', expected number, got " .. type(width))
|
elseif type(t) ~= "table" then
|
||||||
|
return t
|
||||||
end
|
end
|
||||||
if type(height) ~= "number" then
|
local n = {}
|
||||||
error("Incorrect type for 'height', expected number, got " .. type(height))
|
seen = seen or {}
|
||||||
|
seen[t] = n
|
||||||
|
for k, v in pairs(t) do
|
||||||
|
local k_ = seen[k] or copy_filtering(k, seen)
|
||||||
|
local v_ = seen[v] or copy_filtering(v, seen)
|
||||||
|
n[k_] = v_
|
||||||
end
|
end
|
||||||
|
return n
|
||||||
local expected_byte_count = width * height * 4
|
end
|
||||||
|
|
||||||
if type(data) ~= "table" and type(data) ~= "string" then
|
function core.get_globals_to_transfer()
|
||||||
error("Incorrect type for 'height', expected table or string, got " .. type(height))
|
local all = {
|
||||||
end
|
registered_items = copy_filtering(core.registered_items),
|
||||||
|
registered_aliases = core.registered_aliases,
|
||||||
local data_length = type(data) == "table" and #data * 4 or string.len(data)
|
}
|
||||||
|
return all
|
||||||
if data_length ~= expected_byte_count then
|
|
||||||
error(string.format(
|
|
||||||
"Incorrect length of 'data', width and height imply %d bytes but %d were provided",
|
|
||||||
expected_byte_count,
|
|
||||||
data_length
|
|
||||||
))
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(data) == "table" then
|
|
||||||
local dataBuf = {}
|
|
||||||
for i = 1, #data do
|
|
||||||
dataBuf[i] = core.colorspec_to_bytes(data[i])
|
|
||||||
end
|
|
||||||
data = table.concat(dataBuf)
|
|
||||||
end
|
|
||||||
|
|
||||||
return o_encode_png(width, height, data, compression or 6)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
-- Minetest: builtin/misc_s.lua
|
||||||
|
-- The distinction of what goes here is a bit tricky, basically it's everything
|
||||||
|
-- that does not (directly or indirectly) need access to ServerEnvironment,
|
||||||
|
-- Server or writable access to IGameDef on the engine side.
|
||||||
|
-- (The '_s' stands for standalone.)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Misc. API functions
|
||||||
|
--
|
||||||
|
|
||||||
|
function core.hash_node_position(pos)
|
||||||
|
return (pos.z + 32768) * 65536 * 65536
|
||||||
|
+ (pos.y + 32768) * 65536
|
||||||
|
+ pos.x + 32768
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function core.get_position_from_hash(hash)
|
||||||
|
local x = (hash % 65536) - 32768
|
||||||
|
hash = math.floor(hash / 65536)
|
||||||
|
local y = (hash % 65536) - 32768
|
||||||
|
hash = math.floor(hash / 65536)
|
||||||
|
local z = (hash % 65536) - 32768
|
||||||
|
return vector.new(x, y, z)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function core.get_item_group(name, group)
|
||||||
|
if not core.registered_items[name] or not
|
||||||
|
core.registered_items[name].groups[group] then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
return core.registered_items[name].groups[group]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function core.get_node_group(name, group)
|
||||||
|
core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead")
|
||||||
|
return core.get_item_group(name, group)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function core.setting_get_pos(name)
|
||||||
|
local value = core.settings:get(name)
|
||||||
|
if not value then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return core.string_to_pos(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- See l_env.cpp for the other functions
|
||||||
|
function core.get_artificial_light(param1)
|
||||||
|
return math.floor(param1 / 16)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- PNG encoder safety wrapper
|
||||||
|
|
||||||
|
local o_encode_png = core.encode_png
|
||||||
|
function core.encode_png(width, height, data, compression)
|
||||||
|
if type(width) ~= "number" then
|
||||||
|
error("Incorrect type for 'width', expected number, got " .. type(width))
|
||||||
|
end
|
||||||
|
if type(height) ~= "number" then
|
||||||
|
error("Incorrect type for 'height', expected number, got " .. type(height))
|
||||||
|
end
|
||||||
|
|
||||||
|
local expected_byte_count = width * height * 4
|
||||||
|
|
||||||
|
if type(data) ~= "table" and type(data) ~= "string" then
|
||||||
|
error("Incorrect type for 'data', expected table or string, got " .. type(data))
|
||||||
|
end
|
||||||
|
|
||||||
|
local data_length = type(data) == "table" and #data * 4 or string.len(data)
|
||||||
|
|
||||||
|
if data_length ~= expected_byte_count then
|
||||||
|
error(string.format(
|
||||||
|
"Incorrect length of 'data', width and height imply %d bytes but %d were provided",
|
||||||
|
expected_byte_count,
|
||||||
|
data_length
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(data) == "table" then
|
||||||
|
local dataBuf = {}
|
||||||
|
for i = 1, #data do
|
||||||
|
dataBuf[i] = core.colorspec_to_bytes(data[i])
|
||||||
|
end
|
||||||
|
data = table.concat(dataBuf)
|
||||||
|
end
|
||||||
|
|
||||||
|
return o_encode_png(width, height, data, compression or 6)
|
||||||
|
end
|
|
@ -97,10 +97,6 @@ core.register_privilege("rollback", {
|
||||||
description = S("Can use the rollback functionality"),
|
description = S("Can use the rollback functionality"),
|
||||||
give_to_singleplayer = false,
|
give_to_singleplayer = false,
|
||||||
})
|
})
|
||||||
core.register_privilege("basic_debug", {
|
|
||||||
description = S("Can view more debug info that might give a gameplay advantage"),
|
|
||||||
give_to_singleplayer = false,
|
|
||||||
})
|
|
||||||
core.register_privilege("debug", {
|
core.register_privilege("debug", {
|
||||||
description = S("Can enable wireframe"),
|
description = S("Can enable wireframe"),
|
||||||
give_to_singleplayer = false,
|
give_to_singleplayer = false,
|
||||||
|
|
|
@ -403,8 +403,14 @@ function core.override_item(name, redefinition)
|
||||||
register_item_raw(item)
|
register_item_raw(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do
|
||||||
core.callback_origins = {}
|
local default = {mod = "??", name = "??"}
|
||||||
|
core.callback_origins = setmetatable({}, {
|
||||||
|
__index = function()
|
||||||
|
return default
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
function core.run_callbacks(callbacks, mode, ...)
|
function core.run_callbacks(callbacks, mode, ...)
|
||||||
assert(type(callbacks) == "table")
|
assert(type(callbacks) == "table")
|
||||||
|
@ -419,9 +425,7 @@ function core.run_callbacks(callbacks, mode, ...)
|
||||||
local ret = nil
|
local ret = nil
|
||||||
for i = 1, cb_len do
|
for i = 1, cb_len do
|
||||||
local origin = core.callback_origins[callbacks[i]]
|
local origin = core.callback_origins[callbacks[i]]
|
||||||
if origin then
|
core.set_last_run_mod(origin.mod)
|
||||||
core.set_last_run_mod(origin.mod)
|
|
||||||
end
|
|
||||||
local cb_ret = callbacks[i](...)
|
local cb_ret = callbacks[i](...)
|
||||||
|
|
||||||
if mode == 0 and i == 1 then
|
if mode == 0 and i == 1 then
|
||||||
|
|
|
@ -1,39 +1,39 @@
|
||||||
-- cache setting
|
-- cache setting
|
||||||
local enable_damage = core.settings:get_bool("enable_damage")
|
local enable_damage = core.settings:get_bool("enable_damage")
|
||||||
|
|
||||||
local health_bar_definition = {
|
local bar_definitions = {
|
||||||
hud_elem_type = "statbar",
|
hp = {
|
||||||
position = {x = 0.5, y = 1},
|
hud_elem_type = "statbar",
|
||||||
text = "heart.png",
|
position = {x = 0.5, y = 1},
|
||||||
text2 = "heart_gone.png",
|
text = "heart.png",
|
||||||
number = core.PLAYER_MAX_HP_DEFAULT,
|
text2 = "heart_gone.png",
|
||||||
item = core.PLAYER_MAX_HP_DEFAULT,
|
number = core.PLAYER_MAX_HP_DEFAULT,
|
||||||
direction = 0,
|
item = core.PLAYER_MAX_HP_DEFAULT,
|
||||||
size = {x = 24, y = 24},
|
direction = 0,
|
||||||
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 = {
|
breath = {
|
||||||
hud_elem_type = "statbar",
|
hud_elem_type = "statbar",
|
||||||
position = {x = 0.5, y = 1},
|
position = {x = 0.5, y = 1},
|
||||||
text = "bubble.png",
|
text = "bubble.png",
|
||||||
text2 = "bubble_gone.png",
|
text2 = "bubble_gone.png",
|
||||||
number = core.PLAYER_MAX_BREATH_DEFAULT,
|
number = core.PLAYER_MAX_BREATH_DEFAULT * 2,
|
||||||
item = core.PLAYER_MAX_BREATH_DEFAULT * 2,
|
item = core.PLAYER_MAX_BREATH_DEFAULT * 2,
|
||||||
direction = 0,
|
direction = 0,
|
||||||
size = {x = 24, y = 24},
|
size = {x = 24, y = 24},
|
||||||
offset = {x = 25, y= -(48 + 24 + 16)},
|
offset = {x = 25, y= -(48 + 24 + 16)},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local hud_ids = {}
|
local hud_ids = {}
|
||||||
|
|
||||||
local function scaleToDefault(player, field)
|
local function scaleToHudMax(player, field)
|
||||||
-- Scale "hp" or "breath" to the default dimensions
|
-- Scale "hp" or "breath" to the hud maximum dimensions
|
||||||
local current = player["get_" .. field](player)
|
local current = player["get_" .. field](player)
|
||||||
local nominal = core["PLAYER_MAX_" .. field:upper() .. "_DEFAULT"]
|
local nominal = bar_definitions[field].item
|
||||||
local max_display = math.max(nominal,
|
local max_display = math.max(player:get_properties()[field .. "_max"], current)
|
||||||
math.max(player:get_properties()[field .. "_max"], current))
|
return math.ceil(current / max_display * nominal)
|
||||||
return current / max_display * nominal
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function update_builtin_statbars(player)
|
local function update_builtin_statbars(player)
|
||||||
|
@ -55,9 +55,9 @@ local function update_builtin_statbars(player)
|
||||||
local immortal = player:get_armor_groups().immortal == 1
|
local immortal = player:get_armor_groups().immortal == 1
|
||||||
|
|
||||||
if flags.healthbar and enable_damage and not immortal then
|
if flags.healthbar and enable_damage and not immortal then
|
||||||
local number = scaleToDefault(player, "hp")
|
local number = scaleToHudMax(player, "hp")
|
||||||
if hud.id_healthbar == nil then
|
if hud.id_healthbar == nil then
|
||||||
local hud_def = table.copy(health_bar_definition)
|
local hud_def = table.copy(bar_definitions.hp)
|
||||||
hud_def.number = number
|
hud_def.number = number
|
||||||
hud.id_healthbar = player:hud_add(hud_def)
|
hud.id_healthbar = player:hud_add(hud_def)
|
||||||
else
|
else
|
||||||
|
@ -73,9 +73,9 @@ local function update_builtin_statbars(player)
|
||||||
local breath = player:get_breath()
|
local breath = player:get_breath()
|
||||||
local breath_max = player:get_properties().breath_max
|
local breath_max = player:get_properties().breath_max
|
||||||
if show_breathbar and breath <= breath_max then
|
if show_breathbar and breath <= breath_max then
|
||||||
local number = 2 * scaleToDefault(player, "breath")
|
local number = scaleToHudMax(player, "breath")
|
||||||
if not hud.id_breathbar and breath < breath_max then
|
if not hud.id_breathbar and breath < breath_max then
|
||||||
local hud_def = table.copy(breath_bar_definition)
|
local hud_def = table.copy(bar_definitions.breath)
|
||||||
hud_def.number = number
|
hud_def.number = number
|
||||||
hud.id_breathbar = player:hud_add(hud_def)
|
hud.id_breathbar = player:hud_add(hud_def)
|
||||||
elseif hud.id_breathbar then
|
elseif hud.id_breathbar then
|
||||||
|
@ -145,7 +145,7 @@ function core.hud_replace_builtin(hud_name, definition)
|
||||||
end
|
end
|
||||||
|
|
||||||
if hud_name == "health" then
|
if hud_name == "health" then
|
||||||
health_bar_definition = definition
|
bar_definitions.hp = definition
|
||||||
|
|
||||||
for name, ids in pairs(hud_ids) do
|
for name, ids in pairs(hud_ids) do
|
||||||
local player = core.get_player_by_name(name)
|
local player = core.get_player_by_name(name)
|
||||||
|
@ -159,7 +159,7 @@ function core.hud_replace_builtin(hud_name, definition)
|
||||||
end
|
end
|
||||||
|
|
||||||
if hud_name == "breath" then
|
if hud_name == "breath" then
|
||||||
breath_bar_definition = definition
|
bar_definitions.breath = definition
|
||||||
|
|
||||||
for name, ids in pairs(hud_ids) do
|
for name, ids in pairs(hud_ids) do
|
||||||
local player = core.get_player_by_name(name)
|
local player = core.get_player_by_name(name)
|
||||||
|
|
|
@ -55,8 +55,10 @@ elseif INIT == "mainmenu" then
|
||||||
if not custom_loaded then
|
if not custom_loaded then
|
||||||
dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua")
|
dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua")
|
||||||
end
|
end
|
||||||
elseif INIT == "async" then
|
elseif INIT == "async" then
|
||||||
dofile(asyncpath .. "init.lua")
|
dofile(asyncpath .. "mainmenu.lua")
|
||||||
|
elseif INIT == "async_game" then
|
||||||
|
dofile(asyncpath .. "game.lua")
|
||||||
elseif INIT == "client" then
|
elseif INIT == "client" then
|
||||||
dofile(clientpath .. "init.lua")
|
dofile(clientpath .. "init.lua")
|
||||||
else
|
else
|
||||||
|
|
|
@ -139,7 +139,7 @@ This command was disabled by a mod or game.=Dieser Befehl wurde von einer Mod od
|
||||||
Show or set time of day=Tageszeit anzeigen oder setzen
|
Show or set time of day=Tageszeit anzeigen oder setzen
|
||||||
Current time is @1:@2.=Es ist jetzt @1:@2 Uhr.
|
Current time is @1:@2.=Es ist jetzt @1:@2 Uhr.
|
||||||
You don't have permission to run this command (missing privilege: @1).=Sie haben nicht die Erlaubnis, diesen Befehl auszuführen (fehlendes Privileg: @1).
|
You don't have permission to run this command (missing privilege: @1).=Sie haben nicht die Erlaubnis, diesen Befehl auszuführen (fehlendes Privileg: @1).
|
||||||
Invalid time.=Ungültige Zeit.
|
Invalid time (must be between 0 and 24000).=Ungültige Zeit (muss zwischen 0 und 24000 liegen).
|
||||||
Time of day changed.=Tageszeit geändert.
|
Time of day changed.=Tageszeit geändert.
|
||||||
Invalid hour (must be between 0 and 23 inclusive).=Ungültige Stunde (muss zwischen 0 und 23 inklusive liegen).
|
Invalid hour (must be between 0 and 23 inclusive).=Ungültige Stunde (muss zwischen 0 und 23 inklusive liegen).
|
||||||
Invalid minute (must be between 0 and 59 inclusive).=Ungültige Minute (muss zwischen 0 und 59 inklusive liegen).
|
Invalid minute (must be between 0 and 59 inclusive).=Ungültige Minute (muss zwischen 0 und 59 inklusive liegen).
|
||||||
|
@ -187,12 +187,14 @@ You are already dead.=Sie sind schon tot.
|
||||||
@1 is already dead.=@1 ist bereits tot.
|
@1 is already dead.=@1 ist bereits tot.
|
||||||
@1 has been killed.=@1 wurde getötet.
|
@1 has been killed.=@1 wurde getötet.
|
||||||
Kill player or yourself=Einen Spieler oder Sie selbst töten
|
Kill player or yourself=Einen Spieler oder Sie selbst töten
|
||||||
|
Invalid parameters (see /help @1).=Ungültige Parameter (siehe „/help @1“).
|
||||||
|
Too many arguments, try using just /help <command>=Zu viele Argumente. Probieren Sie es mit „/help <Befehl>“
|
||||||
Available commands: @1=Verfügbare Befehle: @1
|
Available commands: @1=Verfügbare Befehle: @1
|
||||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=„/help <Befehl>“ benutzen, um mehr Informationen zu erhalten, oder „/help all“, um alles aufzulisten.
|
Use '/help <cmd>' to get more information, or '/help all' to list everything.=„/help <Befehl>“ benutzen, um mehr Informationen zu erhalten, oder „/help all“, um alles aufzulisten.
|
||||||
Available commands:=Verfügbare Befehle:
|
Available commands:=Verfügbare Befehle:
|
||||||
Command not available: @1=Befehl nicht verfügbar: @1
|
Command not available: @1=Befehl nicht verfügbar: @1
|
||||||
[all | privs | <cmd>]=[all | privs | <Befehl>]
|
[all | privs | <cmd>] [-t]=[all | privs | <Befehl>] [-t]
|
||||||
Get help for commands or list privileges=Hilfe für Befehle erhalten oder Privilegien auflisten
|
Get help for commands or list privileges (-t: output in chat)=Hilfe für Befehle erhalten oder Privilegien auflisten (-t: Ausgabe im Chat)
|
||||||
Available privileges:=Verfügbare Privilegien:
|
Available privileges:=Verfügbare Privilegien:
|
||||||
Command=Befehl
|
Command=Befehl
|
||||||
Parameters=Parameter
|
Parameters=Parameter
|
||||||
|
@ -230,7 +232,8 @@ Can use fly mode=Kann den Flugmodus benutzen
|
||||||
Can use fast mode=Kann den Schnellmodus benutzen
|
Can use fast mode=Kann den Schnellmodus benutzen
|
||||||
Can fly through solid nodes using noclip mode=Kann durch feste Blöcke mit dem Geistmodus fliegen
|
Can fly through solid nodes using noclip mode=Kann durch feste Blöcke mit dem Geistmodus fliegen
|
||||||
Can use the rollback functionality=Kann die Rollback-Funktionalität benutzen
|
Can use the rollback functionality=Kann die Rollback-Funktionalität benutzen
|
||||||
Allows enabling various debug options that may affect gameplay=Erlaubt die Aktivierung diverser Debugoptionen, die das Spielgeschehen beeinflussen könnten
|
Can view more debug info that might give a gameplay advantage=Kann zusätzliche Debuginformationen betrachten, welche einen spielerischen Vorteil geben könnten
|
||||||
|
Can enable wireframe=Kann Drahtmodell aktivieren
|
||||||
Unknown Item=Unbekannter Gegenstand
|
Unknown Item=Unbekannter Gegenstand
|
||||||
Air=Luft
|
Air=Luft
|
||||||
Ignore=Ignorieren
|
Ignore=Ignorieren
|
||||||
|
|
|
@ -139,7 +139,7 @@ This command was disabled by a mod or game.=Questo comando è stato disabilitato
|
||||||
Show or set time of day=Mostra o imposta l'orario della giornata
|
Show or set time of day=Mostra o imposta l'orario della giornata
|
||||||
Current time is @1:@2.=Orario corrente: @1:@2.
|
Current time is @1:@2.=Orario corrente: @1:@2.
|
||||||
You don't have permission to run this command (missing privilege: @1).=Non hai il permesso di eseguire questo comando (privilegio mancante: @1)
|
You don't have permission to run this command (missing privilege: @1).=Non hai il permesso di eseguire questo comando (privilegio mancante: @1)
|
||||||
Invalid time.=Orario non valido.
|
Invalid time (must be between 0 and 24000).=
|
||||||
Time of day changed.=Orario della giornata cambiato.
|
Time of day changed.=Orario della giornata cambiato.
|
||||||
Invalid hour (must be between 0 and 23 inclusive).=Ora non valida (deve essere tra 0 e 23 inclusi)
|
Invalid hour (must be between 0 and 23 inclusive).=Ora non valida (deve essere tra 0 e 23 inclusi)
|
||||||
Invalid minute (must be between 0 and 59 inclusive).=Minuto non valido (deve essere tra 0 e 59 inclusi)
|
Invalid minute (must be between 0 and 59 inclusive).=Minuto non valido (deve essere tra 0 e 59 inclusi)
|
||||||
|
@ -187,12 +187,14 @@ You are already dead.=Sei già mortǝ.
|
||||||
@1 is already dead.=@1 è già mortǝ.
|
@1 is already dead.=@1 è già mortǝ.
|
||||||
@1 has been killed.=@1 è stato uccisǝ.
|
@1 has been killed.=@1 è stato uccisǝ.
|
||||||
Kill player or yourself=Uccide un giocatore o te stessǝ
|
Kill player or yourself=Uccide un giocatore o te stessǝ
|
||||||
|
Invalid parameters (see /help @1).=
|
||||||
|
Too many arguments, try using just /help <command>=
|
||||||
Available commands: @1=Comandi disponibili: @1
|
Available commands: @1=Comandi disponibili: @1
|
||||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Usa '/help <comando>' per ottenere più informazioni, o '/help all' per elencare tutti i comandi.
|
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Usa '/help <comando>' per ottenere più informazioni, o '/help all' per elencare tutti i comandi.
|
||||||
Available commands:=Comandi disponibili:
|
Available commands:=Comandi disponibili:
|
||||||
Command not available: @1=Comando non disponibile: @1
|
Command not available: @1=Comando non disponibile: @1
|
||||||
[all | privs | <cmd>]=[all | privs | <comando>]
|
[all | privs | <cmd>] [-t]=
|
||||||
Get help for commands or list privileges=Richiama la finestra d'aiuto dei comandi o dei privilegi
|
Get help for commands or list privileges (-t: output in chat)=
|
||||||
Available privileges:=Privilegi disponibili:
|
Available privileges:=Privilegi disponibili:
|
||||||
Command=Comando
|
Command=Comando
|
||||||
Parameters=Parametri
|
Parameters=Parametri
|
||||||
|
@ -230,7 +232,8 @@ Can use fly mode=Si può usare la modalità volo
|
||||||
Can use fast mode=Si può usare la modalità rapida
|
Can use fast mode=Si può usare la modalità rapida
|
||||||
Can fly through solid nodes using noclip mode=Si può volare attraverso i nodi solidi con la modalità incorporea
|
Can fly through solid nodes using noclip mode=Si può volare attraverso i nodi solidi con la modalità incorporea
|
||||||
Can use the rollback functionality=Si può usare la funzione di rollback
|
Can use the rollback functionality=Si può usare la funzione di rollback
|
||||||
Allows enabling various debug options that may affect gameplay=Permette di abilitare varie opzioni di debug che potrebbero influenzare l'esperienza di gioco
|
Can view more debug info that might give a gameplay advantage=
|
||||||
|
Can enable wireframe=
|
||||||
Unknown Item=Oggetto sconosciuto
|
Unknown Item=Oggetto sconosciuto
|
||||||
Air=Aria
|
Air=Aria
|
||||||
Ignore=Ignora
|
Ignore=Ignora
|
||||||
|
@ -244,6 +247,10 @@ Profile saved to @1=
|
||||||
|
|
||||||
##### not used anymore #####
|
##### not used anymore #####
|
||||||
|
|
||||||
|
Invalid time.=Orario non valido.
|
||||||
|
[all | privs | <cmd>]=[all | privs | <comando>]
|
||||||
|
Get help for commands or list privileges=Richiama la finestra d'aiuto dei comandi o dei privilegi
|
||||||
|
Allows enabling various debug options that may affect gameplay=Permette di abilitare varie opzioni di debug che potrebbero influenzare l'esperienza di gioco
|
||||||
[<delay_in_seconds> | -1] [reconnect] [<message>]=[<ritardo_in_secondi> | -1] [reconnect] [<messaggio>]
|
[<delay_in_seconds> | -1] [reconnect] [<message>]=[<ritardo_in_secondi> | -1] [reconnect] [<messaggio>]
|
||||||
Shutdown server (-1 cancels a delayed shutdown)=Arresta il server (-1 annulla un arresto programmato)
|
Shutdown server (-1 cancels a delayed shutdown)=Arresta il server (-1 annulla un arresto programmato)
|
||||||
<name> (<privilege> | all)=<nome> (<privilegio> | all)
|
<name> (<privilege> | all)=<nome> (<privilegio> | all)
|
||||||
|
|
|
@ -139,7 +139,7 @@ This command was disabled by a mod or game.=
|
||||||
Show or set time of day=
|
Show or set time of day=
|
||||||
Current time is @1:@2.=
|
Current time is @1:@2.=
|
||||||
You don't have permission to run this command (missing privilege: @1).=
|
You don't have permission to run this command (missing privilege: @1).=
|
||||||
Invalid time.=
|
Invalid time (must be between 0 and 24000).=
|
||||||
Time of day changed.=
|
Time of day changed.=
|
||||||
Invalid hour (must be between 0 and 23 inclusive).=
|
Invalid hour (must be between 0 and 23 inclusive).=
|
||||||
Invalid minute (must be between 0 and 59 inclusive).=
|
Invalid minute (must be between 0 and 59 inclusive).=
|
||||||
|
@ -187,12 +187,14 @@ You are already dead.=
|
||||||
@1 is already dead.=
|
@1 is already dead.=
|
||||||
@1 has been killed.=
|
@1 has been killed.=
|
||||||
Kill player or yourself=
|
Kill player or yourself=
|
||||||
|
Invalid parameters (see /help @1).=
|
||||||
|
Too many arguments, try using just /help <command>=
|
||||||
Available commands: @1=
|
Available commands: @1=
|
||||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=
|
Use '/help <cmd>' to get more information, or '/help all' to list everything.=
|
||||||
Available commands:=
|
Available commands:=
|
||||||
Command not available: @1=
|
Command not available: @1=
|
||||||
[all | privs | <cmd>]=
|
[all | privs | <cmd>] [-t]=
|
||||||
Get help for commands or list privileges=
|
Get help for commands or list privileges (-t: output in chat)=
|
||||||
Available privileges:=
|
Available privileges:=
|
||||||
Command=
|
Command=
|
||||||
Parameters=
|
Parameters=
|
||||||
|
@ -230,7 +232,8 @@ Can use fly mode=
|
||||||
Can use fast mode=
|
Can use fast mode=
|
||||||
Can fly through solid nodes using noclip mode=
|
Can fly through solid nodes using noclip mode=
|
||||||
Can use the rollback functionality=
|
Can use the rollback functionality=
|
||||||
Allows enabling various debug options that may affect gameplay=
|
Can view more debug info that might give a gameplay advantage=
|
||||||
|
Can enable wireframe=
|
||||||
Unknown Item=
|
Unknown Item=
|
||||||
Air=
|
Air=
|
||||||
Ignore=
|
Ignore=
|
||||||
|
|
|
@ -119,31 +119,27 @@ function render_serverlist_row(spec)
|
||||||
|
|
||||||
return table.concat(details, ",")
|
return table.concat(details, ",")
|
||||||
end
|
end
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
os.tempfolder = function()
|
|
||||||
local temp = core.get_temp_path()
|
|
||||||
return temp .. DIR_DELIM .. "MT_" .. math.random(0, 10000)
|
|
||||||
end
|
|
||||||
|
|
||||||
os.tmpname = function()
|
os.tmpname = function()
|
||||||
local path = os.tempfolder()
|
error('do not use') -- instead use core.get_temp_path()
|
||||||
io.open(path, "w"):close()
|
|
||||||
return path
|
|
||||||
end
|
end
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
function menu_render_worldlist()
|
function menu_render_worldlist(show_gameid)
|
||||||
local retval = ""
|
local retval = {}
|
||||||
local current_worldlist = menudata.worldlist:get_list()
|
local current_worldlist = menudata.worldlist:get_list()
|
||||||
|
|
||||||
|
local row
|
||||||
for i, v in ipairs(current_worldlist) do
|
for i, v in ipairs(current_worldlist) do
|
||||||
if retval ~= "" then retval = retval .. "," end
|
row = v.name
|
||||||
retval = retval .. core.formspec_escape(v.name) ..
|
if show_gameid == nil or show_gameid == true then
|
||||||
" \\[" .. core.formspec_escape(v.gameid) .. "\\]"
|
row = row .. " [" .. v.gameid .. "]"
|
||||||
|
end
|
||||||
|
retval[#retval+1] = core.formspec_escape(row)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return retval
|
return table.concat(retval, ",")
|
||||||
end
|
end
|
||||||
|
|
||||||
function menu_handle_key_up_down(fields, textlist, settingname)
|
function menu_handle_key_up_down(fields, textlist, settingname)
|
||||||
|
|
|
@ -163,10 +163,13 @@ local function get_formspec(data)
|
||||||
"button[8.95,0.125;2.5,0.5;btn_enable_all_mods;" ..
|
"button[8.95,0.125;2.5,0.5;btn_enable_all_mods;" ..
|
||||||
fgettext("Enable all") .. "]"
|
fgettext("Enable all") .. "]"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local use_technical_names = core.settings:get_bool("show_technical_names")
|
||||||
|
|
||||||
return retval ..
|
return retval ..
|
||||||
"tablecolumns[color;tree;text]" ..
|
"tablecolumns[color;tree;text]" ..
|
||||||
"table[5.5,0.75;5.75,6;world_config_modlist;" ..
|
"table[5.5,0.75;5.75,6;world_config_modlist;" ..
|
||||||
pkgmgr.render_packagelist(data.list) .. ";" .. data.selected_mod .."]"
|
pkgmgr.render_packagelist(data.list, use_technical_names) .. ";" .. data.selected_mod .."]"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function handle_buttons(this, fields)
|
local function handle_buttons(this, fields)
|
||||||
|
@ -205,14 +208,19 @@ local function handle_buttons(this, fields)
|
||||||
local mods = worldfile:to_table()
|
local mods = worldfile:to_table()
|
||||||
|
|
||||||
local rawlist = this.data.list:get_raw_list()
|
local rawlist = this.data.list:get_raw_list()
|
||||||
|
local was_set = {}
|
||||||
|
|
||||||
for i = 1, #rawlist do
|
for i = 1, #rawlist do
|
||||||
local mod = rawlist[i]
|
local mod = rawlist[i]
|
||||||
if not mod.is_modpack and
|
if not mod.is_modpack and
|
||||||
not mod.is_game_content then
|
not mod.is_game_content then
|
||||||
if modname_valid(mod.name) then
|
if modname_valid(mod.name) then
|
||||||
worldfile:set("load_mod_" .. mod.name,
|
if mod.enabled then
|
||||||
mod.enabled and "true" or "false")
|
worldfile:set("load_mod_" .. mod.name, mod.virtual_path)
|
||||||
|
was_set[mod.name] = true
|
||||||
|
elseif not was_set[mod.name] then
|
||||||
|
worldfile:set("load_mod_" .. mod.name, "false")
|
||||||
|
end
|
||||||
elseif mod.enabled then
|
elseif mod.enabled then
|
||||||
gamedata.errormessage = fgettext_ne("Failed to enable mo" ..
|
gamedata.errormessage = fgettext_ne("Failed to enable mo" ..
|
||||||
"d \"$1\" as it contains disallowed characters. " ..
|
"d \"$1\" as it contains disallowed characters. " ..
|
||||||
|
@ -256,12 +264,26 @@ local function handle_buttons(this, fields)
|
||||||
if fields.btn_enable_all_mods then
|
if fields.btn_enable_all_mods then
|
||||||
local list = this.data.list:get_raw_list()
|
local list = this.data.list:get_raw_list()
|
||||||
|
|
||||||
|
-- When multiple copies of a mod are installed, we need to avoid enabling multiple of them
|
||||||
|
-- at a time. So lets first collect all the enabled mods, and then use this to exclude
|
||||||
|
-- multiple enables.
|
||||||
|
|
||||||
|
local was_enabled = {}
|
||||||
for i = 1, #list do
|
for i = 1, #list do
|
||||||
if not list[i].is_game_content
|
if not list[i].is_game_content
|
||||||
and not list[i].is_modpack then
|
and not list[i].is_modpack and list[i].enabled then
|
||||||
list[i].enabled = true
|
was_enabled[list[i].name] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for i = 1, #list do
|
||||||
|
if not list[i].is_game_content and not list[i].is_modpack and
|
||||||
|
not was_enabled[list[i].name] then
|
||||||
|
list[i].enabled = true
|
||||||
|
was_enabled[list[i].name] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
enabled_all = true
|
enabled_all = true
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,7 +25,7 @@ end
|
||||||
|
|
||||||
-- Unordered preserves the original order of the ContentDB API,
|
-- Unordered preserves the original order of the ContentDB API,
|
||||||
-- before the package list is ordered based on installed state.
|
-- before the package list is ordered based on installed state.
|
||||||
local store = { packages = {}, packages_full = {}, packages_full_unordered = {} }
|
local store = { packages = {}, packages_full = {}, packages_full_unordered = {}, aliases = {} }
|
||||||
|
|
||||||
local http = core.get_http_api()
|
local http = core.get_http_api()
|
||||||
|
|
||||||
|
@ -62,9 +62,19 @@ local REASON_UPDATE = "update"
|
||||||
local REASON_DEPENDENCY = "dependency"
|
local REASON_DEPENDENCY = "dependency"
|
||||||
|
|
||||||
|
|
||||||
|
-- encodes for use as URL parameter or path component
|
||||||
|
local function urlencode(str)
|
||||||
|
return str:gsub("[^%a%d()._~-]", function(char)
|
||||||
|
return string.format("%%%02X", string.byte(char))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
assert(urlencode("sample text?") == "sample%20text%3F")
|
||||||
|
|
||||||
|
|
||||||
local function get_download_url(package, reason)
|
local function get_download_url(package, reason)
|
||||||
local base_url = core.settings:get("contentdb_url")
|
local base_url = core.settings:get("contentdb_url")
|
||||||
local ret = base_url .. ("/packages/%s/%s/releases/%d/download/"):format(package.author, package.name, package.release)
|
local ret = base_url .. ("/packages/%s/releases/%d/download/"):format(
|
||||||
|
package.url_part, package.release)
|
||||||
if reason then
|
if reason then
|
||||||
ret = ret .. "?reason=" .. reason
|
ret = ret .. "?reason=" .. reason
|
||||||
end
|
end
|
||||||
|
@ -72,34 +82,52 @@ local function get_download_url(package, reason)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function download_package(param)
|
local function download_and_extract(param)
|
||||||
if core.download_file(param.url, param.filename) then
|
local package = param.package
|
||||||
|
|
||||||
|
local filename = core.get_temp_path(true)
|
||||||
|
if filename == "" or not core.download_file(param.url, filename) then
|
||||||
|
core.log("error", "Downloading " .. dump(param.url) .. " failed")
|
||||||
return {
|
return {
|
||||||
filename = param.filename,
|
msg = fgettext("Failed to download $1", package.name)
|
||||||
successful = true,
|
|
||||||
}
|
|
||||||
else
|
|
||||||
core.log("error", "downloading " .. dump(param.url) .. " failed")
|
|
||||||
return {
|
|
||||||
successful = false,
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local tempfolder = core.get_temp_path()
|
||||||
|
if tempfolder ~= "" then
|
||||||
|
tempfolder = tempfolder .. DIR_DELIM .. "MT_" .. math.random(1, 1024000)
|
||||||
|
if not core.extract_zip(filename, tempfolder) then
|
||||||
|
tempfolder = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
tempfolder = nil
|
||||||
|
end
|
||||||
|
os.remove(filename)
|
||||||
|
if not tempfolder then
|
||||||
|
return {
|
||||||
|
msg = fgettext("Install: Unsupported file type or broken archive"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
path = tempfolder
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function start_install(package, reason)
|
local function start_install(package, reason)
|
||||||
local params = {
|
local params = {
|
||||||
package = package,
|
package = package,
|
||||||
url = get_download_url(package, reason),
|
url = get_download_url(package, reason),
|
||||||
filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
number_downloading = number_downloading + 1
|
number_downloading = number_downloading + 1
|
||||||
|
|
||||||
local function callback(result)
|
local function callback(result)
|
||||||
if result.successful then
|
if result.msg then
|
||||||
local path, msg = pkgmgr.install(package.type,
|
gamedata.errormessage = result.msg
|
||||||
result.filename, package.name,
|
else
|
||||||
package.path)
|
local path, msg = pkgmgr.install_dir(package.type, result.path, package.name, package.path)
|
||||||
|
core.delete_dir(result.path)
|
||||||
if not path then
|
if not path then
|
||||||
gamedata.errormessage = msg
|
gamedata.errormessage = msg
|
||||||
else
|
else
|
||||||
|
@ -137,9 +165,6 @@ local function start_install(package, reason)
|
||||||
conf:write()
|
conf:write()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
os.remove(result.filename)
|
|
||||||
else
|
|
||||||
gamedata.errormessage = fgettext("Failed to download $1", package.name)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
package.downloading = false
|
package.downloading = false
|
||||||
|
@ -159,7 +184,7 @@ local function start_install(package, reason)
|
||||||
package.queued = false
|
package.queued = false
|
||||||
package.downloading = true
|
package.downloading = true
|
||||||
|
|
||||||
if not core.handle_async(download_package, params, callback) then
|
if not core.handle_async(download_and_extract, params, callback) then
|
||||||
core.log("error", "ERROR: async event failed")
|
core.log("error", "ERROR: async event failed")
|
||||||
gamedata.errormessage = fgettext("Failed to download $1", package.name)
|
gamedata.errormessage = fgettext("Failed to download $1", package.name)
|
||||||
return
|
return
|
||||||
|
@ -184,7 +209,7 @@ local function get_raw_dependencies(package)
|
||||||
local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
|
local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
|
||||||
local version = core.get_version()
|
local version = core.get_version()
|
||||||
local base_url = core.settings:get("contentdb_url")
|
local base_url = core.settings:get("contentdb_url")
|
||||||
local url = base_url .. url_fmt:format(package.id, core.get_max_supp_proto(), version.string)
|
local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), urlencode(version.string))
|
||||||
|
|
||||||
local response = http.fetch_sync({ url = url })
|
local response = http.fetch_sync({ url = url })
|
||||||
if not response.succeeded then
|
if not response.succeeded then
|
||||||
|
@ -559,17 +584,16 @@ function store.load()
|
||||||
local base_url = core.settings:get("contentdb_url")
|
local base_url = core.settings:get("contentdb_url")
|
||||||
local url = base_url ..
|
local url = base_url ..
|
||||||
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
||||||
core.get_max_supp_proto() .. "&engine_version=" .. version.string
|
core.get_max_supp_proto() .. "&engine_version=" .. urlencode(version.string)
|
||||||
|
|
||||||
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
|
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
|
||||||
item = item:trim()
|
item = item:trim()
|
||||||
if item ~= "" then
|
if item ~= "" then
|
||||||
url = url .. "&hide=" .. item
|
url = url .. "&hide=" .. urlencode(item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local timeout = tonumber(core.settings:get("curl_file_download_timeout"))
|
local response = http.fetch_sync({ url = url })
|
||||||
local response = http.fetch_sync({ url = url, timeout = timeout })
|
|
||||||
if not response.succeeded then
|
if not response.succeeded then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -579,12 +603,16 @@ function store.load()
|
||||||
|
|
||||||
for _, package in pairs(store.packages_full) do
|
for _, package in pairs(store.packages_full) do
|
||||||
local name_len = #package.name
|
local name_len = #package.name
|
||||||
|
-- This must match what store.update_paths() does!
|
||||||
|
package.id = package.author:lower() .. "/"
|
||||||
if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then
|
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)
|
package.id = package.id .. package.name:sub(1, name_len - 5)
|
||||||
else
|
else
|
||||||
package.id = package.author:lower() .. "/" .. package.name
|
package.id = package.id .. package.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
package.url_part = urlencode(package.author) .. "/" .. urlencode(package.name)
|
||||||
|
|
||||||
if package.aliases then
|
if package.aliases then
|
||||||
for _, alias in ipairs(package.aliases) do
|
for _, alias in ipairs(package.aliases) do
|
||||||
-- We currently don't support name changing
|
-- We currently don't support name changing
|
||||||
|
@ -834,8 +862,7 @@ function store.get_formspec(dlgdata)
|
||||||
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
|
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
|
||||||
elseif package.queued then
|
elseif package.queued then
|
||||||
formspec[#formspec + 1] = left_base
|
formspec[#formspec + 1] = left_base
|
||||||
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
|
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
|
||||||
formspec[#formspec + 1] = "cdb_queued.png;queued]"
|
|
||||||
elseif not package.path then
|
elseif not package.path then
|
||||||
local elem_name = "install_" .. i .. ";"
|
local elem_name = "install_" .. i .. ";"
|
||||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
|
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
|
||||||
|
@ -998,9 +1025,9 @@ function store.handle_submit(this, fields)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fields["view_" .. i] then
|
if fields["view_" .. i] then
|
||||||
local url = ("%s/packages/%s/%s?protocol_version=%d"):format(
|
local url = ("%s/packages/%s?protocol_version=%d"):format(
|
||||||
core.settings:get("contentdb_url"),
|
core.settings:get("contentdb_url"), package.url_part,
|
||||||
package.author, package.name, core.get_max_supp_proto())
|
core.get_max_supp_proto())
|
||||||
core.open_url(url)
|
core.open_url(url)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
local worldname = ""
|
-- cf. tab_local, the gamebar already provides game selection so we hide the list from here
|
||||||
|
local hide_gamelist = PLATFORM ~= "Android"
|
||||||
|
|
||||||
local function table_to_flags(ftable)
|
local function table_to_flags(ftable)
|
||||||
-- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"
|
-- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"
|
||||||
|
@ -31,9 +32,8 @@ local function strflag(flags, flag)
|
||||||
return (flags[flag] == true) and "true" or "false"
|
return (flags[flag] == true) and "true" or "false"
|
||||||
end
|
end
|
||||||
|
|
||||||
local cb_caverns = { "caverns", fgettext("Caverns"), "caverns",
|
local cb_caverns = { "caverns", fgettext("Caverns"),
|
||||||
fgettext("Very large caverns deep in the underground") }
|
fgettext("Very large caverns deep in the underground") }
|
||||||
local tt_sea_rivers = fgettext("Sea level rivers")
|
|
||||||
|
|
||||||
local flag_checkboxes = {
|
local flag_checkboxes = {
|
||||||
v5 = {
|
v5 = {
|
||||||
|
@ -41,39 +41,38 @@ local flag_checkboxes = {
|
||||||
},
|
},
|
||||||
v7 = {
|
v7 = {
|
||||||
cb_caverns,
|
cb_caverns,
|
||||||
{ "ridges", fgettext("Rivers"), "ridges", tt_sea_rivers },
|
{ "ridges", fgettext("Rivers"), fgettext("Sea level rivers") },
|
||||||
{ "mountains", fgettext("Mountains"), "mountains" },
|
{ "mountains", fgettext("Mountains") },
|
||||||
{ "floatlands", fgettext("Floatlands (experimental)"), "floatlands",
|
{ "floatlands", fgettext("Floatlands (experimental)"),
|
||||||
fgettext("Floating landmasses in the sky") },
|
fgettext("Floating landmasses in the sky") },
|
||||||
},
|
},
|
||||||
carpathian = {
|
carpathian = {
|
||||||
cb_caverns,
|
cb_caverns,
|
||||||
{ "rivers", fgettext("Rivers"), "rivers", tt_sea_rivers },
|
{ "rivers", fgettext("Rivers"), fgettext("Sea level rivers") },
|
||||||
},
|
},
|
||||||
valleys = {
|
valleys = {
|
||||||
{ "altitude-chill", fgettext("Altitude chill"), "altitude_chill",
|
{ "altitude_chill", fgettext("Altitude chill"),
|
||||||
fgettext("Reduces heat with altitude") },
|
fgettext("Reduces heat with altitude") },
|
||||||
{ "altitude-dry", fgettext("Altitude dry"), "altitude_dry",
|
{ "altitude_dry", fgettext("Altitude dry"),
|
||||||
fgettext("Reduces humidity with altitude") },
|
fgettext("Reduces humidity with altitude") },
|
||||||
{ "humid-rivers", fgettext("Humid rivers"), "humid_rivers",
|
{ "humid_rivers", fgettext("Humid rivers"),
|
||||||
fgettext("Increases humidity around rivers") },
|
fgettext("Increases humidity around rivers") },
|
||||||
{ "vary-river-depth", fgettext("Vary river depth"), "vary_river_depth",
|
{ "vary_river_depth", fgettext("Vary river depth"),
|
||||||
fgettext("Low humidity and high heat causes shallow or dry rivers") },
|
fgettext("Low humidity and high heat causes shallow or dry rivers") },
|
||||||
},
|
},
|
||||||
flat = {
|
flat = {
|
||||||
cb_caverns,
|
cb_caverns,
|
||||||
{ "hills", fgettext("Hills"), "hills" },
|
{ "hills", fgettext("Hills") },
|
||||||
{ "lakes", fgettext("Lakes"), "lakes" },
|
{ "lakes", fgettext("Lakes") },
|
||||||
},
|
},
|
||||||
fractal = {
|
fractal = {
|
||||||
{ "terrain", fgettext("Additional terrain"), "terrain",
|
{ "terrain", fgettext("Additional terrain"),
|
||||||
fgettext("Generate non-fractal terrain: Oceans and underground") },
|
fgettext("Generate non-fractal terrain: Oceans and underground") },
|
||||||
},
|
},
|
||||||
v6 = {
|
v6 = {
|
||||||
{ "trees", fgettext("Trees and jungle grass"), "trees" },
|
{ "trees", fgettext("Trees and jungle grass") },
|
||||||
{ "flat", fgettext("Flat terrain"), "flat" },
|
{ "flat", fgettext("Flat terrain") },
|
||||||
{ "mudflow", fgettext("Mud flow"), "mudflow",
|
{ "mudflow", fgettext("Mud flow"), fgettext("Terrain surface erosion") },
|
||||||
fgettext("Terrain surface erosion") },
|
|
||||||
-- Biome settings are in mgv6_biomes below
|
-- Biome settings are in mgv6_biomes below
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -105,38 +104,26 @@ local function create_world_formspec(dialogdata)
|
||||||
"button[4.75,2.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
|
"button[4.75,2.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local current_mg = dialogdata.mg
|
||||||
local mapgens = core.get_mapgen_names()
|
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 gameid = core.settings:get("menu_last_game")
|
||||||
|
|
||||||
local flags = {
|
local flags = dialogdata.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
|
local game, gameidx = pkgmgr.find_by_gameid(gameid)
|
||||||
if gameid ~= nil then
|
if game == nil and hide_gamelist then
|
||||||
local _
|
-- should never happen but just pick the first game
|
||||||
_, gameidx = pkgmgr.find_by_gameid(gameid)
|
game = pkgmgr.get_game(1)
|
||||||
|
gameidx = 1
|
||||||
if gameidx == nil then
|
core.settings:set("menu_last_game", game.id)
|
||||||
gameidx = 0
|
elseif game == nil then
|
||||||
end
|
gameidx = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local game_by_gameidx = core.get_game(gameidx)
|
|
||||||
local disallowed_mapgen_settings = {}
|
local disallowed_mapgen_settings = {}
|
||||||
if game_by_gameidx ~= nil then
|
if game ~= nil then
|
||||||
local gamepath = game_by_gameidx.path
|
local gameconfig = Settings(game.path.."/game.conf")
|
||||||
local gameconfig = Settings(gamepath.."/game.conf")
|
|
||||||
|
|
||||||
local allowed_mapgens = (gameconfig:get("allowed_mapgens") or ""):split()
|
local allowed_mapgens = (gameconfig:get("allowed_mapgens") or ""):split()
|
||||||
for key, value in pairs(allowed_mapgens) do
|
for key, value in pairs(allowed_mapgens) do
|
||||||
|
@ -156,7 +143,7 @@ local function create_world_formspec(dialogdata)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if disallowed_mapgens then
|
if #disallowed_mapgens > 0 then
|
||||||
for i = #mapgens, 1, -1 do
|
for i = #mapgens, 1, -1 do
|
||||||
if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then
|
if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then
|
||||||
table.remove(mapgens, i)
|
table.remove(mapgens, i)
|
||||||
|
@ -172,23 +159,29 @@ local function create_world_formspec(dialogdata)
|
||||||
|
|
||||||
local mglist = ""
|
local mglist = ""
|
||||||
local selindex
|
local selindex
|
||||||
local i = 1
|
do -- build the list of mapgens
|
||||||
local first_mg
|
local i = 1
|
||||||
for k,v in pairs(mapgens) do
|
local first_mg
|
||||||
if not first_mg then
|
for k, v in pairs(mapgens) do
|
||||||
first_mg = v
|
if not first_mg then
|
||||||
|
first_mg = v
|
||||||
|
end
|
||||||
|
if current_mg == v then
|
||||||
|
selindex = i
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
mglist = mglist .. core.formspec_escape(v) .. ","
|
||||||
end
|
end
|
||||||
if current_mg == v then
|
if not selindex then
|
||||||
selindex = i
|
selindex = 1
|
||||||
|
current_mg = first_mg
|
||||||
end
|
end
|
||||||
i = i + 1
|
mglist = mglist:sub(1, -2)
|
||||||
mglist = mglist .. v .. ","
|
|
||||||
end
|
end
|
||||||
if not selindex then
|
|
||||||
selindex = 1
|
-- The logic of the flag element IDs is as follows:
|
||||||
current_mg = first_mg
|
-- "flag_main_foo-bar-baz" controls dialogdata.flags["main"]["foo_bar_baz"]
|
||||||
end
|
-- see the buttonhandler for the implementation of this
|
||||||
mglist = mglist:sub(1, -2)
|
|
||||||
|
|
||||||
local mg_main_flags = function(mapgen, y)
|
local mg_main_flags = function(mapgen, y)
|
||||||
if mapgen == "singlenode" then
|
if mapgen == "singlenode" then
|
||||||
|
@ -198,11 +191,11 @@ local function create_world_formspec(dialogdata)
|
||||||
return "", y
|
return "", y
|
||||||
end
|
end
|
||||||
|
|
||||||
local form = "checkbox[0," .. y .. ";flag_mg_caves;" ..
|
local form = "checkbox[0," .. y .. ";flag_main_caves;" ..
|
||||||
fgettext("Caves") .. ";"..strflag(flags.main, "caves").."]"
|
fgettext("Caves") .. ";"..strflag(flags.main, "caves").."]"
|
||||||
y = y + 0.5
|
y = y + 0.5
|
||||||
|
|
||||||
form = form .. "checkbox[0,"..y..";flag_mg_dungeons;" ..
|
form = form .. "checkbox[0,"..y..";flag_main_dungeons;" ..
|
||||||
fgettext("Dungeons") .. ";"..strflag(flags.main, "dungeons").."]"
|
fgettext("Dungeons") .. ";"..strflag(flags.main, "dungeons").."]"
|
||||||
y = y + 0.5
|
y = y + 0.5
|
||||||
|
|
||||||
|
@ -213,7 +206,7 @@ local function create_world_formspec(dialogdata)
|
||||||
else
|
else
|
||||||
d_tt = fgettext("Structures appearing on the terrain, typically trees and plants")
|
d_tt = fgettext("Structures appearing on the terrain, typically trees and plants")
|
||||||
end
|
end
|
||||||
form = form .. "checkbox[0,"..y..";flag_mg_decorations;" ..
|
form = form .. "checkbox[0,"..y..";flag_main_decorations;" ..
|
||||||
d_name .. ";" ..
|
d_name .. ";" ..
|
||||||
strflag(flags.main, "decorations").."]" ..
|
strflag(flags.main, "decorations").."]" ..
|
||||||
"tooltip[flag_mg_decorations;" ..
|
"tooltip[flag_mg_decorations;" ..
|
||||||
|
@ -221,7 +214,7 @@ local function create_world_formspec(dialogdata)
|
||||||
"]"
|
"]"
|
||||||
y = y + 0.5
|
y = y + 0.5
|
||||||
|
|
||||||
form = form .. "tooltip[flag_mg_caves;" ..
|
form = form .. "tooltip[flag_main_caves;" ..
|
||||||
fgettext("Network of tunnels and caves")
|
fgettext("Network of tunnels and caves")
|
||||||
.. "]"
|
.. "]"
|
||||||
return form, y
|
return form, y
|
||||||
|
@ -235,13 +228,13 @@ local function create_world_formspec(dialogdata)
|
||||||
return "", y
|
return "", y
|
||||||
end
|
end
|
||||||
local form = ""
|
local form = ""
|
||||||
for _,tab in pairs(flag_checkboxes[mapgen]) do
|
for _, tab in pairs(flag_checkboxes[mapgen]) do
|
||||||
local id = "flag_mg"..mapgen.."_"..tab[1]
|
local id = "flag_"..mapgen.."_"..tab[1]:gsub("_", "-")
|
||||||
form = form .. ("checkbox[0,%f;%s;%s;%s]"):
|
form = form .. ("checkbox[0,%f;%s;%s;%s]"):
|
||||||
format(y, id, tab[2], strflag(flags[mapgen], tab[3]))
|
format(y, id, tab[2], strflag(flags[mapgen], tab[1]))
|
||||||
|
|
||||||
if tab[4] then
|
if tab[3] then
|
||||||
form = form .. "tooltip["..id..";"..tab[4].."]"
|
form = form .. "tooltip["..id..";"..tab[3].."]"
|
||||||
end
|
end
|
||||||
y = y + 0.5
|
y = y + 0.5
|
||||||
end
|
end
|
||||||
|
@ -277,16 +270,14 @@ local function create_world_formspec(dialogdata)
|
||||||
|
|
||||||
-- biomeblend
|
-- biomeblend
|
||||||
y = y + 0.55
|
y = y + 0.55
|
||||||
form = form .. "checkbox[0,"..y..";flag_mgv6_biomeblend;" ..
|
form = form .. "checkbox[0,"..y..";flag_v6_biomeblend;" ..
|
||||||
fgettext("Biome blending") .. ";"..strflag(flags.v6, "biomeblend").."]" ..
|
fgettext("Biome blending") .. ";"..strflag(flags.v6, "biomeblend").."]" ..
|
||||||
"tooltip[flag_mgv6_biomeblend;" ..
|
"tooltip[flag_v6_biomeblend;" ..
|
||||||
fgettext("Smooth transition between biomes") .. "]"
|
fgettext("Smooth transition between biomes") .. "]"
|
||||||
|
|
||||||
return form, y
|
return form, y
|
||||||
end
|
end
|
||||||
|
|
||||||
current_seed = core.formspec_escape(current_seed)
|
|
||||||
|
|
||||||
local y_start = 0.0
|
local y_start = 0.0
|
||||||
local y = y_start
|
local y = y_start
|
||||||
local str_flags, str_spflags
|
local str_flags, str_spflags
|
||||||
|
@ -323,21 +314,32 @@ local function create_world_formspec(dialogdata)
|
||||||
"container[0,0]"..
|
"container[0,0]"..
|
||||||
"field[0.3,0.6;6,0.5;te_world_name;" ..
|
"field[0.3,0.6;6,0.5;te_world_name;" ..
|
||||||
fgettext("World name") ..
|
fgettext("World name") ..
|
||||||
";" .. core.formspec_escape(worldname) .. "]" ..
|
";" .. core.formspec_escape(dialogdata.worldname) .. "]" ..
|
||||||
|
"set_focus[te_world_name;false]"
|
||||||
|
|
||||||
"field[0.3,1.7;6,0.5;te_seed;" ..
|
if not disallowed_mapgen_settings["seed"] then
|
||||||
fgettext("Seed") ..
|
|
||||||
";".. current_seed .. "]" ..
|
|
||||||
|
|
||||||
|
retval = retval .. "field[0.3,1.7;6,0.5;te_seed;" ..
|
||||||
|
fgettext("Seed") ..
|
||||||
|
";".. core.formspec_escape(dialogdata.seed) .. "]"
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
retval = retval ..
|
||||||
"label[0,2;" .. fgettext("Mapgen") .. "]"..
|
"label[0,2;" .. fgettext("Mapgen") .. "]"..
|
||||||
"dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" ..
|
"dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]"
|
||||||
|
|
||||||
"label[0,3.35;" .. fgettext("Game") .. "]"..
|
if not hide_gamelist or devtest_only ~= "" then
|
||||||
"textlist[0,3.85;5.8,"..gamelist_height..";games;" ..
|
retval = retval ..
|
||||||
pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" ..
|
"label[0,3.35;" .. fgettext("Game") .. "]"..
|
||||||
"container[0,4.5]" ..
|
"textlist[0,3.85;5.8,"..gamelist_height..";games;" ..
|
||||||
devtest_only ..
|
pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" ..
|
||||||
"container_end[]" ..
|
"container[0,4.5]" ..
|
||||||
|
devtest_only ..
|
||||||
|
"container_end[]"
|
||||||
|
end
|
||||||
|
|
||||||
|
retval = retval ..
|
||||||
"container_end[]" ..
|
"container_end[]" ..
|
||||||
|
|
||||||
-- Right side
|
-- Right side
|
||||||
|
@ -360,9 +362,20 @@ local function create_world_buttonhandler(this, fields)
|
||||||
fields["key_enter"] then
|
fields["key_enter"] then
|
||||||
|
|
||||||
local worldname = fields["te_world_name"]
|
local worldname = fields["te_world_name"]
|
||||||
local gameindex = core.get_textlist_index("games")
|
local game, gameindex
|
||||||
|
if hide_gamelist then
|
||||||
|
game, gameindex = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
|
||||||
|
else
|
||||||
|
gameindex = core.get_textlist_index("games")
|
||||||
|
game = pkgmgr.get_game(gameindex)
|
||||||
|
end
|
||||||
|
|
||||||
if gameindex ~= nil then
|
local message
|
||||||
|
if game == nil then
|
||||||
|
message = fgettext("No game selected")
|
||||||
|
end
|
||||||
|
|
||||||
|
if message == nil then
|
||||||
-- For unnamed worlds use the generated name 'world<number>',
|
-- For unnamed worlds use the generated name 'world<number>',
|
||||||
-- where the number increments: it is set to 1 larger than the largest
|
-- where the number increments: it is set to 1 larger than the largest
|
||||||
-- generated name number found.
|
-- generated name number found.
|
||||||
|
@ -377,36 +390,48 @@ local function create_world_buttonhandler(this, fields)
|
||||||
worldname = "world" .. worldnum_max + 1
|
worldname = "world" .. worldnum_max + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
core.settings:set("fixed_map_seed", fields["te_seed"])
|
if menudata.worldlist:uid_exists_raw(worldname) then
|
||||||
|
|
||||||
local message
|
|
||||||
if not menudata.worldlist:uid_exists_raw(worldname) then
|
|
||||||
core.settings:set("mg_name",fields["dd_mapgen"])
|
|
||||||
message = core.create_world(worldname,gameindex)
|
|
||||||
else
|
|
||||||
message = fgettext("A world named \"$1\" already exists", worldname)
|
message = fgettext("A world named \"$1\" already exists", worldname)
|
||||||
end
|
end
|
||||||
|
|
||||||
if message ~= nil then
|
|
||||||
gamedata.errormessage = message
|
|
||||||
else
|
|
||||||
core.settings:set("menu_last_game",pkgmgr.games[gameindex].id)
|
|
||||||
if this.data.update_worldlist_filter then
|
|
||||||
menudata.worldlist:set_filtercriteria(pkgmgr.games[gameindex].id)
|
|
||||||
mm_texture.update("singleplayer", pkgmgr.games[gameindex].id)
|
|
||||||
end
|
|
||||||
menudata.worldlist:refresh()
|
|
||||||
core.settings:set("mainmenu_last_selected_world",
|
|
||||||
menudata.worldlist:raw_index_by_uid(worldname))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
gamedata.errormessage = fgettext("No game selected")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if message == nil then
|
||||||
|
this.data.seed = fields["te_seed"] or ""
|
||||||
|
this.data.mg = fields["dd_mapgen"]
|
||||||
|
|
||||||
|
-- actual names as used by engine
|
||||||
|
local settings = {
|
||||||
|
fixed_map_seed = this.data.seed,
|
||||||
|
mg_name = this.data.mg,
|
||||||
|
mg_flags = table_to_flags(this.data.flags.main),
|
||||||
|
mgv5_spflags = table_to_flags(this.data.flags.v5),
|
||||||
|
mgv6_spflags = table_to_flags(this.data.flags.v6),
|
||||||
|
mgv7_spflags = table_to_flags(this.data.flags.v7),
|
||||||
|
mgfractal_spflags = table_to_flags(this.data.flags.fractal),
|
||||||
|
mgcarpathian_spflags = table_to_flags(this.data.flags.carpathian),
|
||||||
|
mgvalleys_spflags = table_to_flags(this.data.flags.valleys),
|
||||||
|
mgflat_spflags = table_to_flags(this.data.flags.flat),
|
||||||
|
}
|
||||||
|
message = core.create_world(worldname, gameindex, settings)
|
||||||
|
end
|
||||||
|
|
||||||
|
if message == nil then
|
||||||
|
core.settings:set("menu_last_game", game.id)
|
||||||
|
if this.data.update_worldlist_filter then
|
||||||
|
menudata.worldlist:set_filtercriteria(game.id)
|
||||||
|
end
|
||||||
|
menudata.worldlist:refresh()
|
||||||
|
core.settings:set("mainmenu_last_selected_world",
|
||||||
|
menudata.worldlist:raw_index_by_uid(worldname))
|
||||||
|
end
|
||||||
|
|
||||||
|
gamedata.errormessage = message
|
||||||
this:delete()
|
this:delete()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
worldname = fields.te_world_name
|
this.data.worldname = fields["te_world_name"]
|
||||||
|
this.data.seed = fields["te_seed"] or ""
|
||||||
|
|
||||||
if fields["games"] then
|
if fields["games"] then
|
||||||
local gameindex = core.get_textlist_index("games")
|
local gameindex = core.get_textlist_index("games")
|
||||||
|
@ -417,22 +442,11 @@ local function create_world_buttonhandler(this, fields)
|
||||||
for k,v in pairs(fields) do
|
for k,v in pairs(fields) do
|
||||||
local split = string.split(k, "_", nil, 3)
|
local split = string.split(k, "_", nil, 3)
|
||||||
if split and split[1] == "flag" then
|
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.
|
-- We replaced the underscore of flag names with a dash.
|
||||||
local flag = string.gsub(split[3], "-", "_")
|
local flag = string.gsub(split[3], "-", "_")
|
||||||
local ftable = core.settings:get_flags(setting)
|
local ftable = this.data.flags[split[2]]
|
||||||
if v == "true" then
|
assert(ftable)
|
||||||
ftable[flag] = true
|
ftable[flag] = v == "true"
|
||||||
else
|
|
||||||
ftable[flag] = false
|
|
||||||
end
|
|
||||||
local flags = table_to_flags(ftable)
|
|
||||||
core.settings:set(setting, flags)
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -446,18 +460,16 @@ local function create_world_buttonhandler(this, fields)
|
||||||
local entry = core.formspec_escape(fields["mgv6_biomes"])
|
local entry = core.formspec_escape(fields["mgv6_biomes"])
|
||||||
for b=1, #mgv6_biomes do
|
for b=1, #mgv6_biomes do
|
||||||
if entry == mgv6_biomes[b][1] then
|
if entry == mgv6_biomes[b][1] then
|
||||||
local ftable = core.settings:get_flags("mgv6_spflags")
|
local ftable = this.data.flags.v6
|
||||||
ftable.jungles = mgv6_biomes[b][2].jungles
|
ftable.jungles = mgv6_biomes[b][2].jungles
|
||||||
ftable.snowbiomes = mgv6_biomes[b][2].snowbiomes
|
ftable.snowbiomes = mgv6_biomes[b][2].snowbiomes
|
||||||
local flags = table_to_flags(ftable)
|
|
||||||
core.settings:set("mgv6_spflags", flags)
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if fields["dd_mapgen"] then
|
if fields["dd_mapgen"] then
|
||||||
core.settings:set("mg_name", fields["dd_mapgen"])
|
this.data.mg = fields["dd_mapgen"]
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -466,12 +478,27 @@ end
|
||||||
|
|
||||||
|
|
||||||
function create_create_world_dlg(update_worldlistfilter)
|
function create_create_world_dlg(update_worldlistfilter)
|
||||||
worldname = ""
|
|
||||||
local retval = dialog_create("sp_create_world",
|
local retval = dialog_create("sp_create_world",
|
||||||
create_world_formspec,
|
create_world_formspec,
|
||||||
create_world_buttonhandler,
|
create_world_buttonhandler,
|
||||||
nil)
|
nil)
|
||||||
retval.update_worldlist_filter = update_worldlistfilter
|
retval.data = {
|
||||||
|
update_worldlist_filter = update_worldlistfilter,
|
||||||
|
worldname = "",
|
||||||
|
-- settings the world is created with:
|
||||||
|
seed = core.settings:get("fixed_map_seed") or "",
|
||||||
|
mg = core.settings:get("mg_name"),
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
end
|
end
|
||||||
|
|
|
@ -378,7 +378,7 @@ local function parse_config_file(read_all, parse_mods)
|
||||||
-- Parse mods
|
-- Parse mods
|
||||||
local mods_category_initialized = false
|
local mods_category_initialized = false
|
||||||
local mods = {}
|
local mods = {}
|
||||||
get_mods(core.get_modpath(), mods)
|
get_mods(core.get_modpath(), "mods", mods)
|
||||||
for _, mod in ipairs(mods) do
|
for _, mod in ipairs(mods) do
|
||||||
local path = mod.path .. DIR_DELIM .. FILENAME
|
local path = mod.path .. DIR_DELIM .. FILENAME
|
||||||
local file = io.open(path, "r")
|
local file = io.open(path, "r")
|
||||||
|
@ -395,6 +395,7 @@ local function parse_config_file(read_all, parse_mods)
|
||||||
|
|
||||||
table.insert(settings, {
|
table.insert(settings, {
|
||||||
name = mod.name,
|
name = mod.name,
|
||||||
|
readable_name = mod.title,
|
||||||
level = 1,
|
level = 1,
|
||||||
type = "category",
|
type = "category",
|
||||||
})
|
})
|
||||||
|
@ -408,7 +409,7 @@ local function parse_config_file(read_all, parse_mods)
|
||||||
-- Parse clientmods
|
-- Parse clientmods
|
||||||
local clientmods_category_initialized = false
|
local clientmods_category_initialized = false
|
||||||
local clientmods = {}
|
local clientmods = {}
|
||||||
get_mods(core.get_clientmodpath(), clientmods)
|
get_mods(core.get_clientmodpath(), "clientmods", clientmods)
|
||||||
for _, clientmod in ipairs(clientmods) do
|
for _, clientmod in ipairs(clientmods) do
|
||||||
local path = clientmod.path .. DIR_DELIM .. FILENAME
|
local path = clientmod.path .. DIR_DELIM .. FILENAME
|
||||||
local file = io.open(path, "r")
|
local file = io.open(path, "r")
|
||||||
|
@ -527,44 +528,40 @@ end
|
||||||
|
|
||||||
local function get_current_np_group(setting)
|
local function get_current_np_group(setting)
|
||||||
local value = core.settings:get_np_group(setting.name)
|
local value = core.settings:get_np_group(setting.name)
|
||||||
local t = {}
|
|
||||||
if value == nil then
|
if value == nil then
|
||||||
t = setting.values
|
return setting.values
|
||||||
else
|
|
||||||
table.insert(t, value.offset)
|
|
||||||
table.insert(t, value.scale)
|
|
||||||
table.insert(t, value.spread.x)
|
|
||||||
table.insert(t, value.spread.y)
|
|
||||||
table.insert(t, value.spread.z)
|
|
||||||
table.insert(t, value.seed)
|
|
||||||
table.insert(t, value.octaves)
|
|
||||||
table.insert(t, value.persistence)
|
|
||||||
table.insert(t, value.lacunarity)
|
|
||||||
table.insert(t, value.flags)
|
|
||||||
end
|
end
|
||||||
return t
|
local p = "%g"
|
||||||
|
return {
|
||||||
|
p:format(value.offset),
|
||||||
|
p:format(value.scale),
|
||||||
|
p:format(value.spread.x),
|
||||||
|
p:format(value.spread.y),
|
||||||
|
p:format(value.spread.z),
|
||||||
|
p:format(value.seed),
|
||||||
|
p:format(value.octaves),
|
||||||
|
p:format(value.persistence),
|
||||||
|
p:format(value.lacunarity),
|
||||||
|
value.flags
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_current_np_group_as_string(setting)
|
local function get_current_np_group_as_string(setting)
|
||||||
local value = core.settings:get_np_group(setting.name)
|
local value = core.settings:get_np_group(setting.name)
|
||||||
local t
|
|
||||||
if value == nil then
|
if value == nil then
|
||||||
t = setting.default
|
return setting.default
|
||||||
else
|
|
||||||
t = value.offset .. ", " ..
|
|
||||||
value.scale .. ", (" ..
|
|
||||||
value.spread.x .. ", " ..
|
|
||||||
value.spread.y .. ", " ..
|
|
||||||
value.spread.z .. "), " ..
|
|
||||||
value.seed .. ", " ..
|
|
||||||
value.octaves .. ", " ..
|
|
||||||
value.persistence .. ", " ..
|
|
||||||
value.lacunarity
|
|
||||||
if value.flags ~= "" then
|
|
||||||
t = t .. ", " .. value.flags
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return t
|
return ("%g, %g, (%g, %g, %g), %g, %g, %g, %g"):format(
|
||||||
|
value.offset,
|
||||||
|
value.scale,
|
||||||
|
value.spread.x,
|
||||||
|
value.spread.y,
|
||||||
|
value.spread.z,
|
||||||
|
value.seed,
|
||||||
|
value.octaves,
|
||||||
|
value.persistence,
|
||||||
|
value.lacunarity
|
||||||
|
) .. (value.flags ~= "" and (", " .. value.flags) or "")
|
||||||
end
|
end
|
||||||
|
|
||||||
local checkboxes = {} -- handle checkboxes events
|
local checkboxes = {} -- handle checkboxes events
|
||||||
|
@ -697,7 +694,7 @@ local function create_change_setting_formspec(dialogdata)
|
||||||
elseif setting.type == "v3f" then
|
elseif setting.type == "v3f" then
|
||||||
local val = get_current_value(setting)
|
local val = get_current_value(setting)
|
||||||
local v3f = {}
|
local v3f = {}
|
||||||
for line in val:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
|
for line in val:gmatch("[+-]?[%d.+-eE]+") do -- All numeric characters
|
||||||
table.insert(v3f, line)
|
table.insert(v3f, line)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -990,7 +987,7 @@ local function create_settings_formspec(tabview, _, tabdata)
|
||||||
local current_level = 0
|
local current_level = 0
|
||||||
for _, entry in ipairs(settings) do
|
for _, entry in ipairs(settings) do
|
||||||
local name
|
local name
|
||||||
if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then
|
if not core.settings:get_bool("show_technical_names") and entry.readable_name then
|
||||||
name = fgettext_ne(entry.readable_name)
|
name = fgettext_ne(entry.readable_name)
|
||||||
else
|
else
|
||||||
name = entry.name
|
name = entry.name
|
||||||
|
@ -1031,7 +1028,7 @@ local function create_settings_formspec(tabview, _, tabdata)
|
||||||
"button[10,4.9;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
|
"button[10,4.9;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
|
||||||
"button[7,4.9;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
|
"button[7,4.9;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
|
||||||
"checkbox[0,4.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
|
"checkbox[0,4.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
|
||||||
.. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]"
|
.. dump(core.settings:get_bool("show_technical_names")) .. "]"
|
||||||
|
|
||||||
return formspec
|
return formspec
|
||||||
end
|
end
|
||||||
|
@ -1114,7 +1111,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fields["cb_tech_settings"] then
|
if fields["cb_tech_settings"] then
|
||||||
core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"])
|
core.settings:set("show_technical_names", fields["cb_tech_settings"])
|
||||||
core.settings:write()
|
core.settings:write()
|
||||||
core.update_formspec(this:get_formspec())
|
core.update_formspec(this:get_formspec())
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -16,23 +16,25 @@
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
|
||||||
mm_texture = {}
|
mm_game_theme = {}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function mm_texture.init()
|
function mm_game_theme.init()
|
||||||
mm_texture.defaulttexturedir = core.get_texturepath() .. DIR_DELIM .. "base" ..
|
mm_game_theme.defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
|
||||||
DIR_DELIM .. "pack" .. DIR_DELIM
|
DIR_DELIM .. "pack" .. DIR_DELIM
|
||||||
mm_texture.basetexturedir = mm_texture.defaulttexturedir
|
mm_game_theme.basetexturedir = mm_game_theme.defaulttexturedir
|
||||||
|
|
||||||
mm_texture.texturepack = core.settings:get("texture_path")
|
mm_game_theme.texturepack = core.settings:get("texture_path")
|
||||||
|
|
||||||
mm_texture.gameid = nil
|
mm_game_theme.gameid = nil
|
||||||
|
|
||||||
|
mm_game_theme.music_handle = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function mm_texture.update(tab,gamedetails)
|
function mm_game_theme.update(tab,gamedetails)
|
||||||
if tab ~= "singleplayer" then
|
if tab ~= "singleplayer" then
|
||||||
mm_texture.reset()
|
mm_game_theme.reset()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -40,50 +42,54 @@ function mm_texture.update(tab,gamedetails)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
mm_texture.update_game(gamedetails)
|
mm_game_theme.update_game(gamedetails)
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function mm_texture.reset()
|
function mm_game_theme.reset()
|
||||||
mm_texture.gameid = nil
|
mm_game_theme.gameid = nil
|
||||||
local have_bg = false
|
local have_bg = false
|
||||||
local have_overlay = mm_texture.set_generic("overlay")
|
local have_overlay = mm_game_theme.set_generic("overlay")
|
||||||
|
|
||||||
if not have_overlay then
|
if not have_overlay then
|
||||||
have_bg = mm_texture.set_generic("background")
|
have_bg = mm_game_theme.set_generic("background")
|
||||||
end
|
end
|
||||||
|
|
||||||
mm_texture.clear("header")
|
mm_game_theme.clear("header")
|
||||||
mm_texture.clear("footer")
|
mm_game_theme.clear("footer")
|
||||||
core.set_clouds(false)
|
core.set_clouds(false)
|
||||||
|
|
||||||
mm_texture.set_generic("footer")
|
mm_game_theme.set_generic("footer")
|
||||||
mm_texture.set_generic("header")
|
mm_game_theme.set_generic("header")
|
||||||
|
|
||||||
if not have_bg then
|
if not have_bg then
|
||||||
if core.settings:get_bool("menu_clouds") then
|
if core.settings:get_bool("menu_clouds") then
|
||||||
core.set_clouds(true)
|
core.set_clouds(true)
|
||||||
else
|
else
|
||||||
mm_texture.set_dirt_bg()
|
mm_game_theme.set_dirt_bg()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if mm_game_theme.music_handle ~= nil then
|
||||||
|
core.sound_stop(mm_game_theme.music_handle)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function mm_texture.update_game(gamedetails)
|
function mm_game_theme.update_game(gamedetails)
|
||||||
if mm_texture.gameid == gamedetails.id then
|
if mm_game_theme.gameid == gamedetails.id then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local have_bg = false
|
local have_bg = false
|
||||||
local have_overlay = mm_texture.set_game("overlay",gamedetails)
|
local have_overlay = mm_game_theme.set_game("overlay",gamedetails)
|
||||||
|
|
||||||
if not have_overlay then
|
if not have_overlay then
|
||||||
have_bg = mm_texture.set_game("background",gamedetails)
|
have_bg = mm_game_theme.set_game("background",gamedetails)
|
||||||
end
|
end
|
||||||
|
|
||||||
mm_texture.clear("header")
|
mm_game_theme.clear("header")
|
||||||
mm_texture.clear("footer")
|
mm_game_theme.clear("footer")
|
||||||
core.set_clouds(false)
|
core.set_clouds(false)
|
||||||
|
|
||||||
if not have_bg then
|
if not have_bg then
|
||||||
|
@ -91,34 +97,34 @@ function mm_texture.update_game(gamedetails)
|
||||||
if core.settings:get_bool("menu_clouds") then
|
if core.settings:get_bool("menu_clouds") then
|
||||||
core.set_clouds(true)
|
core.set_clouds(true)
|
||||||
else
|
else
|
||||||
mm_texture.set_dirt_bg()
|
mm_game_theme.set_dirt_bg()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
mm_texture.set_game("footer",gamedetails)
|
mm_game_theme.set_game("footer",gamedetails)
|
||||||
mm_texture.set_game("header",gamedetails)
|
mm_game_theme.set_game("header",gamedetails)
|
||||||
|
|
||||||
mm_texture.gameid = gamedetails.id
|
mm_game_theme.gameid = gamedetails.id
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function mm_texture.clear(identifier)
|
function mm_game_theme.clear(identifier)
|
||||||
core.set_background(identifier,"")
|
core.set_background(identifier,"")
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function mm_texture.set_generic(identifier)
|
function mm_game_theme.set_generic(identifier)
|
||||||
--try texture pack first
|
--try texture pack first
|
||||||
if mm_texture.texturepack ~= nil then
|
if mm_game_theme.texturepack ~= nil then
|
||||||
local path = mm_texture.texturepack .. DIR_DELIM .."menu_" ..
|
local path = mm_game_theme.texturepack .. DIR_DELIM .."menu_" ..
|
||||||
identifier .. ".png"
|
identifier .. ".png"
|
||||||
if core.set_background(identifier,path) then
|
if core.set_background(identifier,path) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if mm_texture.defaulttexturedir ~= nil then
|
if mm_game_theme.defaulttexturedir ~= nil then
|
||||||
local path = mm_texture.defaulttexturedir .. DIR_DELIM .."menu_" ..
|
local path = mm_game_theme.defaulttexturedir .. DIR_DELIM .."menu_" ..
|
||||||
identifier .. ".png"
|
identifier .. ".png"
|
||||||
if core.set_background(identifier,path) then
|
if core.set_background(identifier,path) then
|
||||||
return true
|
return true
|
||||||
|
@ -129,14 +135,16 @@ function mm_texture.set_generic(identifier)
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function mm_texture.set_game(identifier, gamedetails)
|
function mm_game_theme.set_game(identifier, gamedetails)
|
||||||
|
|
||||||
if gamedetails == nil then
|
if gamedetails == nil then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if mm_texture.texturepack ~= nil then
|
mm_game_theme.set_music(gamedetails)
|
||||||
local path = mm_texture.texturepack .. DIR_DELIM ..
|
|
||||||
|
if mm_game_theme.texturepack ~= nil then
|
||||||
|
local path = mm_game_theme.texturepack .. DIR_DELIM ..
|
||||||
gamedetails.id .. "_menu_" .. identifier .. ".png"
|
gamedetails.id .. "_menu_" .. identifier .. ".png"
|
||||||
if core.set_background(identifier, path) then
|
if core.set_background(identifier, path) then
|
||||||
return true
|
return true
|
||||||
|
@ -171,9 +179,10 @@ function mm_texture.set_game(identifier, gamedetails)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function mm_texture.set_dirt_bg()
|
--------------------------------------------------------------------------------
|
||||||
if mm_texture.texturepack ~= nil then
|
function mm_game_theme.set_dirt_bg()
|
||||||
local path = mm_texture.texturepack .. DIR_DELIM .."default_dirt.png"
|
if mm_game_theme.texturepack ~= nil then
|
||||||
|
local path = mm_game_theme.texturepack .. DIR_DELIM .."default_dirt.png"
|
||||||
if core.set_background("background", path, true, 128) then
|
if core.set_background("background", path, true, 128) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -183,3 +192,12 @@ function mm_texture.set_dirt_bg()
|
||||||
local minimalpath = defaulttexturedir .. "menu_bg.png"
|
local minimalpath = defaulttexturedir .. "menu_bg.png"
|
||||||
core.set_background("background", minimalpath, true, 128)
|
core.set_background("background", minimalpath, true, 128)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
function mm_game_theme.set_music(gamedetails)
|
||||||
|
if mm_game_theme.music_handle ~= nil then
|
||||||
|
core.sound_stop(mm_game_theme.music_handle)
|
||||||
|
end
|
||||||
|
local music_path = gamedetails.path .. DIR_DELIM .. "menu" .. DIR_DELIM .. "theme"
|
||||||
|
mm_game_theme.music_handle = core.sound_play(music_path, true)
|
||||||
|
end
|
|
@ -31,7 +31,7 @@ local group_format_template = [[
|
||||||
# octaves = %s,
|
# octaves = %s,
|
||||||
# persistence = %s,
|
# persistence = %s,
|
||||||
# lacunarity = %s,
|
# lacunarity = %s,
|
||||||
# flags = %s
|
# flags =%s
|
||||||
# }
|
# }
|
||||||
|
|
||||||
]]
|
]]
|
||||||
|
@ -55,7 +55,11 @@ local function create_minetest_conf_example()
|
||||||
end
|
end
|
||||||
if entry.comment ~= "" then
|
if entry.comment ~= "" then
|
||||||
for _, comment_line in ipairs(entry.comment:split("\n", true)) do
|
for _, comment_line in ipairs(entry.comment:split("\n", true)) do
|
||||||
insert(result, "# " .. comment_line .. "\n")
|
if comment_line == "" then
|
||||||
|
insert(result, "#\n")
|
||||||
|
else
|
||||||
|
insert(result, "# " .. comment_line .. "\n")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
insert(result, "# type: " .. entry.type)
|
insert(result, "# type: " .. entry.type)
|
||||||
|
@ -73,10 +77,14 @@ local function create_minetest_conf_example()
|
||||||
end
|
end
|
||||||
insert(result, "\n")
|
insert(result, "\n")
|
||||||
if group_format == true then
|
if group_format == true then
|
||||||
|
local flags = entry.values[10]
|
||||||
|
if flags ~= "" then
|
||||||
|
flags = " "..flags
|
||||||
|
end
|
||||||
insert(result, sprintf(group_format_template, entry.name, entry.values[1],
|
insert(result, sprintf(group_format_template, entry.name, entry.values[1],
|
||||||
entry.values[2], entry.values[3], entry.values[4], entry.values[5],
|
entry.values[2], entry.values[3], entry.values[4], entry.values[5],
|
||||||
entry.values[6], entry.values[7], entry.values[8], entry.values[9],
|
entry.values[6], entry.values[7], entry.values[8], entry.values[9],
|
||||||
entry.values[10]))
|
flags))
|
||||||
else
|
else
|
||||||
local append
|
local append
|
||||||
if entry.default ~= "" then
|
if entry.default ~= "" then
|
||||||
|
@ -91,7 +99,7 @@ end
|
||||||
|
|
||||||
local translation_file_header = [[
|
local translation_file_header = [[
|
||||||
// This file is automatically generated
|
// This file is automatically generated
|
||||||
// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files
|
// It contains a bunch of fake gettext calls, to tell xgettext about the strings in config files
|
||||||
// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua
|
// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua
|
||||||
|
|
||||||
fake_function() {]]
|
fake_function() {]]
|
||||||
|
@ -126,4 +134,3 @@ file = assert(io.open("src/settings_translation_file.cpp", "w"))
|
||||||
--file = assert(io.open("settings_translation_file.cpp", "w"))
|
--file = assert(io.open("settings_translation_file.cpp", "w"))
|
||||||
file:write(create_translation_file())
|
file:write(create_translation_file())
|
||||||
file:close()
|
file:close()
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ dofile(menupath .. DIR_DELIM .. "async_event.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "common.lua")
|
dofile(menupath .. DIR_DELIM .. "common.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "pkgmgr.lua")
|
dofile(menupath .. DIR_DELIM .. "pkgmgr.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
|
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "textures.lua")
|
dofile(menupath .. DIR_DELIM .. "game_theme.lua")
|
||||||
|
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua")
|
||||||
|
@ -87,7 +87,7 @@ local function init_globals()
|
||||||
core.settings:set("menu_last_game", default_game)
|
core.settings:set("menu_last_game", default_game)
|
||||||
end
|
end
|
||||||
|
|
||||||
mm_texture.init()
|
mm_game_theme.init()
|
||||||
|
|
||||||
-- Create main tabview
|
-- Create main tabview
|
||||||
local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0})
|
local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0})
|
||||||
|
@ -113,7 +113,7 @@ local function init_globals()
|
||||||
if tv_main.current_tab == "local" then
|
if tv_main.current_tab == "local" then
|
||||||
local game = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
|
local game = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
|
||||||
if game == nil then
|
if game == nil then
|
||||||
mm_texture.reset()
|
mm_game_theme.reset()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -121,8 +121,6 @@ local function init_globals()
|
||||||
tv_main:show()
|
tv_main:show()
|
||||||
|
|
||||||
ui.update()
|
ui.update()
|
||||||
|
|
||||||
core.sound_play("main_menu", true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
init_globals()
|
init_globals()
|
||||||
|
|
|
@ -78,34 +78,35 @@ local function load_texture_packs(txtpath, retval)
|
||||||
|
|
||||||
for _, item in ipairs(list) do
|
for _, item in ipairs(list) do
|
||||||
if item ~= "base" then
|
if item ~= "base" then
|
||||||
local name = item
|
|
||||||
|
|
||||||
local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
|
local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
|
||||||
if path == current_texture_path then
|
|
||||||
name = fgettext("$1 (Enabled)", name)
|
|
||||||
end
|
|
||||||
|
|
||||||
local conf = Settings(path .. "texture_pack.conf")
|
local conf = Settings(path .. "texture_pack.conf")
|
||||||
|
local enabled = path == current_texture_path
|
||||||
|
|
||||||
|
local title = conf:get("title") or item
|
||||||
|
|
||||||
|
-- list_* is only used if non-nil, else the regular versions are used.
|
||||||
retval[#retval + 1] = {
|
retval[#retval + 1] = {
|
||||||
name = item,
|
name = item,
|
||||||
|
title = title,
|
||||||
|
list_name = enabled and fgettext("$1 (Enabled)", item) or nil,
|
||||||
|
list_title = enabled and fgettext("$1 (Enabled)", title) or nil,
|
||||||
author = conf:get("author"),
|
author = conf:get("author"),
|
||||||
release = tonumber(conf:get("release")) or 0,
|
release = tonumber(conf:get("release")) or 0,
|
||||||
list_name = name,
|
|
||||||
type = "txp",
|
type = "txp",
|
||||||
path = path,
|
path = path,
|
||||||
enabled = path == current_texture_path,
|
enabled = enabled,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_mods(path,retval,modpack)
|
function get_mods(path, virtual_path, retval, modpack)
|
||||||
local mods = core.get_dir_list(path, true)
|
local mods = core.get_dir_list(path, true)
|
||||||
|
|
||||||
for _, name in ipairs(mods) do
|
for _, name in ipairs(mods) do
|
||||||
if name:sub(1, 1) ~= "." then
|
if name:sub(1, 1) ~= "." then
|
||||||
local prefix = path .. DIR_DELIM .. name
|
local mod_path = path .. DIR_DELIM .. name
|
||||||
|
local mod_virtual_path = virtual_path .. "/" .. name
|
||||||
local toadd = {
|
local toadd = {
|
||||||
dir_name = name,
|
dir_name = name,
|
||||||
parent_dir = path,
|
parent_dir = path,
|
||||||
|
@ -114,18 +115,18 @@ function get_mods(path,retval,modpack)
|
||||||
|
|
||||||
-- Get config file
|
-- Get config file
|
||||||
local mod_conf
|
local mod_conf
|
||||||
local modpack_conf = io.open(prefix .. DIR_DELIM .. "modpack.conf")
|
local modpack_conf = io.open(mod_path .. DIR_DELIM .. "modpack.conf")
|
||||||
if modpack_conf then
|
if modpack_conf then
|
||||||
toadd.is_modpack = true
|
toadd.is_modpack = true
|
||||||
modpack_conf:close()
|
modpack_conf:close()
|
||||||
|
|
||||||
mod_conf = Settings(prefix .. DIR_DELIM .. "modpack.conf"):to_table()
|
mod_conf = Settings(mod_path .. DIR_DELIM .. "modpack.conf"):to_table()
|
||||||
if mod_conf.name then
|
if mod_conf.name then
|
||||||
name = mod_conf.name
|
name = mod_conf.name
|
||||||
toadd.is_name_explicit = true
|
toadd.is_name_explicit = true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
mod_conf = Settings(prefix .. DIR_DELIM .. "mod.conf"):to_table()
|
mod_conf = Settings(mod_path .. DIR_DELIM .. "mod.conf"):to_table()
|
||||||
if mod_conf.name then
|
if mod_conf.name then
|
||||||
name = mod_conf.name
|
name = mod_conf.name
|
||||||
toadd.is_name_explicit = true
|
toadd.is_name_explicit = true
|
||||||
|
@ -134,14 +135,16 @@ function get_mods(path,retval,modpack)
|
||||||
|
|
||||||
-- Read from config
|
-- Read from config
|
||||||
toadd.name = name
|
toadd.name = name
|
||||||
|
toadd.title = mod_conf.title
|
||||||
toadd.author = mod_conf.author
|
toadd.author = mod_conf.author
|
||||||
toadd.release = tonumber(mod_conf.release) or 0
|
toadd.release = tonumber(mod_conf.release) or 0
|
||||||
toadd.path = prefix
|
toadd.path = mod_path
|
||||||
|
toadd.virtual_path = mod_virtual_path
|
||||||
toadd.type = "mod"
|
toadd.type = "mod"
|
||||||
|
|
||||||
-- Check modpack.txt
|
-- Check modpack.txt
|
||||||
-- Note: modpack.conf is already checked above
|
-- Note: modpack.conf is already checked above
|
||||||
local modpackfile = io.open(prefix .. DIR_DELIM .. "modpack.txt")
|
local modpackfile = io.open(mod_path .. DIR_DELIM .. "modpack.txt")
|
||||||
if modpackfile then
|
if modpackfile then
|
||||||
modpackfile:close()
|
modpackfile:close()
|
||||||
toadd.is_modpack = true
|
toadd.is_modpack = true
|
||||||
|
@ -153,7 +156,7 @@ function get_mods(path,retval,modpack)
|
||||||
elseif toadd.is_modpack then
|
elseif toadd.is_modpack then
|
||||||
toadd.type = "modpack"
|
toadd.type = "modpack"
|
||||||
toadd.is_modpack = true
|
toadd.is_modpack = true
|
||||||
get_mods(prefix, retval, name)
|
get_mods(mod_path, mod_virtual_path, retval, name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -181,21 +184,6 @@ function pkgmgr.get_texture_packs()
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function pkgmgr.extract(modfile)
|
|
||||||
if modfile.type == "zip" then
|
|
||||||
local tempfolder = os.tempfolder()
|
|
||||||
|
|
||||||
if tempfolder ~= nil and
|
|
||||||
tempfolder ~= "" then
|
|
||||||
core.create_dir(tempfolder)
|
|
||||||
if core.extract_zip(modfile.name,tempfolder) then
|
|
||||||
return tempfolder
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkgmgr.get_folder_type(path)
|
function pkgmgr.get_folder_type(path)
|
||||||
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
|
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
|
||||||
if testfile ~= nil then
|
if testfile ~= nil then
|
||||||
|
@ -349,7 +337,7 @@ function pkgmgr.identify_modname(modpath,filename)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function pkgmgr.render_packagelist(render_list)
|
function pkgmgr.render_packagelist(render_list, use_technical_names)
|
||||||
if not render_list then
|
if not render_list then
|
||||||
if not pkgmgr.global_mods then
|
if not pkgmgr.global_mods then
|
||||||
pkgmgr.refresh_globals()
|
pkgmgr.refresh_globals()
|
||||||
|
@ -385,7 +373,12 @@ function pkgmgr.render_packagelist(render_list)
|
||||||
else
|
else
|
||||||
retval[#retval + 1] = "0"
|
retval[#retval + 1] = "0"
|
||||||
end
|
end
|
||||||
retval[#retval + 1] = core.formspec_escape(v.list_name or v.name)
|
|
||||||
|
if use_technical_names then
|
||||||
|
retval[#retval + 1] = core.formspec_escape(v.list_name or v.name)
|
||||||
|
else
|
||||||
|
retval[#retval + 1] = core.formspec_escape(v.list_title or v.list_name or v.title or v.name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.concat(retval, ",")
|
return table.concat(retval, ",")
|
||||||
|
@ -412,6 +405,14 @@ function pkgmgr.is_modpack_entirely_enabled(data, name)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function disable_all_by_name(list, name, except)
|
||||||
|
for i=1, #list do
|
||||||
|
if list[i].name == name and list[i] ~= except then
|
||||||
|
list[i].enabled = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
---------- toggles or en/disables a mod or modpack and its dependencies --------
|
---------- toggles or en/disables a mod or modpack and its dependencies --------
|
||||||
local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
|
local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
|
||||||
if not mod.is_modpack then
|
if not mod.is_modpack then
|
||||||
|
@ -420,13 +421,16 @@ local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mo
|
||||||
toset = not mod.enabled
|
toset = not mod.enabled
|
||||||
end
|
end
|
||||||
if mod.enabled ~= toset then
|
if mod.enabled ~= toset then
|
||||||
mod.enabled = toset
|
|
||||||
toggled_mods[#toggled_mods+1] = mod.name
|
toggled_mods[#toggled_mods+1] = mod.name
|
||||||
end
|
end
|
||||||
if toset then
|
if toset then
|
||||||
-- Mark this mod for recursive dependency traversal
|
-- Mark this mod for recursive dependency traversal
|
||||||
enabled_mods[mod.name] = true
|
enabled_mods[mod.name] = true
|
||||||
|
|
||||||
|
-- Disable other mods with the same name
|
||||||
|
disable_all_by_name(list, mod.name, mod)
|
||||||
end
|
end
|
||||||
|
mod.enabled = toset
|
||||||
else
|
else
|
||||||
-- Toggle or en/disable every mod in the modpack,
|
-- Toggle or en/disable every mod in the modpack,
|
||||||
-- interleaved unsupported
|
-- interleaved unsupported
|
||||||
|
@ -451,7 +455,7 @@ function pkgmgr.enable_mod(this, toset)
|
||||||
local enabled_mods = {}
|
local enabled_mods = {}
|
||||||
toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
|
toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
|
||||||
|
|
||||||
if not toset then
|
if next(enabled_mods) == nil then
|
||||||
-- Mod(s) were disabled, so no dependencies need to be enabled
|
-- Mod(s) were disabled, so no dependencies need to be enabled
|
||||||
table.sort(toggled_mods)
|
table.sort(toggled_mods)
|
||||||
core.log("info", "Following mods were disabled: " ..
|
core.log("info", "Following mods were disabled: " ..
|
||||||
|
@ -461,11 +465,16 @@ function pkgmgr.enable_mod(this, toset)
|
||||||
|
|
||||||
-- Enable mods' depends after activation
|
-- Enable mods' depends after activation
|
||||||
|
|
||||||
-- Make a list of mod ids indexed by their names
|
-- Make a list of mod ids indexed by their names. Among mods with the
|
||||||
|
-- same name, enabled mods take precedence, after which game mods take
|
||||||
|
-- precedence, being last in the mod list.
|
||||||
local mod_ids = {}
|
local mod_ids = {}
|
||||||
for id, mod2 in pairs(list) do
|
for id, mod2 in pairs(list) do
|
||||||
if mod2.type == "mod" and not mod2.is_modpack then
|
if mod2.type == "mod" and not mod2.is_modpack then
|
||||||
mod_ids[mod2.name] = id
|
local prev_id = mod_ids[mod2.name]
|
||||||
|
if not prev_id or not list[prev_id].enabled then
|
||||||
|
mod_ids[mod2.name] = id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -482,6 +491,7 @@ function pkgmgr.enable_mod(this, toset)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If sp is 0, every dependency is already activated
|
-- If sp is 0, every dependency is already activated
|
||||||
while sp > 0 do
|
while sp > 0 do
|
||||||
local name = to_enable[sp]
|
local name = to_enable[sp]
|
||||||
|
@ -494,14 +504,14 @@ function pkgmgr.enable_mod(this, toset)
|
||||||
core.log("warning", "Mod dependency \"" .. name ..
|
core.log("warning", "Mod dependency \"" .. name ..
|
||||||
"\" not found!")
|
"\" not found!")
|
||||||
else
|
else
|
||||||
if mod_to_enable.enabled == false then
|
if not mod_to_enable.enabled then
|
||||||
mod_to_enable.enabled = true
|
mod_to_enable.enabled = true
|
||||||
toggled_mods[#toggled_mods+1] = mod_to_enable.name
|
toggled_mods[#toggled_mods+1] = mod_to_enable.name
|
||||||
end
|
end
|
||||||
-- Push the dependencies of the dependency onto the stack
|
-- Push the dependencies of the dependency onto the stack
|
||||||
local depends = pkgmgr.get_dependencies(mod_to_enable.path)
|
local depends = pkgmgr.get_dependencies(mod_to_enable.path)
|
||||||
for i = 1, #depends do
|
for i = 1, #depends do
|
||||||
if not enabled_mods[name] then
|
if not enabled_mods[depends[i]] then
|
||||||
sp = sp+1
|
sp = sp+1
|
||||||
to_enable[sp] = depends[i]
|
to_enable[sp] = depends[i]
|
||||||
end
|
end
|
||||||
|
@ -561,11 +571,10 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
||||||
local from = basefolder and basefolder.path or path
|
local from = basefolder and basefolder.path or path
|
||||||
if targetpath then
|
if targetpath then
|
||||||
core.delete_dir(targetpath)
|
core.delete_dir(targetpath)
|
||||||
core.create_dir(targetpath)
|
|
||||||
else
|
else
|
||||||
targetpath = core.get_texturepath() .. DIR_DELIM .. basename
|
targetpath = core.get_texturepath() .. DIR_DELIM .. basename
|
||||||
end
|
end
|
||||||
if not core.copy_dir(from, targetpath) then
|
if not core.copy_dir(from, targetpath, false) then
|
||||||
return nil,
|
return nil,
|
||||||
fgettext("Failed to install $1 to $2", basename, targetpath)
|
fgettext("Failed to install $1 to $2", basename, targetpath)
|
||||||
end
|
end
|
||||||
|
@ -586,7 +595,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
||||||
-- Get destination name for modpack
|
-- Get destination name for modpack
|
||||||
if targetpath then
|
if targetpath then
|
||||||
core.delete_dir(targetpath)
|
core.delete_dir(targetpath)
|
||||||
core.create_dir(targetpath)
|
|
||||||
else
|
else
|
||||||
local clean_path = nil
|
local clean_path = nil
|
||||||
if basename ~= nil then
|
if basename ~= nil then
|
||||||
|
@ -610,7 +618,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
||||||
|
|
||||||
if targetpath then
|
if targetpath then
|
||||||
core.delete_dir(targetpath)
|
core.delete_dir(targetpath)
|
||||||
core.create_dir(targetpath)
|
|
||||||
else
|
else
|
||||||
local targetfolder = basename
|
local targetfolder = basename
|
||||||
if targetfolder == nil then
|
if targetfolder == nil then
|
||||||
|
@ -636,14 +643,13 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
||||||
|
|
||||||
if targetpath then
|
if targetpath then
|
||||||
core.delete_dir(targetpath)
|
core.delete_dir(targetpath)
|
||||||
core.create_dir(targetpath)
|
|
||||||
else
|
else
|
||||||
targetpath = core.get_gamepath() .. DIR_DELIM .. basename
|
targetpath = core.get_gamepath() .. DIR_DELIM .. basename
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Copy it
|
-- Copy it
|
||||||
if not core.copy_dir(basefolder.path, targetpath) then
|
if not core.copy_dir(basefolder.path, targetpath, false) then
|
||||||
return nil,
|
return nil,
|
||||||
fgettext("Failed to install $1 to $2", basename, targetpath)
|
fgettext("Failed to install $1 to $2", basename, targetpath)
|
||||||
end
|
end
|
||||||
|
@ -657,23 +663,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
||||||
return targetpath, nil
|
return targetpath, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function pkgmgr.install(type, modfilename, basename, dest)
|
|
||||||
local archive_info = pkgmgr.identify_filetype(modfilename)
|
|
||||||
local path = pkgmgr.extract(archive_info)
|
|
||||||
|
|
||||||
if path == nil then
|
|
||||||
return nil,
|
|
||||||
fgettext("Install: file: \"$1\"", archive_info.name) .. "\n" ..
|
|
||||||
fgettext("Install: Unsupported file type \"$1\" or broken archive",
|
|
||||||
archive_info.type)
|
|
||||||
end
|
|
||||||
|
|
||||||
local targetpath, msg = pkgmgr.install_dir(type, path, basename, dest)
|
|
||||||
core.delete_dir(path)
|
|
||||||
return targetpath, msg
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function pkgmgr.prepareclientmodlist(data)
|
function pkgmgr.prepareclientmodlist(data)
|
||||||
local retval = {}
|
local retval = {}
|
||||||
|
@ -683,9 +672,8 @@ function pkgmgr.prepareclientmodlist(data)
|
||||||
--read clientmods
|
--read clientmods
|
||||||
local modpath = core.get_clientmodpath()
|
local modpath = core.get_clientmodpath()
|
||||||
|
|
||||||
if modpath ~= nil and
|
if modpath ~= nil and modpath ~= "" then
|
||||||
modpath ~= "" then
|
get_mods(modpath, "clientmods", clientmods)
|
||||||
get_mods(modpath,clientmods)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
for i=1,#clientmods,1 do
|
for i=1,#clientmods,1 do
|
||||||
|
@ -730,16 +718,15 @@ function pkgmgr.preparemodlist(data)
|
||||||
local game_mods = {}
|
local game_mods = {}
|
||||||
|
|
||||||
--read global mods
|
--read global mods
|
||||||
local modpath = core.get_modpath()
|
local modpaths = core.get_modpaths()
|
||||||
|
for key, modpath in pairs(modpaths) do
|
||||||
if modpath ~= nil and
|
get_mods(modpath, key, global_mods)
|
||||||
modpath ~= "" then
|
|
||||||
get_mods(modpath,global_mods)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
for i=1,#global_mods,1 do
|
for i=1,#global_mods,1 do
|
||||||
global_mods[i].type = "mod"
|
global_mods[i].type = "mod"
|
||||||
global_mods[i].loc = "global"
|
global_mods[i].loc = "global"
|
||||||
|
global_mods[i].enabled = false
|
||||||
retval[#retval + 1] = global_mods[i]
|
retval[#retval + 1] = global_mods[i]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -773,22 +760,37 @@ function pkgmgr.preparemodlist(data)
|
||||||
DIR_DELIM .. "world.mt"
|
DIR_DELIM .. "world.mt"
|
||||||
|
|
||||||
local worldfile = Settings(filename)
|
local worldfile = Settings(filename)
|
||||||
|
for key, value in pairs(worldfile:to_table()) do
|
||||||
for key,value in pairs(worldfile:to_table()) do
|
|
||||||
if key:sub(1, 9) == "load_mod_" then
|
if key:sub(1, 9) == "load_mod_" then
|
||||||
key = key:sub(10)
|
key = key:sub(10)
|
||||||
local element = nil
|
local mod_found = false
|
||||||
for i=1,#retval,1 do
|
|
||||||
|
local fallback_found = false
|
||||||
|
local fallback_mod = nil
|
||||||
|
|
||||||
|
for i=1, #retval do
|
||||||
if retval[i].name == key and
|
if retval[i].name == key and
|
||||||
not retval[i].is_modpack then
|
not retval[i].is_modpack then
|
||||||
element = retval[i]
|
if core.is_yes(value) or retval[i].virtual_path == value then
|
||||||
break
|
retval[i].enabled = true
|
||||||
|
mod_found = true
|
||||||
|
break
|
||||||
|
elseif fallback_found then
|
||||||
|
-- Only allow fallback if only one mod matches
|
||||||
|
fallback_mod = nil
|
||||||
|
else
|
||||||
|
fallback_found = true
|
||||||
|
fallback_mod = retval[i]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if element ~= nil then
|
|
||||||
element.enabled = value ~= "false" and value ~= "nil" and value
|
if not mod_found then
|
||||||
else
|
if fallback_mod and value:find("/") then
|
||||||
core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
|
fallback_mod.enabled = true
|
||||||
|
else
|
||||||
|
core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -871,45 +873,6 @@ function pkgmgr.refresh_globals()
|
||||||
pkgmgr.clientmods:set_sortmode("alphabetic")
|
pkgmgr.clientmods:set_sortmode("alphabetic")
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function pkgmgr.identify_filetype(name)
|
|
||||||
|
|
||||||
if name:sub(-3):lower() == "zip" then
|
|
||||||
return {
|
|
||||||
name = name,
|
|
||||||
type = "zip"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
if name:sub(-6):lower() == "tar.gz" or
|
|
||||||
name:sub(-3):lower() == "tgz"then
|
|
||||||
return {
|
|
||||||
name = name,
|
|
||||||
type = "tgz"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
if name:sub(-6):lower() == "tar.bz2" then
|
|
||||||
return {
|
|
||||||
name = name,
|
|
||||||
type = "tbz"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
if name:sub(-2):lower() == "7z" then
|
|
||||||
return {
|
|
||||||
name = name,
|
|
||||||
type = "7z"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
name = name,
|
|
||||||
type = "ukn"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function pkgmgr.find_by_gameid(gameid)
|
function pkgmgr.find_by_gameid(gameid)
|
||||||
for i=1,#pkgmgr.games,1 do
|
for i=1,#pkgmgr.games,1 do
|
||||||
|
@ -925,7 +888,7 @@ function pkgmgr.get_game_mods(gamespec, retval)
|
||||||
if gamespec ~= nil and
|
if gamespec ~= nil and
|
||||||
gamespec.gamemods_path ~= nil and
|
gamespec.gamemods_path ~= nil and
|
||||||
gamespec.gamemods_path ~= "" then
|
gamespec.gamemods_path ~= "" then
|
||||||
get_mods(gamespec.gamemods_path, retval)
|
get_mods(gamespec.gamemods_path, ("games/%s/mods"):format(gamespec.id), retval)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -38,32 +38,35 @@ local core_developers = {
|
||||||
"Lars Hofhansl <larsh@apache.org>",
|
"Lars Hofhansl <larsh@apache.org>",
|
||||||
"Pierre-Yves Rollo <dev@pyrollo.com>",
|
"Pierre-Yves Rollo <dev@pyrollo.com>",
|
||||||
"v-rob <robinsonvincent89@gmail.com>",
|
"v-rob <robinsonvincent89@gmail.com>",
|
||||||
|
"hecks",
|
||||||
|
"Hugues Ross <hugues.ross@gmail.com>",
|
||||||
|
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>",
|
||||||
}
|
}
|
||||||
|
|
||||||
-- For updating active/previous contributors, see the script in ./util/gather_git_credits.py
|
-- For updating active/previous contributors, see the script in ./util/gather_git_credits.py
|
||||||
|
|
||||||
local active_contributors = {
|
local active_contributors = {
|
||||||
"Wuzzy [devtest game, visual corrections]",
|
"Wuzzy [I18n for builtin, liquid features, fixes]",
|
||||||
"Zughy [Visual improvements, various fixes]",
|
"Zughy [Various features and fixes]",
|
||||||
"Maksim (MoNTE48) [Android]",
|
|
||||||
"numzero [Graphics and rendering]",
|
"numzero [Graphics and rendering]",
|
||||||
"appgurueu [Various internal fixes]",
|
"Desour [Internal fixes, Clipboard on X11]",
|
||||||
"Desour [Formspec and vector API changes]",
|
"Lars Müller [Various internal fixes]",
|
||||||
"HybridDog [Rendering fixes and documentation]",
|
"JosiahWI [CMake, cleanups and fixes]",
|
||||||
"Hugues Ross [Graphics-related improvements]",
|
"HybridDog [builtin, documentation]",
|
||||||
"ANAND (ClobberXD) [Mouse buttons rebinding]",
|
"Jude Melton-Houghton [Database implementation]",
|
||||||
"luk3yx [Fixes]",
|
"savilli [Fixes]",
|
||||||
"hecks [Audiovisuals, Lua API]",
|
"Liso [Shadow Mapping]",
|
||||||
"LoneWolfHT [Object crosshair, documentation fixes]",
|
"MoNTE48 [Build fix]",
|
||||||
"Lejo [Server-related improvements]",
|
"Jean-Patrick Guerrero (kilbith) [Fixes]",
|
||||||
"EvidenceB [Compass HUD element]",
|
"ROllerozxa [Code cleanups]",
|
||||||
"Paul Ouellette (pauloue) [Lua API, documentation]",
|
"Lejo [bitop library integration]",
|
||||||
"TheTermos [Collision detection, physics]",
|
"LoneWolfHT [Build fixes]",
|
||||||
|
"NeroBurner [Joystick]",
|
||||||
|
"Elias Fleckenstein [Internal fixes]",
|
||||||
"David CARLIER [Unix & Haiku build fixes]",
|
"David CARLIER [Unix & Haiku build fixes]",
|
||||||
"dcbrwn [Object shading]",
|
"pecksin [Clickable web links]",
|
||||||
"Elias Fleckenstein [API features/fixes]",
|
"srfqi [Android & rendering fixes]",
|
||||||
"Jean-Patrick Guerrero (kilbith) [model element, visual fixes]",
|
"EvidenceB [Formspec]",
|
||||||
"k.h.lai [Memory leak fixes, documentation]",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local previous_core_developers = {
|
local previous_core_developers = {
|
||||||
|
@ -80,6 +83,7 @@ local previous_core_developers = {
|
||||||
"Zeno",
|
"Zeno",
|
||||||
"ShadowNinja <shadowninja@minetest.net>",
|
"ShadowNinja <shadowninja@minetest.net>",
|
||||||
"Auke Kok (sofar) <sofar@foo-projects.org>",
|
"Auke Kok (sofar) <sofar@foo-projects.org>",
|
||||||
|
"Aaron Suen <warr1024@gmail.com>",
|
||||||
}
|
}
|
||||||
|
|
||||||
local previous_contributors = {
|
local previous_contributors = {
|
||||||
|
@ -90,10 +94,10 @@ local previous_contributors = {
|
||||||
"MirceaKitsune <mirceakitsune@gmail.com>",
|
"MirceaKitsune <mirceakitsune@gmail.com>",
|
||||||
"Constantin Wenger (SpeedProg)",
|
"Constantin Wenger (SpeedProg)",
|
||||||
"Ciaran Gultnieks (CiaranG)",
|
"Ciaran Gultnieks (CiaranG)",
|
||||||
"stujones11 [Android UX improvements]",
|
"Paul Ouellette (pauloue)",
|
||||||
"Rogier <rogier777@gmail.com> [Fixes]",
|
"stujones11",
|
||||||
"Gregory Currie (gregorycu) [optimisation]",
|
"Rogier <rogier777@gmail.com>",
|
||||||
"srifqi [Fixes]",
|
"Gregory Currie (gregorycu)",
|
||||||
"JacobF",
|
"JacobF",
|
||||||
"Jeija <jeija@mesecons.net> [HTTP, particles]",
|
"Jeija <jeija@mesecons.net> [HTTP, particles]",
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,12 +88,14 @@ local function get_formspec(tabview, name, tabdata)
|
||||||
tabdata.selected_pkg = 1
|
tabdata.selected_pkg = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local use_technical_names = core.settings:get_bool("show_technical_names")
|
||||||
|
|
||||||
|
|
||||||
local retval =
|
local retval =
|
||||||
"label[0.05,-0.25;".. fgettext("Installed Packages:") .. "]" ..
|
"label[0.05,-0.25;".. fgettext("Installed Packages:") .. "]" ..
|
||||||
"tablecolumns[color;tree;text]" ..
|
"tablecolumns[color;tree;text]" ..
|
||||||
"table[0,0.25;5.1,4.3;pkglist;" ..
|
"table[0,0.25;5.1,4.3;pkglist;" ..
|
||||||
pkgmgr.render_packagelist(packages) ..
|
pkgmgr.render_packagelist(packages, use_technical_names) ..
|
||||||
";" .. tabdata.selected_pkg .. "]" ..
|
";" .. tabdata.selected_pkg .. "]" ..
|
||||||
"button[0,4.85;5.25,0.5;btn_contentdb;".. fgettext("Browse online content") .. "]"
|
"button[0,4.85;5.25,0.5;btn_contentdb;".. fgettext("Browse online content") .. "]"
|
||||||
|
|
||||||
|
@ -124,9 +126,17 @@ local function get_formspec(tabview, name, tabdata)
|
||||||
desc = info.description
|
desc = info.description
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local title_and_name
|
||||||
|
if selected_pkg.type == "game" then
|
||||||
|
title_and_name = selected_pkg.name
|
||||||
|
else
|
||||||
|
title_and_name = (selected_pkg.title or selected_pkg.name) .. "\n" ..
|
||||||
|
core.colorize("#BFBFBF", selected_pkg.name)
|
||||||
|
end
|
||||||
|
|
||||||
retval = retval ..
|
retval = retval ..
|
||||||
"image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]" ..
|
"image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]" ..
|
||||||
"label[8.25,0.6;" .. core.formspec_escape(selected_pkg.name) .. "]" ..
|
"label[8.25,0.6;" .. core.formspec_escape(title_and_name) .. "]" ..
|
||||||
"box[5.5,2.2;6.15,2.35;#000]"
|
"box[5.5,2.2;6.15,2.35;#000]"
|
||||||
|
|
||||||
if selected_pkg.type == "mod" then
|
if selected_pkg.type == "mod" then
|
||||||
|
@ -214,6 +224,9 @@ local function handle_doubleclick(pkg, pkg_name)
|
||||||
core.settings:set("texture_path", pkg.path)
|
core.settings:set("texture_path", pkg.path)
|
||||||
end
|
end
|
||||||
packages = nil
|
packages = nil
|
||||||
|
|
||||||
|
mm_game_theme.init()
|
||||||
|
mm_game_theme.reset()
|
||||||
elseif pkg.is_clientside then
|
elseif pkg.is_clientside then
|
||||||
pkgmgr.enable_mod({data = {list = packages, selected_mod = pkg_name}})
|
pkgmgr.enable_mod({data = {list = packages, selected_mod = pkg_name}})
|
||||||
packages = nil
|
packages = nil
|
||||||
|
@ -276,17 +289,17 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if fields.btn_mod_mgr_use_txp then
|
if fields.btn_mod_mgr_use_txp or fields.btn_mod_mgr_disable_txp then
|
||||||
local txp = packages:get_list()[tabdata.selected_pkg]
|
local txp_path = ""
|
||||||
core.settings:set("texture_path", txp.path)
|
if fields.btn_mod_mgr_use_txp then
|
||||||
packages = nil
|
txp_path = packages:get_list()[tabdata.selected_pkg].path
|
||||||
return true
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
core.settings:set("texture_path", txp_path)
|
||||||
if fields.btn_mod_mgr_disable_txp then
|
|
||||||
core.settings:set("texture_path", "")
|
|
||||||
packages = nil
|
packages = nil
|
||||||
|
|
||||||
|
mm_game_theme.init()
|
||||||
|
mm_game_theme.reset()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,30 @@ if enable_gamebar then
|
||||||
return game
|
return game
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Apply menu changes from given game
|
||||||
|
function apply_game(game)
|
||||||
|
core.set_topleft_text(game.name)
|
||||||
|
core.settings:set("menu_last_game", game.id)
|
||||||
|
menudata.worldlist:set_filtercriteria(game.id)
|
||||||
|
|
||||||
|
mm_game_theme.update("singleplayer", game) -- this refreshes the formspec
|
||||||
|
|
||||||
|
local index = filterlist.get_current_index(menudata.worldlist,
|
||||||
|
tonumber(core.settings:get("mainmenu_last_selected_world")))
|
||||||
|
if not index or index < 1 then
|
||||||
|
local selected = core.get_textlist_index("sp_worlds")
|
||||||
|
if selected ~= nil and selected < #menudata.worldlist:get_list() then
|
||||||
|
index = selected
|
||||||
|
else
|
||||||
|
index = #menudata.worldlist:get_list()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
menu_worldmt_legacy(index)
|
||||||
|
end
|
||||||
|
|
||||||
function singleplayer_refresh_gamebar()
|
function singleplayer_refresh_gamebar()
|
||||||
|
|
||||||
local old_bar = ui.find_by_name("game_button_bar")
|
local old_bar = ui.find_by_name("game_button_bar")
|
||||||
|
|
||||||
if old_bar ~= nil then
|
if old_bar ~= nil then
|
||||||
old_bar:delete()
|
old_bar:delete()
|
||||||
end
|
end
|
||||||
|
@ -51,26 +71,10 @@ if enable_gamebar then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
for key,value in pairs(fields) do
|
for _, game in ipairs(pkgmgr.games) do
|
||||||
for j=1,#pkgmgr.games,1 do
|
if fields["game_btnbar_" .. game.id] then
|
||||||
if ("game_btnbar_" .. pkgmgr.games[j].id == key) then
|
apply_game(game)
|
||||||
mm_texture.update("singleplayer", pkgmgr.games[j])
|
return true
|
||||||
core.set_topleft_text(pkgmgr.games[j].name)
|
|
||||||
core.settings:set("menu_last_game",pkgmgr.games[j].id)
|
|
||||||
menudata.worldlist:set_filtercriteria(pkgmgr.games[j].id)
|
|
||||||
local index = filterlist.get_current_index(menudata.worldlist,
|
|
||||||
tonumber(core.settings:get("mainmenu_last_selected_world")))
|
|
||||||
if not index or index < 1 then
|
|
||||||
local selected = core.get_textlist_index("sp_worlds")
|
|
||||||
if selected ~= nil and selected < #menudata.worldlist:get_list() then
|
|
||||||
index = selected
|
|
||||||
else
|
|
||||||
index = #menudata.worldlist:get_list()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
menu_worldmt_legacy(index)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -79,25 +83,22 @@ if enable_gamebar then
|
||||||
game_buttonbar_button_handler,
|
game_buttonbar_button_handler,
|
||||||
{x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15})
|
{x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15})
|
||||||
|
|
||||||
for i=1,#pkgmgr.games,1 do
|
for _, game in ipairs(pkgmgr.games) do
|
||||||
local btn_name = "game_btnbar_" .. pkgmgr.games[i].id
|
local btn_name = "game_btnbar_" .. game.id
|
||||||
|
|
||||||
local image = nil
|
local image = nil
|
||||||
local text = nil
|
local text = nil
|
||||||
local tooltip = core.formspec_escape(pkgmgr.games[i].name)
|
local tooltip = core.formspec_escape(game.name)
|
||||||
|
|
||||||
if pkgmgr.games[i].menuicon_path ~= nil and
|
if (game.menuicon_path or "") ~= "" then
|
||||||
pkgmgr.games[i].menuicon_path ~= "" then
|
image = core.formspec_escape(game.menuicon_path)
|
||||||
image = core.formspec_escape(pkgmgr.games[i].menuicon_path)
|
|
||||||
else
|
else
|
||||||
|
local part1 = game.id:sub(1,5)
|
||||||
local part1 = pkgmgr.games[i].id:sub(1,5)
|
local part2 = game.id:sub(6,10)
|
||||||
local part2 = pkgmgr.games[i].id:sub(6,10)
|
local part3 = game.id:sub(11)
|
||||||
local part3 = pkgmgr.games[i].id:sub(11)
|
|
||||||
|
|
||||||
text = part1 .. "\n" .. part2
|
text = part1 .. "\n" .. part2
|
||||||
if part3 ~= nil and
|
if part3 ~= "" then
|
||||||
part3 ~= "" then
|
|
||||||
text = text .. "\n" .. part3
|
text = text .. "\n" .. part3
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -147,8 +148,12 @@ local function get_formspec(tabview, name, tabdata)
|
||||||
tonumber(core.settings:get("mainmenu_last_selected_world")))
|
tonumber(core.settings:get("mainmenu_last_selected_world")))
|
||||||
local list = menudata.worldlist:get_list()
|
local list = menudata.worldlist:get_list()
|
||||||
local world = list and index and list[index]
|
local world = list and index and list[index]
|
||||||
local gameid = world and world.gameid
|
local game
|
||||||
local game = gameid and pkgmgr.find_by_gameid(gameid)
|
if world then
|
||||||
|
game = pkgmgr.find_by_gameid(world.gameid)
|
||||||
|
else
|
||||||
|
game = current_game()
|
||||||
|
end
|
||||||
local disabled_settings = get_disabled_settings(game)
|
local disabled_settings = get_disabled_settings(game)
|
||||||
|
|
||||||
local creative, damage, host = "", "", ""
|
local creative, damage, host = "", "", ""
|
||||||
|
@ -182,7 +187,7 @@ local function get_formspec(tabview, name, tabdata)
|
||||||
damage ..
|
damage ..
|
||||||
host ..
|
host ..
|
||||||
"textlist[3.9,0.4;7.9,3.45;sp_worlds;" ..
|
"textlist[3.9,0.4;7.9,3.45;sp_worlds;" ..
|
||||||
menu_render_worldlist() ..
|
menu_render_worldlist(not enable_gamebar) ..
|
||||||
";" .. index .. "]"
|
";" .. index .. "]"
|
||||||
|
|
||||||
if core.settings:get_bool("enable_server") and disabled_settings["enable_server"] == nil then
|
if core.settings:get_bool("enable_server") and disabled_settings["enable_server"] == nil then
|
||||||
|
@ -319,11 +324,11 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fields["world_create"] ~= nil then
|
if fields["world_create"] ~= nil then
|
||||||
local create_world_dlg = create_create_world_dlg(true)
|
local create_world_dlg = create_create_world_dlg(enable_gamebar)
|
||||||
create_world_dlg:set_parent(this)
|
create_world_dlg:set_parent(this)
|
||||||
this:hide()
|
this:hide()
|
||||||
create_world_dlg:show()
|
create_world_dlg:show()
|
||||||
mm_texture.update("singleplayer", current_game())
|
mm_game_theme.update("singleplayer", current_game())
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -340,7 +345,7 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||||
delete_world_dlg:set_parent(this)
|
delete_world_dlg:set_parent(this)
|
||||||
this:hide()
|
this:hide()
|
||||||
delete_world_dlg:show()
|
delete_world_dlg:show()
|
||||||
mm_texture.update("singleplayer",current_game())
|
mm_game_theme.update("singleplayer",current_game())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -358,7 +363,7 @@ local function main_button_handler(this, fields, name, tabdata)
|
||||||
configdialog:set_parent(this)
|
configdialog:set_parent(this)
|
||||||
this:hide()
|
this:hide()
|
||||||
configdialog:show()
|
configdialog:show()
|
||||||
mm_texture.update("singleplayer",current_game())
|
mm_game_theme.update("singleplayer",current_game())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -371,11 +376,8 @@ if enable_gamebar then
|
||||||
function on_change(type, old_tab, new_tab)
|
function on_change(type, old_tab, new_tab)
|
||||||
if (type == "ENTER") then
|
if (type == "ENTER") then
|
||||||
local game = current_game()
|
local game = current_game()
|
||||||
|
|
||||||
if game then
|
if game then
|
||||||
menudata.worldlist:set_filtercriteria(game.id)
|
apply_game(game)
|
||||||
core.set_topleft_text(game.name)
|
|
||||||
mm_texture.update("singleplayer",game)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
singleplayer_refresh_gamebar()
|
singleplayer_refresh_gamebar()
|
||||||
|
@ -387,7 +389,7 @@ if enable_gamebar then
|
||||||
gamebar:hide()
|
gamebar:hide()
|
||||||
end
|
end
|
||||||
core.set_topleft_text("")
|
core.set_topleft_text("")
|
||||||
mm_texture.update(new_tab,nil)
|
mm_game_theme.update(new_tab,nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -247,7 +247,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
|
||||||
adv_settings_dlg:set_parent(this)
|
adv_settings_dlg:set_parent(this)
|
||||||
this:hide()
|
this:hide()
|
||||||
adv_settings_dlg:show()
|
adv_settings_dlg:show()
|
||||||
--mm_texture.update("singleplayer", current_game())
|
--mm_game_theme.update("singleplayer", current_game())
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if fields["cb_smooth_lighting"] then
|
if fields["cb_smooth_lighting"] then
|
||||||
|
@ -275,13 +275,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if fields["cb_shaders"] then
|
if fields["cb_shaders"] then
|
||||||
if (core.settings:get("video_driver") == "direct3d8" or
|
core.settings:set("enable_shaders", fields["cb_shaders"])
|
||||||
core.settings:get("video_driver") == "direct3d9") then
|
|
||||||
core.settings:set("enable_shaders", "false")
|
|
||||||
gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.")
|
|
||||||
else
|
|
||||||
core.settings:set("enable_shaders", fields["cb_shaders"])
|
|
||||||
end
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if fields["cb_tonemapping"] then
|
if fields["cb_tonemapping"] then
|
||||||
|
@ -370,11 +364,11 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
|
||||||
core.settings:set("enable_dynamic_shadows", "false")
|
core.settings:set("enable_dynamic_shadows", "false")
|
||||||
else
|
else
|
||||||
local shadow_presets = {
|
local shadow_presets = {
|
||||||
[2] = { 80, 512, "true", 0, "false" },
|
[2] = { 55, 512, "true", 0, "false" },
|
||||||
[3] = { 120, 1024, "true", 1, "false" },
|
[3] = { 82, 1024, "true", 1, "false" },
|
||||||
[4] = { 350, 2048, "true", 1, "false" },
|
[4] = { 240, 2048, "true", 1, "false" },
|
||||||
[5] = { 350, 2048, "true", 2, "true" },
|
[5] = { 240, 2048, "true", 2, "true" },
|
||||||
[6] = { 450, 4096, "true", 2, "true" },
|
[6] = { 300, 4096, "true", 2, "true" },
|
||||||
}
|
}
|
||||||
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
|
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
|
||||||
if s then
|
if s then
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
_G.core = {}
|
_G.core = {}
|
||||||
|
_G.vector = {metatable = {}}
|
||||||
_G.unpack = table.unpack
|
_G.unpack = table.unpack
|
||||||
_G.serverlistmgr = {}
|
_G.serverlistmgr = {}
|
||||||
|
|
||||||
|
|
|
@ -102,8 +102,9 @@ local function instrument(def)
|
||||||
-- also called https://en.wikipedia.org/wiki/Continuation_passing_style
|
-- also called https://en.wikipedia.org/wiki/Continuation_passing_style
|
||||||
-- Compared to table creation and unpacking it won't lose `nil` returns
|
-- Compared to table creation and unpacking it won't lose `nil` returns
|
||||||
-- and is expected to be faster
|
-- and is expected to be faster
|
||||||
-- `measure` will be executed after time() and func(...)
|
-- `measure` will be executed after func(...)
|
||||||
return measure(modname, instrument_name, time(), func(...))
|
local start = time()
|
||||||
|
return measure(modname, instrument_name, start, func(...))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -146,17 +146,17 @@ enable_joysticks (Enable joysticks) bool false
|
||||||
joystick_id (Joystick ID) int 0
|
joystick_id (Joystick ID) int 0
|
||||||
|
|
||||||
# The type of joystick
|
# The type of joystick
|
||||||
joystick_type (Joystick type) enum auto auto,generic,xbox
|
joystick_type (Joystick type) enum auto auto,generic,xbox,dragonrise_gamecube
|
||||||
|
|
||||||
# The time in seconds it takes between repeated events
|
# The time in seconds it takes between repeated events
|
||||||
# when holding down a joystick button combination.
|
# when holding down a joystick button combination.
|
||||||
repeat_joystick_button_time (Joystick button repetition interval) float 0.17 0.001
|
repeat_joystick_button_time (Joystick button repetition interval) float 0.17 0.001
|
||||||
|
|
||||||
# The deadzone of the joystick
|
# The dead zone of the joystick
|
||||||
joystick_deadzone (Joystick deadzone) int 2048
|
joystick_deadzone (Joystick dead zone) int 2048
|
||||||
|
|
||||||
# The sensitivity of the joystick axes for moving the
|
# The sensitivity of the joystick axes for moving the
|
||||||
# ingame view frustum around.
|
# in-game view frustum around.
|
||||||
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170
|
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170
|
||||||
|
|
||||||
# Key for moving the player forward.
|
# Key for moving the player forward.
|
||||||
|
@ -463,9 +463,9 @@ keymap_decrease_viewing_range_min (View range decrease key) key -
|
||||||
|
|
||||||
[**Basic]
|
[**Basic]
|
||||||
|
|
||||||
# Whether nametag backgrounds should be shown by default.
|
# Whether name tag backgrounds should be shown by default.
|
||||||
# Mods may still set a background.
|
# Mods may still set a background.
|
||||||
show_nametag_backgrounds (Show nametag backgrounds by default) bool true
|
show_nametag_backgrounds (Show name tag backgrounds by default) bool true
|
||||||
|
|
||||||
# Enable vertex buffer objects.
|
# Enable vertex buffer objects.
|
||||||
# This should greatly improve graphics performance.
|
# This should greatly improve graphics performance.
|
||||||
|
@ -487,6 +487,10 @@ connected_glass (Connect glass) bool false
|
||||||
# Disable for speed or for different looks.
|
# Disable for speed or for different looks.
|
||||||
smooth_lighting (Smooth lighting) bool true
|
smooth_lighting (Smooth lighting) bool true
|
||||||
|
|
||||||
|
# Enables tradeoffs that reduce CPU load or increase rendering performance
|
||||||
|
# at the expense of minor visual glitches that do not impact game playability.
|
||||||
|
performance_tradeoffs (Tradeoffs for performance) bool false
|
||||||
|
|
||||||
# Clouds are a client side effect.
|
# Clouds are a client side effect.
|
||||||
enable_clouds (Clouds) bool true
|
enable_clouds (Clouds) bool true
|
||||||
|
|
||||||
|
@ -501,7 +505,7 @@ enable_particles (Digging particles) bool true
|
||||||
|
|
||||||
[**Filtering]
|
[**Filtering]
|
||||||
|
|
||||||
# Use mip mapping to scale textures. May slightly increase performance,
|
# Use mipmapping to scale textures. May slightly increase performance,
|
||||||
# especially when using a high resolution texture pack.
|
# especially when using a high resolution texture pack.
|
||||||
# Gamma correct downscaling is not supported.
|
# Gamma correct downscaling is not supported.
|
||||||
mip_map (Mipmapping) bool false
|
mip_map (Mipmapping) bool false
|
||||||
|
@ -600,9 +604,10 @@ enable_waving_plants (Waving plants) bool false
|
||||||
# Requires shaders to be enabled.
|
# Requires shaders to be enabled.
|
||||||
enable_dynamic_shadows (Dynamic shadows) bool false
|
enable_dynamic_shadows (Dynamic shadows) bool false
|
||||||
|
|
||||||
# Set the shadow strength.
|
# Set the shadow strength gamma.
|
||||||
|
# Adjusts the intensity of in-game dynamic shadows.
|
||||||
# Lower value means lighter shadows, higher value means darker shadows.
|
# Lower value means lighter shadows, higher value means darker shadows.
|
||||||
shadow_strength (Shadow strength) float 0.2 0.05 1.0
|
shadow_strength_gamma (Shadow strength gamma) float 1.0 0.1 10.0
|
||||||
|
|
||||||
# Maximum distance to render shadows.
|
# Maximum distance to render shadows.
|
||||||
shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 120.0 10.0 1000.0
|
shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 120.0 10.0 1000.0
|
||||||
|
@ -621,12 +626,12 @@ shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true
|
||||||
# On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering.
|
# On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering.
|
||||||
shadow_poisson_filter (Poisson filtering) bool true
|
shadow_poisson_filter (Poisson filtering) bool true
|
||||||
|
|
||||||
# Define shadow filtering quality
|
# Define shadow filtering quality.
|
||||||
# This simulates the soft shadows effect by applying a PCF or Poisson disk
|
# This simulates the soft shadows effect by applying a PCF or Poisson disk
|
||||||
# but also uses more resources.
|
# but also uses more resources.
|
||||||
shadow_filters (Shadow filter quality) enum 1 0,1,2
|
shadow_filters (Shadow filter quality) enum 1 0,1,2
|
||||||
|
|
||||||
# Enable colored shadows.
|
# Enable colored shadows.
|
||||||
# On true translucent nodes cast colored shadows. This is expensive.
|
# On true translucent nodes cast colored shadows. This is expensive.
|
||||||
shadow_map_color (Colored shadows) bool false
|
shadow_map_color (Colored shadows) bool false
|
||||||
|
|
||||||
|
@ -638,10 +643,10 @@ shadow_update_frames (Map shadows update frames) int 8 1 16
|
||||||
|
|
||||||
# Set the soft shadow radius size.
|
# Set the soft shadow radius size.
|
||||||
# Lower values mean sharper shadows, bigger values mean softer shadows.
|
# Lower values mean sharper shadows, bigger values mean softer shadows.
|
||||||
# Minimum value: 1.0; maxiumum value: 10.0
|
# Minimum value: 1.0; maximum value: 10.0
|
||||||
shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0
|
shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0
|
||||||
|
|
||||||
# Set the tilt of Sun/Moon orbit in degrees
|
# Set the tilt of Sun/Moon orbit in degrees.
|
||||||
# Value of 0 means no tilt / vertical orbit.
|
# Value of 0 means no tilt / vertical orbit.
|
||||||
# Minimum value: 0.0; maximum value: 60.0
|
# Minimum value: 0.0; maximum value: 60.0
|
||||||
shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 0.0 60.0
|
shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 0.0 60.0
|
||||||
|
@ -801,7 +806,7 @@ desynchronize_mapblock_texture_animation (Desynchronize block animation) bool tr
|
||||||
# Useful if there's something to be displayed right or left of hotbar.
|
# Useful if there's something to be displayed right or left of hotbar.
|
||||||
hud_hotbar_max_width (Maximum hotbar width) float 1.0
|
hud_hotbar_max_width (Maximum hotbar width) float 1.0
|
||||||
|
|
||||||
# Modifies the size of the hudbar elements.
|
# Modifies the size of the HUD elements.
|
||||||
hud_scaling (HUD scale factor) float 1.0
|
hud_scaling (HUD scale factor) float 1.0
|
||||||
|
|
||||||
# Enables caching of facedir rotated meshes.
|
# Enables caching of facedir rotated meshes.
|
||||||
|
@ -865,6 +870,10 @@ autoscale_mode (Autoscaling mode) enum disable disable,enable,force
|
||||||
# A restart is required after changing this.
|
# A restart is required after changing this.
|
||||||
show_entity_selectionbox (Show entity selection boxes) bool false
|
show_entity_selectionbox (Show entity selection boxes) bool false
|
||||||
|
|
||||||
|
# Distance in nodes at which transparency depth sorting is enabled
|
||||||
|
# Use this to limit the performance impact of transparency depth sorting
|
||||||
|
transparency_sorting_distance (Transparency Sorting Distance) int 16 0 128
|
||||||
|
|
||||||
[*Menus]
|
[*Menus]
|
||||||
|
|
||||||
# Use a cloud animation for the main menu background.
|
# Use a cloud animation for the main menu background.
|
||||||
|
@ -894,10 +903,6 @@ tooltip_show_delay (Tooltip delay) int 400
|
||||||
# Append item name to tooltip.
|
# Append item name to tooltip.
|
||||||
tooltip_append_itemname (Append item name) bool false
|
tooltip_append_itemname (Append item name) bool false
|
||||||
|
|
||||||
# Whether FreeType fonts are used, requires FreeType support to be compiled in.
|
|
||||||
# If disabled, bitmap and XML vectors fonts are used instead.
|
|
||||||
freetype (FreeType fonts) bool true
|
|
||||||
|
|
||||||
font_bold (Font bold by default) bool false
|
font_bold (Font bold by default) bool false
|
||||||
|
|
||||||
font_italic (Font italic by default) bool false
|
font_italic (Font italic by default) bool false
|
||||||
|
@ -908,12 +913,16 @@ font_shadow (Font shadow) int 1
|
||||||
# Opaqueness (alpha) of the shadow behind the default font, between 0 and 255.
|
# Opaqueness (alpha) of the shadow behind the default font, between 0 and 255.
|
||||||
font_shadow_alpha (Font shadow alpha) int 127 0 255
|
font_shadow_alpha (Font shadow alpha) int 127 0 255
|
||||||
|
|
||||||
# Font size of the default font in point (pt).
|
# Font size of the default font where 1 unit = 1 pixel at 96 DPI
|
||||||
font_size (Font size) int 16 1
|
font_size (Font size) int 16 1
|
||||||
|
|
||||||
# Path to the default font.
|
# For pixel-style fonts that do not scale well, this ensures that font sizes used
|
||||||
# If “freetype” setting is enabled: Must be a TrueType font.
|
# with this font will always be divisible by this value, in pixels. For instance,
|
||||||
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
|
# a pixel font 16 pixels tall should have this set to 16, so it will only ever be
|
||||||
|
# sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32.
|
||||||
|
font_size_divisible_by (Font size divisible by) int 1 1
|
||||||
|
|
||||||
|
# Path to the default font. Must be a TrueType font.
|
||||||
# The fallback font will be used if the font cannot be loaded.
|
# The fallback font will be used if the font cannot be loaded.
|
||||||
font_path (Regular font path) filepath fonts/Arimo-Regular.ttf
|
font_path (Regular font path) filepath fonts/Arimo-Regular.ttf
|
||||||
|
|
||||||
|
@ -921,12 +930,16 @@ font_path_bold (Bold font path) filepath fonts/Arimo-Bold.ttf
|
||||||
font_path_italic (Italic font path) filepath fonts/Arimo-Italic.ttf
|
font_path_italic (Italic font path) filepath fonts/Arimo-Italic.ttf
|
||||||
font_path_bold_italic (Bold and italic font path) filepath fonts/Arimo-BoldItalic.ttf
|
font_path_bold_italic (Bold and italic font path) filepath fonts/Arimo-BoldItalic.ttf
|
||||||
|
|
||||||
# Font size of the monospace font in point (pt).
|
# Font size of the monospace font where 1 unit = 1 pixel at 96 DPI
|
||||||
mono_font_size (Monospace font size) int 15 1
|
mono_font_size (Monospace font size) int 16 1
|
||||||
|
|
||||||
# Path to the monospace font.
|
# For pixel-style fonts that do not scale well, this ensures that font sizes used
|
||||||
# If “freetype” setting is enabled: Must be a TrueType font.
|
# with this font will always be divisible by this value, in pixels. For instance,
|
||||||
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
|
# a pixel font 16 pixels tall should have this set to 16, so it will only ever be
|
||||||
|
# sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32.
|
||||||
|
mono_font_size_divisible_by (Monospace font size divisible by) int 1 1
|
||||||
|
|
||||||
|
# Path to the monospace font. Must be a TrueType font.
|
||||||
# This font is used for e.g. the console and profiler screen.
|
# This font is used for e.g. the console and profiler screen.
|
||||||
mono_font_path (Monospace font path) filepath fonts/Cousine-Regular.ttf
|
mono_font_path (Monospace font path) filepath fonts/Cousine-Regular.ttf
|
||||||
|
|
||||||
|
@ -934,9 +947,7 @@ mono_font_path_bold (Bold monospace font path) filepath fonts/Cousine-Bold.ttf
|
||||||
mono_font_path_italic (Italic monospace font path) filepath fonts/Cousine-Italic.ttf
|
mono_font_path_italic (Italic monospace font path) filepath fonts/Cousine-Italic.ttf
|
||||||
mono_font_path_bold_italic (Bold and italic monospace font path) filepath fonts/Cousine-BoldItalic.ttf
|
mono_font_path_bold_italic (Bold and italic monospace font path) filepath fonts/Cousine-BoldItalic.ttf
|
||||||
|
|
||||||
# Path of the fallback font.
|
# Path of the fallback font. Must be a TrueType font.
|
||||||
# If “freetype” setting is enabled: Must be a TrueType font.
|
|
||||||
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
|
|
||||||
# This font will be used for certain languages or if the default font is unavailable.
|
# 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
|
fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf
|
||||||
|
|
||||||
|
@ -949,7 +960,7 @@ chat_font_size (Chat font size) int 0
|
||||||
screenshot_path (Screenshot folder) path screenshots
|
screenshot_path (Screenshot folder) path screenshots
|
||||||
|
|
||||||
# Format of screenshots.
|
# Format of screenshots.
|
||||||
screenshot_format (Screenshot format) enum png png,jpg,bmp,pcx,ppm,tga
|
screenshot_format (Screenshot format) enum png png,jpg
|
||||||
|
|
||||||
# Screenshot quality. Only used for JPEG format.
|
# Screenshot quality. Only used for JPEG format.
|
||||||
# 1 means worst quality; 100 means best quality.
|
# 1 means worst quality; 100 means best quality.
|
||||||
|
@ -961,6 +972,9 @@ screenshot_quality (Screenshot quality) int 0 0 100
|
||||||
# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.
|
# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.
|
||||||
screen_dpi (DPI) int 72 1
|
screen_dpi (DPI) int 72 1
|
||||||
|
|
||||||
|
# Adjust the detected display density, used for scaling UI elements.
|
||||||
|
display_density_factor (Display Density Scaling Factor) float 1
|
||||||
|
|
||||||
# Windows systems only: Start Minetest with the command line window in the background.
|
# Windows systems only: Start Minetest with the command line window in the background.
|
||||||
# Contains the same information as the file debug.txt (default name).
|
# Contains the same information as the file debug.txt (default name).
|
||||||
enable_console (Enable console window) bool false
|
enable_console (Enable console window) bool false
|
||||||
|
@ -985,8 +999,8 @@ mute_sound (Mute sound) bool false
|
||||||
|
|
||||||
[Client]
|
[Client]
|
||||||
|
|
||||||
# Clickable weblinks (middle-click or ctrl-left-click) enabled in chat console output.
|
# Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output.
|
||||||
clickable_chat_weblinks (Chat weblinks) bool false
|
clickable_chat_weblinks (Chat weblinks) bool true
|
||||||
|
|
||||||
# Optional override for chat weblink color.
|
# Optional override for chat weblink color.
|
||||||
chat_weblink_color (Weblink color) string
|
chat_weblink_color (Weblink color) string
|
||||||
|
@ -1044,6 +1058,12 @@ client_unload_unused_data_timeout (Mapblock unload timeout) int 600
|
||||||
# Set to -1 for unlimited amount.
|
# Set to -1 for unlimited amount.
|
||||||
client_mapblock_limit (Mapblock limit) int 7500
|
client_mapblock_limit (Mapblock limit) int 7500
|
||||||
|
|
||||||
|
# Whether to show technical names.
|
||||||
|
# Affects mods and texture packs in the Content and Select Mods menus, as well as
|
||||||
|
# setting names in All Settings.
|
||||||
|
# Controlled by the checkbox in the "All settings" menu.
|
||||||
|
show_technical_names (Show technical names) bool false
|
||||||
|
|
||||||
# Whether to show the client debug info (has the same effect as hitting F5).
|
# Whether to show the client debug info (has the same effect as hitting F5).
|
||||||
show_debug (Show debug info) bool false
|
show_debug (Show debug info) bool false
|
||||||
|
|
||||||
|
@ -1114,7 +1134,7 @@ max_packets_per_iteration (Max. packets per iteration) int 1024
|
||||||
|
|
||||||
# Compression level to use when sending mapblocks to the client.
|
# Compression level to use when sending mapblocks to the client.
|
||||||
# -1 - use default compression level
|
# -1 - use default compression level
|
||||||
# 0 - least compresson, fastest
|
# 0 - least compression, fastest
|
||||||
# 9 - best compression, slowest
|
# 9 - best compression, slowest
|
||||||
map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9
|
map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9
|
||||||
|
|
||||||
|
@ -1178,7 +1198,7 @@ enable_mod_channels (Mod channels) bool false
|
||||||
# If this is set, players will always (re)spawn at the given position.
|
# If this is set, players will always (re)spawn at the given position.
|
||||||
static_spawnpoint (Static spawnpoint) string
|
static_spawnpoint (Static spawnpoint) string
|
||||||
|
|
||||||
# If enabled, new players cannot join with an empty password.
|
# If enabled, players cannot join without a password or change theirs to an empty password.
|
||||||
disallow_empty_password (Disallow empty passwords) bool false
|
disallow_empty_password (Disallow empty passwords) bool false
|
||||||
|
|
||||||
# If enabled, disable cheat prevention in multiplayer.
|
# If enabled, disable cheat prevention in multiplayer.
|
||||||
|
@ -1300,7 +1320,7 @@ movement_gravity (Gravity) float 9.81
|
||||||
deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error
|
deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error
|
||||||
|
|
||||||
# Number of extra blocks that can be loaded by /clearobjects at once.
|
# Number of extra blocks that can be loaded by /clearobjects at once.
|
||||||
# This is a trade-off between sqlite transaction overhead and
|
# This is a trade-off between SQLite transaction overhead and
|
||||||
# memory consumption (4096=100MB, as a rule of thumb).
|
# memory consumption (4096=100MB, as a rule of thumb).
|
||||||
max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096
|
max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096
|
||||||
|
|
||||||
|
@ -1309,14 +1329,14 @@ max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096
|
||||||
server_unload_unused_data_timeout (Unload unused server data) int 29
|
server_unload_unused_data_timeout (Unload unused server data) int 29
|
||||||
|
|
||||||
# Maximum number of statically stored objects in a block.
|
# Maximum number of statically stored objects in a block.
|
||||||
max_objects_per_block (Maximum objects per block) int 64
|
max_objects_per_block (Maximum objects per block) int 256
|
||||||
|
|
||||||
# See https://www.sqlite.org/pragma.html#pragma_synchronous
|
# See https://www.sqlite.org/pragma.html#pragma_synchronous
|
||||||
sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2
|
sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2
|
||||||
|
|
||||||
# Compression level to use when saving mapblocks to disk.
|
# Compression level to use when saving mapblocks to disk.
|
||||||
# -1 - use default compression level
|
# -1 - use default compression level
|
||||||
# 0 - least compresson, fastest
|
# 0 - least compression, fastest
|
||||||
# 9 - best compression, slowest
|
# 9 - best compression, slowest
|
||||||
map_compression_level_disk (Map Compression Level for Disk Storage) int -1 -1 9
|
map_compression_level_disk (Map Compression Level for Disk Storage) int -1 -1 9
|
||||||
|
|
||||||
|
@ -1423,8 +1443,8 @@ instrument.abm (Active Block Modifiers) bool true
|
||||||
# Instrument the action function of Loading Block Modifiers on registration.
|
# Instrument the action function of Loading Block Modifiers on registration.
|
||||||
instrument.lbm (Loading Block Modifiers) bool true
|
instrument.lbm (Loading Block Modifiers) bool true
|
||||||
|
|
||||||
# Instrument chatcommands on registration.
|
# Instrument chat commands on registration.
|
||||||
instrument.chatcommand (Chatcommands) bool true
|
instrument.chatcommand (Chat commands) bool true
|
||||||
|
|
||||||
# Instrument global callback functions on registration.
|
# Instrument global callback functions on registration.
|
||||||
# (anything you pass to a minetest.register_*() function)
|
# (anything you pass to a minetest.register_*() function)
|
||||||
|
@ -1460,7 +1480,8 @@ language (Language) enum ,be,bg,ca,cs,da,de,el,en,eo,es,et,eu,fi,fr,gd,gl,hu,i
|
||||||
# - action
|
# - action
|
||||||
# - info
|
# - info
|
||||||
# - verbose
|
# - verbose
|
||||||
debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose
|
# - trace
|
||||||
|
debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose,trace
|
||||||
|
|
||||||
# If the file size of debug.txt exceeds the number of megabytes specified in
|
# If the file size of debug.txt exceeds the number of megabytes specified in
|
||||||
# this setting when it is opened, the file is moved to debug.txt.1,
|
# this setting when it is opened, the file is moved to debug.txt.1,
|
||||||
|
@ -1469,7 +1490,7 @@ debug_log_level (Debug log level) enum action ,none,error,warning,action,info,ve
|
||||||
debug_log_size_max (Debug log file size threshold) int 50
|
debug_log_size_max (Debug log file size threshold) int 50
|
||||||
|
|
||||||
# Minimal level of logging to be written to chat.
|
# Minimal level of logging to be written to chat.
|
||||||
chat_log_level (Chat log level) enum error ,none,error,warning,action,info,verbose
|
chat_log_level (Chat log level) enum error ,none,error,warning,action,info,verbose,trace
|
||||||
|
|
||||||
# Enable IPv6 support (for both client and server).
|
# Enable IPv6 support (for both client and server).
|
||||||
# Required for IPv6 connections to work at all.
|
# Required for IPv6 connections to work at all.
|
||||||
|
@ -1514,11 +1535,11 @@ max_block_generate_distance (Max block generate distance) int 10
|
||||||
# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0).
|
# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0).
|
||||||
# Only mapchunks completely within the mapgen limit are generated.
|
# Only mapchunks completely within the mapgen limit are generated.
|
||||||
# Value is stored per-world.
|
# Value is stored per-world.
|
||||||
mapgen_limit (Map generation limit) int 31000 0 31000
|
mapgen_limit (Map generation limit) int 31007 0 31007
|
||||||
|
|
||||||
# Global map generation attributes.
|
# Global map generation attributes.
|
||||||
# In Mapgen v6 the 'decorations' flag controls all decorations except trees
|
# In Mapgen v6 the 'decorations' flag controls all decorations except trees
|
||||||
# and junglegrass, in all other mapgens this flag controls all decorations.
|
# and jungle grass, in all other mapgens this flag controls all decorations.
|
||||||
mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores,nocaves,nodungeons,nolight,nodecorations,nobiomes,noores
|
mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores,nocaves,nodungeons,nolight,nodecorations,nobiomes,noores
|
||||||
|
|
||||||
[*Biome API temperature and humidity noise parameters]
|
[*Biome API temperature and humidity noise parameters]
|
||||||
|
@ -2270,7 +2291,7 @@ contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3
|
||||||
[Cheat Menu]
|
[Cheat Menu]
|
||||||
|
|
||||||
# Font to use for cheat menu
|
# Font to use for cheat menu
|
||||||
cheat_menu_font (MenuFont) enum FM_Mono FM_Standard,FM_Mono,FM_Fallback,FM_Simple,FM_SimpleMono,FM_MaxMode,FM_Unspecified
|
cheat_menu_font (MenuFont) enum FM_Mono FM_Standard,FM_Mono,FM_Fallback,FM_MaxMode,FM_Unspecified
|
||||||
|
|
||||||
# (RGB value)
|
# (RGB value)
|
||||||
cheat_menu_bg_color (Cell background color) v3f 255, 145, 88
|
cheat_menu_bg_color (Cell background color) v3f 255, 145, 88
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
uniform sampler2D baseTexture;
|
uniform sampler2D baseTexture;
|
||||||
|
|
||||||
|
uniform vec3 dayLight;
|
||||||
uniform vec4 skyBgColor;
|
uniform vec4 skyBgColor;
|
||||||
uniform float fogDistance;
|
uniform float fogDistance;
|
||||||
uniform vec3 eyePosition;
|
uniform vec3 eyePosition;
|
||||||
|
@ -15,10 +16,15 @@ uniform float animationTimer;
|
||||||
uniform float f_textureresolution;
|
uniform float f_textureresolution;
|
||||||
uniform mat4 m_ShadowViewProj;
|
uniform mat4 m_ShadowViewProj;
|
||||||
uniform float f_shadowfar;
|
uniform float f_shadowfar;
|
||||||
varying float normalOffsetScale;
|
uniform float f_shadow_strength;
|
||||||
|
uniform vec4 CameraPos;
|
||||||
|
uniform float xyPerspectiveBias0;
|
||||||
|
uniform float xyPerspectiveBias1;
|
||||||
|
|
||||||
varying float adj_shadow_strength;
|
varying float adj_shadow_strength;
|
||||||
varying float cosLight;
|
varying float cosLight;
|
||||||
varying float f_normal_length;
|
varying float f_normal_length;
|
||||||
|
varying vec3 shadow_position;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,23 +48,7 @@ varying float nightRatio;
|
||||||
const float fogStart = FOG_START;
|
const float fogStart = FOG_START;
|
||||||
const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
|
const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
const float bias0 = 0.9;
|
|
||||||
const float zPersFactor = 0.5;
|
|
||||||
const float bias1 = 1.0 - bias0 + 1e-6;
|
|
||||||
|
|
||||||
vec4 getPerspectiveFactor(in vec4 shadowPosition)
|
|
||||||
{
|
|
||||||
|
|
||||||
float pDistance = length(shadowPosition.xy);
|
|
||||||
float pFactor = pDistance * bias0 + bias1;
|
|
||||||
|
|
||||||
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
|
|
||||||
|
|
||||||
return shadowPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
// assuming near is always 1.0
|
// assuming near is always 1.0
|
||||||
float getLinearDepth()
|
float getLinearDepth()
|
||||||
|
@ -68,16 +58,7 @@ float getLinearDepth()
|
||||||
|
|
||||||
vec3 getLightSpacePosition()
|
vec3 getLightSpacePosition()
|
||||||
{
|
{
|
||||||
vec4 pLightSpace;
|
return shadow_position * 0.5 + 0.5;
|
||||||
// some drawtypes have zero normals, so we need to handle it :(
|
|
||||||
#if DRAW_TYPE == NDT_PLANTLIKE
|
|
||||||
pLightSpace = m_ShadowViewProj * vec4(worldPosition, 1.0);
|
|
||||||
#else
|
|
||||||
float offsetScale = (0.0057 * getLinearDepth() + normalOffsetScale);
|
|
||||||
pLightSpace = m_ShadowViewProj * vec4(worldPosition + offsetScale * normalize(vNormal), 1.0);
|
|
||||||
#endif
|
|
||||||
pLightSpace = getPerspectiveFactor(pLightSpace);
|
|
||||||
return pLightSpace.xyz * 0.5 + 0.5;
|
|
||||||
}
|
}
|
||||||
// custom smoothstep implementation because it's not defined in glsl1.2
|
// custom smoothstep implementation because it's not defined in glsl1.2
|
||||||
// https://docs.gl/sl4/smoothstep
|
// https://docs.gl/sl4/smoothstep
|
||||||
|
@ -170,13 +151,13 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis
|
||||||
|
|
||||||
float getBaseLength(vec2 smTexCoord)
|
float getBaseLength(vec2 smTexCoord)
|
||||||
{
|
{
|
||||||
float l = length(2.0 * smTexCoord.xy - 1.0); // length in texture coords
|
float l = length(2.0 * smTexCoord.xy - 1.0 - CameraPos.xy); // length in texture coords
|
||||||
return bias1 / (1.0 / l - bias0); // return to undistorted coords
|
return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0); // return to undistorted coords
|
||||||
}
|
}
|
||||||
|
|
||||||
float getDeltaPerspectiveFactor(float l)
|
float getDeltaPerspectiveFactor(float l)
|
||||||
{
|
{
|
||||||
return 0.1 / (bias0 * l + bias1); // original distortion factor, divided by 10
|
return 0.04 * pow(512.0 / f_textureresolution, 0.4) / (xyPerspectiveBias0 * l + xyPerspectiveBias1); // original distortion factor, divided by 10
|
||||||
}
|
}
|
||||||
|
|
||||||
float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier)
|
float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier)
|
||||||
|
@ -185,6 +166,9 @@ float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDist
|
||||||
float perspectiveFactor;
|
float perspectiveFactor;
|
||||||
|
|
||||||
// Return fast if sharp shadows are requested
|
// Return fast if sharp shadows are requested
|
||||||
|
if (PCFBOUND == 0.0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
if (SOFTSHADOWRADIUS <= 1.0) {
|
if (SOFTSHADOWRADIUS <= 1.0) {
|
||||||
perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
|
perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
|
||||||
return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS);
|
return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS);
|
||||||
|
@ -479,36 +463,59 @@ void main(void)
|
||||||
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
|
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
float shadow_int = 0.0;
|
if (f_shadow_strength > 0.0) {
|
||||||
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
|
float shadow_int = 0.0;
|
||||||
vec3 posLightSpace = getLightSpacePosition();
|
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
|
||||||
|
vec3 posLightSpace = getLightSpacePosition();
|
||||||
|
|
||||||
float distance_rate = (1 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 20.0));
|
float distance_rate = (1.0 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 10.0));
|
||||||
float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1, posLightSpace.z ),0.0);
|
if (max(abs(posLightSpace.x - 0.5), abs(posLightSpace.y - 0.5)) > 0.5)
|
||||||
|
distance_rate = 0.0;
|
||||||
|
float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1, posLightSpace.z),0.0);
|
||||||
|
|
||||||
|
if (distance_rate > 1e-7) {
|
||||||
|
|
||||||
if (distance_rate > 1e-7) {
|
|
||||||
|
|
||||||
#ifdef COLORED_SHADOWS
|
#ifdef COLORED_SHADOWS
|
||||||
vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
vec4 visibility;
|
||||||
shadow_int = visibility.r;
|
if (cosLight > 0.0 || f_normal_length < 1e-3)
|
||||||
shadow_color = visibility.gba;
|
visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
||||||
|
else
|
||||||
|
visibility = vec4(1.0, 0.0, 0.0, 0.0);
|
||||||
|
shadow_int = visibility.r;
|
||||||
|
shadow_color = visibility.gba;
|
||||||
#else
|
#else
|
||||||
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
if (cosLight > 0.0 || f_normal_length < 1e-3)
|
||||||
|
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
||||||
|
else
|
||||||
|
shadow_int = 1.0;
|
||||||
#endif
|
#endif
|
||||||
shadow_int *= distance_rate;
|
shadow_int *= distance_rate;
|
||||||
shadow_int *= 1.0 - nightRatio;
|
shadow_int = clamp(shadow_int, 0.0, 1.0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// turns out that nightRatio falls off much faster than
|
||||||
|
// actual brightness of artificial light in relation to natual light.
|
||||||
|
// Power ratio was measured on torches in MTG (brightness = 14).
|
||||||
|
float adjusted_night_ratio = pow(max(0.0, nightRatio), 0.6);
|
||||||
|
|
||||||
|
// Apply self-shadowing when light falls at a narrow angle to the surface
|
||||||
|
// Cosine of the cut-off angle.
|
||||||
|
const float self_shadow_cutoff_cosine = 0.035;
|
||||||
|
if (f_normal_length != 0 && cosLight < self_shadow_cutoff_cosine) {
|
||||||
|
shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
|
||||||
|
shadow_color = mix(vec3(0.0), shadow_color, min(cosLight, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
|
||||||
|
}
|
||||||
|
|
||||||
|
shadow_int *= f_adj_shadow_strength;
|
||||||
|
|
||||||
|
// calculate fragment color from components:
|
||||||
|
col.rgb =
|
||||||
|
adjusted_night_ratio * col.rgb + // artificial light
|
||||||
|
(1.0 - adjusted_night_ratio) * ( // natural light
|
||||||
|
col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) + // filtered texture color
|
||||||
|
dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f_normal_length != 0 && cosLight < 0.035) {
|
|
||||||
shadow_int = max(shadow_int, min(clamp(1.0-nightRatio, 0.0, 1.0), 1 - clamp(cosLight, 0.0, 0.035)/0.035));
|
|
||||||
}
|
|
||||||
|
|
||||||
shadow_int = 1.0 - (shadow_int * f_adj_shadow_strength);
|
|
||||||
|
|
||||||
col.rgb = mix(shadow_color,col.rgb,shadow_int)*shadow_int;
|
|
||||||
// col.r = 0.5 * clamp(getPenumbraRadius(ShadowMapSampler, posLightSpace.xy, posLightSpace.z, 1.0) / SOFTSHADOWRADIUS, 0.0, 1.0) + 0.5 * col.r;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ENABLE_TONE_MAPPING
|
#if ENABLE_TONE_MAPPING
|
||||||
|
@ -528,6 +535,6 @@ void main(void)
|
||||||
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
|
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
|
||||||
col = mix(skyBgColor, col, clarity);
|
col = mix(skyBgColor, col, clarity);
|
||||||
col = vec4(col.rgb, base.a);
|
col = vec4(col.rgb, base.a);
|
||||||
|
|
||||||
gl_FragColor = col;
|
gl_FragColor = col;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,13 @@ centroid varying vec2 varTexCoord;
|
||||||
uniform float f_shadowfar;
|
uniform float f_shadowfar;
|
||||||
uniform float f_shadow_strength;
|
uniform float f_shadow_strength;
|
||||||
uniform float f_timeofday;
|
uniform float f_timeofday;
|
||||||
|
uniform vec4 CameraPos;
|
||||||
|
|
||||||
varying float cosLight;
|
varying float cosLight;
|
||||||
varying float normalOffsetScale;
|
varying float normalOffsetScale;
|
||||||
varying float adj_shadow_strength;
|
varying float adj_shadow_strength;
|
||||||
varying float f_normal_length;
|
varying float f_normal_length;
|
||||||
|
varying vec3 shadow_position;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,8 +48,38 @@ varying float nightRatio;
|
||||||
const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
|
const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
|
||||||
const float e = 2.718281828459;
|
const float e = 2.718281828459;
|
||||||
const float BS = 10.0;
|
const float BS = 10.0;
|
||||||
|
uniform float xyPerspectiveBias0;
|
||||||
|
uniform float xyPerspectiveBias1;
|
||||||
|
uniform float zPerspectiveBias;
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
|
|
||||||
|
vec4 getRelativePosition(in vec4 position)
|
||||||
|
{
|
||||||
|
vec2 l = position.xy - CameraPos.xy;
|
||||||
|
vec2 s = l / abs(l);
|
||||||
|
s = (1.0 - s * CameraPos.xy);
|
||||||
|
l /= s;
|
||||||
|
return vec4(l, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getPerspectiveFactor(in vec4 relativePosition)
|
||||||
|
{
|
||||||
|
float pDistance = length(relativePosition.xy);
|
||||||
|
float pFactor = pDistance * xyPerspectiveBias0 + xyPerspectiveBias1;
|
||||||
|
return pFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 applyPerspectiveDistortion(in vec4 position)
|
||||||
|
{
|
||||||
|
vec4 l = getRelativePosition(position);
|
||||||
|
float pFactor = getPerspectiveFactor(l);
|
||||||
|
l.xy /= pFactor;
|
||||||
|
position.xy = l.xy * l.zw + CameraPos.xy;
|
||||||
|
position.z *= zPerspectiveBias;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
// custom smoothstep implementation because it's not defined in glsl1.2
|
// custom smoothstep implementation because it's not defined in glsl1.2
|
||||||
// https://docs.gl/sl4/smoothstep
|
// https://docs.gl/sl4/smoothstep
|
||||||
float mtsmoothstep(in float edge0, in float edge1, in float x)
|
float mtsmoothstep(in float edge0, in float edge1, in float x)
|
||||||
|
@ -193,24 +226,45 @@ void main(void)
|
||||||
varColor = clamp(color, 0.0, 1.0);
|
varColor = clamp(color, 0.0, 1.0);
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
vec3 nNormal = normalize(vNormal);
|
if (f_shadow_strength > 0.0) {
|
||||||
cosLight = dot(nNormal, -v_LightDirection);
|
vec3 nNormal;
|
||||||
float texelSize = 767.0 / f_textureresolution;
|
f_normal_length = length(vNormal);
|
||||||
float slopeScale = clamp(1.0 - abs(cosLight), 0.0, 1.0);
|
|
||||||
normalOffsetScale = texelSize * slopeScale;
|
|
||||||
|
|
||||||
if (f_timeofday < 0.2) {
|
|
||||||
adj_shadow_strength = f_shadow_strength * 0.5 *
|
|
||||||
(1.0 - mtsmoothstep(0.18, 0.2, f_timeofday));
|
|
||||||
} else if (f_timeofday >= 0.8) {
|
|
||||||
adj_shadow_strength = f_shadow_strength * 0.5 *
|
|
||||||
mtsmoothstep(0.8, 0.83, f_timeofday);
|
|
||||||
} else {
|
|
||||||
adj_shadow_strength = f_shadow_strength *
|
|
||||||
mtsmoothstep(0.20, 0.25, f_timeofday) *
|
|
||||||
(1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
|
|
||||||
}
|
|
||||||
f_normal_length = length(vNormal);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
/* normalOffsetScale is in world coordinates (1/10th of a meter)
|
||||||
|
z_bias is in light space coordinates */
|
||||||
|
float normalOffsetScale, z_bias;
|
||||||
|
float pFactor = getPerspectiveFactor(getRelativePosition(m_ShadowViewProj * mWorld * inVertexPosition));
|
||||||
|
if (f_normal_length > 0.0) {
|
||||||
|
nNormal = normalize(vNormal);
|
||||||
|
cosLight = dot(nNormal, -v_LightDirection);
|
||||||
|
float sinLight = pow(1 - pow(cosLight, 2.0), 0.5);
|
||||||
|
normalOffsetScale = 2.0 * pFactor * pFactor * sinLight * min(f_shadowfar, 500.0) /
|
||||||
|
xyPerspectiveBias1 / f_textureresolution;
|
||||||
|
z_bias = 1.0 * sinLight / cosLight;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nNormal = vec3(0.0);
|
||||||
|
cosLight = clamp(dot(v_LightDirection, normalize(vec3(v_LightDirection.x, 0.0, v_LightDirection.z))), 1e-2, 1.0);
|
||||||
|
float sinLight = pow(1 - pow(cosLight, 2.0), 0.5);
|
||||||
|
normalOffsetScale = 0.0;
|
||||||
|
z_bias = 3.6e3 * sinLight / cosLight;
|
||||||
|
}
|
||||||
|
z_bias *= pFactor * pFactor / f_textureresolution / f_shadowfar;
|
||||||
|
|
||||||
|
shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (inVertexPosition + vec4(normalOffsetScale * nNormal, 0.0))).xyz;
|
||||||
|
shadow_position.z -= z_bias;
|
||||||
|
|
||||||
|
if (f_timeofday < 0.2) {
|
||||||
|
adj_shadow_strength = f_shadow_strength * 0.5 *
|
||||||
|
(1.0 - mtsmoothstep(0.18, 0.2, f_timeofday));
|
||||||
|
} else if (f_timeofday >= 0.8) {
|
||||||
|
adj_shadow_strength = f_shadow_strength * 0.5 *
|
||||||
|
mtsmoothstep(0.8, 0.83, f_timeofday);
|
||||||
|
} else {
|
||||||
|
adj_shadow_strength = f_shadow_strength *
|
||||||
|
mtsmoothstep(0.20, 0.25, f_timeofday) *
|
||||||
|
(1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,14 @@
|
||||||
uniform sampler2D baseTexture;
|
uniform sampler2D baseTexture;
|
||||||
|
|
||||||
uniform vec4 emissiveColor;
|
uniform vec4 emissiveColor;
|
||||||
|
uniform vec3 dayLight;
|
||||||
uniform vec4 skyBgColor;
|
uniform vec4 skyBgColor;
|
||||||
uniform float fogDistance;
|
uniform float fogDistance;
|
||||||
uniform vec3 eyePosition;
|
uniform vec3 eyePosition;
|
||||||
|
|
||||||
varying vec3 vNormal;
|
// The cameraOffset is the current center of the visible world.
|
||||||
varying vec3 vPosition;
|
uniform vec3 cameraOffset;
|
||||||
varying vec3 worldPosition;
|
uniform float animationTimer;
|
||||||
varying lowp vec4 varColor;
|
|
||||||
#ifdef GL_ES
|
|
||||||
varying mediump vec2 varTexCoord;
|
|
||||||
#else
|
|
||||||
centroid varying vec2 varTexCoord;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
varying vec3 eyeVec;
|
|
||||||
varying float vIDiff;
|
|
||||||
|
|
||||||
const float e = 2.718281828459;
|
|
||||||
const float BS = 10.0;
|
|
||||||
const float fogStart = FOG_START;
|
|
||||||
const float fogShadingParameter = 1.0 / (1.0 - fogStart);
|
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
// shadow texture
|
// shadow texture
|
||||||
uniform sampler2D ShadowMapSampler;
|
uniform sampler2D ShadowMapSampler;
|
||||||
|
@ -31,57 +17,41 @@ const float fogShadingParameter = 1.0 / (1.0 - fogStart);
|
||||||
uniform float f_textureresolution;
|
uniform float f_textureresolution;
|
||||||
uniform mat4 m_ShadowViewProj;
|
uniform mat4 m_ShadowViewProj;
|
||||||
uniform float f_shadowfar;
|
uniform float f_shadowfar;
|
||||||
uniform float f_timeofday;
|
uniform float f_shadow_strength;
|
||||||
varying float normalOffsetScale;
|
uniform vec4 CameraPos;
|
||||||
|
uniform float xyPerspectiveBias0;
|
||||||
|
uniform float xyPerspectiveBias1;
|
||||||
|
|
||||||
varying float adj_shadow_strength;
|
varying float adj_shadow_strength;
|
||||||
varying float cosLight;
|
varying float cosLight;
|
||||||
varying float f_normal_length;
|
varying float f_normal_length;
|
||||||
|
varying vec3 shadow_position;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 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)
|
varying vec3 vNormal;
|
||||||
{
|
varying vec3 vPosition;
|
||||||
return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
|
// World position in the visible world (i.e. relative to the cameraOffset.)
|
||||||
}
|
// This can be used for many shader effects without loss of precision.
|
||||||
|
// If the absolute position is required it can be calculated with
|
||||||
vec4 applyToneMapping(vec4 color)
|
// cameraOffset + worldPosition (for large coordinates the limits of float
|
||||||
{
|
// precision must be considered).
|
||||||
color = vec4(pow(color.rgb, vec3(2.2)), color.a);
|
varying vec3 worldPosition;
|
||||||
const float gamma = 1.6;
|
varying lowp vec4 varColor;
|
||||||
const float exposureBias = 5.5;
|
#ifdef GL_ES
|
||||||
color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
|
varying mediump vec2 varTexCoord;
|
||||||
// Precalculated white_scale from
|
#else
|
||||||
//vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
|
centroid varying vec2 varTexCoord;
|
||||||
vec3 whiteScale = vec3(1.036015346);
|
|
||||||
color.rgb *= whiteScale;
|
|
||||||
return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
varying vec3 eyeVec;
|
||||||
|
varying float nightRatio;
|
||||||
|
|
||||||
|
varying float vIDiff;
|
||||||
|
|
||||||
|
const float fogStart = FOG_START;
|
||||||
|
const float fogShadingParameter = 1.0 / (1.0 - fogStart);
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
const float bias0 = 0.9;
|
|
||||||
const float zPersFactor = 0.5;
|
|
||||||
const float bias1 = 1.0 - bias0;
|
|
||||||
|
|
||||||
vec4 getPerspectiveFactor(in vec4 shadowPosition)
|
|
||||||
{
|
|
||||||
float pDistance = length(shadowPosition.xy);
|
|
||||||
float pFactor = pDistance * bias0 + bias1;
|
|
||||||
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
|
|
||||||
|
|
||||||
return shadowPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
// assuming near is always 1.0
|
// assuming near is always 1.0
|
||||||
float getLinearDepth()
|
float getLinearDepth()
|
||||||
|
@ -91,11 +61,14 @@ float getLinearDepth()
|
||||||
|
|
||||||
vec3 getLightSpacePosition()
|
vec3 getLightSpacePosition()
|
||||||
{
|
{
|
||||||
vec4 pLightSpace;
|
return shadow_position * 0.5 + 0.5;
|
||||||
float normalBias = 0.0005 * getLinearDepth() * cosLight + normalOffsetScale;
|
}
|
||||||
pLightSpace = m_ShadowViewProj * vec4(worldPosition + normalBias * normalize(vNormal), 1.0);
|
// custom smoothstep implementation because it's not defined in glsl1.2
|
||||||
pLightSpace = getPerspectiveFactor(pLightSpace);
|
// https://docs.gl/sl4/smoothstep
|
||||||
return pLightSpace.xyz * 0.5 + 0.5;
|
float mtsmoothstep(in float edge0, in float edge1, in float x)
|
||||||
|
{
|
||||||
|
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
||||||
|
return t * t * (3.0 - 2.0 * t);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COLORED_SHADOWS
|
#ifdef COLORED_SHADOWS
|
||||||
|
@ -124,10 +97,10 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist
|
||||||
{
|
{
|
||||||
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
|
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
|
||||||
|
|
||||||
float visibility = step(0.0, (realDistance-2e-5) - texDepth.r);
|
float visibility = step(0.0, realDistance - texDepth.r);
|
||||||
vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
|
vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
|
||||||
if (visibility < 0.1) {
|
if (visibility < 0.1) {
|
||||||
visibility = step(0.0, (realDistance-2e-5) - texDepth.r);
|
visibility = step(0.0, realDistance - texDepth.b);
|
||||||
result = vec4(visibility, unpackColor(texDepth.a));
|
result = vec4(visibility, unpackColor(texDepth.a));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -138,13 +111,13 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist
|
||||||
float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
||||||
{
|
{
|
||||||
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
|
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
|
||||||
float visibility = step(0.0, (realDistance-2e-5) - texDepth);
|
float visibility = step(0.0, realDistance - texDepth);
|
||||||
|
|
||||||
return visibility;
|
return visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if SHADOW_FILTER == 2
|
#if SHADOW_FILTER == 2
|
||||||
#define PCFBOUND 3.5
|
#define PCFBOUND 3.5
|
||||||
#define PCFSAMPLES 64.0
|
#define PCFSAMPLES 64.0
|
||||||
|
@ -163,6 +136,76 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
|
||||||
#define PCFSAMPLES 1.0
|
#define PCFSAMPLES 1.0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef COLORED_SHADOWS
|
||||||
|
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
||||||
|
{
|
||||||
|
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy);
|
||||||
|
float depth = max(realDistance - texDepth.r, realDistance - texDepth.b);
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
||||||
|
{
|
||||||
|
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
|
||||||
|
float depth = realDistance - texDepth;
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float getBaseLength(vec2 smTexCoord)
|
||||||
|
{
|
||||||
|
float l = length(2.0 * smTexCoord.xy - 1.0 - CameraPos.xy); // length in texture coords
|
||||||
|
return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0); // return to undistorted coords
|
||||||
|
}
|
||||||
|
|
||||||
|
float getDeltaPerspectiveFactor(float l)
|
||||||
|
{
|
||||||
|
return 0.04 * pow(512.0 / f_textureresolution, 0.4) / (xyPerspectiveBias0 * l + xyPerspectiveBias1); // original distortion factor, divided by 10
|
||||||
|
}
|
||||||
|
|
||||||
|
float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier)
|
||||||
|
{
|
||||||
|
float baseLength = getBaseLength(smTexCoord);
|
||||||
|
float perspectiveFactor;
|
||||||
|
|
||||||
|
// Return fast if sharp shadows are requested
|
||||||
|
if (PCFBOUND == 0.0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
if (SOFTSHADOWRADIUS <= 1.0) {
|
||||||
|
perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
|
||||||
|
return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 clampedpos;
|
||||||
|
float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5);
|
||||||
|
float y, x;
|
||||||
|
float depth = 0.0;
|
||||||
|
float pointDepth;
|
||||||
|
float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
|
||||||
|
|
||||||
|
float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND);
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
for (y = -bound; y <= bound; y += 1.0)
|
||||||
|
for (x = -bound; x <= bound; x += 1.0) {
|
||||||
|
clampedpos = vec2(x,y);
|
||||||
|
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius);
|
||||||
|
clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy;
|
||||||
|
|
||||||
|
pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance);
|
||||||
|
if (pointDepth > -0.01) {
|
||||||
|
depth += pointDepth;
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
depth = depth / n;
|
||||||
|
depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001;
|
||||||
|
|
||||||
|
perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
|
||||||
|
return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef POISSON_FILTER
|
#ifdef POISSON_FILTER
|
||||||
const vec2[64] poissonDisk = vec2[64](
|
const vec2[64] poissonDisk = vec2[64](
|
||||||
|
@ -238,17 +281,28 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
|
||||||
{
|
{
|
||||||
vec2 clampedpos;
|
vec2 clampedpos;
|
||||||
vec4 visibility = vec4(0.0);
|
vec4 visibility = vec4(0.0);
|
||||||
|
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
|
||||||
|
if (radius < 0.1) {
|
||||||
|
// we are in the middle of even brightness, no need for filtering
|
||||||
|
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
float baseLength = getBaseLength(smTexCoord);
|
||||||
|
float perspectiveFactor;
|
||||||
|
|
||||||
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
||||||
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
|
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
|
||||||
int end_offset = int(PCFSAMPLES) + init_offset;
|
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
|
||||||
|
int end_offset = int(samples) + init_offset;
|
||||||
|
|
||||||
for (int x = init_offset; x < end_offset; x++) {
|
for (int x = init_offset; x < end_offset; x++) {
|
||||||
clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy;
|
clampedpos = poissonDisk[x];
|
||||||
|
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
|
||||||
|
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
|
||||||
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
|
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
return visibility / PCFSAMPLES;
|
return visibility / samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -257,17 +311,28 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
||||||
{
|
{
|
||||||
vec2 clampedpos;
|
vec2 clampedpos;
|
||||||
float visibility = 0.0;
|
float visibility = 0.0;
|
||||||
|
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
|
||||||
|
if (radius < 0.1) {
|
||||||
|
// we are in the middle of even brightness, no need for filtering
|
||||||
|
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
float baseLength = getBaseLength(smTexCoord);
|
||||||
|
float perspectiveFactor;
|
||||||
|
|
||||||
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
||||||
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
|
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
|
||||||
int end_offset = int(PCFSAMPLES) + init_offset;
|
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
|
||||||
|
int end_offset = int(samples) + init_offset;
|
||||||
|
|
||||||
for (int x = init_offset; x < end_offset; x++) {
|
for (int x = init_offset; x < end_offset; x++) {
|
||||||
clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy;
|
clampedpos = poissonDisk[x];
|
||||||
|
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
|
||||||
|
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
|
||||||
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
|
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
return visibility / PCFSAMPLES;
|
return visibility / samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -281,19 +346,31 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
|
||||||
{
|
{
|
||||||
vec2 clampedpos;
|
vec2 clampedpos;
|
||||||
vec4 visibility = vec4(0.0);
|
vec4 visibility = vec4(0.0);
|
||||||
float sradius=0.0;
|
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
|
||||||
if( PCFBOUND>0)
|
if (radius < 0.1) {
|
||||||
sradius = SOFTSHADOWRADIUS / PCFBOUND;
|
// we are in the middle of even brightness, no need for filtering
|
||||||
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
|
||||||
float y, x;
|
|
||||||
// basic PCF filter
|
|
||||||
for (y = -PCFBOUND; y <= PCFBOUND; y += 1.0)
|
|
||||||
for (x = -PCFBOUND; x <= PCFBOUND; x += 1.0) {
|
|
||||||
clampedpos = vec2(x,y) * texture_size* sradius + smTexCoord.xy;
|
|
||||||
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return visibility / PCFSAMPLES;
|
float baseLength = getBaseLength(smTexCoord);
|
||||||
|
float perspectiveFactor;
|
||||||
|
|
||||||
|
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
||||||
|
float y, x;
|
||||||
|
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
// basic PCF filter
|
||||||
|
for (y = -bound; y <= bound; y += 1.0)
|
||||||
|
for (x = -bound; x <= bound; x += 1.0) {
|
||||||
|
clampedpos = vec2(x,y); // screen offset
|
||||||
|
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
|
||||||
|
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
|
||||||
|
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return visibility / n;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -301,20 +378,31 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
||||||
{
|
{
|
||||||
vec2 clampedpos;
|
vec2 clampedpos;
|
||||||
float visibility = 0.0;
|
float visibility = 0.0;
|
||||||
float sradius=0.0;
|
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
|
||||||
if( PCFBOUND>0)
|
if (radius < 0.1) {
|
||||||
sradius = SOFTSHADOWRADIUS / PCFBOUND;
|
// we are in the middle of even brightness, no need for filtering
|
||||||
|
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
|
||||||
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
|
||||||
float y, x;
|
|
||||||
// basic PCF filter
|
|
||||||
for (y = -PCFBOUND; y <= PCFBOUND; y += 1.0)
|
|
||||||
for (x = -PCFBOUND; x <= PCFBOUND; x += 1.0) {
|
|
||||||
clampedpos = vec2(x,y) * texture_size * sradius + smTexCoord.xy;
|
|
||||||
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return visibility / PCFSAMPLES;
|
float baseLength = getBaseLength(smTexCoord);
|
||||||
|
float perspectiveFactor;
|
||||||
|
|
||||||
|
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
||||||
|
float y, x;
|
||||||
|
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
// basic PCF filter
|
||||||
|
for (y = -bound; y <= bound; y += 1.0)
|
||||||
|
for (x = -bound; x <= bound; x += 1.0) {
|
||||||
|
clampedpos = vec2(x,y); // screen offset
|
||||||
|
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
|
||||||
|
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
|
||||||
|
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return visibility / n;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -322,12 +410,46 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if 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 main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
vec3 color;
|
vec3 color;
|
||||||
vec2 uv = varTexCoord.st;
|
vec2 uv = varTexCoord.st;
|
||||||
vec4 base = texture2D(baseTexture, uv).rgba;
|
|
||||||
|
|
||||||
|
vec4 base = texture2D(baseTexture, uv).rgba;
|
||||||
// If alpha is zero, we can just discard the pixel. This fixes transparency
|
// If alpha is zero, we can just discard the pixel. This fixes transparency
|
||||||
// on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
|
// on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
|
||||||
// and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
|
// and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
|
||||||
|
@ -341,34 +463,66 @@ void main(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
color = base.rgb;
|
color = base.rgb;
|
||||||
vec4 col = vec4(color.rgb, base.a);
|
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
|
||||||
col.rgb *= varColor.rgb;
|
col.rgb *= vIDiff;
|
||||||
col.rgb *= emissiveColor.rgb * vIDiff;
|
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
float shadow_int = 0.0;
|
if (f_shadow_strength > 0.0) {
|
||||||
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
|
float shadow_int = 0.0;
|
||||||
vec3 posLightSpace = getLightSpacePosition();
|
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
|
||||||
|
vec3 posLightSpace = getLightSpacePosition();
|
||||||
|
|
||||||
|
float distance_rate = (1.0 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 10.0));
|
||||||
|
if (max(abs(posLightSpace.x - 0.5), abs(posLightSpace.y - 0.5)) > 0.5)
|
||||||
|
distance_rate = 0.0;
|
||||||
|
float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1, posLightSpace.z),0.0);
|
||||||
|
|
||||||
|
if (distance_rate > 1e-7) {
|
||||||
|
|
||||||
#ifdef COLORED_SHADOWS
|
#ifdef COLORED_SHADOWS
|
||||||
vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
vec4 visibility;
|
||||||
shadow_int = visibility.r;
|
if (cosLight > 0.0 || f_normal_length < 1e-3)
|
||||||
shadow_color = visibility.gba;
|
visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
||||||
|
else
|
||||||
|
visibility = vec4(1.0, 0.0, 0.0, 0.0);
|
||||||
|
shadow_int = visibility.r;
|
||||||
|
shadow_color = visibility.gba;
|
||||||
#else
|
#else
|
||||||
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
if (cosLight > 0.0 || f_normal_length < 1e-3)
|
||||||
|
if (cosLight > 0.0)
|
||||||
|
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
||||||
|
else
|
||||||
|
shadow_int = 1.0;
|
||||||
#endif
|
#endif
|
||||||
|
shadow_int *= distance_rate;
|
||||||
|
shadow_int = clamp(shadow_int, 0.0, 1.0);
|
||||||
|
|
||||||
if (f_normal_length != 0 && cosLight <= 0.001) {
|
}
|
||||||
shadow_int = clamp(shadow_int + 0.5 * abs(cosLight), 0.0, 1.0);
|
|
||||||
|
// turns out that nightRatio falls off much faster than
|
||||||
|
// actual brightness of artificial light in relation to natual light.
|
||||||
|
// Power ratio was measured on torches in MTG (brightness = 14).
|
||||||
|
float adjusted_night_ratio = pow(max(0.0, nightRatio), 0.6);
|
||||||
|
|
||||||
|
// Apply self-shadowing when light falls at a narrow angle to the surface
|
||||||
|
// Cosine of the cut-off angle.
|
||||||
|
const float self_shadow_cutoff_cosine = 0.14;
|
||||||
|
if (f_normal_length != 0 && cosLight < self_shadow_cutoff_cosine) {
|
||||||
|
shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
|
||||||
|
shadow_color = mix(vec3(0.0), shadow_color, min(cosLight, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
|
||||||
|
}
|
||||||
|
|
||||||
|
shadow_int *= f_adj_shadow_strength;
|
||||||
|
|
||||||
|
// calculate fragment color from components:
|
||||||
|
col.rgb =
|
||||||
|
adjusted_night_ratio * col.rgb + // artificial light
|
||||||
|
(1.0 - adjusted_night_ratio) * ( // natural light
|
||||||
|
col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) + // filtered texture color
|
||||||
|
dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight
|
||||||
}
|
}
|
||||||
|
|
||||||
shadow_int = 1.0 - (shadow_int * adj_shadow_strength);
|
|
||||||
|
|
||||||
col.rgb = mix(shadow_color, col.rgb, shadow_int) * shadow_int;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if ENABLE_TONE_MAPPING
|
#if ENABLE_TONE_MAPPING
|
||||||
col = applyToneMapping(col);
|
col = applyToneMapping(col);
|
||||||
#endif
|
#endif
|
||||||
|
@ -385,6 +539,7 @@ void main(void)
|
||||||
float clarity = clamp(fogShadingParameter
|
float clarity = clamp(fogShadingParameter
|
||||||
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
|
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
|
||||||
col = mix(skyBgColor, col, clarity);
|
col = mix(skyBgColor, col, clarity);
|
||||||
|
col = vec4(col.rgb, base.a);
|
||||||
gl_FragColor = vec4(col.rgb, base.a);
|
|
||||||
|
gl_FragColor = col;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
uniform mat4 mWorld;
|
uniform mat4 mWorld;
|
||||||
|
uniform vec3 dayLight;
|
||||||
uniform vec3 eyePosition;
|
uniform vec3 eyePosition;
|
||||||
uniform float animationTimer;
|
uniform float animationTimer;
|
||||||
|
uniform vec4 emissiveColor;
|
||||||
|
uniform vec3 cameraOffset;
|
||||||
|
|
||||||
|
|
||||||
varying vec3 vNormal;
|
varying vec3 vNormal;
|
||||||
varying vec3 vPosition;
|
varying vec3 vPosition;
|
||||||
|
@ -21,19 +24,53 @@ centroid varying vec2 varTexCoord;
|
||||||
uniform float f_shadowfar;
|
uniform float f_shadowfar;
|
||||||
uniform float f_shadow_strength;
|
uniform float f_shadow_strength;
|
||||||
uniform float f_timeofday;
|
uniform float f_timeofday;
|
||||||
|
uniform vec4 CameraPos;
|
||||||
|
|
||||||
varying float cosLight;
|
varying float cosLight;
|
||||||
varying float normalOffsetScale;
|
|
||||||
varying float adj_shadow_strength;
|
varying float adj_shadow_strength;
|
||||||
varying float f_normal_length;
|
varying float f_normal_length;
|
||||||
|
varying vec3 shadow_position;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
varying vec3 eyeVec;
|
varying vec3 eyeVec;
|
||||||
|
varying float nightRatio;
|
||||||
|
// Color of the light emitted by the light sources.
|
||||||
|
const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
|
||||||
varying float vIDiff;
|
varying float vIDiff;
|
||||||
|
|
||||||
const float e = 2.718281828459;
|
const float e = 2.718281828459;
|
||||||
const float BS = 10.0;
|
const float BS = 10.0;
|
||||||
|
uniform float xyPerspectiveBias0;
|
||||||
|
uniform float xyPerspectiveBias1;
|
||||||
|
uniform float zPerspectiveBias;
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
|
|
||||||
|
vec4 getRelativePosition(in vec4 position)
|
||||||
|
{
|
||||||
|
vec2 l = position.xy - CameraPos.xy;
|
||||||
|
vec2 s = l / abs(l);
|
||||||
|
s = (1.0 - s * CameraPos.xy);
|
||||||
|
l /= s;
|
||||||
|
return vec4(l, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getPerspectiveFactor(in vec4 relativePosition)
|
||||||
|
{
|
||||||
|
float pDistance = length(relativePosition.xy);
|
||||||
|
float pFactor = pDistance * xyPerspectiveBias0 + xyPerspectiveBias1;
|
||||||
|
return pFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 applyPerspectiveDistortion(in vec4 position)
|
||||||
|
{
|
||||||
|
vec4 l = getRelativePosition(position);
|
||||||
|
float pFactor = getPerspectiveFactor(l);
|
||||||
|
l.xy /= pFactor;
|
||||||
|
position.xy = l.xy * l.zw + CameraPos.xy;
|
||||||
|
position.z *= zPerspectiveBias;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
// custom smoothstep implementation because it's not defined in glsl1.2
|
// custom smoothstep implementation because it's not defined in glsl1.2
|
||||||
// https://docs.gl/sl4/smoothstep
|
// https://docs.gl/sl4/smoothstep
|
||||||
float mtsmoothstep(in float edge0, in float edge1, in float x)
|
float mtsmoothstep(in float edge0, in float edge1, in float x)
|
||||||
|
@ -60,7 +97,7 @@ void main(void)
|
||||||
gl_Position = mWorldViewProj * inVertexPosition;
|
gl_Position = mWorldViewProj * inVertexPosition;
|
||||||
|
|
||||||
vPosition = gl_Position.xyz;
|
vPosition = gl_Position.xyz;
|
||||||
vNormal = inVertexNormal;
|
vNormal = (mWorld * vec4(inVertexNormal, 0.0)).xyz;
|
||||||
worldPosition = (mWorld * inVertexPosition).xyz;
|
worldPosition = (mWorld * inVertexPosition).xyz;
|
||||||
eyeVec = -(mWorldView * inVertexPosition).xyz;
|
eyeVec = -(mWorldView * inVertexPosition).xyz;
|
||||||
|
|
||||||
|
@ -75,29 +112,68 @@ void main(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
varColor = inVertexColor.bgra;
|
vec4 color = inVertexColor.bgra;
|
||||||
#else
|
#else
|
||||||
varColor = inVertexColor;
|
vec4 color = inVertexColor;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
color *= emissiveColor;
|
||||||
|
|
||||||
|
// The alpha gives the ratio of sunlight in the incoming light.
|
||||||
|
nightRatio = 1.0 - color.a;
|
||||||
|
color.rgb = color.rgb * (color.a * dayLight.rgb +
|
||||||
|
nightRatio * artificialLight.rgb) * 2.0;
|
||||||
|
color.a = 1.0;
|
||||||
|
|
||||||
|
// Emphase blue a bit in darker places
|
||||||
|
// See C++ implementation in mapblock_mesh.cpp final_color_blend()
|
||||||
|
float brightness = (color.r + color.g + color.b) / 3.0;
|
||||||
|
color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) +
|
||||||
|
0.07 * brightness);
|
||||||
|
|
||||||
|
varColor = clamp(color, 0.0, 1.0);
|
||||||
|
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
|
if (f_shadow_strength > 0.0) {
|
||||||
|
vec3 nNormal = normalize(vNormal);
|
||||||
|
f_normal_length = length(vNormal);
|
||||||
|
|
||||||
cosLight = max(0.0, dot(vNormal, -v_LightDirection));
|
/* normalOffsetScale is in world coordinates (1/10th of a meter)
|
||||||
float texelSize = 0.51;
|
z_bias is in light space coordinates */
|
||||||
float slopeScale = clamp(1.0 - cosLight, 0.0, 1.0);
|
float normalOffsetScale, z_bias;
|
||||||
normalOffsetScale = texelSize * slopeScale;
|
float pFactor = getPerspectiveFactor(getRelativePosition(m_ShadowViewProj * mWorld * inVertexPosition));
|
||||||
if (f_timeofday < 0.2) {
|
if (f_normal_length > 0.0) {
|
||||||
adj_shadow_strength = f_shadow_strength * 0.5 *
|
nNormal = normalize(vNormal);
|
||||||
(1.0 - mtsmoothstep(0.18, 0.2, f_timeofday));
|
cosLight = dot(nNormal, -v_LightDirection);
|
||||||
} else if (f_timeofday >= 0.8) {
|
float sinLight = pow(1 - pow(cosLight, 2.0), 0.5);
|
||||||
adj_shadow_strength = f_shadow_strength * 0.5 *
|
normalOffsetScale = 0.1 * pFactor * pFactor * sinLight * min(f_shadowfar, 500.0) /
|
||||||
mtsmoothstep(0.8, 0.83, f_timeofday);
|
xyPerspectiveBias1 / f_textureresolution;
|
||||||
} else {
|
z_bias = 1e3 * sinLight / cosLight * (0.5 + f_textureresolution / 1024.0);
|
||||||
adj_shadow_strength = f_shadow_strength *
|
}
|
||||||
mtsmoothstep(0.20, 0.25, f_timeofday) *
|
else {
|
||||||
(1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
|
nNormal = vec3(0.0);
|
||||||
|
cosLight = clamp(dot(v_LightDirection, normalize(vec3(v_LightDirection.x, 0.0, v_LightDirection.z))), 1e-2, 1.0);
|
||||||
|
float sinLight = pow(1 - pow(cosLight, 2.0), 0.5);
|
||||||
|
normalOffsetScale = 0.0;
|
||||||
|
z_bias = 3.6e3 * sinLight / cosLight;
|
||||||
|
}
|
||||||
|
z_bias *= pFactor * pFactor / f_textureresolution / f_shadowfar;
|
||||||
|
|
||||||
|
shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (inVertexPosition + vec4(normalOffsetScale * nNormal, 0.0))).xyz;
|
||||||
|
shadow_position.z -= z_bias;
|
||||||
|
|
||||||
|
if (f_timeofday < 0.2) {
|
||||||
|
adj_shadow_strength = f_shadow_strength * 0.5 *
|
||||||
|
(1.0 - mtsmoothstep(0.18, 0.2, f_timeofday));
|
||||||
|
} else if (f_timeofday >= 0.8) {
|
||||||
|
adj_shadow_strength = f_shadow_strength * 0.5 *
|
||||||
|
mtsmoothstep(0.8, 0.83, f_timeofday);
|
||||||
|
} else {
|
||||||
|
adj_shadow_strength = f_shadow_strength *
|
||||||
|
mtsmoothstep(0.20, 0.25, f_timeofday) *
|
||||||
|
(1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f_normal_length = length(vNormal);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ uniform sampler2D ColorMapSampler;
|
||||||
varying vec4 tPos;
|
varying vec4 tPos;
|
||||||
|
|
||||||
#ifdef COLORED_SHADOWS
|
#ifdef COLORED_SHADOWS
|
||||||
|
varying vec3 varColor;
|
||||||
|
|
||||||
// c_precision of 128 fits within 7 base-10 digits
|
// c_precision of 128 fits within 7 base-10 digits
|
||||||
const float c_precision = 128.0;
|
const float c_precision = 128.0;
|
||||||
const float c_precisionp1 = c_precision + 1.0;
|
const float c_precisionp1 = c_precision + 1.0;
|
||||||
|
@ -30,7 +32,9 @@ void main()
|
||||||
|
|
||||||
//col.rgb = col.a == 1.0 ? vec3(1.0) : col.rgb;
|
//col.rgb = col.a == 1.0 ? vec3(1.0) : col.rgb;
|
||||||
#ifdef COLORED_SHADOWS
|
#ifdef COLORED_SHADOWS
|
||||||
float packedColor = packColor(mix(col.rgb, black, col.a));
|
col.rgb *= varColor.rgb;
|
||||||
|
// premultiply color alpha (see-through side)
|
||||||
|
float packedColor = packColor(col.rgb * (1.0 - col.a));
|
||||||
gl_FragColor = vec4(depth, packedColor, 0.0,1.0);
|
gl_FragColor = vec4(depth, packedColor, 0.0,1.0);
|
||||||
#else
|
#else
|
||||||
gl_FragColor = vec4(depth, 0.0, 0.0, 1.0);
|
gl_FragColor = vec4(depth, 0.0, 0.0, 1.0);
|
||||||
|
|
|
@ -1,26 +1,50 @@
|
||||||
uniform mat4 LightMVP; // world matrix
|
uniform mat4 LightMVP; // world matrix
|
||||||
|
uniform vec4 CameraPos;
|
||||||
varying vec4 tPos;
|
varying vec4 tPos;
|
||||||
|
#ifdef COLORED_SHADOWS
|
||||||
|
varying vec3 varColor;
|
||||||
|
#endif
|
||||||
|
|
||||||
const float bias0 = 0.9;
|
uniform float xyPerspectiveBias0;
|
||||||
const float zPersFactor = 0.5;
|
uniform float xyPerspectiveBias1;
|
||||||
const float bias1 = 1.0 - bias0 + 1e-6;
|
uniform float zPerspectiveBias;
|
||||||
|
|
||||||
vec4 getPerspectiveFactor(in vec4 shadowPosition)
|
vec4 getRelativePosition(in vec4 position)
|
||||||
{
|
{
|
||||||
float pDistance = length(shadowPosition.xy);
|
vec2 l = position.xy - CameraPos.xy;
|
||||||
float pFactor = pDistance * bias0 + bias1;
|
vec2 s = l / abs(l);
|
||||||
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
|
s = (1.0 - s * CameraPos.xy);
|
||||||
|
l /= s;
|
||||||
return shadowPosition;
|
return vec4(l, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getPerspectiveFactor(in vec4 relativePosition)
|
||||||
|
{
|
||||||
|
float pDistance = length(relativePosition.xy);
|
||||||
|
float pFactor = pDistance * xyPerspectiveBias0 + xyPerspectiveBias1;
|
||||||
|
return pFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 applyPerspectiveDistortion(in vec4 position)
|
||||||
|
{
|
||||||
|
vec4 l = getRelativePosition(position);
|
||||||
|
float pFactor = getPerspectiveFactor(l);
|
||||||
|
l.xy /= pFactor;
|
||||||
|
position.xy = l.xy * l.zw + CameraPos.xy;
|
||||||
|
position.z *= zPerspectiveBias;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 pos = LightMVP * gl_Vertex;
|
vec4 pos = LightMVP * gl_Vertex;
|
||||||
|
|
||||||
tPos = getPerspectiveFactor(LightMVP * gl_Vertex);
|
tPos = applyPerspectiveDistortion(LightMVP * gl_Vertex);
|
||||||
|
|
||||||
gl_Position = vec4(tPos.xyz, 1.0);
|
gl_Position = vec4(tPos.xyz, 1.0);
|
||||||
gl_TexCoord[0].st = gl_MultiTexCoord0.st;
|
gl_TexCoord[0].st = gl_MultiTexCoord0.st;
|
||||||
|
|
||||||
|
#ifdef COLORED_SHADOWS
|
||||||
|
varColor = gl_Color.rgb;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,43 @@
|
||||||
uniform mat4 LightMVP; // world matrix
|
uniform mat4 LightMVP; // world matrix
|
||||||
|
uniform vec4 CameraPos; // camera position
|
||||||
varying vec4 tPos;
|
varying vec4 tPos;
|
||||||
|
|
||||||
const float bias0 = 0.9;
|
uniform float xyPerspectiveBias0;
|
||||||
const float zPersFactor = 0.5;
|
uniform float xyPerspectiveBias1;
|
||||||
const float bias1 = 1.0 - bias0 + 1e-6;
|
uniform float zPerspectiveBias;
|
||||||
|
|
||||||
vec4 getPerspectiveFactor(in vec4 shadowPosition)
|
vec4 getRelativePosition(in vec4 position)
|
||||||
{
|
{
|
||||||
float pDistance = length(shadowPosition.xy);
|
vec2 l = position.xy - CameraPos.xy;
|
||||||
float pFactor = pDistance * bias0 + bias1;
|
vec2 s = l / abs(l);
|
||||||
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
|
s = (1.0 - s * CameraPos.xy);
|
||||||
|
l /= s;
|
||||||
return shadowPosition;
|
return vec4(l, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getPerspectiveFactor(in vec4 relativePosition)
|
||||||
|
{
|
||||||
|
float pDistance = length(relativePosition.xy);
|
||||||
|
float pFactor = pDistance * xyPerspectiveBias0 + xyPerspectiveBias1;
|
||||||
|
return pFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 applyPerspectiveDistortion(in vec4 position)
|
||||||
|
{
|
||||||
|
vec4 l = getRelativePosition(position);
|
||||||
|
float pFactor = getPerspectiveFactor(l);
|
||||||
|
l.xy /= pFactor;
|
||||||
|
position.xy = l.xy * l.zw + CameraPos.xy;
|
||||||
|
position.z *= zPerspectiveBias;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 pos = LightMVP * gl_Vertex;
|
vec4 pos = LightMVP * gl_Vertex;
|
||||||
|
|
||||||
tPos = getPerspectiveFactor(pos);
|
tPos = applyPerspectiveDistortion(pos);
|
||||||
|
|
||||||
gl_Position = vec4(tPos.xyz, 1.0);
|
gl_Position = vec4(tPos.xyz, 1.0);
|
||||||
gl_TexCoord[0].st = gl_MultiTexCoord0.st;
|
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
name = preview
|
name = preview
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
#-------------------------------------------------------------------
|
|
||||||
# The contents of this file are placed in the public domain. Feel
|
|
||||||
# free to make use of it in any way you like.
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Try to find OpenGLES and EGL
|
|
||||||
# Once done this will define
|
|
||||||
#
|
|
||||||
# OPENGLES2_FOUND - system has OpenGLES
|
|
||||||
# OPENGLES2_INCLUDE_DIR - the GL include directory
|
|
||||||
# OPENGLES2_LIBRARIES - Link these to use OpenGLES
|
|
||||||
#
|
|
||||||
# EGL_FOUND - system has EGL
|
|
||||||
# EGL_INCLUDE_DIR - the EGL include directory
|
|
||||||
# EGL_LIBRARIES - Link these to use EGL
|
|
||||||
|
|
||||||
# Win32 and Apple are not tested!
|
|
||||||
# Linux tested and works
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
find_path(OPENGLES2_INCLUDE_DIR GLES2/gl2.h)
|
|
||||||
find_library(OPENGLES2_LIBRARY libGLESv2)
|
|
||||||
elseif(APPLE)
|
|
||||||
create_search_paths(/Developer/Platforms)
|
|
||||||
findpkg_framework(OpenGLES2)
|
|
||||||
set(OPENGLES2_LIBRARY "-framework OpenGLES")
|
|
||||||
else()
|
|
||||||
# Unix
|
|
||||||
find_path(OPENGLES2_INCLUDE_DIR GLES2/gl2.h
|
|
||||||
PATHS /usr/openwin/share/include
|
|
||||||
/opt/graphics/OpenGL/include
|
|
||||||
/usr/X11R6/include
|
|
||||||
/usr/include
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(OPENGLES2_LIBRARY
|
|
||||||
NAMES GLESv2
|
|
||||||
PATHS /opt/graphics/OpenGL/lib
|
|
||||||
/usr/openwin/lib
|
|
||||||
/usr/X11R6/lib
|
|
||||||
/usr/lib
|
|
||||||
)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(OpenGLES2 DEFAULT_MSG OPENGLES2_LIBRARY OPENGLES2_INCLUDE_DIR)
|
|
||||||
|
|
||||||
find_path(EGL_INCLUDE_DIR EGL/egl.h
|
|
||||||
PATHS /usr/openwin/share/include
|
|
||||||
/opt/graphics/OpenGL/include
|
|
||||||
/usr/X11R6/include
|
|
||||||
/usr/include
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(EGL_LIBRARY
|
|
||||||
NAMES EGL
|
|
||||||
PATHS /opt/graphics/OpenGL/lib
|
|
||||||
/usr/openwin/lib
|
|
||||||
/usr/X11R6/lib
|
|
||||||
/usr/lib
|
|
||||||
)
|
|
||||||
|
|
||||||
find_package_handle_standard_args(EGL DEFAULT_MSG EGL_LIBRARY EGL_INCLUDE_DIR)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(OPENGLES2_LIBRARIES ${OPENGLES2_LIBRARY})
|
|
||||||
set(EGL_LIBRARIES ${EGL_LIBRARY})
|
|
||||||
|
|
||||||
mark_as_advanced(
|
|
||||||
OPENGLES2_INCLUDE_DIR
|
|
||||||
OPENGLES2_LIBRARY
|
|
||||||
EGL_INCLUDE_DIR
|
|
||||||
EGL_LIBRARY
|
|
||||||
)
|
|
|
@ -1,4 +1,4 @@
|
||||||
mark_as_advanced(SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR)
|
mark_as_advanced(SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR)
|
||||||
|
|
||||||
find_path(SQLITE3_INCLUDE_DIR sqlite3.h)
|
find_path(SQLITE3_INCLUDE_DIR sqlite3.h)
|
||||||
|
|
||||||
|
|
|
@ -29,18 +29,17 @@ else(NOT GP2XWIZ)
|
||||||
find_package_handle_standard_args(Vorbis DEFAULT_MSG
|
find_package_handle_standard_args(Vorbis DEFAULT_MSG
|
||||||
VORBIS_INCLUDE_DIR VORBIS_LIBRARY)
|
VORBIS_INCLUDE_DIR VORBIS_LIBRARY)
|
||||||
endif(NOT GP2XWIZ)
|
endif(NOT GP2XWIZ)
|
||||||
|
|
||||||
if(VORBIS_FOUND)
|
if(VORBIS_FOUND)
|
||||||
if(NOT GP2XWIZ)
|
if(NOT GP2XWIZ)
|
||||||
set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY}
|
set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY}
|
||||||
${OGG_LIBRARY})
|
${OGG_LIBRARY})
|
||||||
else(NOT GP2XWIZ)
|
else(NOT GP2XWIZ)
|
||||||
set(VORBIS_LIBRARIES ${VORBIS_LIBRARY})
|
set(VORBIS_LIBRARIES ${VORBIS_LIBRARY})
|
||||||
endif(NOT GP2XWIZ)
|
endif(NOT GP2XWIZ)
|
||||||
else(VORBIS_FOUND)
|
else(VORBIS_FOUND)
|
||||||
set(VORBIS_LIBRARIES)
|
set(VORBIS_LIBRARIES)
|
||||||
endif(VORBIS_FOUND)
|
endif(VORBIS_FOUND)
|
||||||
|
|
||||||
mark_as_advanced(OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR)
|
mark_as_advanced(OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR)
|
||||||
mark_as_advanced(OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY)
|
mark_as_advanced(OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY)
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ PREDEFINED = "USE_SPATIAL=1" \
|
||||||
"USE_REDIS=1" \
|
"USE_REDIS=1" \
|
||||||
"USE_SOUND=1" \
|
"USE_SOUND=1" \
|
||||||
"USE_CURL=1" \
|
"USE_CURL=1" \
|
||||||
"USE_FREETYPE=1" \
|
|
||||||
"USE_GETTEXT=1"
|
"USE_GETTEXT=1"
|
||||||
|
|
||||||
# Input
|
# Input
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Minetest Major Breakages List
|
||||||
|
|
||||||
|
This document contains a list of breaking changes to be made in the next major version.
|
||||||
|
|
||||||
|
* Remove attachment space multiplier (*10)
|
||||||
|
* `get_sky()` returns a table (without arg)
|
||||||
|
* `game.conf` name/id mess
|
||||||
|
* remove `depends.txt` / `description.txt` (would simplify ContentDB and Minetest code a little)
|
|
@ -1,4 +1,4 @@
|
||||||
Minetest Lua Client Modding API Reference 5.5.0
|
Minetest Lua Client Modding API Reference 5.6.0
|
||||||
================================================
|
================================================
|
||||||
* More information at <http://www.minetest.net/>
|
* More information at <http://www.minetest.net/>
|
||||||
* Developer Wiki: <http://dev.minetest.net/>
|
* Developer Wiki: <http://dev.minetest.net/>
|
||||||
|
@ -587,6 +587,7 @@ Spatial Vectors
|
||||||
* `vector.floor(v)`: returns a vector, each dimension rounded down
|
* `vector.floor(v)`: returns a vector, each dimension rounded down
|
||||||
* `vector.round(v)`: returns a vector, each dimension rounded to nearest int
|
* `vector.round(v)`: returns a vector, each dimension rounded to nearest int
|
||||||
* `vector.apply(v, func)`: returns a vector
|
* `vector.apply(v, func)`: returns a vector
|
||||||
|
* `vector.combine(v, w, func)`: returns a vector
|
||||||
* `vector.equals(v1, v2)`: returns a boolean
|
* `vector.equals(v1, v2)`: returns a boolean
|
||||||
|
|
||||||
For the following functions `x` can be either a vector or a number:
|
For the following functions `x` can be either a vector or a number:
|
||||||
|
@ -1276,8 +1277,8 @@ Methods:
|
||||||
* returns true if player is in a liquid (This oscillates so that the player jumps a bit above the surface)
|
* returns true if player is in a liquid (This oscillates so that the player jumps a bit above the surface)
|
||||||
* `is_in_liquid_stable()`
|
* `is_in_liquid_stable()`
|
||||||
* returns true if player is in a stable liquid (This is more stable and defines the maximum speed of the player)
|
* returns true if player is in a stable liquid (This is more stable and defines the maximum speed of the player)
|
||||||
* `get_liquid_viscosity()`
|
* `get_move_resistance()`
|
||||||
* returns liquid viscosity (Gets the viscosity of liquid to calculate friction)
|
* returns move resistance of current node, the higher the slower the player moves
|
||||||
* `is_climbing()`
|
* `is_climbing()`
|
||||||
* returns true if player is climbing
|
* returns true if player is climbing
|
||||||
* `swimming_vertical()`
|
* `swimming_vertical()`
|
||||||
|
@ -1581,7 +1582,7 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
|
||||||
liquid_type = <string>, -- A string containing "none", "flowing", or "source" *May not exist*
|
liquid_type = <string>, -- A string containing "none", "flowing", or "source" *May not exist*
|
||||||
liquid_alternative_flowing = <string>, -- Alternative node for liquid *May not exist*
|
liquid_alternative_flowing = <string>, -- Alternative node for liquid *May not exist*
|
||||||
liquid_alternative_source = <string>, -- Alternative node for liquid *May not exist*
|
liquid_alternative_source = <string>, -- Alternative node for liquid *May not exist*
|
||||||
liquid_viscosity = <number>, -- How fast the liquid flows *May not exist*
|
liquid_viscosity = <number>, -- How slow the liquid flows *May not exist*
|
||||||
liquid_renewable = <boolean>, -- Whether the liquid makes an infinite source *May not exist*
|
liquid_renewable = <boolean>, -- Whether the liquid makes an infinite source *May not exist*
|
||||||
liquid_range = <number>, -- How far the liquid flows *May not exist*
|
liquid_range = <number>, -- How far the liquid flows *May not exist*
|
||||||
drowning = bool, -- Whether the player will drown in the node
|
drowning = bool, -- Whether the player will drown in the node
|
||||||
|
@ -1596,6 +1597,7 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
|
||||||
},
|
},
|
||||||
legacy_facedir_simple = bool, -- Whether to use old facedir
|
legacy_facedir_simple = bool, -- Whether to use old facedir
|
||||||
legacy_wallmounted = bool -- Whether to use old wallmounted
|
legacy_wallmounted = bool -- Whether to use old wallmounted
|
||||||
|
move_resistance = <number>, -- How slow players can move through the node *May not exist*
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ modified by someone else and passed on, the recipients should know
|
||||||
that what they have is not the original version, so that the original
|
that what they have is not the original version, so that the original
|
||||||
author's reputation will not be affected by problems that might be
|
author's reputation will not be affected by problems that might be
|
||||||
introduced by others.
|
introduced by others.
|
||||||
|
|
||||||
Finally, software patents pose a constant threat to the existence of
|
Finally, software patents pose a constant threat to the existence of
|
||||||
any free program. We wish to make sure that a company cannot
|
any free program. We wish to make sure that a company cannot
|
||||||
effectively restrict the users of a free program by obtaining a
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
@ -111,7 +111,7 @@ modification follow. Pay close attention to the difference between a
|
||||||
"work based on the library" and a "work that uses the library". The
|
"work based on the library" and a "work that uses the library". The
|
||||||
former contains code derived from the library, whereas the latter must
|
former contains code derived from the library, whereas the latter must
|
||||||
be combined with the library in order to run.
|
be combined with the library in order to run.
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ Library.
|
||||||
You may charge a fee for the physical act of transferring a copy,
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
and you may at your option offer warranty protection in exchange for a
|
and you may at your option offer warranty protection in exchange for a
|
||||||
fee.
|
fee.
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Library or any portion
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
of it, thus forming a work based on the Library, and copy and
|
of it, thus forming a work based on the Library, and copy and
|
||||||
distribute such modifications or work under the terms of Section 1
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
@ -216,7 +216,7 @@ instead of to this License. (If a newer version than version 2 of the
|
||||||
ordinary GNU General Public License has appeared, then you can specify
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
that version instead if you wish.) Do not make any other change in
|
that version instead if you wish.) Do not make any other change in
|
||||||
these notices.
|
these notices.
|
||||||
|
|
||||||
Once this change is made in a given copy, it is irreversible for
|
Once this change is made in a given copy, it is irreversible for
|
||||||
that copy, so the ordinary GNU General Public License applies to all
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
subsequent copies and derivative works made from that copy.
|
subsequent copies and derivative works made from that copy.
|
||||||
|
@ -267,7 +267,7 @@ Library will still fall under Section 6.)
|
||||||
distribute the object code for the work under the terms of Section 6.
|
distribute the object code for the work under the terms of Section 6.
|
||||||
Any executables containing that work also fall under Section 6,
|
Any executables containing that work also fall under Section 6,
|
||||||
whether or not they are linked directly with the Library itself.
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
6. As an exception to the Sections above, you may also combine or
|
6. As an exception to the Sections above, you may also combine or
|
||||||
link a "work that uses the Library" with the Library to produce a
|
link a "work that uses the Library" with the Library to produce a
|
||||||
work containing portions of the Library, and distribute that work
|
work containing portions of the Library, and distribute that work
|
||||||
|
@ -329,7 +329,7 @@ restrictions of other proprietary libraries that do not normally
|
||||||
accompany the operating system. Such a contradiction means you cannot
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
use both them and the Library together in an executable that you
|
use both them and the Library together in an executable that you
|
||||||
distribute.
|
distribute.
|
||||||
|
|
||||||
7. You may place library facilities that are a work based on the
|
7. You may place library facilities that are a work based on the
|
||||||
Library side-by-side in a single library together with other library
|
Library side-by-side in a single library together with other library
|
||||||
facilities not covered by this License, and distribute such a combined
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
@ -370,7 +370,7 @@ subject to these terms and conditions. You may not impose any further
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
You are not responsible for enforcing compliance by third parties with
|
You are not responsible for enforcing compliance by third parties with
|
||||||
this License.
|
this License.
|
||||||
|
|
||||||
11. If, as a consequence of a court judgment or allegation of patent
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
infringement or for any other reason (not limited to patent issues),
|
infringement or for any other reason (not limited to patent issues),
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
@ -422,7 +422,7 @@ conditions either of that version or of any later version published by
|
||||||
the Free Software Foundation. If the Library does not specify a
|
the Free Software Foundation. If the Library does not specify a
|
||||||
license version number, you may choose any version ever published by
|
license version number, you may choose any version ever published by
|
||||||
the Free Software Foundation.
|
the Free Software Foundation.
|
||||||
|
|
||||||
14. If you wish to incorporate parts of the Library into other free
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
programs whose distribution conditions are incompatible with these,
|
programs whose distribution conditions are incompatible with these,
|
||||||
write to the author to ask for permission. For software which is
|
write to the author to ask for permission. For software which is
|
||||||
|
@ -456,7 +456,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
DAMAGES.
|
DAMAGES.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
How to Apply These Terms to Your New Libraries
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
If you develop a new library, and you want it to be of the greatest
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
|
577
doc/lua_api.txt
577
doc/lua_api.txt
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
||||||
Minetest Lua Mainmenu API Reference 5.5.0
|
Minetest Lua Mainmenu API Reference 5.6.0
|
||||||
=========================================
|
=========================================
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
|
@ -85,7 +85,9 @@ core.get_video_drivers()
|
||||||
core.get_mapgen_names([include_hidden=false]) -> table of map generator algorithms
|
core.get_mapgen_names([include_hidden=false]) -> table of map generator algorithms
|
||||||
registered in the core (possible in async calls)
|
registered in the core (possible in async calls)
|
||||||
core.get_cache_path() -> path of cache
|
core.get_cache_path() -> path of cache
|
||||||
core.get_temp_path() -> path of temp folder
|
core.get_temp_path([param]) (possible in async calls)
|
||||||
|
^ param=true: returns path to a temporary file
|
||||||
|
^ otherwise: returns path to the temporary folder
|
||||||
|
|
||||||
|
|
||||||
HTTP Requests
|
HTTP Requests
|
||||||
|
@ -219,7 +221,24 @@ Package - content which is downloadable from the content db, may or may not be i
|
||||||
* returns path to global user data,
|
* returns path to global user data,
|
||||||
the directory that contains user-provided mods, worlds, games, and texture packs.
|
the directory that contains user-provided mods, worlds, games, and texture packs.
|
||||||
* core.get_modpath() (possible in async calls)
|
* core.get_modpath() (possible in async calls)
|
||||||
* returns path to global modpath
|
* returns path to global modpath in the user path, where mods can be installed
|
||||||
|
* core.get_modpaths() (possible in async calls)
|
||||||
|
* returns table of virtual path to global modpaths, where mods have been installed
|
||||||
|
The difference with "core.get_modpath" is that no mods should be installed in these
|
||||||
|
directories by Minetest -- they might be read-only.
|
||||||
|
|
||||||
|
Ex:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
mods = "/home/user/.minetest/mods",
|
||||||
|
share = "/usr/share/minetest/mods",
|
||||||
|
|
||||||
|
-- Custom dirs can be specified by the MINETEST_MOD_DIR env variable
|
||||||
|
["/path/to/custom/dir"] = "/path/to/custom/dir",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
* core.get_clientmodpath() (possible in async calls)
|
* core.get_clientmodpath() (possible in async calls)
|
||||||
* returns path to global client-side modpath
|
* returns path to global client-side modpath
|
||||||
* core.get_gamepath() (possible in async calls)
|
* core.get_gamepath() (possible in async calls)
|
||||||
|
|
|
@ -112,6 +112,10 @@ leveldb, and files.
|
||||||
Migrate from current players backend to another. Possible values are sqlite3,
|
Migrate from current players backend to another. Possible values are sqlite3,
|
||||||
leveldb, postgresql, dummy, and files.
|
leveldb, postgresql, dummy, and files.
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-migrate-mod-storage <value>
|
||||||
|
Migrate from current mod storage backend to another. Possible values are
|
||||||
|
sqlite3, dummy, and files.
|
||||||
|
.TP
|
||||||
.B \-\-terminal
|
.B \-\-terminal
|
||||||
Display an interactive terminal over ncurses during execution.
|
Display an interactive terminal over ncurses during execution.
|
||||||
|
|
||||||
|
@ -119,6 +123,9 @@ Display an interactive terminal over ncurses during execution.
|
||||||
.TP
|
.TP
|
||||||
.B MINETEST_SUBGAME_PATH
|
.B MINETEST_SUBGAME_PATH
|
||||||
Colon delimited list of directories to search for games.
|
Colon delimited list of directories to search for games.
|
||||||
|
.TP
|
||||||
|
.B MINETEST_MOD_PATH
|
||||||
|
Colon delimited list of directories to search for mods.
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Please report all bugs at https://github.com/minetest/minetest/issues.
|
Please report all bugs at https://github.com/minetest/minetest/issues.
|
||||||
|
|
|
@ -90,9 +90,10 @@ by texture packs. All existing fallback textures can be found in the directory
|
||||||
* `minimap_mask_square.png`: mask used for the square minimap
|
* `minimap_mask_square.png`: mask used for the square minimap
|
||||||
* `minimap_overlay_round.png`: overlay texture for the round minimap
|
* `minimap_overlay_round.png`: overlay texture for the round minimap
|
||||||
* `minimap_overlay_square.png`: overlay texture for the square minimap
|
* `minimap_overlay_square.png`: overlay texture for the square minimap
|
||||||
* `no_texture_airlike.png`: fallback inventory image for airlike nodes
|
|
||||||
* `object_marker_red.png`: texture for players on the minimap
|
* `object_marker_red.png`: texture for players on the minimap
|
||||||
* `player_marker.png`: texture for the own player on the square minimap
|
* `player_marker.png`: texture for the own player on the square minimap
|
||||||
|
* `no_texture_airlike.png`: fallback inventory image for airlike nodes
|
||||||
|
* `no_texture.png`: fallback image for unspecified textures
|
||||||
|
|
||||||
* `player.png`: front texture of the 2D upright sprite player
|
* `player.png`: front texture of the 2D upright sprite player
|
||||||
* `player_back.png`: back texture of the 2D upright sprite player
|
* `player_back.png`: back texture of the 2D upright sprite player
|
||||||
|
|
|
@ -129,9 +129,34 @@ Example content (added indentation and - explanations):
|
||||||
backend = sqlite3 - which DB backend to use for blocks (sqlite3, dummy, leveldb, redis, postgresql)
|
backend = sqlite3 - which DB backend to use for blocks (sqlite3, dummy, leveldb, redis, postgresql)
|
||||||
player_backend = sqlite3 - which DB backend to use for player data
|
player_backend = sqlite3 - which DB backend to use for player data
|
||||||
readonly_backend = sqlite3 - optionally readonly seed DB (DB file _must_ be located in "readonly" subfolder)
|
readonly_backend = sqlite3 - optionally readonly seed DB (DB file _must_ be located in "readonly" subfolder)
|
||||||
|
auth_backend = files - which DB backend to use for authentication data
|
||||||
server_announce = false - whether the server is publicly announced or not
|
server_announce = false - whether the server is publicly announced or not
|
||||||
load_mod_<mod> = false - whether <mod> is to be loaded in this world
|
load_mod_<mod> = false - whether <mod> is to be loaded in this world
|
||||||
auth_backend = files - which DB backend to use for authentication data
|
|
||||||
|
For load_mod_<mod>, the possible values are:
|
||||||
|
|
||||||
|
* `false` - Do not load the mod.
|
||||||
|
* `true` - Load the mod from wherever it is found (may cause conflicts if the same mod appears also in some other place).
|
||||||
|
* `mods/modpack/moddir` - Relative path to the mod
|
||||||
|
* Must be one of the following:
|
||||||
|
* `mods/`: mods in the user path's mods folder (ex `/home/user/.minetest/mods`)
|
||||||
|
* `share/`: mods in the share's mods folder (ex: `/usr/share/minetest/mods`)
|
||||||
|
* `/path/to/env`: you can use absolute paths to mods inside folders specified with the `MINETEST_MOD_PATH` env variable.
|
||||||
|
* Other locations and absolute paths are not supported
|
||||||
|
* Note that `moddir` is the directory name, not the mod name specified in mod.conf.
|
||||||
|
|
||||||
|
PostgreSQL backend specific settings:
|
||||||
|
pgsql_connection = host=127.0.0.1 port=5432 user=mt_user password=mt_password dbname=minetest
|
||||||
|
pgsql_player_connection = (same parameters as above)
|
||||||
|
pgsql_readonly_connection = (same parameters as above)
|
||||||
|
pgsql_auth_connection = (same parameters as above)
|
||||||
|
|
||||||
|
Redis backend specific settings:
|
||||||
|
redis_address = 127.0.0.1 - Redis server address
|
||||||
|
redis_hash = foo - Database hash
|
||||||
|
redis_port = 6379 - (optional) connection port
|
||||||
|
redis_password = hunter2 - (optional) server password
|
||||||
|
|
||||||
|
|
||||||
Player File Format
|
Player File Format
|
||||||
===================
|
===================
|
||||||
|
@ -333,6 +358,9 @@ if map format version >= 29:
|
||||||
- 0xffffffff = invalid/unknown timestamp, nothing should be done with the time
|
- 0xffffffff = invalid/unknown timestamp, nothing should be done with the time
|
||||||
difference when loaded
|
difference when loaded
|
||||||
|
|
||||||
|
u8 name_id_mapping_version
|
||||||
|
- Should be zero for map format version 29.
|
||||||
|
|
||||||
u16 num_name_id_mappings
|
u16 num_name_id_mappings
|
||||||
foreach num_name_id_mappings
|
foreach num_name_id_mappings
|
||||||
u16 id
|
u16 id
|
||||||
|
@ -434,7 +462,7 @@ if map format version < 29:
|
||||||
u8[name_len] name
|
u8[name_len] name
|
||||||
|
|
||||||
- Node timers
|
- Node timers
|
||||||
if map format version == 25:
|
if map format version >= 25:
|
||||||
u8 length of the data of a single timer (always 2+4+4=10)
|
u8 length of the data of a single timer (always 2+4+4=10)
|
||||||
u16 num_of_timers
|
u16 num_of_timers
|
||||||
foreach num_of_timers:
|
foreach num_of_timers:
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 55 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 72 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 87 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue