Merge branch 'master' of https://github.com/minetest/minetest
commit
21df26984d
|
@ -1,6 +1,7 @@
|
|||
BasedOnStyle: LLVM
|
||||
IndentWidth: 8
|
||||
IndentWidth: 4
|
||||
UseTab: Always
|
||||
TabWidth: 4
|
||||
BreakBeforeBraces: Custom
|
||||
Standard: Cpp11
|
||||
BraceWrapping:
|
||||
|
@ -16,7 +17,7 @@ BraceWrapping:
|
|||
FixNamespaceComments: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
IndentCaseLabels: false
|
||||
AccessModifierOffset: -8
|
||||
AccessModifierOffset: -4
|
||||
ColumnLimit: 90
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
SortIncludes: false
|
||||
|
@ -26,7 +27,7 @@ IncludeCategories:
|
|||
- Regex: '^<.*'
|
||||
Priority: 1
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
ContinuationIndentWidth: 16
|
||||
ConstructorInitializerIndentWidth: 16
|
||||
ContinuationIndentWidth: 8
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
BreakConstructorInitializers: AfterColon
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
|
|
@ -21,24 +21,22 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- uses: actions/checkout@v3
|
||||
- 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
|
||||
run: cd android; ./gradlew assemblerelease
|
||||
- name: Save armeabi artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Minetest-armeabi-v7a.apk
|
||||
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
||||
- name: Save arm64 artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Minetest-arm64-v8a.apk
|
||||
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
||||
|
|
|
@ -30,53 +30,53 @@ on:
|
|||
- '.dockerignore'
|
||||
|
||||
jobs:
|
||||
# This is our minor gcc compiler
|
||||
gcc_6:
|
||||
# Older gcc version (should be close to our minimum supported version)
|
||||
gcc_5:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps g++-6
|
||||
install_linux_deps g++-5
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: gcc-6
|
||||
CXX: g++-6
|
||||
CC: gcc-5
|
||||
CXX: g++-5
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# This is the current gcc compiler (available in bionic)
|
||||
gcc_8:
|
||||
runs-on: ubuntu-18.04
|
||||
# Current gcc version
|
||||
gcc_10:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps g++-8
|
||||
install_linux_deps g++-10
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: gcc-8
|
||||
CXX: g++-8
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# This is our minor clang compiler
|
||||
# Older clang version (should be close to our minimum supported version)
|
||||
clang_3_9:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
|
@ -93,26 +93,26 @@ jobs:
|
|||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
- name: Integration test
|
||||
- name: Integration test + devtest
|
||||
run: |
|
||||
./util/test_multiplayer.sh
|
||||
|
||||
# This is the current clang version
|
||||
clang_9:
|
||||
runs-on: ubuntu-18.04
|
||||
# Current clang version
|
||||
clang_10:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
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
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-9
|
||||
CXX: clang++-9
|
||||
CC: clang-10
|
||||
CXX: clang++-10
|
||||
CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
|
||||
|
||||
- name: Test
|
||||
|
@ -126,9 +126,9 @@ jobs:
|
|||
# Build with prometheus-cpp (server-only)
|
||||
clang_9_prometheus:
|
||||
name: "clang_9 (PROMETHEUS=1)"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
|
@ -150,34 +150,11 @@ jobs:
|
|||
run: |
|
||||
./bin/minetestserver --run-unittests
|
||||
|
||||
# Build without freetype (client-only)
|
||||
clang_9_no_freetype:
|
||||
name: "clang_9 (FREETYPE=0)"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install deps
|
||||
run: |
|
||||
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:
|
||||
name: "Docker image"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build docker image
|
||||
run: |
|
||||
docker build . -t minetest:latest
|
||||
|
@ -185,13 +162,13 @@ jobs:
|
|||
|
||||
win32:
|
||||
name: "MinGW cross-compiler (32-bit)"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get update -q && sudo apt-get install gettext -qyy
|
||||
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
sudo apt-get update && sudo apt-get install -y gettext
|
||||
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
|
||||
|
||||
- name: Build
|
||||
|
@ -203,13 +180,13 @@ jobs:
|
|||
|
||||
win64:
|
||||
name: "MinGW cross-compiler (64-bit)"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get update -q && sudo apt-get install gettext -qyy
|
||||
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
sudo apt-get update && sudo apt-get install -y gettext
|
||||
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
|
||||
|
||||
- name: Build
|
||||
|
@ -222,13 +199,10 @@ jobs:
|
|||
msvc:
|
||||
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
||||
runs-on: windows-2019
|
||||
#### Disabled due to Irrlicht switch
|
||||
if: false
|
||||
#### Disabled due to Irrlicht switch
|
||||
env:
|
||||
VCPKG_VERSION: 0bf3923f9fab4001c00f0f429682a0853b5749e0
|
||||
# 2020.11
|
||||
vcpkg_packages: irrlicht zlib zstd curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit
|
||||
VCPKG_VERSION: 5cf60186a241e84e8232641ee973395d4fde90e1
|
||||
# 2022.02
|
||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
@ -249,11 +223,17 @@ jobs:
|
|||
# Enable it, when working on the installer.
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- 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
|
||||
uses: lukka/run-vcpkg@v5
|
||||
uses: lukka/run-vcpkg@v7
|
||||
with:
|
||||
vcpkgArguments: ${{env.vcpkg_packages}}
|
||||
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
||||
|
@ -261,7 +241,7 @@ jobs:
|
|||
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
||||
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
||||
|
||||
- name: CMake
|
||||
- name: Minetest CMake
|
||||
run: |
|
||||
cmake ${{matrix.config.generator}} `
|
||||
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
||||
|
@ -269,7 +249,7 @@ jobs:
|
|||
-DENABLE_POSTGRESQL=OFF `
|
||||
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
||||
|
||||
- name: Build
|
||||
- name: Build Minetest
|
||||
run: cmake --build . --config Release
|
||||
|
||||
- name: CPack
|
||||
|
@ -288,7 +268,7 @@ jobs:
|
|||
- name: Package Clean
|
||||
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
||||
path: .\Package\
|
||||
|
|
|
@ -26,12 +26,13 @@ on:
|
|||
jobs:
|
||||
|
||||
# clang_format:
|
||||
# runs-on: ubuntu-18.04
|
||||
# runs-on: ubuntu-20.04
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: actions/checkout@v3
|
||||
# - name: Install clang-format
|
||||
# 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
|
||||
# run: |
|
||||
|
@ -41,14 +42,13 @@ jobs:
|
|||
# CLANG_FORMAT: clang-format-9
|
||||
|
||||
clang_tidy:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install clang-tidy-9 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
install_linux_deps clang-tidy-9
|
||||
|
||||
- name: Run clang-tidy
|
||||
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'
|
||||
|
||||
env:
|
||||
IRRLICHT_TAG: 1.9.0mt3
|
||||
IRRLICHT_TAG: 1.9.0mt5
|
||||
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
|
||||
MINETEST_GAME_BRANCH: master
|
||||
MINETEST_GAME_NAME: minetest_game
|
||||
|
@ -31,7 +31,7 @@ jobs:
|
|||
build:
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
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
|
||||
rm -rvf games/$MINETEST_GAME_NAME/.git
|
||||
git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
|
||||
mkdir cmakebuild
|
||||
cd cmakebuild
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||
|
@ -60,7 +60,7 @@ jobs:
|
|||
run: |
|
||||
./build/macos/minetest.app/Contents/MacOS/minetest --run-unittests
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: minetest-macos
|
||||
path: ./build/macos/
|
||||
|
|
|
@ -107,6 +107,13 @@ CMakeDoxy*
|
|||
compile_commands.json
|
||||
*.apk
|
||||
*.zip
|
||||
# Visual Studio
|
||||
*.vcxproj*
|
||||
*.sln
|
||||
.vs/
|
||||
|
||||
# Optional user provided library folder
|
||||
lib/irrlichtmt
|
||||
|
||||
# Generated mod storage database
|
||||
client/mod_storage.sqlite
|
||||
|
|
|
@ -9,7 +9,7 @@ stages:
|
|||
- deploy
|
||||
|
||||
variables:
|
||||
IRRLICHT_TAG: "1.9.0mt3"
|
||||
IRRLICHT_TAG: "1.9.0mt5"
|
||||
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
|
||||
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
|
||||
script:
|
||||
- git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
|
||||
- mkdir cmakebuild
|
||||
- cd cmakebuild
|
||||
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
||||
- make -j2
|
||||
- make install
|
||||
- cmake -B build -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
||||
- cmake --build build --parallel $(($(nproc) + 1))
|
||||
- cmake --install build
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 1h
|
||||
|
@ -198,25 +196,12 @@ build:fedora-28:
|
|||
before_script:
|
||||
- apt-get update
|
||||
- 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
|
||||
|
||||
.build_win_template:
|
||||
extends: .generic_win_template
|
||||
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:
|
||||
expire_in: 90 day
|
||||
paths:
|
||||
|
@ -226,28 +211,15 @@ build:win32:
|
|||
extends: .build_win_template
|
||||
script:
|
||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh build
|
||||
- unzip -q build/build/*.zip
|
||||
variables:
|
||||
WIN_ARCH: "i686"
|
||||
|
||||
package:win32:
|
||||
extends: .package_win_template
|
||||
needs:
|
||||
- build:win32
|
||||
variables:
|
||||
WIN_ARCH: "i686"
|
||||
|
||||
|
||||
build:win64:
|
||||
extends: .build_win_template
|
||||
script:
|
||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh build
|
||||
variables:
|
||||
WIN_ARCH: "x86_64"
|
||||
|
||||
package:win64:
|
||||
extends: .package_win_template
|
||||
needs:
|
||||
- build:win64
|
||||
- unzip -q build/build/*.zip
|
||||
variables:
|
||||
WIN_ARCH: "x86_64"
|
||||
|
||||
|
|
|
@ -11,13 +11,14 @@ endif()
|
|||
project(minetest)
|
||||
set(PROJECT_NAME_CAPITALIZED "Dragonfire")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(GCC_MINIMUM_VERSION "4.8")
|
||||
set(CLANG_MINIMUM_VERSION "3.4")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(GCC_MINIMUM_VERSION "5.1")
|
||||
set(CLANG_MINIMUM_VERSION "3.5")
|
||||
|
||||
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
|
||||
set(VERSION_MAJOR 5)
|
||||
set(VERSION_MINOR 5)
|
||||
set(VERSION_MINOR 6)
|
||||
set(VERSION_PATCH 0)
|
||||
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}")
|
||||
if(VERSION_EXTRA)
|
||||
set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
|
||||
set(VERSION_STRING "${VERSION_STRING}-${VERSION_EXTRA}")
|
||||
elseif(DEVELOPMENT_BUILD)
|
||||
set(VERSION_STRING "${VERSION_STRING}-dev")
|
||||
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_SERVER FALSE CACHE BOOL "Build server")
|
||||
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")
|
||||
|
||||
|
@ -64,8 +65,21 @@ endif()
|
|||
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
|
||||
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'")
|
||||
if(BUILD_CLIENT)
|
||||
# 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
|
||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${IRRLICHT_INCLUDE_DIR}")
|
||||
else()
|
||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(TARGET IrrlichtMt::IrrlichtMt)
|
||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
||||
endif()
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
|
@ -135,15 +151,16 @@ elseif(UNIX) # Linux, BSD etc
|
|||
set(ICONDIR "unix/icons")
|
||||
set(LOCALEDIR "locale")
|
||||
else()
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
|
||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}")
|
||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man")
|
||||
include(GNUInstallDirs)
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}")
|
||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
|
||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DOCDIR}")
|
||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_MANDIR}")
|
||||
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/metainfo")
|
||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons")
|
||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/locale")
|
||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications")
|
||||
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/metainfo")
|
||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons")
|
||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -246,10 +263,10 @@ endif()
|
|||
find_package(GMP REQUIRED)
|
||||
find_package(Json REQUIRED)
|
||||
find_package(Lua REQUIRED)
|
||||
|
||||
# JsonCpp doesn't compile well on GCC 4.8
|
||||
if(NOT USE_SYSTEM_JSONCPP)
|
||||
set(GCC_MINIMUM_VERSION "4.9")
|
||||
if(NOT USE_LUAJIT)
|
||||
set(LUA_BIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/bitop)
|
||||
set(LUA_BIT_LIBRARY bitop)
|
||||
add_subdirectory(lib/bitop)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
|
@ -264,9 +281,12 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang")
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_BENCHMARKS)
|
||||
add_subdirectory(lib/catch2)
|
||||
endif()
|
||||
|
||||
# Subdirectories
|
||||
# Be sure to add all relevant definitions above this
|
||||
|
||||
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/
|
||||
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
||||
mkdir prometheus-cpp/build && \
|
||||
cd prometheus-cpp/build && \
|
||||
cmake .. \
|
||||
cd prometheus-cpp && \
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DENABLE_TESTING=0 \
|
||||
-GNinja && \
|
||||
ninja && \
|
||||
ninja install
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
RUN git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
|
||||
cp -r irrlicht/include /usr/include/irrlichtmt
|
||||
|
||||
WORKDIR /usr/src/minetest
|
||||
RUN mkdir build && \
|
||||
cd build && \
|
||||
cmake .. \
|
||||
RUN cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SERVER=TRUE \
|
||||
|
@ -51,8 +48,8 @@ RUN mkdir build && \
|
|||
-DBUILD_UNITTESTS=FALSE \
|
||||
-DBUILD_CLIENT=FALSE \
|
||||
-GNinja && \
|
||||
ninja && \
|
||||
ninja install
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
ARG DOCKER_IMAGE=alpine:3.14
|
||||
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.
|
||||
|
||||
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)
|
||||
|
||||
In case you downloaded the source code
|
||||
|
@ -132,10 +132,11 @@ Compiling
|
|||
|
||||
| Dependency | Version | Commentary |
|
||||
|------------|---------|------------|
|
||||
| GCC | 4.9+ | Can be replaced with Clang 3.4+ |
|
||||
| GCC | 5.1+ | or Clang 3.5+ |
|
||||
| CMake | 3.5+ | |
|
||||
| IrrlichtMt | - | Custom version of Irrlicht, see https://github.com/minetest/irrlicht |
|
||||
| SQLite3 | 3.0+ | |
|
||||
| Freetype | 2.0+ | |
|
||||
| SQLite3 | 3+ | |
|
||||
| Zstd | 1.0+ | |
|
||||
| LuaJIT | 2.0+ | Bundled Lua 5.1 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:
|
||||
|
||||
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:
|
||||
|
||||
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:
|
||||
|
||||
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.
|
||||
- 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`.
|
||||
- IrrlichtMt can also be installed somewhere that is not a standard install path.
|
||||
- In that case use `-DCMAKE_PREFIX_PATH=/path/to/install_prefix`
|
||||
- The path must be set so that `$(CMAKE_PREFIX_PATH)/lib/cmake/IrrlichtMt` exists
|
||||
or that `$(CMAKE_PREFIX_PATH)` is the path of an IrrlichtMt build folder.
|
||||
|
||||
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
|
||||
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
|
||||
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
|
||||
|
||||
|
@ -236,6 +240,7 @@ General options and their default values:
|
|||
BUILD_CLIENT=TRUE - Build Minetest client
|
||||
BUILD_SERVER=FALSE - Build Minetest server
|
||||
BUILD_UNITTESTS=TRUE - Build unittest sources
|
||||
BUILD_BENCHMARKS=FALSE - Build benchmark sources
|
||||
CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug)
|
||||
Release - Release 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
|
||||
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_FREETYPE=ON - Build with FreeType2; Allows using TTF fonts
|
||||
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_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
|
||||
|
@ -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_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
|
||||
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)
|
||||
USE_GPROF=FALSE - Enable profiling using GProf
|
||||
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:
|
||||
|
||||
|
@ -268,10 +272,11 @@ Library specific options:
|
|||
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_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
|
||||
FREETYPE_INCLUDE_DIR_ft2build - Only if building with FreeType 2; directory that contains ft2build.h
|
||||
FREETYPE_LIBRARY - Only if building with FreeType 2; path to libfreetype.a/libfreetype.so/freetype.lib
|
||||
FREETYPE_DLL - Only if building with FreeType 2 on Windows; path to libfreetype.dll
|
||||
EXTRA_DLL - Only on Windows; optional paths to additional DLLs that should be packaged
|
||||
FREETYPE_INCLUDE_DIR_freetype2 - Directory that contains files such as ftimage.h
|
||||
FREETYPE_INCLUDE_DIR_ft2build - Directory that contains ft2build.h
|
||||
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_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
|
||||
|
@ -295,15 +300,12 @@ Library specific options:
|
|||
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_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_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
|
||||
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_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_INCLUDE_DIR - Directory that contains zlib.h
|
||||
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:
|
||||
```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.
|
||||
- `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.
|
||||
- `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.
|
||||
- `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**.
|
||||
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
|
||||
------
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion '22.0.7026061'
|
||||
ndkVersion "$ndk_version"
|
||||
defaultConfig {
|
||||
applicationId 'net.minetest.minetest'
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||
versionCode project.versionCode
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ task prepareAssets() {
|
|||
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
|
||||
}
|
||||
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 {
|
||||
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
|
||||
|
@ -112,5 +112,5 @@ android.applicationVariants.all { variant ->
|
|||
|
||||
dependencies {
|
||||
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:maxAspectRatio="3.0"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
@ -44,7 +45,8 @@
|
|||
android:launchMode="singleTask"
|
||||
android:maxAspectRatio="3.0"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</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));
|
||||
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.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
@ -43,11 +45,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minetest.minetest.UnzipService.ACTION_FAILURE;
|
||||
import static net.minetest.minetest.UnzipService.ACTION_PROGRESS;
|
||||
import static net.minetest.minetest.UnzipService.ACTION_UPDATE;
|
||||
import static net.minetest.minetest.UnzipService.FAILURE;
|
||||
import static net.minetest.minetest.UnzipService.SUCCESS;
|
||||
import static net.minetest.minetest.UnzipService.*;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private final static int versionCode = BuildConfig.VERSION_CODE;
|
||||
|
@ -56,26 +54,40 @@ public class MainActivity extends AppCompatActivity {
|
|||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
private static final String SETTINGS = "MinetestSettings";
|
||||
private static final String TAG_VERSION_CODE = "versionCode";
|
||||
|
||||
private ProgressBar mProgressBar;
|
||||
private TextView mTextView;
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
int progress = 0;
|
||||
if (intent != null)
|
||||
@StringRes int message = 0;
|
||||
if (intent != null) {
|
||||
progress = intent.getIntExtra(ACTION_PROGRESS, 0);
|
||||
if (progress >= 0) {
|
||||
if (mProgressBar != null) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setProgress(progress);
|
||||
}
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
} else if (progress == FAILURE) {
|
||||
message = intent.getIntExtra(ACTION_PROGRESS_MESSAGE, 0);
|
||||
}
|
||||
|
||||
if (progress == FAILURE) {
|
||||
Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
} else if (progress == SUCCESS)
|
||||
} else if (progress == SUCCESS) {
|
||||
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);
|
||||
mTextView = findViewById(R.id.textView);
|
||||
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();
|
||||
else
|
||||
checkAppVersion();
|
||||
|
@ -120,6 +134,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
checkAppVersion();
|
||||
|
@ -127,10 +142,27 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
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();
|
||||
else
|
||||
new CopyZipTask(this).execute(getCacheDir() + "/Minetest.zip");
|
||||
} else {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setIndeterminate(true);
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
|
||||
Intent intent = new Intent(this, UnzipService.class);
|
||||
startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void startNative() {
|
||||
|
|
|
@ -24,16 +24,22 @@ import android.app.IntentService;
|
|||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
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.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
@ -42,32 +48,61 @@ import java.util.zip.ZipInputStream;
|
|||
public class UnzipService extends IntentService {
|
||||
public static final String ACTION_UPDATE = "net.minetest.minetest.UPDATE";
|
||||
public static final String ACTION_PROGRESS = "net.minetest.minetest.PROGRESS";
|
||||
public static final String ACTION_PROGRESS_MESSAGE = "net.minetest.minetest.PROGRESS_MESSAGE";
|
||||
public static final String ACTION_FAILURE = "net.minetest.minetest.FAILURE";
|
||||
public static final String EXTRA_KEY_IN_FILE = "file";
|
||||
public static final int SUCCESS = -1;
|
||||
public static final int FAILURE = -2;
|
||||
public static final int INDETERMINATE = -3;
|
||||
private final int id = 1;
|
||||
private NotificationManager mNotifyManager;
|
||||
private boolean isSuccess = true;
|
||||
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() {
|
||||
super("net.minetest.minetest.UnzipService");
|
||||
}
|
||||
|
||||
private void isDir(String dir, String location) {
|
||||
File f = new File(location, dir);
|
||||
if (!f.isDirectory())
|
||||
f.mkdirs();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
createNotification();
|
||||
unzip(intent);
|
||||
Notification.Builder notificationBuilder = createNotification();
|
||||
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 channelId = "Minetest channel";
|
||||
String description = "notifications from Minetest";
|
||||
|
@ -92,66 +127,133 @@ public class UnzipService extends IntentService {
|
|||
} else {
|
||||
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))
|
||||
.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());
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void unzip(Intent intent) {
|
||||
String zip = intent.getStringExtra(EXTRA_KEY_IN_FILE);
|
||||
isDir("Minetest", Environment.getExternalStorageDirectory().toString());
|
||||
String location = Environment.getExternalStorageDirectory() + File.separator + "Minetest" + File.separator;
|
||||
private void unzip(Notification.Builder notificationBuilder, File zipFile, File userDataDirectory) throws IOException {
|
||||
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;
|
||||
byte[] readBuffer = new byte[8192];
|
||||
byte[] readBuffer = new byte[16384];
|
||||
try (FileInputStream fileInputStream = new FileInputStream(zipFile);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
|
||||
ZipEntry ze;
|
||||
while ((ze = zipInputStream.getNextEntry()) != null) {
|
||||
if (ze.isDirectory()) {
|
||||
++per;
|
||||
isDir(ze.getName(), location);
|
||||
} else {
|
||||
publishProgress(100 * ++per / size);
|
||||
try (OutputStream outputStream = new FileOutputStream(location + ze.getName())) {
|
||||
while ((readLen = zipInputStream.read(readBuffer)) != -1) {
|
||||
outputStream.write(readBuffer, 0, readLen);
|
||||
}
|
||||
Utils.createDirs(userDataDirectory, ze.getName());
|
||||
continue;
|
||||
}
|
||||
publishProgress(notificationBuilder, R.string.loading, 100 * ++per / size);
|
||||
try (OutputStream outputStream = new FileOutputStream(
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
private int getSummarySize(String zip) {
|
||||
int size = 0;
|
||||
try {
|
||||
ZipFile zipSize = new ZipFile(zip);
|
||||
size += zipSize.size();
|
||||
} catch (IOException e) {
|
||||
Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
if (notificationBuilder != null) {
|
||||
notificationBuilder.setContentText(getString(message));
|
||||
if (progress == INDETERMINATE) {
|
||||
notificationBuilder.setProgress(100, 50, true);
|
||||
} else {
|
||||
notificationBuilder.setProgress(100, progress, false);
|
||||
}
|
||||
mNotifyManager.notify(id, notificationBuilder.build());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -14,7 +15,8 @@
|
|||
android:layout_marginRight="90dp"
|
||||
android:indeterminate="false"
|
||||
android:max="100"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
|
@ -25,6 +27,7 @@
|
|||
android:background="@android:color/transparent"
|
||||
android:text="@string/loading"
|
||||
android:textColor="#FEFEFE"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
<string name="label">Minetest</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="notification_title">Loading Minetest</string>
|
||||
<string name="notification_description">Less than 1 minute…</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>
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
project.ext.set("versionMajor", 5) // Version Major
|
||||
project.ext.set("versionMinor", 5) // Version Minor
|
||||
project.ext.set("versionMinor", 6) // Version Minor
|
||||
project.ext.set("versionPatch", 0) // Version Patch
|
||||
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!
|
||||
// +1 for ARM and +1 for ARM64 APK's, because
|
||||
// each APK must have a larger `versionCode` than the previous
|
||||
|
||||
buildscript {
|
||||
ext.ndk_version = '23.0.7599858'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
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'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#Fri Jan 08 17:52:00 UTC 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
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
|
||||
location of your Java installation."
|
||||
|
|
|
@ -2,12 +2,12 @@ apply plugin: 'com.android.library'
|
|||
apply plugin: 'de.undercouch.download'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion '22.0.7026061'
|
||||
ndkVersion "$ndk_version"
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments '-j' + Runtime.getRuntime().availableProcessors(),
|
||||
|
@ -41,58 +41,28 @@ android {
|
|||
arguments 'NDEBUG=1'
|
||||
}
|
||||
}
|
||||
|
||||
ndk {
|
||||
debugSymbolLevel 'SYMBOL_TABLE'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get precompiled deps
|
||||
def folder = 'minetest_android_deps_binaries'
|
||||
|
||||
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')
|
||||
overwrite false
|
||||
}
|
||||
|
||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||
def deps = file('deps')
|
||||
def f = file("$buildDir/" + folder + "-master")
|
||||
|
||||
if (!deps.exists() && !f.exists()) {
|
||||
def deps = new File(buildDir.parent, 'deps')
|
||||
if (!deps.exists()) {
|
||||
deps.mkdir()
|
||||
from zipTree(downloadDeps.dest)
|
||||
into buildDir
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
into deps
|
||||
}
|
||||
}
|
||||
|
||||
preBuild.dependsOn getDeps
|
||||
preBuild.dependsOn getSqlite
|
||||
|
|
|
@ -4,62 +4,82 @@ LOCAL_PATH := $(call my-dir)/..
|
|||
|
||||
include $(CLEAR_VARS)
|
||||
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 $(CLEAR_VARS)
|
||||
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 $(CLEAR_VARS)
|
||||
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 $(CLEAR_VARS)
|
||||
#LOCAL_MODULE := LevelDB
|
||||
#LOCAL_SRC_FILES := deps/Android/LevelDB/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libleveldb.a
|
||||
#include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := LuaJIT
|
||||
LOCAL_SRC_FILES := deps/Android/LuaJIT/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libluajit.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedTLS
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedtls.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedx509
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedx509.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedcrypto
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedcrypto.a
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/libluajit.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := OpenAL
|
||||
LOCAL_SRC_FILES := deps/Android/OpenAL-Soft/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libopenal.a
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := GetText
|
||||
LOCAL_SRC_FILES := deps/Android/GetText/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libintl.a
|
||||
LOCAL_MODULE := Gettext
|
||||
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 $(CLEAR_VARS)
|
||||
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 $(CLEAR_VARS)
|
||||
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 $(CLEAR_VARS)
|
||||
|
@ -71,7 +91,6 @@ LOCAL_CFLAGS += \
|
|||
-DENABLE_GLES=1 \
|
||||
-DUSE_CURL=1 \
|
||||
-DUSE_SOUND=1 \
|
||||
-DUSE_FREETYPE=1 \
|
||||
-DUSE_LEVELDB=0 \
|
||||
-DUSE_LUAJIT=1 \
|
||||
-DUSE_GETTEXT=1 \
|
||||
|
@ -96,18 +115,16 @@ LOCAL_C_INCLUDES := \
|
|||
../../src/script \
|
||||
../../lib/gmp \
|
||||
../../lib/jsoncpp \
|
||||
deps/Android/Curl/include \
|
||||
deps/Android/Freetype/include \
|
||||
deps/Android/Irrlicht/include \
|
||||
deps/Android/LevelDB/include \
|
||||
deps/Android/GetText/include \
|
||||
deps/Android/libiconv/include \
|
||||
deps/Android/libiconv/libcharset/include \
|
||||
deps/Android/LuaJIT/src \
|
||||
deps/Android/OpenAL-Soft/include \
|
||||
deps/Android/sqlite \
|
||||
deps/Android/Vorbis/include \
|
||||
deps/Android/Zstd/include
|
||||
deps/$(APP_ABI)/Curl/include \
|
||||
deps/$(APP_ABI)/Freetype/include/freetype2 \
|
||||
deps/$(APP_ABI)/Irrlicht/include \
|
||||
deps/$(APP_ABI)/Gettext/include \
|
||||
deps/$(APP_ABI)/Iconv/include \
|
||||
deps/$(APP_ABI)/LuaJIT/include \
|
||||
deps/$(APP_ABI)/OpenAL-Soft/include \
|
||||
deps/$(APP_ABI)/SQLite/include \
|
||||
deps/$(APP_ABI)/Vorbis/include \
|
||||
deps/$(APP_ABI)/Zstd/include
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(wildcard ../../src/client/*.cpp) \
|
||||
|
@ -190,24 +207,24 @@ LOCAL_SRC_FILES := \
|
|||
../../src/voxel.cpp \
|
||||
../../src/voxelalgorithms.cpp
|
||||
|
||||
# LevelDB backend is disabled
|
||||
# ../../src/database/database-leveldb.cpp
|
||||
|
||||
# GMP
|
||||
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
|
||||
|
||||
# JSONCPP
|
||||
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
|
||||
|
||||
# iconv
|
||||
LOCAL_SRC_FILES += \
|
||||
deps/Android/libiconv/lib/iconv.c \
|
||||
deps/Android/libiconv/libcharset/lib/localcharset.c
|
||||
|
||||
# SQLite3
|
||||
LOCAL_SRC_FILES += deps/Android/sqlite/sqlite3.c
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += Curl Freetype Irrlicht OpenAL mbedTLS mbedx509 mbedcrypto Vorbis LuaJIT GetText Zstd android_native_app_glue $(PROFILER_LIBS) #LevelDB
|
||||
LOCAL_STATIC_LIBRARIES += \
|
||||
Curl libmbedcrypto libmbedtls libmbedx509 \
|
||||
Freetype \
|
||||
Iconv libcharset \
|
||||
Irrlicht \
|
||||
LuaJIT \
|
||||
OpenAL \
|
||||
Gettext \
|
||||
SQLite3 \
|
||||
Vorbis libvorbisfile libogg \
|
||||
Zstd
|
||||
LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
|
||||
|
||||
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
|
||||
|
||||
|
|
|
@ -5,22 +5,22 @@ NDK_TOOLCHAIN_VERSION := clang
|
|||
APP_SHORT_COMMANDS := true
|
||||
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)
|
||||
APP_CPPFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
|
||||
APP_CPPFLAGS += -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
|
||||
endif
|
||||
|
||||
#ifeq ($(APP_ABI),x86)
|
||||
#APP_CPPFLAGS += -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32 -funroll-loops
|
||||
#endif
|
||||
ifeq ($(APP_ABI),x86)
|
||||
APP_CPPFLAGS += -mssse3 -mfpmath=sse -funroll-loops
|
||||
endif
|
||||
|
||||
ifndef NDEBUG
|
||||
APP_CPPFLAGS := -g -D_DEBUG -O0 -fno-omit-frame-pointer -fexceptions
|
||||
APP_CPPFLAGS := -g -Og -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-parentheses-equality #-Werror=shorten-64-to-32
|
||||
APP_CXXFLAGS := $(APP_CPPFLAGS) -frtti -std=gnu++17
|
||||
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-inconsistent-missing-override -Wno-parentheses-equality
|
||||
APP_CXXFLAGS := $(APP_CPPFLAGS) -fexceptions -frtti -std=gnu++14
|
||||
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections,--icf=safe
|
||||
|
||||
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)
|
||||
local param = core.deserialize(serialized_param)
|
||||
|
@ -8,4 +7,3 @@ function core.job_processor(func, serialized_param)
|
|||
|
||||
return retval or core.serialize(nil)
|
||||
end
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
core.callback_origins = {}
|
||||
|
||||
local getinfo = debug.getinfo
|
||||
|
|
|
@ -37,7 +37,14 @@ function core.after(after, func, ...)
|
|||
arg = {...},
|
||||
mod_origin = core.get_last_run_mod(),
|
||||
}
|
||||
|
||||
jobs[#jobs + 1] = new_job
|
||||
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
|
||||
|
|
|
@ -6,6 +6,42 @@ local S = core.get_translator("__builtin")
|
|||
|
||||
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)
|
||||
def = def or {}
|
||||
def.params = def.params or ""
|
||||
|
@ -78,22 +114,30 @@ if INIT == "client" then
|
|||
end
|
||||
end
|
||||
|
||||
local function do_help_cmd(name, param)
|
||||
local function format_help_line(cmd, def)
|
||||
local cmd_marker = "/"
|
||||
if INIT == "client" then
|
||||
cmd_marker = "."
|
||||
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
|
||||
local function format_help_line(cmd, def)
|
||||
local cmd_marker = INIT == "client" and "." or "/"
|
||||
local msg = core.colorize("#00ffff", cmd_marker .. cmd)
|
||||
if def.params and def.params ~= "" then
|
||||
msg = msg .. " " .. def.params
|
||||
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 = {}
|
||||
for cmd, def in pairs(core.registered_chatcommands) do
|
||||
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
||||
|
@ -116,7 +160,10 @@ local function do_help_cmd(name, param)
|
|||
.. "everything.")
|
||||
end
|
||||
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 = {}
|
||||
for cmd, def in pairs(core.registered_chatcommands) do
|
||||
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:")
|
||||
end
|
||||
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 = {}
|
||||
for priv, def in pairs(core.registered_privileges) do
|
||||
privs[#privs + 1] = priv .. ": " .. def.description
|
||||
|
@ -139,7 +190,7 @@ local function do_help_cmd(name, param)
|
|||
table.sort(privs)
|
||||
return true, S("Available privileges:").."\n"..table.concat(privs, "\n")
|
||||
else
|
||||
local cmd = param
|
||||
local cmd = args[1]
|
||||
local def = core.registered_chatcommands[cmd]
|
||||
if not def then
|
||||
local msg
|
||||
|
@ -165,8 +216,8 @@ if INIT == "client" then
|
|||
})
|
||||
else
|
||||
core.register_chatcommand("help", {
|
||||
params = S("[all | privs | <cmd>]"),
|
||||
description = S("Get help for commands or list privileges"),
|
||||
params = S("[all | privs | <cmd>] [-t]"),
|
||||
description = S("Get help for commands or list privileges (-t: output in chat)"),
|
||||
func = do_help_cmd,
|
||||
})
|
||||
end
|
||||
|
|
|
@ -125,30 +125,12 @@ core.register_on_player_receive_fields(function(player, formname, fields)
|
|||
end
|
||||
end)
|
||||
|
||||
|
||||
local help_command = core.registered_chatcommands["help"]
|
||||
local old_help_func = help_command.func
|
||||
|
||||
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)
|
||||
function core.show_general_help_formspec(name)
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name))
|
||||
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
|
||||
|
||||
if INIT == "client" or INIT == "mainmenu" then
|
||||
if core.gettext then -- for client and mainmenu
|
||||
function fgettext_ne(text, ...)
|
||||
text = core.gettext(text)
|
||||
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
|
||||
|
||||
function core.global_exists(name)
|
||||
|
@ -33,11 +28,6 @@ function meta:__newindex(name, value)
|
|||
end
|
||||
declared[name] = true
|
||||
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)
|
||||
end
|
||||
|
||||
|
@ -54,4 +44,3 @@ function meta:__index(name)
|
|||
end
|
||||
|
||||
setmetatable(_G, meta)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
_G.core = {}
|
||||
_G.vector = {metatable = {}}
|
||||
dofile("builtin/common/vector.lua")
|
||||
dofile("builtin/common/misc_helpers.lua")
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
_G.core = {}
|
||||
_G.vector = {metatable = {}}
|
||||
|
||||
_G.setfenv = require 'busted.compatibility'.setfenv
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
_G.vector = {}
|
||||
_G.vector = {metatable = {}}
|
||||
dofile("builtin/common/vector.lua")
|
||||
|
||||
describe("vector", function()
|
||||
|
@ -128,6 +128,14 @@ describe("vector", function()
|
|||
assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f))
|
||||
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()
|
||||
local function assertE(a, b)
|
||||
assert.is_true(vector.equals(a, b))
|
||||
|
@ -300,6 +308,7 @@ describe("vector", function()
|
|||
|
||||
it("from_string()", function()
|
||||
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, 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
|
||||
local setmetatable = setmetatable
|
||||
|
||||
vector = {}
|
||||
|
||||
local metatable = {}
|
||||
vector.metatable = metatable
|
||||
-- vector.metatable is set by C++.
|
||||
local metatable = vector.metatable
|
||||
|
||||
local xyz = {"x", "y", "z"}
|
||||
|
||||
|
@ -61,7 +59,7 @@ function vector.from_string(s, init)
|
|||
if not (x and y and z) then
|
||||
return nil
|
||||
end
|
||||
return {x = x, y = y, z = z}, np
|
||||
return fast_new(x, y, z), np
|
||||
end
|
||||
|
||||
function vector.to_string(v)
|
||||
|
@ -112,6 +110,14 @@ function vector.apply(v, func)
|
|||
)
|
||||
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)
|
||||
local x = a.x - b.x
|
||||
local y = a.y - b.y
|
||||
|
|
|
@ -63,7 +63,7 @@ function ui.update()
|
|||
-- handle errors
|
||||
if gamedata ~= nil and gamedata.reconnect_requested then
|
||||
local error_message = core.formspec_escape(
|
||||
gamedata.errormessage or "<none available>")
|
||||
gamedata.errormessage or fgettext("<none available>"))
|
||||
formspec = {
|
||||
"size[14,8]",
|
||||
"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 ~= ""
|
||||
if revokeprivstr == "all" then
|
||||
revokeprivs = privs
|
||||
privs = {}
|
||||
else
|
||||
for priv, _ in pairs(revokeprivs) do
|
||||
privs[priv] = nil
|
||||
end
|
||||
revokeprivs = table.copy(privs)
|
||||
end
|
||||
|
||||
local privs_unknown = ""
|
||||
|
@ -332,7 +327,10 @@ local function handle_revoke_command(caller, revokename, revokeprivstr)
|
|||
end
|
||||
local def = core.registered_privileges[priv]
|
||||
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
|
||||
irrevokable[priv] = true
|
||||
elseif is_admin and def.give_to_admin then
|
||||
|
@ -359,19 +357,22 @@ local function handle_revoke_command(caller, revokename, revokeprivstr)
|
|||
end
|
||||
|
||||
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)
|
||||
for priv, _ in pairs(revokeprivs) do
|
||||
-- call the on_revoke callbacks
|
||||
core.run_priv_callbacks(revokename, priv, caller, "revoke")
|
||||
revokecount = revokecount + 1
|
||||
end
|
||||
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.privs_to_string(revokeprivs, ', ')
|
||||
..') privileges from '..revokename)
|
||||
|
@ -524,7 +525,7 @@ end
|
|||
|
||||
-- Teleports player <name> to <p> if possible
|
||||
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
|
||||
or p.z < -lm or p.z > lm then
|
||||
return false, S("Cannot teleport out of map bounds!")
|
||||
|
@ -621,6 +622,10 @@ core.register_chatcommand("set", {
|
|||
|
||||
setname, setvalue = string.match(param, "([^ ]+) (.+)")
|
||||
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
|
||||
return false, S("Failed. Use '/set -n <name> <value>' "
|
||||
.. "to create a new setting.")
|
||||
|
@ -1034,12 +1039,11 @@ core.register_chatcommand("time", {
|
|||
end
|
||||
local hour, minute = param:match("^(%d+):(%d+)$")
|
||||
if not hour then
|
||||
local new_time = tonumber(param)
|
||||
if not new_time then
|
||||
return false, S("Invalid time.")
|
||||
local new_time = tonumber(param) or -1
|
||||
if new_time ~= new_time or new_time < 0 or new_time > 24000 then
|
||||
return false, S("Invalid time (must be between 0 and 24000).")
|
||||
end
|
||||
-- Backward compatibility.
|
||||
core.set_timeofday((new_time % 24000) / 24000)
|
||||
core.set_timeofday(new_time / 24000)
|
||||
core.log("action", name .. " sets time to " .. new_time)
|
||||
return true, S("Time of day changed.")
|
||||
end
|
||||
|
@ -1283,7 +1287,7 @@ local function handle_kill_command(killer, victim)
|
|||
return false, S("@1 is already dead.", victim)
|
||||
end
|
||||
end
|
||||
if not killer == victim then
|
||||
if killer ~= victim then
|
||||
core.log("action", string.format("%s killed %s", killer, victim))
|
||||
end
|
||||
-- Kill victim
|
||||
|
|
|
@ -22,6 +22,7 @@ core.features = {
|
|||
degrotate_240_steps = true,
|
||||
abm_min_max_y = true,
|
||||
dynamic_add_media_table = true,
|
||||
get_sky_as_table = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
|
|
@ -8,6 +8,7 @@ local gamepath = scriptpath .. "game".. DIR_DELIM
|
|||
local builtin_shared = {}
|
||||
|
||||
dofile(gamepath .. "constants.lua")
|
||||
dofile(gamepath .. "item_s.lua")
|
||||
assert(loadfile(gamepath .. "item.lua"))(builtin_shared)
|
||||
dofile(gamepath .. "register.lua")
|
||||
|
||||
|
@ -19,6 +20,7 @@ dofile(commonpath .. "after.lua")
|
|||
dofile(commonpath .. "voxelarea.lua")
|
||||
dofile(gamepath .. "item_entity.lua")
|
||||
dofile(gamepath .. "deprecated.lua")
|
||||
dofile(gamepath .. "misc_s.lua")
|
||||
dofile(gamepath .. "misc.lua")
|
||||
dofile(gamepath .. "privileges.lua")
|
||||
dofile(gamepath .. "auth.lua")
|
||||
|
@ -32,5 +34,6 @@ dofile(gamepath .. "features.lua")
|
|||
dofile(gamepath .. "forceloading.lua")
|
||||
dofile(gamepath .. "statbars.lua")
|
||||
dofile(gamepath .. "knockback.lua")
|
||||
dofile(gamepath .. "async.lua")
|
||||
|
||||
profiler = nil
|
||||
|
|
|
@ -15,144 +15,19 @@ end
|
|||
-- Item definition helpers
|
||||
--
|
||||
|
||||
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
|
||||
function core.get_pointed_thing_position(pointed_thing, above)
|
||||
if pointed_thing.type == "node" then
|
||||
if above then
|
||||
-- The position where a node would be placed
|
||||
return pointed_thing.above
|
||||
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
|
||||
|
||||
-- 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)
|
||||
if type(required_groups) == "string" then
|
||||
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
|
||||
end
|
||||
end
|
||||
-- read definition before potentially emptying the stack
|
||||
local def = itemstack:get_definition()
|
||||
if itemstack:take_item() ~= nil then
|
||||
user:set_hp(user:get_hp() + hp_change)
|
||||
if itemstack:take_item():is_empty() then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
if def and def.sound and def.sound.eat then
|
||||
core.sound_play(def.sound.eat, {
|
||||
pos = user:get_pos(),
|
||||
max_hear_distance = 16
|
||||
}, true)
|
||||
end
|
||||
if def and def.sound and def.sound.eat then
|
||||
core.sound_play(def.sound.eat, {
|
||||
pos = user:get_pos(),
|
||||
max_hear_distance = 16
|
||||
}, true)
|
||||
end
|
||||
|
||||
if replace_with_item then
|
||||
if itemstack:is_empty() then
|
||||
itemstack:add_item(replace_with_item)
|
||||
-- Changing hp might kill the player causing mods to do who-knows-what to the
|
||||
-- inventory, so do this before set_hp().
|
||||
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
|
||||
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
|
||||
local pos = user:get_pos()
|
||||
pos.y = math.floor(pos.y + 0.5)
|
||||
core.add_item(pos, replace_with_item)
|
||||
end
|
||||
local pos = user:get_pos()
|
||||
pos.y = math.floor(pos.y + 0.5)
|
||||
core.add_item(pos, replace_with_item)
|
||||
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
|
||||
|
||||
function core.item_eat(hp_change, replace_with_item)
|
||||
|
@ -585,7 +467,7 @@ function core.node_dig(pos, node, digger)
|
|||
if wielded then
|
||||
local wdef = wielded:get_definition()
|
||||
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
|
||||
wielded = wdef.after_use(wielded, digger, node, dp) or wielded
|
||||
else
|
||||
|
@ -647,9 +529,7 @@ function core.node_dig(pos, node, digger)
|
|||
-- Run script hook
|
||||
for _, callback in ipairs(core.registered_on_dignodes) do
|
||||
local origin = core.callback_origins[callback]
|
||||
if origin then
|
||||
core.set_last_run_mod(origin.mod)
|
||||
end
|
||||
core.set_last_run_mod(origin.mod)
|
||||
|
||||
-- Copy pos and node because callback can modify them
|
||||
local pos_copy = vector.new(pos)
|
||||
|
|
|
@ -58,17 +58,21 @@ core.register_entity(":__builtin:item", {
|
|||
local glow = def and def.light_source and
|
||||
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({
|
||||
is_visible = true,
|
||||
visual = "wielditem",
|
||||
textures = {itemname},
|
||||
visual_size = {x = size, y = size},
|
||||
collisionbox = {-size, -size, -size, size, size, size},
|
||||
visual_size = {x = size + size_bias, y = size + size_bias},
|
||||
collisionbox = c,
|
||||
automatic_rotate = math.pi * 0.5 * 0.2 / size,
|
||||
wield_item = self.itemstring,
|
||||
glow = glow,
|
||||
})
|
||||
|
||||
-- cache for usage in on_step
|
||||
self._collisionbox = c
|
||||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
|
@ -93,6 +97,7 @@ core.register_entity(":__builtin:item", {
|
|||
self.object:set_armor_groups({immortal = 1})
|
||||
self.object:set_velocity({x = 0, y = 2, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = -gravity, z = 0})
|
||||
self._collisionbox = self.initial_properties.collisionbox
|
||||
self:set_item()
|
||||
end,
|
||||
|
||||
|
@ -163,7 +168,7 @@ core.register_entity(":__builtin:item", {
|
|||
local pos = self.object:get_pos()
|
||||
local node = core.get_node_or_nil({
|
||||
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
|
||||
})
|
||||
-- Delete in 'ignore' nodes
|
||||
|
@ -176,7 +181,7 @@ core.register_entity(":__builtin:item", {
|
|||
if self.force_out then
|
||||
-- This code runs after the entity got a push from the is_stuck code.
|
||||
-- It makes sure the entity is entirely outside the solid node
|
||||
local c = self.object:get_properties().collisionbox
|
||||
local c = self._collisionbox
|
||||
local s = self.force_out_start
|
||||
local f = self.force_out
|
||||
local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or
|
||||
|
|
|
@ -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
|
||||
--
|
||||
|
||||
-- @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, ...)
|
||||
if core.is_player(name) then
|
||||
name = name:get_player_name()
|
||||
|
@ -111,53 +121,6 @@ function core.get_player_radius_area(player_name, radius)
|
|||
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
|
||||
|
||||
function core.is_protected(pos, name)
|
||||
|
@ -240,7 +203,7 @@ end
|
|||
|
||||
-- HTTP callback interface
|
||||
|
||||
function core.http_add_fetch(httpenv)
|
||||
core.set_http_api_lua(function(httpenv)
|
||||
httpenv.fetch = function(req, callback)
|
||||
local handle = httpenv.fetch_async(req)
|
||||
|
||||
|
@ -256,7 +219,8 @@ function core.http_add_fetch(httpenv)
|
|||
end
|
||||
|
||||
return httpenv
|
||||
end
|
||||
end)
|
||||
core.set_http_api_lua = nil
|
||||
|
||||
|
||||
function core.close_formspec(player_name, formname)
|
||||
|
@ -273,40 +237,30 @@ end
|
|||
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
|
||||
function core.encode_png(width, height, data, compression)
|
||||
if type(width) ~= "number" then
|
||||
error("Incorrect type for 'width', expected number, got " .. type(width))
|
||||
local function copy_filtering(t, seen)
|
||||
if type(t) == "userdata" or type(t) == "function" then
|
||||
return true -- don't use nil so presence can still be detected
|
||||
elseif type(t) ~= "table" then
|
||||
return t
|
||||
end
|
||||
if type(height) ~= "number" then
|
||||
error("Incorrect type for 'height', expected number, got " .. type(height))
|
||||
local n = {}
|
||||
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
|
||||
|
||||
local expected_byte_count = width * height * 4
|
||||
|
||||
if type(data) ~= "table" and type(data) ~= "string" then
|
||||
error("Incorrect type for 'height', expected table or string, got " .. type(height))
|
||||
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)
|
||||
return n
|
||||
end
|
||||
|
||||
function core.get_globals_to_transfer()
|
||||
local all = {
|
||||
registered_items = copy_filtering(core.registered_items),
|
||||
registered_aliases = core.registered_aliases,
|
||||
}
|
||||
return all
|
||||
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"),
|
||||
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", {
|
||||
description = S("Can enable wireframe"),
|
||||
give_to_singleplayer = false,
|
||||
|
|
|
@ -403,8 +403,14 @@ function core.override_item(name, redefinition)
|
|||
register_item_raw(item)
|
||||
end
|
||||
|
||||
|
||||
core.callback_origins = {}
|
||||
do
|
||||
local default = {mod = "??", name = "??"}
|
||||
core.callback_origins = setmetatable({}, {
|
||||
__index = function()
|
||||
return default
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function core.run_callbacks(callbacks, mode, ...)
|
||||
assert(type(callbacks) == "table")
|
||||
|
@ -419,9 +425,7 @@ function core.run_callbacks(callbacks, mode, ...)
|
|||
local ret = nil
|
||||
for i = 1, cb_len do
|
||||
local origin = core.callback_origins[callbacks[i]]
|
||||
if origin then
|
||||
core.set_last_run_mod(origin.mod)
|
||||
end
|
||||
core.set_last_run_mod(origin.mod)
|
||||
local cb_ret = callbacks[i](...)
|
||||
|
||||
if mode == 0 and i == 1 then
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
-- cache setting
|
||||
local enable_damage = core.settings:get_bool("enable_damage")
|
||||
|
||||
local health_bar_definition = {
|
||||
hud_elem_type = "statbar",
|
||||
position = {x = 0.5, y = 1},
|
||||
text = "heart.png",
|
||||
text2 = "heart_gone.png",
|
||||
number = core.PLAYER_MAX_HP_DEFAULT,
|
||||
item = core.PLAYER_MAX_HP_DEFAULT,
|
||||
direction = 0,
|
||||
size = {x = 24, y = 24},
|
||||
offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)},
|
||||
}
|
||||
|
||||
local breath_bar_definition = {
|
||||
hud_elem_type = "statbar",
|
||||
position = {x = 0.5, y = 1},
|
||||
text = "bubble.png",
|
||||
text2 = "bubble_gone.png",
|
||||
number = core.PLAYER_MAX_BREATH_DEFAULT,
|
||||
item = core.PLAYER_MAX_BREATH_DEFAULT * 2,
|
||||
direction = 0,
|
||||
size = {x = 24, y = 24},
|
||||
offset = {x = 25, y= -(48 + 24 + 16)},
|
||||
local bar_definitions = {
|
||||
hp = {
|
||||
hud_elem_type = "statbar",
|
||||
position = {x = 0.5, y = 1},
|
||||
text = "heart.png",
|
||||
text2 = "heart_gone.png",
|
||||
number = core.PLAYER_MAX_HP_DEFAULT,
|
||||
item = core.PLAYER_MAX_HP_DEFAULT,
|
||||
direction = 0,
|
||||
size = {x = 24, y = 24},
|
||||
offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)},
|
||||
},
|
||||
breath = {
|
||||
hud_elem_type = "statbar",
|
||||
position = {x = 0.5, y = 1},
|
||||
text = "bubble.png",
|
||||
text2 = "bubble_gone.png",
|
||||
number = core.PLAYER_MAX_BREATH_DEFAULT * 2,
|
||||
item = core.PLAYER_MAX_BREATH_DEFAULT * 2,
|
||||
direction = 0,
|
||||
size = {x = 24, y = 24},
|
||||
offset = {x = 25, y= -(48 + 24 + 16)},
|
||||
},
|
||||
}
|
||||
|
||||
local hud_ids = {}
|
||||
|
||||
local function scaleToDefault(player, field)
|
||||
-- Scale "hp" or "breath" to the default dimensions
|
||||
local function scaleToHudMax(player, field)
|
||||
-- Scale "hp" or "breath" to the hud maximum dimensions
|
||||
local current = player["get_" .. field](player)
|
||||
local nominal = core["PLAYER_MAX_" .. field:upper() .. "_DEFAULT"]
|
||||
local max_display = math.max(nominal,
|
||||
math.max(player:get_properties()[field .. "_max"], current))
|
||||
return current / max_display * nominal
|
||||
local nominal = bar_definitions[field].item
|
||||
local max_display = math.max(player:get_properties()[field .. "_max"], current)
|
||||
return math.ceil(current / max_display * nominal)
|
||||
end
|
||||
|
||||
local function update_builtin_statbars(player)
|
||||
|
@ -55,9 +55,9 @@ local function update_builtin_statbars(player)
|
|||
local immortal = player:get_armor_groups().immortal == 1
|
||||
|
||||
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
|
||||
local hud_def = table.copy(health_bar_definition)
|
||||
local hud_def = table.copy(bar_definitions.hp)
|
||||
hud_def.number = number
|
||||
hud.id_healthbar = player:hud_add(hud_def)
|
||||
else
|
||||
|
@ -73,9 +73,9 @@ local function update_builtin_statbars(player)
|
|||
local breath = player:get_breath()
|
||||
local breath_max = player:get_properties().breath_max
|
||||
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
|
||||
local hud_def = table.copy(breath_bar_definition)
|
||||
local hud_def = table.copy(bar_definitions.breath)
|
||||
hud_def.number = number
|
||||
hud.id_breathbar = player:hud_add(hud_def)
|
||||
elseif hud.id_breathbar then
|
||||
|
@ -145,7 +145,7 @@ function core.hud_replace_builtin(hud_name, definition)
|
|||
end
|
||||
|
||||
if hud_name == "health" then
|
||||
health_bar_definition = definition
|
||||
bar_definitions.hp = definition
|
||||
|
||||
for name, ids in pairs(hud_ids) do
|
||||
local player = core.get_player_by_name(name)
|
||||
|
@ -159,7 +159,7 @@ function core.hud_replace_builtin(hud_name, definition)
|
|||
end
|
||||
|
||||
if hud_name == "breath" then
|
||||
breath_bar_definition = definition
|
||||
bar_definitions.breath = definition
|
||||
|
||||
for name, ids in pairs(hud_ids) do
|
||||
local player = core.get_player_by_name(name)
|
||||
|
|
|
@ -55,8 +55,10 @@ elseif INIT == "mainmenu" then
|
|||
if not custom_loaded then
|
||||
dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua")
|
||||
end
|
||||
elseif INIT == "async" then
|
||||
dofile(asyncpath .. "init.lua")
|
||||
elseif INIT == "async" then
|
||||
dofile(asyncpath .. "mainmenu.lua")
|
||||
elseif INIT == "async_game" then
|
||||
dofile(asyncpath .. "game.lua")
|
||||
elseif INIT == "client" then
|
||||
dofile(clientpath .. "init.lua")
|
||||
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
|
||||
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).
|
||||
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.
|
||||
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).
|
||||
|
@ -187,12 +187,14 @@ You are already dead.=Sie sind schon tot.
|
|||
@1 is already dead.=@1 ist bereits tot.
|
||||
@1 has been killed.=@1 wurde getötet.
|
||||
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
|
||||
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:
|
||||
Command not available: @1=Befehl nicht verfügbar: @1
|
||||
[all | privs | <cmd>]=[all | privs | <Befehl>]
|
||||
Get help for commands or list privileges=Hilfe für Befehle erhalten oder Privilegien auflisten
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <Befehl>] [-t]
|
||||
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:
|
||||
Command=Befehl
|
||||
Parameters=Parameter
|
||||
|
@ -230,7 +232,8 @@ Can use fly mode=Kann den Flugmodus 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 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
|
||||
Air=Luft
|
||||
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
|
||||
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)
|
||||
Invalid time.=Orario non valido.
|
||||
Invalid time (must be between 0 and 24000).=
|
||||
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 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 has been killed.=@1 è stato uccisǝ.
|
||||
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
|
||||
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:
|
||||
Command not available: @1=Comando non disponibile: @1
|
||||
[all | privs | <cmd>]=[all | privs | <comando>]
|
||||
Get help for commands or list privileges=Richiama la finestra d'aiuto dei comandi o dei privilegi
|
||||
[all | privs | <cmd>] [-t]=
|
||||
Get help for commands or list privileges (-t: output in chat)=
|
||||
Available privileges:=Privilegi disponibili:
|
||||
Command=Comando
|
||||
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 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
|
||||
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
|
||||
Air=Aria
|
||||
Ignore=Ignora
|
||||
|
@ -244,6 +247,10 @@ Profile saved to @1=
|
|||
|
||||
##### 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>]
|
||||
Shutdown server (-1 cancels a delayed shutdown)=Arresta il server (-1 annulla un arresto programmato)
|
||||
<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=
|
||||
Current time is @1:@2.=
|
||||
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.=
|
||||
Invalid hour (must be between 0 and 23 inclusive).=
|
||||
Invalid minute (must be between 0 and 59 inclusive).=
|
||||
|
@ -187,12 +187,14 @@ You are already dead.=
|
|||
@1 is already dead.=
|
||||
@1 has been killed.=
|
||||
Kill player or yourself=
|
||||
Invalid parameters (see /help @1).=
|
||||
Too many arguments, try using just /help <command>=
|
||||
Available commands: @1=
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=
|
||||
Available commands:=
|
||||
Command not available: @1=
|
||||
[all | privs | <cmd>]=
|
||||
Get help for commands or list privileges=
|
||||
[all | privs | <cmd>] [-t]=
|
||||
Get help for commands or list privileges (-t: output in chat)=
|
||||
Available privileges:=
|
||||
Command=
|
||||
Parameters=
|
||||
|
@ -230,7 +232,8 @@ Can use fly mode=
|
|||
Can use fast mode=
|
||||
Can fly through solid nodes using noclip mode=
|
||||
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=
|
||||
Air=
|
||||
Ignore=
|
||||
|
|
|
@ -119,31 +119,27 @@ function render_serverlist_row(spec)
|
|||
|
||||
return table.concat(details, ",")
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
os.tempfolder = function()
|
||||
local temp = core.get_temp_path()
|
||||
return temp .. DIR_DELIM .. "MT_" .. math.random(0, 10000)
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------------
|
||||
os.tmpname = function()
|
||||
local path = os.tempfolder()
|
||||
io.open(path, "w"):close()
|
||||
return path
|
||||
error('do not use') -- instead use core.get_temp_path()
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function menu_render_worldlist()
|
||||
local retval = ""
|
||||
function menu_render_worldlist(show_gameid)
|
||||
local retval = {}
|
||||
local current_worldlist = menudata.worldlist:get_list()
|
||||
|
||||
local row
|
||||
for i, v in ipairs(current_worldlist) do
|
||||
if retval ~= "" then retval = retval .. "," end
|
||||
retval = retval .. core.formspec_escape(v.name) ..
|
||||
" \\[" .. core.formspec_escape(v.gameid) .. "\\]"
|
||||
row = v.name
|
||||
if show_gameid == nil or show_gameid == true then
|
||||
row = row .. " [" .. v.gameid .. "]"
|
||||
end
|
||||
retval[#retval+1] = core.formspec_escape(row)
|
||||
|
||||
end
|
||||
|
||||
return retval
|
||||
return table.concat(retval, ",")
|
||||
end
|
||||
|
||||
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;" ..
|
||||
fgettext("Enable all") .. "]"
|
||||
end
|
||||
|
||||
local use_technical_names = core.settings:get_bool("show_technical_names")
|
||||
|
||||
return retval ..
|
||||
"tablecolumns[color;tree;text]" ..
|
||||
"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
|
||||
|
||||
local function handle_buttons(this, fields)
|
||||
|
@ -205,14 +208,19 @@ local function handle_buttons(this, fields)
|
|||
local mods = worldfile:to_table()
|
||||
|
||||
local rawlist = this.data.list:get_raw_list()
|
||||
local was_set = {}
|
||||
|
||||
for i = 1, #rawlist do
|
||||
local mod = rawlist[i]
|
||||
if not mod.is_modpack and
|
||||
not mod.is_game_content then
|
||||
if modname_valid(mod.name) then
|
||||
worldfile:set("load_mod_" .. mod.name,
|
||||
mod.enabled and "true" or "false")
|
||||
if mod.enabled then
|
||||
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
|
||||
gamedata.errormessage = fgettext_ne("Failed to enable mo" ..
|
||||
"d \"$1\" as it contains disallowed characters. " ..
|
||||
|
@ -256,12 +264,26 @@ local function handle_buttons(this, fields)
|
|||
if fields.btn_enable_all_mods then
|
||||
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
|
||||
if not list[i].is_game_content
|
||||
and not list[i].is_modpack then
|
||||
list[i].enabled = true
|
||||
and not list[i].is_modpack and list[i].enabled then
|
||||
was_enabled[list[i].name] = true
|
||||
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
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -25,7 +25,7 @@ end
|
|||
|
||||
-- Unordered preserves the original order of the ContentDB API,
|
||||
-- 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()
|
||||
|
||||
|
@ -62,9 +62,19 @@ local REASON_UPDATE = "update"
|
|||
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 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
|
||||
ret = ret .. "?reason=" .. reason
|
||||
end
|
||||
|
@ -72,34 +82,52 @@ local function get_download_url(package, reason)
|
|||
end
|
||||
|
||||
|
||||
local function download_package(param)
|
||||
if core.download_file(param.url, param.filename) then
|
||||
local function download_and_extract(param)
|
||||
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 {
|
||||
filename = param.filename,
|
||||
successful = true,
|
||||
}
|
||||
else
|
||||
core.log("error", "downloading " .. dump(param.url) .. " failed")
|
||||
return {
|
||||
successful = false,
|
||||
msg = fgettext("Failed to download $1", package.name)
|
||||
}
|
||||
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
|
||||
|
||||
local function start_install(package, reason)
|
||||
local params = {
|
||||
package = package,
|
||||
url = get_download_url(package, reason),
|
||||
filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip",
|
||||
}
|
||||
|
||||
number_downloading = number_downloading + 1
|
||||
|
||||
local function callback(result)
|
||||
if result.successful then
|
||||
local path, msg = pkgmgr.install(package.type,
|
||||
result.filename, package.name,
|
||||
package.path)
|
||||
if result.msg then
|
||||
gamedata.errormessage = result.msg
|
||||
else
|
||||
local path, msg = pkgmgr.install_dir(package.type, result.path, package.name, package.path)
|
||||
core.delete_dir(result.path)
|
||||
if not path then
|
||||
gamedata.errormessage = msg
|
||||
else
|
||||
|
@ -137,9 +165,6 @@ local function start_install(package, reason)
|
|||
conf:write()
|
||||
end
|
||||
end
|
||||
os.remove(result.filename)
|
||||
else
|
||||
gamedata.errormessage = fgettext("Failed to download $1", package.name)
|
||||
end
|
||||
|
||||
package.downloading = false
|
||||
|
@ -159,7 +184,7 @@ local function start_install(package, reason)
|
|||
package.queued = false
|
||||
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")
|
||||
gamedata.errormessage = fgettext("Failed to download $1", package.name)
|
||||
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 version = core.get_version()
|
||||
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 })
|
||||
if not response.succeeded then
|
||||
|
@ -559,17 +584,16 @@ function store.load()
|
|||
local base_url = core.settings:get("contentdb_url")
|
||||
local url = base_url ..
|
||||
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
||||
core.get_max_supp_proto() .. "&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
|
||||
item = item:trim()
|
||||
if item ~= "" then
|
||||
url = url .. "&hide=" .. item
|
||||
url = url .. "&hide=" .. urlencode(item)
|
||||
end
|
||||
end
|
||||
|
||||
local timeout = tonumber(core.settings:get("curl_file_download_timeout"))
|
||||
local response = http.fetch_sync({ url = url, timeout = timeout })
|
||||
local response = http.fetch_sync({ url = url })
|
||||
if not response.succeeded then
|
||||
return
|
||||
end
|
||||
|
@ -579,12 +603,16 @@ function store.load()
|
|||
|
||||
for _, package in pairs(store.packages_full) do
|
||||
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
|
||||
package.id = package.author:lower() .. "/" .. package.name:sub(1, name_len - 5)
|
||||
package.id = package.id .. package.name:sub(1, name_len - 5)
|
||||
else
|
||||
package.id = package.author:lower() .. "/" .. package.name
|
||||
package.id = package.id .. package.name
|
||||
end
|
||||
|
||||
package.url_part = urlencode(package.author) .. "/" .. urlencode(package.name)
|
||||
|
||||
if package.aliases then
|
||||
for _, alias in ipairs(package.aliases) do
|
||||
-- We currently don't support name changing
|
||||
|
@ -834,8 +862,7 @@ function store.get_formspec(dlgdata)
|
|||
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
|
||||
elseif package.queued then
|
||||
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
|
||||
local elem_name = "install_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
|
||||
|
@ -998,9 +1025,9 @@ function store.handle_submit(this, fields)
|
|||
end
|
||||
|
||||
if fields["view_" .. i] then
|
||||
local url = ("%s/packages/%s/%s?protocol_version=%d"):format(
|
||||
core.settings:get("contentdb_url"),
|
||||
package.author, package.name, core.get_max_supp_proto())
|
||||
local url = ("%s/packages/%s?protocol_version=%d"):format(
|
||||
core.settings:get("contentdb_url"), package.url_part,
|
||||
core.get_max_supp_proto())
|
||||
core.open_url(url)
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--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)
|
||||
-- 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"
|
||||
end
|
||||
|
||||
local cb_caverns = { "caverns", fgettext("Caverns"), "caverns",
|
||||
local cb_caverns = { "caverns", fgettext("Caverns"),
|
||||
fgettext("Very large caverns deep in the underground") }
|
||||
local tt_sea_rivers = fgettext("Sea level rivers")
|
||||
|
||||
local flag_checkboxes = {
|
||||
v5 = {
|
||||
|
@ -41,39 +41,38 @@ local flag_checkboxes = {
|
|||
},
|
||||
v7 = {
|
||||
cb_caverns,
|
||||
{ "ridges", fgettext("Rivers"), "ridges", tt_sea_rivers },
|
||||
{ "mountains", fgettext("Mountains"), "mountains" },
|
||||
{ "floatlands", fgettext("Floatlands (experimental)"), "floatlands",
|
||||
{ "ridges", fgettext("Rivers"), fgettext("Sea level rivers") },
|
||||
{ "mountains", fgettext("Mountains") },
|
||||
{ "floatlands", fgettext("Floatlands (experimental)"),
|
||||
fgettext("Floating landmasses in the sky") },
|
||||
},
|
||||
carpathian = {
|
||||
cb_caverns,
|
||||
{ "rivers", fgettext("Rivers"), "rivers", tt_sea_rivers },
|
||||
{ "rivers", fgettext("Rivers"), fgettext("Sea level rivers") },
|
||||
},
|
||||
valleys = {
|
||||
{ "altitude-chill", fgettext("Altitude chill"), "altitude_chill",
|
||||
{ "altitude_chill", fgettext("Altitude chill"),
|
||||
fgettext("Reduces heat with altitude") },
|
||||
{ "altitude-dry", fgettext("Altitude dry"), "altitude_dry",
|
||||
{ "altitude_dry", fgettext("Altitude dry"),
|
||||
fgettext("Reduces humidity with altitude") },
|
||||
{ "humid-rivers", fgettext("Humid rivers"), "humid_rivers",
|
||||
{ "humid_rivers", fgettext("Humid 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") },
|
||||
},
|
||||
flat = {
|
||||
cb_caverns,
|
||||
{ "hills", fgettext("Hills"), "hills" },
|
||||
{ "lakes", fgettext("Lakes"), "lakes" },
|
||||
{ "hills", fgettext("Hills") },
|
||||
{ "lakes", fgettext("Lakes") },
|
||||
},
|
||||
fractal = {
|
||||
{ "terrain", fgettext("Additional terrain"), "terrain",
|
||||
{ "terrain", fgettext("Additional terrain"),
|
||||
fgettext("Generate non-fractal terrain: Oceans and underground") },
|
||||
},
|
||||
v6 = {
|
||||
{ "trees", fgettext("Trees and jungle grass"), "trees" },
|
||||
{ "flat", fgettext("Flat terrain"), "flat" },
|
||||
{ "mudflow", fgettext("Mud flow"), "mudflow",
|
||||
fgettext("Terrain surface erosion") },
|
||||
{ "trees", fgettext("Trees and jungle grass") },
|
||||
{ "flat", fgettext("Flat terrain") },
|
||||
{ "mudflow", fgettext("Mud flow"), fgettext("Terrain surface erosion") },
|
||||
-- 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") .. "]"
|
||||
end
|
||||
|
||||
local current_mg = dialogdata.mg
|
||||
local mapgens = core.get_mapgen_names()
|
||||
|
||||
local current_seed = core.settings:get("fixed_map_seed") or ""
|
||||
local current_mg = core.settings:get("mg_name")
|
||||
local gameid = core.settings:get("menu_last_game")
|
||||
|
||||
local flags = {
|
||||
main = core.settings:get_flags("mg_flags"),
|
||||
v5 = core.settings:get_flags("mgv5_spflags"),
|
||||
v6 = core.settings:get_flags("mgv6_spflags"),
|
||||
v7 = core.settings:get_flags("mgv7_spflags"),
|
||||
fractal = core.settings:get_flags("mgfractal_spflags"),
|
||||
carpathian = core.settings:get_flags("mgcarpathian_spflags"),
|
||||
valleys = core.settings:get_flags("mgvalleys_spflags"),
|
||||
flat = core.settings:get_flags("mgflat_spflags"),
|
||||
}
|
||||
local flags = dialogdata.flags
|
||||
|
||||
local gameidx = 0
|
||||
if gameid ~= nil then
|
||||
local _
|
||||
_, gameidx = pkgmgr.find_by_gameid(gameid)
|
||||
|
||||
if gameidx == nil then
|
||||
gameidx = 0
|
||||
end
|
||||
local game, gameidx = pkgmgr.find_by_gameid(gameid)
|
||||
if game == nil and hide_gamelist then
|
||||
-- should never happen but just pick the first game
|
||||
game = pkgmgr.get_game(1)
|
||||
gameidx = 1
|
||||
core.settings:set("menu_last_game", game.id)
|
||||
elseif game == nil then
|
||||
gameidx = 0
|
||||
end
|
||||
|
||||
local game_by_gameidx = core.get_game(gameidx)
|
||||
local disallowed_mapgen_settings = {}
|
||||
if game_by_gameidx ~= nil then
|
||||
local gamepath = game_by_gameidx.path
|
||||
local gameconfig = Settings(gamepath.."/game.conf")
|
||||
if game ~= nil then
|
||||
local gameconfig = Settings(game.path.."/game.conf")
|
||||
|
||||
local allowed_mapgens = (gameconfig:get("allowed_mapgens") or ""):split()
|
||||
for key, value in pairs(allowed_mapgens) do
|
||||
|
@ -156,7 +143,7 @@ local function create_world_formspec(dialogdata)
|
|||
end
|
||||
end
|
||||
|
||||
if disallowed_mapgens then
|
||||
if #disallowed_mapgens > 0 then
|
||||
for i = #mapgens, 1, -1 do
|
||||
if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then
|
||||
table.remove(mapgens, i)
|
||||
|
@ -172,23 +159,29 @@ local function create_world_formspec(dialogdata)
|
|||
|
||||
local mglist = ""
|
||||
local selindex
|
||||
local i = 1
|
||||
local first_mg
|
||||
for k,v in pairs(mapgens) do
|
||||
if not first_mg then
|
||||
first_mg = v
|
||||
do -- build the list of mapgens
|
||||
local i = 1
|
||||
local first_mg
|
||||
for k, v in pairs(mapgens) do
|
||||
if not first_mg then
|
||||
first_mg = v
|
||||
end
|
||||
if current_mg == v then
|
||||
selindex = i
|
||||
end
|
||||
i = i + 1
|
||||
mglist = mglist .. core.formspec_escape(v) .. ","
|
||||
end
|
||||
if current_mg == v then
|
||||
selindex = i
|
||||
if not selindex then
|
||||
selindex = 1
|
||||
current_mg = first_mg
|
||||
end
|
||||
i = i + 1
|
||||
mglist = mglist .. v .. ","
|
||||
mglist = mglist:sub(1, -2)
|
||||
end
|
||||
if not selindex then
|
||||
selindex = 1
|
||||
current_mg = first_mg
|
||||
end
|
||||
mglist = mglist:sub(1, -2)
|
||||
|
||||
-- The logic of the flag element IDs is as follows:
|
||||
-- "flag_main_foo-bar-baz" controls dialogdata.flags["main"]["foo_bar_baz"]
|
||||
-- see the buttonhandler for the implementation of this
|
||||
|
||||
local mg_main_flags = function(mapgen, y)
|
||||
if mapgen == "singlenode" then
|
||||
|
@ -198,11 +191,11 @@ local function create_world_formspec(dialogdata)
|
|||
return "", y
|
||||
end
|
||||
|
||||
local form = "checkbox[0," .. y .. ";flag_mg_caves;" ..
|
||||
local form = "checkbox[0," .. y .. ";flag_main_caves;" ..
|
||||
fgettext("Caves") .. ";"..strflag(flags.main, "caves").."]"
|
||||
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").."]"
|
||||
y = y + 0.5
|
||||
|
||||
|
@ -213,7 +206,7 @@ local function create_world_formspec(dialogdata)
|
|||
else
|
||||
d_tt = fgettext("Structures appearing on the terrain, typically trees and plants")
|
||||
end
|
||||
form = form .. "checkbox[0,"..y..";flag_mg_decorations;" ..
|
||||
form = form .. "checkbox[0,"..y..";flag_main_decorations;" ..
|
||||
d_name .. ";" ..
|
||||
strflag(flags.main, "decorations").."]" ..
|
||||
"tooltip[flag_mg_decorations;" ..
|
||||
|
@ -221,7 +214,7 @@ local function create_world_formspec(dialogdata)
|
|||
"]"
|
||||
y = y + 0.5
|
||||
|
||||
form = form .. "tooltip[flag_mg_caves;" ..
|
||||
form = form .. "tooltip[flag_main_caves;" ..
|
||||
fgettext("Network of tunnels and caves")
|
||||
.. "]"
|
||||
return form, y
|
||||
|
@ -235,13 +228,13 @@ local function create_world_formspec(dialogdata)
|
|||
return "", y
|
||||
end
|
||||
local form = ""
|
||||
for _,tab in pairs(flag_checkboxes[mapgen]) do
|
||||
local id = "flag_mg"..mapgen.."_"..tab[1]
|
||||
for _, tab in pairs(flag_checkboxes[mapgen]) do
|
||||
local id = "flag_"..mapgen.."_"..tab[1]:gsub("_", "-")
|
||||
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
|
||||
form = form .. "tooltip["..id..";"..tab[4].."]"
|
||||
if tab[3] then
|
||||
form = form .. "tooltip["..id..";"..tab[3].."]"
|
||||
end
|
||||
y = y + 0.5
|
||||
end
|
||||
|
@ -277,16 +270,14 @@ local function create_world_formspec(dialogdata)
|
|||
|
||||
-- biomeblend
|
||||
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").."]" ..
|
||||
"tooltip[flag_mgv6_biomeblend;" ..
|
||||
"tooltip[flag_v6_biomeblend;" ..
|
||||
fgettext("Smooth transition between biomes") .. "]"
|
||||
|
||||
return form, y
|
||||
end
|
||||
|
||||
current_seed = core.formspec_escape(current_seed)
|
||||
|
||||
local y_start = 0.0
|
||||
local y = y_start
|
||||
local str_flags, str_spflags
|
||||
|
@ -323,21 +314,32 @@ local function create_world_formspec(dialogdata)
|
|||
"container[0,0]"..
|
||||
"field[0.3,0.6;6,0.5;te_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;" ..
|
||||
fgettext("Seed") ..
|
||||
";".. current_seed .. "]" ..
|
||||
if not disallowed_mapgen_settings["seed"] then
|
||||
|
||||
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") .. "]"..
|
||||
"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") .. "]"..
|
||||
"textlist[0,3.85;5.8,"..gamelist_height..";games;" ..
|
||||
pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" ..
|
||||
"container[0,4.5]" ..
|
||||
devtest_only ..
|
||||
"container_end[]" ..
|
||||
if not hide_gamelist or devtest_only ~= "" then
|
||||
retval = retval ..
|
||||
"label[0,3.35;" .. fgettext("Game") .. "]"..
|
||||
"textlist[0,3.85;5.8,"..gamelist_height..";games;" ..
|
||||
pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" ..
|
||||
"container[0,4.5]" ..
|
||||
devtest_only ..
|
||||
"container_end[]"
|
||||
end
|
||||
|
||||
retval = retval ..
|
||||
"container_end[]" ..
|
||||
|
||||
-- Right side
|
||||
|
@ -360,9 +362,20 @@ local function create_world_buttonhandler(this, fields)
|
|||
fields["key_enter"] then
|
||||
|
||||
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>',
|
||||
-- where the number increments: it is set to 1 larger than the largest
|
||||
-- generated name number found.
|
||||
|
@ -377,36 +390,48 @@ local function create_world_buttonhandler(this, fields)
|
|||
worldname = "world" .. worldnum_max + 1
|
||||
end
|
||||
|
||||
core.settings:set("fixed_map_seed", fields["te_seed"])
|
||||
|
||||
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
|
||||
if menudata.worldlist:uid_exists_raw(worldname) then
|
||||
message = fgettext("A world named \"$1\" already exists", worldname)
|
||||
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
|
||||
|
||||
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()
|
||||
return true
|
||||
end
|
||||
|
||||
worldname = fields.te_world_name
|
||||
this.data.worldname = fields["te_world_name"]
|
||||
this.data.seed = fields["te_seed"] or ""
|
||||
|
||||
if fields["games"] then
|
||||
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
|
||||
local split = string.split(k, "_", nil, 3)
|
||||
if split and split[1] == "flag" then
|
||||
local setting
|
||||
if split[2] == "mg" then
|
||||
setting = "mg_flags"
|
||||
else
|
||||
setting = split[2].."_spflags"
|
||||
end
|
||||
-- We replaced the underscore of flag names with a dash.
|
||||
local flag = string.gsub(split[3], "-", "_")
|
||||
local ftable = core.settings:get_flags(setting)
|
||||
if v == "true" then
|
||||
ftable[flag] = true
|
||||
else
|
||||
ftable[flag] = false
|
||||
end
|
||||
local flags = table_to_flags(ftable)
|
||||
core.settings:set(setting, flags)
|
||||
local ftable = this.data.flags[split[2]]
|
||||
assert(ftable)
|
||||
ftable[flag] = v == "true"
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
@ -446,18 +460,16 @@ local function create_world_buttonhandler(this, fields)
|
|||
local entry = core.formspec_escape(fields["mgv6_biomes"])
|
||||
for b=1, #mgv6_biomes do
|
||||
if entry == mgv6_biomes[b][1] then
|
||||
local ftable = core.settings:get_flags("mgv6_spflags")
|
||||
local ftable = this.data.flags.v6
|
||||
ftable.jungles = mgv6_biomes[b][2].jungles
|
||||
ftable.snowbiomes = mgv6_biomes[b][2].snowbiomes
|
||||
local flags = table_to_flags(ftable)
|
||||
core.settings:set("mgv6_spflags", flags)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if fields["dd_mapgen"] then
|
||||
core.settings:set("mg_name", fields["dd_mapgen"])
|
||||
this.data.mg = fields["dd_mapgen"]
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -466,12 +478,27 @@ end
|
|||
|
||||
|
||||
function create_create_world_dlg(update_worldlistfilter)
|
||||
worldname = ""
|
||||
local retval = dialog_create("sp_create_world",
|
||||
create_world_formspec,
|
||||
create_world_buttonhandler,
|
||||
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
|
||||
end
|
||||
|
|
|
@ -378,7 +378,7 @@ local function parse_config_file(read_all, parse_mods)
|
|||
-- Parse mods
|
||||
local mods_category_initialized = false
|
||||
local mods = {}
|
||||
get_mods(core.get_modpath(), mods)
|
||||
get_mods(core.get_modpath(), "mods", mods)
|
||||
for _, mod in ipairs(mods) do
|
||||
local path = mod.path .. DIR_DELIM .. FILENAME
|
||||
local file = io.open(path, "r")
|
||||
|
@ -395,6 +395,7 @@ local function parse_config_file(read_all, parse_mods)
|
|||
|
||||
table.insert(settings, {
|
||||
name = mod.name,
|
||||
readable_name = mod.title,
|
||||
level = 1,
|
||||
type = "category",
|
||||
})
|
||||
|
@ -408,7 +409,7 @@ local function parse_config_file(read_all, parse_mods)
|
|||
-- Parse clientmods
|
||||
local clientmods_category_initialized = false
|
||||
local clientmods = {}
|
||||
get_mods(core.get_clientmodpath(), clientmods)
|
||||
get_mods(core.get_clientmodpath(), "clientmods", clientmods)
|
||||
for _, clientmod in ipairs(clientmods) do
|
||||
local path = clientmod.path .. DIR_DELIM .. FILENAME
|
||||
local file = io.open(path, "r")
|
||||
|
@ -527,44 +528,40 @@ end
|
|||
|
||||
local function get_current_np_group(setting)
|
||||
local value = core.settings:get_np_group(setting.name)
|
||||
local t = {}
|
||||
if value == nil then
|
||||
t = 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)
|
||||
return setting.values
|
||||
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
|
||||
|
||||
local function get_current_np_group_as_string(setting)
|
||||
local value = core.settings:get_np_group(setting.name)
|
||||
local t
|
||||
if value == nil then
|
||||
t = 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
|
||||
return setting.default
|
||||
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
|
||||
|
||||
local checkboxes = {} -- handle checkboxes events
|
||||
|
@ -697,7 +694,7 @@ local function create_change_setting_formspec(dialogdata)
|
|||
elseif setting.type == "v3f" then
|
||||
local val = get_current_value(setting)
|
||||
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)
|
||||
end
|
||||
|
||||
|
@ -990,7 +987,7 @@ local function create_settings_formspec(tabview, _, tabdata)
|
|||
local current_level = 0
|
||||
for _, entry in ipairs(settings) do
|
||||
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)
|
||||
else
|
||||
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[7,4.9;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
|
||||
"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
|
||||
end
|
||||
|
@ -1114,7 +1111,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
|
|||
end
|
||||
|
||||
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.update_formspec(this:get_formspec())
|
||||
return true
|
||||
|
|
|
@ -16,23 +16,25 @@
|
|||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
mm_texture = {}
|
||||
mm_game_theme = {}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_texture.init()
|
||||
mm_texture.defaulttexturedir = core.get_texturepath() .. DIR_DELIM .. "base" ..
|
||||
function mm_game_theme.init()
|
||||
mm_game_theme.defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
|
||||
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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_texture.update(tab,gamedetails)
|
||||
function mm_game_theme.update(tab,gamedetails)
|
||||
if tab ~= "singleplayer" then
|
||||
mm_texture.reset()
|
||||
mm_game_theme.reset()
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -40,50 +42,54 @@ function mm_texture.update(tab,gamedetails)
|
|||
return
|
||||
end
|
||||
|
||||
mm_texture.update_game(gamedetails)
|
||||
mm_game_theme.update_game(gamedetails)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_texture.reset()
|
||||
mm_texture.gameid = nil
|
||||
function mm_game_theme.reset()
|
||||
mm_game_theme.gameid = nil
|
||||
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
|
||||
have_bg = mm_texture.set_generic("background")
|
||||
have_bg = mm_game_theme.set_generic("background")
|
||||
end
|
||||
|
||||
mm_texture.clear("header")
|
||||
mm_texture.clear("footer")
|
||||
mm_game_theme.clear("header")
|
||||
mm_game_theme.clear("footer")
|
||||
core.set_clouds(false)
|
||||
|
||||
mm_texture.set_generic("footer")
|
||||
mm_texture.set_generic("header")
|
||||
mm_game_theme.set_generic("footer")
|
||||
mm_game_theme.set_generic("header")
|
||||
|
||||
if not have_bg then
|
||||
if core.settings:get_bool("menu_clouds") then
|
||||
core.set_clouds(true)
|
||||
else
|
||||
mm_texture.set_dirt_bg()
|
||||
mm_game_theme.set_dirt_bg()
|
||||
end
|
||||
end
|
||||
|
||||
if mm_game_theme.music_handle ~= nil then
|
||||
core.sound_stop(mm_game_theme.music_handle)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_texture.update_game(gamedetails)
|
||||
if mm_texture.gameid == gamedetails.id then
|
||||
function mm_game_theme.update_game(gamedetails)
|
||||
if mm_game_theme.gameid == gamedetails.id then
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
have_bg = mm_texture.set_game("background",gamedetails)
|
||||
have_bg = mm_game_theme.set_game("background",gamedetails)
|
||||
end
|
||||
|
||||
mm_texture.clear("header")
|
||||
mm_texture.clear("footer")
|
||||
mm_game_theme.clear("header")
|
||||
mm_game_theme.clear("footer")
|
||||
core.set_clouds(false)
|
||||
|
||||
if not have_bg then
|
||||
|
@ -91,34 +97,34 @@ function mm_texture.update_game(gamedetails)
|
|||
if core.settings:get_bool("menu_clouds") then
|
||||
core.set_clouds(true)
|
||||
else
|
||||
mm_texture.set_dirt_bg()
|
||||
mm_game_theme.set_dirt_bg()
|
||||
end
|
||||
end
|
||||
|
||||
mm_texture.set_game("footer",gamedetails)
|
||||
mm_texture.set_game("header",gamedetails)
|
||||
mm_game_theme.set_game("footer",gamedetails)
|
||||
mm_game_theme.set_game("header",gamedetails)
|
||||
|
||||
mm_texture.gameid = gamedetails.id
|
||||
mm_game_theme.gameid = gamedetails.id
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_texture.clear(identifier)
|
||||
function mm_game_theme.clear(identifier)
|
||||
core.set_background(identifier,"")
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_texture.set_generic(identifier)
|
||||
function mm_game_theme.set_generic(identifier)
|
||||
--try texture pack first
|
||||
if mm_texture.texturepack ~= nil then
|
||||
local path = mm_texture.texturepack .. DIR_DELIM .."menu_" ..
|
||||
if mm_game_theme.texturepack ~= nil then
|
||||
local path = mm_game_theme.texturepack .. DIR_DELIM .."menu_" ..
|
||||
identifier .. ".png"
|
||||
if core.set_background(identifier,path) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if mm_texture.defaulttexturedir ~= nil then
|
||||
local path = mm_texture.defaulttexturedir .. DIR_DELIM .."menu_" ..
|
||||
if mm_game_theme.defaulttexturedir ~= nil then
|
||||
local path = mm_game_theme.defaulttexturedir .. DIR_DELIM .."menu_" ..
|
||||
identifier .. ".png"
|
||||
if core.set_background(identifier,path) then
|
||||
return true
|
||||
|
@ -129,14 +135,16 @@ function mm_texture.set_generic(identifier)
|
|||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_texture.set_game(identifier, gamedetails)
|
||||
function mm_game_theme.set_game(identifier, gamedetails)
|
||||
|
||||
if gamedetails == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
if mm_texture.texturepack ~= nil then
|
||||
local path = mm_texture.texturepack .. DIR_DELIM ..
|
||||
mm_game_theme.set_music(gamedetails)
|
||||
|
||||
if mm_game_theme.texturepack ~= nil then
|
||||
local path = mm_game_theme.texturepack .. DIR_DELIM ..
|
||||
gamedetails.id .. "_menu_" .. identifier .. ".png"
|
||||
if core.set_background(identifier, path) then
|
||||
return true
|
||||
|
@ -171,9 +179,10 @@ function mm_texture.set_game(identifier, gamedetails)
|
|||
return false
|
||||
end
|
||||
|
||||
function mm_texture.set_dirt_bg()
|
||||
if mm_texture.texturepack ~= nil then
|
||||
local path = mm_texture.texturepack .. DIR_DELIM .."default_dirt.png"
|
||||
--------------------------------------------------------------------------------
|
||||
function mm_game_theme.set_dirt_bg()
|
||||
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
|
||||
return true
|
||||
end
|
||||
|
@ -183,3 +192,12 @@ function mm_texture.set_dirt_bg()
|
|||
local minimalpath = defaulttexturedir .. "menu_bg.png"
|
||||
core.set_background("background", minimalpath, true, 128)
|
||||
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,
|
||||
# persistence = %s,
|
||||
# lacunarity = %s,
|
||||
# flags = %s
|
||||
# flags =%s
|
||||
# }
|
||||
|
||||
]]
|
||||
|
@ -55,7 +55,11 @@ local function create_minetest_conf_example()
|
|||
end
|
||||
if entry.comment ~= "" then
|
||||
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
|
||||
insert(result, "# type: " .. entry.type)
|
||||
|
@ -73,10 +77,14 @@ local function create_minetest_conf_example()
|
|||
end
|
||||
insert(result, "\n")
|
||||
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],
|
||||
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[10]))
|
||||
flags))
|
||||
else
|
||||
local append
|
||||
if entry.default ~= "" then
|
||||
|
@ -91,7 +99,7 @@ end
|
|||
|
||||
local translation_file_header = [[
|
||||
// This file is automatically generated
|
||||
// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files
|
||||
// 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
|
||||
|
||||
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:write(create_translation_file())
|
||||
file:close()
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ dofile(menupath .. DIR_DELIM .. "async_event.lua")
|
|||
dofile(menupath .. DIR_DELIM .. "common.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "pkgmgr.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_settings_advanced.lua")
|
||||
|
@ -87,7 +87,7 @@ local function init_globals()
|
|||
core.settings:set("menu_last_game", default_game)
|
||||
end
|
||||
|
||||
mm_texture.init()
|
||||
mm_game_theme.init()
|
||||
|
||||
-- Create main tabview
|
||||
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
|
||||
local game = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
|
||||
if game == nil then
|
||||
mm_texture.reset()
|
||||
mm_game_theme.reset()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -121,8 +121,6 @@ local function init_globals()
|
|||
tv_main:show()
|
||||
|
||||
ui.update()
|
||||
|
||||
core.sound_play("main_menu", true)
|
||||
end
|
||||
|
||||
init_globals()
|
||||
|
|
|
@ -78,34 +78,35 @@ local function load_texture_packs(txtpath, retval)
|
|||
|
||||
for _, item in ipairs(list) do
|
||||
if item ~= "base" then
|
||||
local name = item
|
||||
|
||||
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 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] = {
|
||||
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"),
|
||||
release = tonumber(conf:get("release")) or 0,
|
||||
list_name = name,
|
||||
type = "txp",
|
||||
path = path,
|
||||
enabled = path == current_texture_path,
|
||||
enabled = enabled,
|
||||
}
|
||||
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)
|
||||
|
||||
for _, name in ipairs(mods) do
|
||||
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 = {
|
||||
dir_name = name,
|
||||
parent_dir = path,
|
||||
|
@ -114,18 +115,18 @@ function get_mods(path,retval,modpack)
|
|||
|
||||
-- Get config file
|
||||
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
|
||||
toadd.is_modpack = true
|
||||
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
|
||||
name = mod_conf.name
|
||||
toadd.is_name_explicit = true
|
||||
end
|
||||
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
|
||||
name = mod_conf.name
|
||||
toadd.is_name_explicit = true
|
||||
|
@ -134,14 +135,16 @@ function get_mods(path,retval,modpack)
|
|||
|
||||
-- Read from config
|
||||
toadd.name = name
|
||||
toadd.title = mod_conf.title
|
||||
toadd.author = mod_conf.author
|
||||
toadd.release = tonumber(mod_conf.release) or 0
|
||||
toadd.path = prefix
|
||||
toadd.path = mod_path
|
||||
toadd.virtual_path = mod_virtual_path
|
||||
toadd.type = "mod"
|
||||
|
||||
-- Check modpack.txt
|
||||
-- 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
|
||||
modpackfile:close()
|
||||
toadd.is_modpack = true
|
||||
|
@ -153,7 +156,7 @@ function get_mods(path,retval,modpack)
|
|||
elseif toadd.is_modpack then
|
||||
toadd.type = "modpack"
|
||||
toadd.is_modpack = true
|
||||
get_mods(prefix, retval, name)
|
||||
get_mods(mod_path, mod_virtual_path, retval, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -181,21 +184,6 @@ function pkgmgr.get_texture_packs()
|
|||
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)
|
||||
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
|
||||
if testfile ~= nil then
|
||||
|
@ -349,7 +337,7 @@ function pkgmgr.identify_modname(modpath,filename)
|
|||
return nil
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.render_packagelist(render_list)
|
||||
function pkgmgr.render_packagelist(render_list, use_technical_names)
|
||||
if not render_list then
|
||||
if not pkgmgr.global_mods then
|
||||
pkgmgr.refresh_globals()
|
||||
|
@ -385,7 +373,12 @@ function pkgmgr.render_packagelist(render_list)
|
|||
else
|
||||
retval[#retval + 1] = "0"
|
||||
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
|
||||
|
||||
return table.concat(retval, ",")
|
||||
|
@ -412,6 +405,14 @@ function pkgmgr.is_modpack_entirely_enabled(data, name)
|
|||
return true
|
||||
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 --------
|
||||
local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
|
||||
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
|
||||
end
|
||||
if mod.enabled ~= toset then
|
||||
mod.enabled = toset
|
||||
toggled_mods[#toggled_mods+1] = mod.name
|
||||
end
|
||||
if toset then
|
||||
-- Mark this mod for recursive dependency traversal
|
||||
enabled_mods[mod.name] = true
|
||||
|
||||
-- Disable other mods with the same name
|
||||
disable_all_by_name(list, mod.name, mod)
|
||||
end
|
||||
mod.enabled = toset
|
||||
else
|
||||
-- Toggle or en/disable every mod in the modpack,
|
||||
-- interleaved unsupported
|
||||
|
@ -451,7 +455,7 @@ function pkgmgr.enable_mod(this, toset)
|
|||
local enabled_mods = {}
|
||||
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
|
||||
table.sort(toggled_mods)
|
||||
core.log("info", "Following mods were disabled: " ..
|
||||
|
@ -461,11 +465,16 @@ function pkgmgr.enable_mod(this, toset)
|
|||
|
||||
-- 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 = {}
|
||||
for id, mod2 in pairs(list) do
|
||||
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
|
||||
|
||||
|
@ -482,6 +491,7 @@ function pkgmgr.enable_mod(this, toset)
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If sp is 0, every dependency is already activated
|
||||
while sp > 0 do
|
||||
local name = to_enable[sp]
|
||||
|
@ -494,14 +504,14 @@ function pkgmgr.enable_mod(this, toset)
|
|||
core.log("warning", "Mod dependency \"" .. name ..
|
||||
"\" not found!")
|
||||
else
|
||||
if mod_to_enable.enabled == false then
|
||||
if not mod_to_enable.enabled then
|
||||
mod_to_enable.enabled = true
|
||||
toggled_mods[#toggled_mods+1] = mod_to_enable.name
|
||||
end
|
||||
-- Push the dependencies of the dependency onto the stack
|
||||
local depends = pkgmgr.get_dependencies(mod_to_enable.path)
|
||||
for i = 1, #depends do
|
||||
if not enabled_mods[name] then
|
||||
if not enabled_mods[depends[i]] then
|
||||
sp = sp+1
|
||||
to_enable[sp] = depends[i]
|
||||
end
|
||||
|
@ -561,11 +571,10 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
|||
local from = basefolder and basefolder.path or path
|
||||
if targetpath then
|
||||
core.delete_dir(targetpath)
|
||||
core.create_dir(targetpath)
|
||||
else
|
||||
targetpath = core.get_texturepath() .. DIR_DELIM .. basename
|
||||
end
|
||||
if not core.copy_dir(from, targetpath) then
|
||||
if not core.copy_dir(from, targetpath, false) then
|
||||
return nil,
|
||||
fgettext("Failed to install $1 to $2", basename, targetpath)
|
||||
end
|
||||
|
@ -586,7 +595,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
|||
-- Get destination name for modpack
|
||||
if targetpath then
|
||||
core.delete_dir(targetpath)
|
||||
core.create_dir(targetpath)
|
||||
else
|
||||
local clean_path = nil
|
||||
if basename ~= nil then
|
||||
|
@ -610,7 +618,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
|||
|
||||
if targetpath then
|
||||
core.delete_dir(targetpath)
|
||||
core.create_dir(targetpath)
|
||||
else
|
||||
local targetfolder = basename
|
||||
if targetfolder == nil then
|
||||
|
@ -636,14 +643,13 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
|||
|
||||
if targetpath then
|
||||
core.delete_dir(targetpath)
|
||||
core.create_dir(targetpath)
|
||||
else
|
||||
targetpath = core.get_gamepath() .. DIR_DELIM .. basename
|
||||
end
|
||||
end
|
||||
|
||||
-- Copy it
|
||||
if not core.copy_dir(basefolder.path, targetpath) then
|
||||
if not core.copy_dir(basefolder.path, targetpath, false) then
|
||||
return nil,
|
||||
fgettext("Failed to install $1 to $2", basename, targetpath)
|
||||
end
|
||||
|
@ -657,23 +663,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
|
|||
return targetpath, nil
|
||||
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)
|
||||
local retval = {}
|
||||
|
@ -683,9 +672,8 @@ function pkgmgr.prepareclientmodlist(data)
|
|||
--read clientmods
|
||||
local modpath = core.get_clientmodpath()
|
||||
|
||||
if modpath ~= nil and
|
||||
modpath ~= "" then
|
||||
get_mods(modpath,clientmods)
|
||||
if modpath ~= nil and modpath ~= "" then
|
||||
get_mods(modpath, "clientmods", clientmods)
|
||||
end
|
||||
|
||||
for i=1,#clientmods,1 do
|
||||
|
@ -730,16 +718,15 @@ function pkgmgr.preparemodlist(data)
|
|||
local game_mods = {}
|
||||
|
||||
--read global mods
|
||||
local modpath = core.get_modpath()
|
||||
|
||||
if modpath ~= nil and
|
||||
modpath ~= "" then
|
||||
get_mods(modpath,global_mods)
|
||||
local modpaths = core.get_modpaths()
|
||||
for key, modpath in pairs(modpaths) do
|
||||
get_mods(modpath, key, global_mods)
|
||||
end
|
||||
|
||||
for i=1,#global_mods,1 do
|
||||
global_mods[i].type = "mod"
|
||||
global_mods[i].loc = "global"
|
||||
global_mods[i].enabled = false
|
||||
retval[#retval + 1] = global_mods[i]
|
||||
end
|
||||
|
||||
|
@ -773,22 +760,37 @@ function pkgmgr.preparemodlist(data)
|
|||
DIR_DELIM .. "world.mt"
|
||||
|
||||
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
|
||||
key = key:sub(10)
|
||||
local element = nil
|
||||
for i=1,#retval,1 do
|
||||
local mod_found = false
|
||||
|
||||
local fallback_found = false
|
||||
local fallback_mod = nil
|
||||
|
||||
for i=1, #retval do
|
||||
if retval[i].name == key and
|
||||
not retval[i].is_modpack then
|
||||
element = retval[i]
|
||||
break
|
||||
not retval[i].is_modpack then
|
||||
if core.is_yes(value) or retval[i].virtual_path == value then
|
||||
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
|
||||
if element ~= nil then
|
||||
element.enabled = value ~= "false" and value ~= "nil" and value
|
||||
else
|
||||
core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
|
||||
|
||||
if not mod_found then
|
||||
if fallback_mod and value:find("/") then
|
||||
fallback_mod.enabled = true
|
||||
else
|
||||
core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -871,45 +873,6 @@ function pkgmgr.refresh_globals()
|
|||
pkgmgr.clientmods:set_sortmode("alphabetic")
|
||||
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)
|
||||
for i=1,#pkgmgr.games,1 do
|
||||
|
@ -925,7 +888,7 @@ function pkgmgr.get_game_mods(gamespec, retval)
|
|||
if gamespec ~= nil and
|
||||
gamespec.gamemods_path ~= nil and
|
||||
gamespec.gamemods_path ~= "" then
|
||||
get_mods(gamespec.gamemods_path, retval)
|
||||
get_mods(gamespec.gamemods_path, ("games/%s/mods"):format(gamespec.id), retval)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -38,32 +38,35 @@ local core_developers = {
|
|||
"Lars Hofhansl <larsh@apache.org>",
|
||||
"Pierre-Yves Rollo <dev@pyrollo.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
|
||||
|
||||
local active_contributors = {
|
||||
"Wuzzy [devtest game, visual corrections]",
|
||||
"Zughy [Visual improvements, various fixes]",
|
||||
"Maksim (MoNTE48) [Android]",
|
||||
"Wuzzy [I18n for builtin, liquid features, fixes]",
|
||||
"Zughy [Various features and fixes]",
|
||||
"numzero [Graphics and rendering]",
|
||||
"appgurueu [Various internal fixes]",
|
||||
"Desour [Formspec and vector API changes]",
|
||||
"HybridDog [Rendering fixes and documentation]",
|
||||
"Hugues Ross [Graphics-related improvements]",
|
||||
"ANAND (ClobberXD) [Mouse buttons rebinding]",
|
||||
"luk3yx [Fixes]",
|
||||
"hecks [Audiovisuals, Lua API]",
|
||||
"LoneWolfHT [Object crosshair, documentation fixes]",
|
||||
"Lejo [Server-related improvements]",
|
||||
"EvidenceB [Compass HUD element]",
|
||||
"Paul Ouellette (pauloue) [Lua API, documentation]",
|
||||
"TheTermos [Collision detection, physics]",
|
||||
"Desour [Internal fixes, Clipboard on X11]",
|
||||
"Lars Müller [Various internal fixes]",
|
||||
"JosiahWI [CMake, cleanups and fixes]",
|
||||
"HybridDog [builtin, documentation]",
|
||||
"Jude Melton-Houghton [Database implementation]",
|
||||
"savilli [Fixes]",
|
||||
"Liso [Shadow Mapping]",
|
||||
"MoNTE48 [Build fix]",
|
||||
"Jean-Patrick Guerrero (kilbith) [Fixes]",
|
||||
"ROllerozxa [Code cleanups]",
|
||||
"Lejo [bitop library integration]",
|
||||
"LoneWolfHT [Build fixes]",
|
||||
"NeroBurner [Joystick]",
|
||||
"Elias Fleckenstein [Internal fixes]",
|
||||
"David CARLIER [Unix & Haiku build fixes]",
|
||||
"dcbrwn [Object shading]",
|
||||
"Elias Fleckenstein [API features/fixes]",
|
||||
"Jean-Patrick Guerrero (kilbith) [model element, visual fixes]",
|
||||
"k.h.lai [Memory leak fixes, documentation]",
|
||||
"pecksin [Clickable web links]",
|
||||
"srfqi [Android & rendering fixes]",
|
||||
"EvidenceB [Formspec]",
|
||||
}
|
||||
|
||||
local previous_core_developers = {
|
||||
|
@ -80,6 +83,7 @@ local previous_core_developers = {
|
|||
"Zeno",
|
||||
"ShadowNinja <shadowninja@minetest.net>",
|
||||
"Auke Kok (sofar) <sofar@foo-projects.org>",
|
||||
"Aaron Suen <warr1024@gmail.com>",
|
||||
}
|
||||
|
||||
local previous_contributors = {
|
||||
|
@ -90,10 +94,10 @@ local previous_contributors = {
|
|||
"MirceaKitsune <mirceakitsune@gmail.com>",
|
||||
"Constantin Wenger (SpeedProg)",
|
||||
"Ciaran Gultnieks (CiaranG)",
|
||||
"stujones11 [Android UX improvements]",
|
||||
"Rogier <rogier777@gmail.com> [Fixes]",
|
||||
"Gregory Currie (gregorycu) [optimisation]",
|
||||
"srifqi [Fixes]",
|
||||
"Paul Ouellette (pauloue)",
|
||||
"stujones11",
|
||||
"Rogier <rogier777@gmail.com>",
|
||||
"Gregory Currie (gregorycu)",
|
||||
"JacobF",
|
||||
"Jeija <jeija@mesecons.net> [HTTP, particles]",
|
||||
}
|
||||
|
|
|
@ -88,12 +88,14 @@ local function get_formspec(tabview, name, tabdata)
|
|||
tabdata.selected_pkg = 1
|
||||
end
|
||||
|
||||
local use_technical_names = core.settings:get_bool("show_technical_names")
|
||||
|
||||
|
||||
local retval =
|
||||
"label[0.05,-0.25;".. fgettext("Installed Packages:") .. "]" ..
|
||||
"tablecolumns[color;tree;text]" ..
|
||||
"table[0,0.25;5.1,4.3;pkglist;" ..
|
||||
pkgmgr.render_packagelist(packages) ..
|
||||
pkgmgr.render_packagelist(packages, use_technical_names) ..
|
||||
";" .. tabdata.selected_pkg .. "]" ..
|
||||
"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
|
||||
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 ..
|
||||
"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]"
|
||||
|
||||
if selected_pkg.type == "mod" then
|
||||
|
@ -214,6 +224,9 @@ local function handle_doubleclick(pkg, pkg_name)
|
|||
core.settings:set("texture_path", pkg.path)
|
||||
end
|
||||
packages = nil
|
||||
|
||||
mm_game_theme.init()
|
||||
mm_game_theme.reset()
|
||||
elseif pkg.is_clientside then
|
||||
pkgmgr.enable_mod({data = {list = packages, selected_mod = pkg_name}})
|
||||
packages = nil
|
||||
|
@ -276,17 +289,17 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
|||
return true
|
||||
end
|
||||
|
||||
if fields.btn_mod_mgr_use_txp then
|
||||
local txp = packages:get_list()[tabdata.selected_pkg]
|
||||
core.settings:set("texture_path", txp.path)
|
||||
packages = nil
|
||||
return true
|
||||
end
|
||||
if fields.btn_mod_mgr_use_txp or fields.btn_mod_mgr_disable_txp then
|
||||
local txp_path = ""
|
||||
if fields.btn_mod_mgr_use_txp then
|
||||
txp_path = packages:get_list()[tabdata.selected_pkg].path
|
||||
end
|
||||
|
||||
|
||||
if fields.btn_mod_mgr_disable_txp then
|
||||
core.settings:set("texture_path", "")
|
||||
core.settings:set("texture_path", txp_path)
|
||||
packages = nil
|
||||
|
||||
mm_game_theme.init()
|
||||
mm_game_theme.reset()
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
|
@ -33,10 +33,30 @@ if enable_gamebar then
|
|||
return game
|
||||
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()
|
||||
|
||||
local old_bar = ui.find_by_name("game_button_bar")
|
||||
|
||||
if old_bar ~= nil then
|
||||
old_bar:delete()
|
||||
end
|
||||
|
@ -51,26 +71,10 @@ if enable_gamebar then
|
|||
return true
|
||||
end
|
||||
|
||||
for key,value in pairs(fields) do
|
||||
for j=1,#pkgmgr.games,1 do
|
||||
if ("game_btnbar_" .. pkgmgr.games[j].id == key) then
|
||||
mm_texture.update("singleplayer", pkgmgr.games[j])
|
||||
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
|
||||
for _, game in ipairs(pkgmgr.games) do
|
||||
if fields["game_btnbar_" .. game.id] then
|
||||
apply_game(game)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -79,25 +83,22 @@ if enable_gamebar then
|
|||
game_buttonbar_button_handler,
|
||||
{x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15})
|
||||
|
||||
for i=1,#pkgmgr.games,1 do
|
||||
local btn_name = "game_btnbar_" .. pkgmgr.games[i].id
|
||||
for _, game in ipairs(pkgmgr.games) do
|
||||
local btn_name = "game_btnbar_" .. game.id
|
||||
|
||||
local image = 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
|
||||
pkgmgr.games[i].menuicon_path ~= "" then
|
||||
image = core.formspec_escape(pkgmgr.games[i].menuicon_path)
|
||||
if (game.menuicon_path or "") ~= "" then
|
||||
image = core.formspec_escape(game.menuicon_path)
|
||||
else
|
||||
|
||||
local part1 = pkgmgr.games[i].id:sub(1,5)
|
||||
local part2 = pkgmgr.games[i].id:sub(6,10)
|
||||
local part3 = pkgmgr.games[i].id:sub(11)
|
||||
local part1 = game.id:sub(1,5)
|
||||
local part2 = game.id:sub(6,10)
|
||||
local part3 = game.id:sub(11)
|
||||
|
||||
text = part1 .. "\n" .. part2
|
||||
if part3 ~= nil and
|
||||
part3 ~= "" then
|
||||
if part3 ~= "" then
|
||||
text = text .. "\n" .. part3
|
||||
end
|
||||
end
|
||||
|
@ -147,8 +148,12 @@ local function get_formspec(tabview, name, tabdata)
|
|||
tonumber(core.settings:get("mainmenu_last_selected_world")))
|
||||
local list = menudata.worldlist:get_list()
|
||||
local world = list and index and list[index]
|
||||
local gameid = world and world.gameid
|
||||
local game = gameid and pkgmgr.find_by_gameid(gameid)
|
||||
local game
|
||||
if world then
|
||||
game = pkgmgr.find_by_gameid(world.gameid)
|
||||
else
|
||||
game = current_game()
|
||||
end
|
||||
local disabled_settings = get_disabled_settings(game)
|
||||
|
||||
local creative, damage, host = "", "", ""
|
||||
|
@ -182,7 +187,7 @@ local function get_formspec(tabview, name, tabdata)
|
|||
damage ..
|
||||
host ..
|
||||
"textlist[3.9,0.4;7.9,3.45;sp_worlds;" ..
|
||||
menu_render_worldlist() ..
|
||||
menu_render_worldlist(not enable_gamebar) ..
|
||||
";" .. index .. "]"
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
this:hide()
|
||||
create_world_dlg:show()
|
||||
mm_texture.update("singleplayer", current_game())
|
||||
mm_game_theme.update("singleplayer", current_game())
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -340,7 +345,7 @@ local function main_button_handler(this, fields, name, tabdata)
|
|||
delete_world_dlg:set_parent(this)
|
||||
this:hide()
|
||||
delete_world_dlg:show()
|
||||
mm_texture.update("singleplayer",current_game())
|
||||
mm_game_theme.update("singleplayer",current_game())
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -358,7 +363,7 @@ local function main_button_handler(this, fields, name, tabdata)
|
|||
configdialog:set_parent(this)
|
||||
this:hide()
|
||||
configdialog:show()
|
||||
mm_texture.update("singleplayer",current_game())
|
||||
mm_game_theme.update("singleplayer",current_game())
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -371,11 +376,8 @@ if enable_gamebar then
|
|||
function on_change(type, old_tab, new_tab)
|
||||
if (type == "ENTER") then
|
||||
local game = current_game()
|
||||
|
||||
if game then
|
||||
menudata.worldlist:set_filtercriteria(game.id)
|
||||
core.set_topleft_text(game.name)
|
||||
mm_texture.update("singleplayer",game)
|
||||
apply_game(game)
|
||||
end
|
||||
|
||||
singleplayer_refresh_gamebar()
|
||||
|
@ -387,7 +389,7 @@ if enable_gamebar then
|
|||
gamebar:hide()
|
||||
end
|
||||
core.set_topleft_text("")
|
||||
mm_texture.update(new_tab,nil)
|
||||
mm_game_theme.update(new_tab,nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -247,7 +247,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
|
|||
adv_settings_dlg:set_parent(this)
|
||||
this:hide()
|
||||
adv_settings_dlg:show()
|
||||
--mm_texture.update("singleplayer", current_game())
|
||||
--mm_game_theme.update("singleplayer", current_game())
|
||||
return true
|
||||
end
|
||||
if fields["cb_smooth_lighting"] then
|
||||
|
@ -275,13 +275,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
|
|||
return true
|
||||
end
|
||||
if fields["cb_shaders"] then
|
||||
if (core.settings:get("video_driver") == "direct3d8" or
|
||||
core.settings:get("video_driver") == "direct3d9") then
|
||||
core.settings:set("enable_shaders", "false")
|
||||
gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.")
|
||||
else
|
||||
core.settings:set("enable_shaders", fields["cb_shaders"])
|
||||
end
|
||||
core.settings:set("enable_shaders", fields["cb_shaders"])
|
||||
return true
|
||||
end
|
||||
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")
|
||||
else
|
||||
local shadow_presets = {
|
||||
[2] = { 80, 512, "true", 0, "false" },
|
||||
[3] = { 120, 1024, "true", 1, "false" },
|
||||
[4] = { 350, 2048, "true", 1, "false" },
|
||||
[5] = { 350, 2048, "true", 2, "true" },
|
||||
[6] = { 450, 4096, "true", 2, "true" },
|
||||
[2] = { 55, 512, "true", 0, "false" },
|
||||
[3] = { 82, 1024, "true", 1, "false" },
|
||||
[4] = { 240, 2048, "true", 1, "false" },
|
||||
[5] = { 240, 2048, "true", 2, "true" },
|
||||
[6] = { 300, 4096, "true", 2, "true" },
|
||||
}
|
||||
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
|
||||
if s then
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
_G.core = {}
|
||||
_G.vector = {metatable = {}}
|
||||
_G.unpack = table.unpack
|
||||
_G.serverlistmgr = {}
|
||||
|
||||
|
|
|
@ -102,8 +102,9 @@ local function instrument(def)
|
|||
-- also called https://en.wikipedia.org/wiki/Continuation_passing_style
|
||||
-- Compared to table creation and unpacking it won't lose `nil` returns
|
||||
-- and is expected to be faster
|
||||
-- `measure` will be executed after time() and func(...)
|
||||
return measure(modname, instrument_name, time(), func(...))
|
||||
-- `measure` will be executed after func(...)
|
||||
local start = time()
|
||||
return measure(modname, instrument_name, start, func(...))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -146,17 +146,17 @@ enable_joysticks (Enable joysticks) bool false
|
|||
joystick_id (Joystick ID) int 0
|
||||
|
||||
# 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
|
||||
# when holding down a joystick button combination.
|
||||
repeat_joystick_button_time (Joystick button repetition interval) float 0.17 0.001
|
||||
|
||||
# The deadzone of the joystick
|
||||
joystick_deadzone (Joystick deadzone) int 2048
|
||||
# The dead zone of the joystick
|
||||
joystick_deadzone (Joystick dead zone) int 2048
|
||||
|
||||
# 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
|
||||
|
||||
# Key for moving the player forward.
|
||||
|
@ -463,9 +463,9 @@ keymap_decrease_viewing_range_min (View range decrease key) key -
|
|||
|
||||
[**Basic]
|
||||
|
||||
# Whether nametag backgrounds should be shown by default.
|
||||
# Whether name tag backgrounds should be shown by default.
|
||||
# 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.
|
||||
# This should greatly improve graphics performance.
|
||||
|
@ -487,6 +487,10 @@ connected_glass (Connect glass) bool false
|
|||
# Disable for speed or for different looks.
|
||||
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.
|
||||
enable_clouds (Clouds) bool true
|
||||
|
||||
|
@ -501,7 +505,7 @@ enable_particles (Digging particles) bool true
|
|||
|
||||
[**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.
|
||||
# Gamma correct downscaling is not supported.
|
||||
mip_map (Mipmapping) bool false
|
||||
|
@ -600,9 +604,10 @@ enable_waving_plants (Waving plants) bool false
|
|||
# Requires shaders to be enabled.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
# but also uses more resources.
|
||||
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.
|
||||
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.
|
||||
# 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
|
||||
|
||||
# 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.
|
||||
# Minimum value: 0.0; maximum value: 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.
|
||||
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
|
||||
|
||||
# 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.
|
||||
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]
|
||||
|
||||
# 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.
|
||||
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_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.
|
||||
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
|
||||
|
||||
# Path to the default font.
|
||||
# If “freetype” setting is enabled: Must be a TrueType font.
|
||||
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
|
||||
# For pixel-style fonts that do not scale well, this ensures that font sizes used
|
||||
# with this font will always be divisible by this value, in pixels. For instance,
|
||||
# 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.
|
||||
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_bold_italic (Bold and italic font path) filepath fonts/Arimo-BoldItalic.ttf
|
||||
|
||||
# Font size of the monospace font in point (pt).
|
||||
mono_font_size (Monospace font size) int 15 1
|
||||
# Font size of the monospace font where 1 unit = 1 pixel at 96 DPI
|
||||
mono_font_size (Monospace font size) int 16 1
|
||||
|
||||
# Path to the monospace font.
|
||||
# If “freetype” setting is enabled: Must be a TrueType font.
|
||||
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
|
||||
# For pixel-style fonts that do not scale well, this ensures that font sizes used
|
||||
# with this font will always be divisible by this value, in pixels. For instance,
|
||||
# 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.
|
||||
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_bold_italic (Bold and italic monospace font path) filepath fonts/Cousine-BoldItalic.ttf
|
||||
|
||||
# Path of the fallback font.
|
||||
# If “freetype” setting is enabled: Must be a TrueType font.
|
||||
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
|
||||
# Path of the fallback font. Must be a TrueType font.
|
||||
# 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
|
||||
|
||||
|
@ -949,7 +960,7 @@ chat_font_size (Chat font size) int 0
|
|||
screenshot_path (Screenshot folder) path 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.
|
||||
# 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.
|
||||
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.
|
||||
# Contains the same information as the file debug.txt (default name).
|
||||
enable_console (Enable console window) bool false
|
||||
|
@ -985,8 +999,8 @@ mute_sound (Mute sound) bool false
|
|||
|
||||
[Client]
|
||||
|
||||
# Clickable weblinks (middle-click or ctrl-left-click) enabled in chat console output.
|
||||
clickable_chat_weblinks (Chat weblinks) bool false
|
||||
# Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output.
|
||||
clickable_chat_weblinks (Chat weblinks) bool true
|
||||
|
||||
# Optional override for chat weblink color.
|
||||
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.
|
||||
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).
|
||||
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.
|
||||
# -1 - use default compression level
|
||||
# 0 - least compresson, fastest
|
||||
# 0 - least compression, fastest
|
||||
# 9 - best compression, slowest
|
||||
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.
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
# 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).
|
||||
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
|
||||
|
||||
# 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
|
||||
sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2
|
||||
|
||||
# Compression level to use when saving mapblocks to disk.
|
||||
# -1 - use default compression level
|
||||
# 0 - least compresson, fastest
|
||||
# 0 - least compression, fastest
|
||||
# 9 - best compression, slowest
|
||||
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.lbm (Loading Block Modifiers) bool true
|
||||
|
||||
# Instrument chatcommands on registration.
|
||||
instrument.chatcommand (Chatcommands) bool true
|
||||
# Instrument chat commands on registration.
|
||||
instrument.chatcommand (Chat commands) bool true
|
||||
|
||||
# Instrument global callback functions on registration.
|
||||
# (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
|
||||
# - info
|
||||
# - 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
|
||||
# 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
|
||||
|
||||
# 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).
|
||||
# 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).
|
||||
# Only mapchunks completely within the mapgen limit are generated.
|
||||
# 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.
|
||||
# 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
|
||||
|
||||
[*Biome API temperature and humidity noise parameters]
|
||||
|
@ -2270,7 +2291,7 @@ contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3
|
|||
[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)
|
||||
cheat_menu_bg_color (Cell background color) v3f 255, 145, 88
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
uniform sampler2D baseTexture;
|
||||
|
||||
uniform vec3 dayLight;
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform vec3 eyePosition;
|
||||
|
@ -15,10 +16,15 @@ uniform float animationTimer;
|
|||
uniform float f_textureresolution;
|
||||
uniform mat4 m_ShadowViewProj;
|
||||
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 cosLight;
|
||||
varying float f_normal_length;
|
||||
varying vec3 shadow_position;
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -42,23 +48,7 @@ varying float nightRatio;
|
|||
const float fogStart = FOG_START;
|
||||
const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
|
||||
|
||||
|
||||
|
||||
#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
|
||||
float getLinearDepth()
|
||||
|
@ -68,16 +58,7 @@ float getLinearDepth()
|
|||
|
||||
vec3 getLightSpacePosition()
|
||||
{
|
||||
vec4 pLightSpace;
|
||||
// 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;
|
||||
return shadow_position * 0.5 + 0.5;
|
||||
}
|
||||
// custom smoothstep implementation because it's not defined in glsl1.2
|
||||
// https://docs.gl/sl4/smoothstep
|
||||
|
@ -170,13 +151,13 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis
|
|||
|
||||
float getBaseLength(vec2 smTexCoord)
|
||||
{
|
||||
float l = length(2.0 * smTexCoord.xy - 1.0); // length in texture coords
|
||||
return bias1 / (1.0 / l - bias0); // return to undistorted coords
|
||||
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.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)
|
||||
|
@ -185,6 +166,9 @@ float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDist
|
|||
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);
|
||||
|
@ -479,36 +463,59 @@ void main(void)
|
|||
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
|
||||
|
||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||
float shadow_int = 0.0;
|
||||
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
|
||||
vec3 posLightSpace = getLightSpacePosition();
|
||||
if (f_shadow_strength > 0.0) {
|
||||
float shadow_int = 0.0;
|
||||
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 f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1, posLightSpace.z ),0.0);
|
||||
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) {
|
||||
|
||||
if (distance_rate > 1e-7) {
|
||||
|
||||
#ifdef COLORED_SHADOWS
|
||||
vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
||||
shadow_int = visibility.r;
|
||||
shadow_color = visibility.gba;
|
||||
vec4 visibility;
|
||||
if (cosLight > 0.0 || f_normal_length < 1e-3)
|
||||
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
|
||||
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
|
||||
shadow_int *= distance_rate;
|
||||
shadow_int *= 1.0 - nightRatio;
|
||||
shadow_int *= distance_rate;
|
||||
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
|
||||
|
||||
#if ENABLE_TONE_MAPPING
|
||||
|
@ -528,6 +535,6 @@ void main(void)
|
|||
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
|
||||
col = mix(skyBgColor, col, clarity);
|
||||
col = vec4(col.rgb, base.a);
|
||||
|
||||
|
||||
gl_FragColor = col;
|
||||
}
|
||||
|
|
|
@ -32,10 +32,13 @@ centroid varying vec2 varTexCoord;
|
|||
uniform float f_shadowfar;
|
||||
uniform float f_shadow_strength;
|
||||
uniform float f_timeofday;
|
||||
uniform vec4 CameraPos;
|
||||
|
||||
varying float cosLight;
|
||||
varying float normalOffsetScale;
|
||||
varying float adj_shadow_strength;
|
||||
varying float f_normal_length;
|
||||
varying vec3 shadow_position;
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -45,8 +48,38 @@ varying float nightRatio;
|
|||
const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
|
||||
const float e = 2.718281828459;
|
||||
const float BS = 10.0;
|
||||
uniform float xyPerspectiveBias0;
|
||||
uniform float xyPerspectiveBias1;
|
||||
uniform float zPerspectiveBias;
|
||||
|
||||
#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
|
||||
// https://docs.gl/sl4/smoothstep
|
||||
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);
|
||||
|
||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||
vec3 nNormal = normalize(vNormal);
|
||||
cosLight = dot(nNormal, -v_LightDirection);
|
||||
float texelSize = 767.0 / f_textureresolution;
|
||||
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
|
||||
if (f_shadow_strength > 0.0) {
|
||||
vec3 nNormal;
|
||||
f_normal_length = length(vNormal);
|
||||
|
||||
/* 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 vec4 emissiveColor;
|
||||
uniform vec3 dayLight;
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vPosition;
|
||||
varying vec3 worldPosition;
|
||||
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);
|
||||
|
||||
// The cameraOffset is the current center of the visible world.
|
||||
uniform vec3 cameraOffset;
|
||||
uniform float animationTimer;
|
||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||
// shadow texture
|
||||
uniform sampler2D ShadowMapSampler;
|
||||
|
@ -31,57 +17,41 @@ const float fogShadingParameter = 1.0 / (1.0 - fogStart);
|
|||
uniform float f_textureresolution;
|
||||
uniform mat4 m_ShadowViewProj;
|
||||
uniform float f_shadowfar;
|
||||
uniform float f_timeofday;
|
||||
varying float normalOffsetScale;
|
||||
uniform float f_shadow_strength;
|
||||
uniform vec4 CameraPos;
|
||||
uniform float xyPerspectiveBias0;
|
||||
uniform float xyPerspectiveBias1;
|
||||
|
||||
varying float adj_shadow_strength;
|
||||
varying float cosLight;
|
||||
varying float f_normal_length;
|
||||
varying vec3 shadow_position;
|
||||
#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);
|
||||
}
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vPosition;
|
||||
// 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
|
||||
// cameraOffset + worldPosition (for large coordinates the limits of float
|
||||
// precision must be considered).
|
||||
varying vec3 worldPosition;
|
||||
varying lowp vec4 varColor;
|
||||
#ifdef GL_ES
|
||||
varying mediump vec2 varTexCoord;
|
||||
#else
|
||||
centroid varying vec2 varTexCoord;
|
||||
#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
|
||||
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
|
||||
float getLinearDepth()
|
||||
|
@ -91,11 +61,14 @@ float getLinearDepth()
|
|||
|
||||
vec3 getLightSpacePosition()
|
||||
{
|
||||
vec4 pLightSpace;
|
||||
float normalBias = 0.0005 * getLinearDepth() * cosLight + normalOffsetScale;
|
||||
pLightSpace = m_ShadowViewProj * vec4(worldPosition + normalBias * normalize(vNormal), 1.0);
|
||||
pLightSpace = getPerspectiveFactor(pLightSpace);
|
||||
return pLightSpace.xyz * 0.5 + 0.5;
|
||||
return shadow_position * 0.5 + 0.5;
|
||||
}
|
||||
// custom smoothstep implementation because it's not defined in glsl1.2
|
||||
// https://docs.gl/sl4/smoothstep
|
||||
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
|
||||
|
@ -124,10 +97,10 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist
|
|||
{
|
||||
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));
|
||||
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));
|
||||
}
|
||||
return result;
|
||||
|
@ -138,13 +111,13 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist
|
|||
float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if SHADOW_FILTER == 2
|
||||
#define PCFBOUND 3.5
|
||||
#define PCFSAMPLES 64.0
|
||||
|
@ -163,6 +136,76 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
|
|||
#define PCFSAMPLES 1.0
|
||||
#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
|
||||
const vec2[64] poissonDisk = vec2[64](
|
||||
|
@ -238,17 +281,28 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
|
|||
{
|
||||
vec2 clampedpos;
|
||||
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);
|
||||
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
|
||||
int end_offset = int(PCFSAMPLES) + init_offset;
|
||||
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
|
||||
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++) {
|
||||
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);
|
||||
}
|
||||
|
||||
return visibility / PCFSAMPLES;
|
||||
return visibility / samples;
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -257,17 +311,28 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
|||
{
|
||||
vec2 clampedpos;
|
||||
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);
|
||||
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
|
||||
int end_offset = int(PCFSAMPLES) + init_offset;
|
||||
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
|
||||
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++) {
|
||||
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);
|
||||
}
|
||||
|
||||
return visibility / PCFSAMPLES;
|
||||
return visibility / samples;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -281,19 +346,31 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
|
|||
{
|
||||
vec2 clampedpos;
|
||||
vec4 visibility = vec4(0.0);
|
||||
float sradius=0.0;
|
||||
if( PCFBOUND>0)
|
||||
sradius = SOFTSHADOWRADIUS / PCFBOUND;
|
||||
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 += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
|
||||
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
|
||||
if (radius < 0.1) {
|
||||
// we are in the middle of even brightness, no need for filtering
|
||||
return getHardShadowColor(shadowsampler, smTexCoord.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
|
||||
|
@ -301,20 +378,31 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
|||
{
|
||||
vec2 clampedpos;
|
||||
float visibility = 0.0;
|
||||
float sradius=0.0;
|
||||
if( PCFBOUND>0)
|
||||
sradius = SOFTSHADOWRADIUS / PCFBOUND;
|
||||
|
||||
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);
|
||||
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
|
||||
if (radius < 0.1) {
|
||||
// we are in the middle of even brightness, no need for filtering
|
||||
return getHardShadow(shadowsampler, smTexCoord.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
|
||||
|
@ -322,12 +410,46 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
|||
#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)
|
||||
{
|
||||
vec3 color;
|
||||
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
|
||||
// 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.
|
||||
|
@ -341,34 +463,66 @@ void main(void)
|
|||
#endif
|
||||
|
||||
color = base.rgb;
|
||||
vec4 col = vec4(color.rgb, base.a);
|
||||
col.rgb *= varColor.rgb;
|
||||
col.rgb *= emissiveColor.rgb * vIDiff;
|
||||
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
|
||||
col.rgb *= vIDiff;
|
||||
|
||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||
float shadow_int = 0.0;
|
||||
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
|
||||
vec3 posLightSpace = getLightSpacePosition();
|
||||
if (f_shadow_strength > 0.0) {
|
||||
float shadow_int = 0.0;
|
||||
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
|
||||
vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
|
||||
shadow_int = visibility.r;
|
||||
shadow_color = visibility.gba;
|
||||
vec4 visibility;
|
||||
if (cosLight > 0.0 || f_normal_length < 1e-3)
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
|
||||
#if ENABLE_TONE_MAPPING
|
||||
col = applyToneMapping(col);
|
||||
#endif
|
||||
|
@ -385,6 +539,7 @@ void main(void)
|
|||
float clarity = clamp(fogShadingParameter
|
||||
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
|
||||
col = mix(skyBgColor, col, clarity);
|
||||
|
||||
gl_FragColor = vec4(col.rgb, base.a);
|
||||
col = vec4(col.rgb, base.a);
|
||||
|
||||
gl_FragColor = col;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
uniform mat4 mWorld;
|
||||
|
||||
uniform vec3 dayLight;
|
||||
uniform vec3 eyePosition;
|
||||
uniform float animationTimer;
|
||||
uniform vec4 emissiveColor;
|
||||
uniform vec3 cameraOffset;
|
||||
|
||||
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vPosition;
|
||||
|
@ -21,19 +24,53 @@ centroid varying vec2 varTexCoord;
|
|||
uniform float f_shadowfar;
|
||||
uniform float f_shadow_strength;
|
||||
uniform float f_timeofday;
|
||||
uniform vec4 CameraPos;
|
||||
|
||||
varying float cosLight;
|
||||
varying float normalOffsetScale;
|
||||
varying float adj_shadow_strength;
|
||||
varying float f_normal_length;
|
||||
varying vec3 shadow_position;
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
const float e = 2.718281828459;
|
||||
const float BS = 10.0;
|
||||
uniform float xyPerspectiveBias0;
|
||||
uniform float xyPerspectiveBias1;
|
||||
uniform float zPerspectiveBias;
|
||||
|
||||
#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
|
||||
// https://docs.gl/sl4/smoothstep
|
||||
float mtsmoothstep(in float edge0, in float edge1, in float x)
|
||||
|
@ -60,7 +97,7 @@ void main(void)
|
|||
gl_Position = mWorldViewProj * inVertexPosition;
|
||||
|
||||
vPosition = gl_Position.xyz;
|
||||
vNormal = inVertexNormal;
|
||||
vNormal = (mWorld * vec4(inVertexNormal, 0.0)).xyz;
|
||||
worldPosition = (mWorld * inVertexPosition).xyz;
|
||||
eyeVec = -(mWorldView * inVertexPosition).xyz;
|
||||
|
||||
|
@ -75,29 +112,68 @@ void main(void)
|
|||
#endif
|
||||
|
||||
#ifdef GL_ES
|
||||
varColor = inVertexColor.bgra;
|
||||
vec4 color = inVertexColor.bgra;
|
||||
#else
|
||||
varColor = inVertexColor;
|
||||
vec4 color = inVertexColor;
|
||||
#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
|
||||
if (f_shadow_strength > 0.0) {
|
||||
vec3 nNormal = normalize(vNormal);
|
||||
f_normal_length = length(vNormal);
|
||||
|
||||
cosLight = max(0.0, dot(vNormal, -v_LightDirection));
|
||||
float texelSize = 0.51;
|
||||
float slopeScale = clamp(1.0 - 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));
|
||||
/* 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 = 0.1 * pFactor * pFactor * sinLight * min(f_shadowfar, 500.0) /
|
||||
xyPerspectiveBias1 / f_textureresolution;
|
||||
z_bias = 1e3 * sinLight / cosLight * (0.5 + f_textureresolution / 1024.0);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
f_normal_length = length(vNormal);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ uniform sampler2D ColorMapSampler;
|
|||
varying vec4 tPos;
|
||||
|
||||
#ifdef COLORED_SHADOWS
|
||||
varying vec3 varColor;
|
||||
|
||||
// c_precision of 128 fits within 7 base-10 digits
|
||||
const float c_precision = 128.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;
|
||||
#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);
|
||||
#else
|
||||
gl_FragColor = vec4(depth, 0.0, 0.0, 1.0);
|
||||
|
|
|
@ -1,26 +1,50 @@
|
|||
uniform mat4 LightMVP; // world matrix
|
||||
uniform vec4 CameraPos;
|
||||
varying vec4 tPos;
|
||||
#ifdef COLORED_SHADOWS
|
||||
varying vec3 varColor;
|
||||
#endif
|
||||
|
||||
const float bias0 = 0.9;
|
||||
const float zPersFactor = 0.5;
|
||||
const float bias1 = 1.0 - bias0 + 1e-6;
|
||||
uniform float xyPerspectiveBias0;
|
||||
uniform float xyPerspectiveBias1;
|
||||
uniform float zPerspectiveBias;
|
||||
|
||||
vec4 getPerspectiveFactor(in vec4 shadowPosition)
|
||||
vec4 getRelativePosition(in vec4 position)
|
||||
{
|
||||
float pDistance = length(shadowPosition.xy);
|
||||
float pFactor = pDistance * bias0 + bias1;
|
||||
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
|
||||
|
||||
return shadowPosition;
|
||||
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;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 pos = LightMVP * gl_Vertex;
|
||||
|
||||
tPos = getPerspectiveFactor(LightMVP * gl_Vertex);
|
||||
tPos = applyPerspectiveDistortion(LightMVP * gl_Vertex);
|
||||
|
||||
gl_Position = vec4(tPos.xyz, 1.0);
|
||||
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 vec4 CameraPos; // camera position
|
||||
varying vec4 tPos;
|
||||
|
||||
const float bias0 = 0.9;
|
||||
const float zPersFactor = 0.5;
|
||||
const float bias1 = 1.0 - bias0 + 1e-6;
|
||||
uniform float xyPerspectiveBias0;
|
||||
uniform float xyPerspectiveBias1;
|
||||
uniform float zPerspectiveBias;
|
||||
|
||||
vec4 getPerspectiveFactor(in vec4 shadowPosition)
|
||||
vec4 getRelativePosition(in vec4 position)
|
||||
{
|
||||
float pDistance = length(shadowPosition.xy);
|
||||
float pFactor = pDistance * bias0 + bias1;
|
||||
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
|
||||
|
||||
return shadowPosition;
|
||||
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;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 pos = LightMVP * gl_Vertex;
|
||||
|
||||
tPos = getPerspectiveFactor(pos);
|
||||
tPos = applyPerspectiveDistortion(pos);
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -29,18 +29,17 @@ else(NOT GP2XWIZ)
|
|||
find_package_handle_standard_args(Vorbis DEFAULT_MSG
|
||||
VORBIS_INCLUDE_DIR VORBIS_LIBRARY)
|
||||
endif(NOT GP2XWIZ)
|
||||
|
||||
|
||||
if(VORBIS_FOUND)
|
||||
if(NOT GP2XWIZ)
|
||||
set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY}
|
||||
${OGG_LIBRARY})
|
||||
else(NOT GP2XWIZ)
|
||||
set(VORBIS_LIBRARIES ${VORBIS_LIBRARY})
|
||||
endif(NOT GP2XWIZ)
|
||||
if(NOT GP2XWIZ)
|
||||
set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY}
|
||||
${OGG_LIBRARY})
|
||||
else(NOT GP2XWIZ)
|
||||
set(VORBIS_LIBRARIES ${VORBIS_LIBRARY})
|
||||
endif(NOT GP2XWIZ)
|
||||
else(VORBIS_FOUND)
|
||||
set(VORBIS_LIBRARIES)
|
||||
set(VORBIS_LIBRARIES)
|
||||
endif(VORBIS_FOUND)
|
||||
|
||||
mark_as_advanced(OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR)
|
||||
mark_as_advanced(OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY)
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ PREDEFINED = "USE_SPATIAL=1" \
|
|||
"USE_REDIS=1" \
|
||||
"USE_SOUND=1" \
|
||||
"USE_CURL=1" \
|
||||
"USE_FREETYPE=1" \
|
||||
"USE_GETTEXT=1"
|
||||
|
||||
# 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/>
|
||||
* Developer Wiki: <http://dev.minetest.net/>
|
||||
|
@ -587,6 +587,7 @@ Spatial Vectors
|
|||
* `vector.floor(v)`: returns a vector, each dimension rounded down
|
||||
* `vector.round(v)`: returns a vector, each dimension rounded to nearest int
|
||||
* `vector.apply(v, func)`: returns a vector
|
||||
* `vector.combine(v, w, func)`: returns a vector
|
||||
* `vector.equals(v1, v2)`: returns a boolean
|
||||
|
||||
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)
|
||||
* `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)
|
||||
* `get_liquid_viscosity()`
|
||||
* returns liquid viscosity (Gets the viscosity of liquid to calculate friction)
|
||||
* `get_move_resistance()`
|
||||
* returns move resistance of current node, the higher the slower the player moves
|
||||
* `is_climbing()`
|
||||
* returns true if player is climbing
|
||||
* `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_alternative_flowing = <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_range = <number>, -- How far the liquid flows *May not exist*
|
||||
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_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
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
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
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
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,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
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
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
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
|
||||
|
@ -85,7 +85,9 @@ core.get_video_drivers()
|
|||
core.get_mapgen_names([include_hidden=false]) -> table of map generator algorithms
|
||||
registered in the core (possible in async calls)
|
||||
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
|
||||
|
@ -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,
|
||||
the directory that contains user-provided mods, worlds, games, and texture packs.
|
||||
* 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)
|
||||
* returns path to global client-side modpath
|
||||
* 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,
|
||||
leveldb, postgresql, dummy, and files.
|
||||
.TP
|
||||
.B \-\-migrate-mod-storage <value>
|
||||
Migrate from current mod storage backend to another. Possible values are
|
||||
sqlite3, dummy, and files.
|
||||
.TP
|
||||
.B \-\-terminal
|
||||
Display an interactive terminal over ncurses during execution.
|
||||
|
||||
|
@ -119,6 +123,9 @@ Display an interactive terminal over ncurses during execution.
|
|||
.TP
|
||||
.B MINETEST_SUBGAME_PATH
|
||||
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
|
||||
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_overlay_round.png`: overlay texture for the round 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
|
||||
* `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_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)
|
||||
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)
|
||||
auth_backend = files - which DB backend to use for authentication data
|
||||
server_announce = false - whether the server is publicly announced or not
|
||||
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
|
||||
===================
|
||||
|
@ -333,6 +358,9 @@ if map format version >= 29:
|
|||
- 0xffffffff = invalid/unknown timestamp, nothing should be done with the time
|
||||
difference when loaded
|
||||
|
||||
u8 name_id_mapping_version
|
||||
- Should be zero for map format version 29.
|
||||
|
||||
u16 num_name_id_mappings
|
||||
foreach num_name_id_mappings
|
||||
u16 id
|
||||
|
@ -434,7 +462,7 @@ if map format version < 29:
|
|||
u8[name_len] name
|
||||
|
||||
- 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)
|
||||
u16 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