Merge tag '5.4.1' into main

5.4.1
master
luk3yx 2021-06-08 10:37:38 +12:00
commit 1aabc2ca14
455 changed files with 63408 additions and 53531 deletions

View File

@ -29,3 +29,4 @@ AlignAfterOpenBracket: DontAlign
ContinuationIndentWidth: 16 ContinuationIndentWidth: 16
ConstructorInitializerIndentWidth: 16 ConstructorInitializerIndentWidth: 16
BreakConstructorInitializers: AfterColon BreakConstructorInitializers: AfterColon
AlwaysBreakTemplateDeclarations: Yes

View File

@ -43,6 +43,12 @@ Contributions are welcome! Here's how you can help:
4. The code's interfaces are well designed, regardless of other aspects that might need more work in the future. 4. The code's interfaces are well designed, regardless of other aspects that might need more work in the future.
5. It uses protocols and formats which include the required compatibility. 5. It uses protocols and formats which include the required compatibility.
### Important note about automated GitHub checks
When you submit a pull request, GitHub automatically runs checks on the Minetest Engine combined with your changes. One of these checks is called 'cpp lint / clang format', which checks code formatting. Because formatting for readability requires human judgement this check often fails and often makes unsuitable formatting requests which make code readability worse.
If this check fails, look at the details to check for any clear mistakes and correct those. However, you should not apply everything ClangFormat requests. Ignore requests that make code readability worse and any other clearly unsuitable requests. Discuss in the pull request with a core developer about how to progress.
## Issues ## Issues
If you experience an issue, we would like to know the details - especially when a stable release is on the way. If you experience an issue, we would like to know the details - especially when a stable release is on the way.

42
.github/workflows/android.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: android
# build on c/cpp changes or workflow changes
on:
push:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- 'build/android/**'
- '.github/workflows/android.yml'
pull_request:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- 'build/android/**'
- '.github/workflows/android.yml'
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: cd build/android; ./gradlew assemblerelease
- name: Save armeabi artifact
uses: actions/upload-artifact@v2
with:
name: Minetest-armeabi-v7a.apk
path: build/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
- name: Save arm64 artifact
uses: actions/upload-artifact@v2
with:
name: Minetest-arm64-v8a.apk
path: build/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk

View File

@ -55,9 +55,8 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install deps - name: Install deps
run: | run: |
sudo apt-get install g++-8 gcc-8 -qyy
source ./util/ci/common.sh source ./util/ci/common.sh
install_linux_deps install_linux_deps g++-8
- name: Build - name: Build
run: | run: |
@ -99,11 +98,8 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install deps - name: Install deps
run: | run: |
sudo apt-get install clang-9 valgrind -qyy
source ./util/ci/common.sh source ./util/ci/common.sh
install_linux_deps install_linux_deps clang-9 valgrind libluajit-5.1-dev
env:
WITH_LUAJIT: 1
- name: Build - name: Build
run: | run: |
@ -111,6 +107,7 @@ jobs:
env: env:
CC: clang-9 CC: clang-9
CXX: clang++-9 CXX: clang++-9
CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
- name: Test - name: Test
run: | run: |
@ -188,7 +185,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install compiler - name: Install compiler
run: | run: |
sudo apt-get install gettext -qyy 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 wget http://minetest.kitsunemimi.pw/mingw-w64-i686_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
sudo tar -xaf mingw.tar.xz -C /usr sudo tar -xaf mingw.tar.xz -C /usr
@ -206,7 +203,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install compiler - name: Install compiler
run: | run: |
sudo apt-get install gettext -qyy 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 wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
sudo tar -xaf mingw.tar.xz -C /usr sudo tar -xaf mingw.tar.xz -C /usr

View File

@ -18,12 +18,12 @@ variables:
- mkdir cmakebuild - mkdir cmakebuild
- mkdir -p artifact/minetest/usr/ - mkdir -p artifact/minetest/usr/
- cd cmakebuild - cd cmakebuild
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE .. - cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DENABLE_SYSTEM_JSONCPP=TRUE -DBUILD_SERVER=TRUE ..
- make -j2 - make -j2
- make install - make install
artifacts: artifacts:
when: on_success when: on_success
expire_in: 2h expire_in: 1h
paths: paths:
- artifact/* - artifact/*
@ -34,15 +34,14 @@ variables:
- apt-get install -y git - apt-get install -y git
- mkdir -p build/deb/minetest/DEBIAN/ - mkdir -p build/deb/minetest/DEBIAN/
- cp misc/debpkg-control build/deb/minetest/DEBIAN/control - cp misc/debpkg-control build/deb/minetest/DEBIAN/control
- cp -Rp artifact/minetest/usr build/deb/minetest/ - cp -a artifact/minetest/usr build/deb/minetest/
script: script:
- git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest - git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest_game
- rm -Rf build/deb/minetest/usr/share/minetest/games/minetest/.git - rm -rf build/deb/minetest/usr/share/minetest/games/minetest/.git
- sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control - sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control
- sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control - sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control
- cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../ - cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../
artifacts: artifacts:
when: on_success
expire_in: 90 day expire_in: 90 day
paths: paths:
- ./*.deb - ./*.deb
@ -51,44 +50,14 @@ variables:
stage: deploy stage: deploy
before_script: before_script:
- apt-get update -y - apt-get update -y
- apt-get install -y libc6 libcurl3-gnutls libfreetype6 libirrlicht1.8 $LEVELDB_PKG liblua5.1-0 libluajit-5.1-2 libopenal1 libstdc++6 libvorbisfile3 libx11-6 zlib1g
script: script:
- dpkg -i ./*.deb - apt-get install -y ./*.deb
- minetest --version
## ##
## Debian ## Debian
## ##
# Jessie
build:debian-8:
extends: .build_template
image: debian:8
before_script:
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list
- apt-key adv --keyserver keyserver.ubuntu.com --recv BA9EF27F
- apt-get update -y
- apt-get -y install build-essential gcc-6 g++-6 libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
variables:
CC: gcc-6
CXX: g++-6
package:debian-8:
extends: .debpkg_template
image: debian:8
dependencies:
- build:debian-8
variables:
LEVELDB_PKG: libleveldb1
deploy:debian-8:
extends: .debpkg_install
image: debian:8
dependencies:
- package:debian-8
variables:
LEVELDB_PKG: libleveldb1
# Stretch # Stretch
build:debian-9: build:debian-9:
@ -101,7 +70,7 @@ build:debian-9:
package:debian-9: package:debian-9:
extends: .debpkg_template extends: .debpkg_template
image: debian:9 image: debian:9
dependencies: needs:
- build:debian-9 - build:debian-9
variables: variables:
LEVELDB_PKG: libleveldb1v5 LEVELDB_PKG: libleveldb1v5
@ -109,12 +78,10 @@ package:debian-9:
deploy:debian-9: deploy:debian-9:
extends: .debpkg_install extends: .debpkg_install
image: debian:9 image: debian:9
dependencies: needs:
- package:debian-9 - package:debian-9
variables:
LEVELDB_PKG: libleveldb1v5
# Stretch # Buster
build:debian-10: build:debian-10:
extends: .build_template extends: .build_template
@ -126,7 +93,7 @@ build:debian-10:
package:debian-10: package:debian-10:
extends: .debpkg_template extends: .debpkg_template
image: debian:10 image: debian:10
dependencies: needs:
- build:debian-10 - build:debian-10
variables: variables:
LEVELDB_PKG: libleveldb1d LEVELDB_PKG: libleveldb1d
@ -134,44 +101,13 @@ package:debian-10:
deploy:debian-10: deploy:debian-10:
extends: .debpkg_install extends: .debpkg_install
image: debian:10 image: debian:10
dependencies: needs:
- package:debian-10 - package:debian-10
variables:
LEVELDB_PKG: libleveldb1d
## ##
## Ubuntu ## Ubuntu
## ##
# Trusty
build:ubuntu-14.04:
extends: .build_template
image: ubuntu:trusty
before_script:
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list
- apt-key adv --keyserver keyserver.ubuntu.com --recv BA9EF27F
- apt-get update -y
- apt-get -y install build-essential gcc-6 g++-6 libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
variables:
CC: gcc-6
CXX: g++-6
package:ubuntu-14.04:
extends: .debpkg_template
image: ubuntu:trusty
dependencies:
- build:ubuntu-14.04
variables:
LEVELDB_PKG: libleveldb1
deploy:ubuntu-14.04:
extends: .debpkg_install
image: ubuntu:trusty
dependencies:
- package:ubuntu-14.04
variables:
LEVELDB_PKG: libleveldb1
# Xenial # Xenial
build:ubuntu-16.04: build:ubuntu-16.04:
@ -184,7 +120,7 @@ build:ubuntu-16.04:
package:ubuntu-16.04: package:ubuntu-16.04:
extends: .debpkg_template extends: .debpkg_template
image: ubuntu:xenial image: ubuntu:xenial
dependencies: needs:
- build:ubuntu-16.04 - build:ubuntu-16.04
variables: variables:
LEVELDB_PKG: libleveldb1v5 LEVELDB_PKG: libleveldb1v5
@ -192,25 +128,45 @@ package:ubuntu-16.04:
deploy:ubuntu-16.04: deploy:ubuntu-16.04:
extends: .debpkg_install extends: .debpkg_install
image: ubuntu:xenial image: ubuntu:xenial
dependencies: needs:
- package:ubuntu-16.04 - package:ubuntu-16.04
# Bionic
build:ubuntu-18.04:
extends: .build_template
image: ubuntu:bionic
before_script:
- apt-get update -y
- apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
package:ubuntu-18.04:
extends: .debpkg_template
image: ubuntu:bionic
needs:
- build:ubuntu-18.04
variables: variables:
LEVELDB_PKG: libleveldb1v5 LEVELDB_PKG: libleveldb1v5
deploy:ubuntu-18.04:
extends: .debpkg_install
image: ubuntu:bionic
needs:
- package:ubuntu-18.04
## ##
## Fedora ## Fedora
## ##
# Do we need to support this old version ? # Fedora 28 <-> RHEL 8
build:fedora-24: build:fedora-28:
extends: .build_template extends: .build_template
image: fedora:24 image: fedora:28
before_script: before_script:
- dnf -y install make automake gcc gcc-c++ kernel-devel cmake libcurl* openal* libvorbis* libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel bzip2-libs gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel doxygen spatialindex-devel bzip2-devel - dnf -y 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 irrlicht-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel
## ##
## Mingw for Windows ## MinGW for Windows
## ##
.generic_win_template: .generic_win_template:
@ -218,68 +174,63 @@ build:fedora-24:
before_script: before_script:
- apt-get update -y - apt-get update -y
- apt-get install -y wget xz-utils unzip git cmake gettext - apt-get install -y wget xz-utils unzip git cmake gettext
- wget -q http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz - wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
- tar -xaf mingw.tar.xz -C /usr - tar -xaf mingw.tar.xz -C /usr
.build_win_template: .build_win_template:
extends: .generic_win_template extends: .generic_win_template
stage: build stage: build
artifacts: artifacts:
when: on_success expire_in: 1h
expire_in: 2h
paths: paths:
- build/* - build/minetest/_build/*
.package_win_template: .package_win_template:
extends: .generic_win_template extends: .generic_win_template
stage: package stage: package
script: script:
- cd build/minetest/_build - unzip build/minetest/_build/minetest-*.zip
- make package - cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-*-win*/bin/
- cd ../../../ - cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-*-win*/bin/
- mkdir minetest-win-${WIN_ARCH} - cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-*-win*/bin/
- unzip build/minetest/_build/minetest-*-win*.zip -d minetest-win-${WIN_ARCH}
- cp /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
- cp /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
- cp /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
artifacts: artifacts:
when: on_success
expire_in: 90 day expire_in: 90 day
paths: paths:
- minetest-win-*/* - minetest-*-win*/*
build:win32: build:win32:
extends: .build_win_template extends: .build_win_template
script: script:
- ./util/buildbot/buildwin32.sh build - ./util/buildbot/buildwin32.sh build
variables: variables:
NO_PACKAGE: "1"
WIN_ARCH: "i686" WIN_ARCH: "i686"
package:win32: package:win32:
extends: .package_win_template extends: .package_win_template
dependencies: needs:
- build:win32 - build:win32
variables: variables:
NO_PACKAGE: "1"
WIN_ARCH: "i686" WIN_ARCH: "i686"
build:win64: build:win64:
extends: .build_win_template extends: .build_win_template
script: script:
- ./util/buildbot/buildwin64.sh build - ./util/buildbot/buildwin64.sh build
variables: variables:
NO_PACKAGE: "1"
WIN_ARCH: "x86_64" WIN_ARCH: "x86_64"
package:win64: package:win64:
extends: .package_win_template extends: .package_win_template
dependencies: needs:
- build:win64 - build:win64
variables: variables:
NO_PACKAGE: "1"
WIN_ARCH: "x86_64" WIN_ARCH: "x86_64"
##
## Docker
##
package:docker: package:docker:
stage: package stage: package
image: docker:stable image: docker:stable
@ -293,6 +244,10 @@ package:docker:
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME - docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME
- docker push ${CONTAINER_IMAGE}/server:latest - docker push ${CONTAINER_IMAGE}/server:latest
##
## Gitlab Pages (Lua API documentation)
##
pages: pages:
stage: deploy stage: deploy
image: python:3.8 image: python:3.8
@ -308,3 +263,31 @@ pages:
only: only:
- master - master
##
## AppImage
##
package:appimage-client:
stage: package
image: appimagecrafters/appimage-builder
needs:
- build:ubuntu-18.04
before_script:
- apt-get update -y
- apt-get install -y git wget
# Collect files
- mkdir AppDir
- cp -a artifact/minetest/usr/ AppDir/usr/
- rm AppDir/usr/bin/minetestserver
- cp -a clientmods AppDir/usr/share/minetest
script:
- git clone $MINETEST_GAME_REPO AppDir/usr/share/minetest/games/minetest_game
- rm -rf AppDir/usr/share/minetest/games/minetest/.git
- export VERSION=$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA
# Remove PrefersNonDefaultGPU property due to validation errors
- sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/net.minetest.minetest.desktop
- appimage-builder --skip-test
artifacts:
expire_in: 90 day
paths:
- ./*.AppImage

View File

@ -72,3 +72,11 @@ files["builtin/mainmenu"] = {
"PLATFORM", "PLATFORM",
}, },
} }
files["builtin/common/tests"] = {
read_globals = {
"describe",
"it",
"assert",
},
}

View File

@ -1,33 +1,67 @@
# Documentation: https://git-scm.com/docs/git-check-mailmap#_mapping_authors
0gb.us <0gb.us@0gb.us> <us_0gb@laptop-0gb-us.0gb.us> 0gb.us <0gb.us@0gb.us> <us_0gb@laptop-0gb-us.0gb.us>
Calinou <calinou9999@gmail.com> <calinou9999spam@gmail.com> Calinou <calinou@opmbx.org> <calinou9999@gmail.com>
Perttu Ahola <celeron55@gmail.com> celeron55 <celeron55@gmail.com> Calinou <calinou@opmbx.org> <calinou9999spam@gmail.com>
Perttu Ahola <celeron55@gmail.com>
Perttu Ahola <celeron55@gmail.com> celeron55 <celeron55@armada.(none)> Perttu Ahola <celeron55@gmail.com> celeron55 <celeron55@armada.(none)>
Craig Robbins <kde.psych@gmail.com> <crobbins@localhost.localdomain> Zeno- <kde.psych@gmail.com>
Zeno- <kde.psych@gmail.com> <crobbins@localhost.localdomain>
Diego Martínez <kaeza@users.sf.net>
Diego Martínez <kaeza@users.sf.net> <lkaezadl3@gmail.com> Diego Martínez <kaeza@users.sf.net> <lkaezadl3@gmail.com>
Ilya Zhuravlev <zhuravlevilya@ya.ru>
Ilya Zhuravlev <zhuravlevilya@ya.ru> <whatever@xyz.is> Ilya Zhuravlev <zhuravlevilya@ya.ru> <whatever@xyz.is>
kwolekr <kwolekr@minetest.net> <mirrorisim@gmail.com> kwolekr <kwolekr@minetest.net> <mirrorisim@gmail.com>
PilzAdam <pilzadam@minetest.net> PilzAdam <adam-k@outlook.com> PilzAdam <pilzadam@minetest.net> <adam-k@outlook.com>
PilzAdam <pilzadam@minetest.net> Pilz Adam <PilzAdam@gmx.de> PilzAdam <pilzadam@minetest.net> <PilzAdam@gmx.de>
PilzAdam <pilzadam@minetest.net> PilzAdam <PilzAdam@gmx.de>
proller <proller@github.com> <proler@github.com> proller <proller@github.com> <proler@github.com>
proller <proller@github.com> <proler@gmail.com> proller <proller@github.com> <proler@gmail.com>
RealBadAngel <maciej.kasatkin@o2.pl> <mk@realbadangel.pl> RealBadAngel <maciej.kasatkin@o2.pl> <mk@realbadangel.pl>
RealBadAngel <maciej.kasatkin@o2.pl> <maciej.kasatkin@yahoo.com> RealBadAngel <maciej.kasatkin@o2.pl> <maciej.kasatkin@yahoo.com>
Selat <LongExampleTestName@gmail.com> <LongExampletestName@gmail.com> Selat <LongExampleTestName@gmail.com> <LongExampletestName@gmail.com>
ShadowNinja <shadowninja@minetest.net> ShadowNinja <noreply@gmail.com> ShadowNinja <shadowninja@minetest.net> ShadowNinja <noreply@gmail.com>
Shen Zheyu <arsdragonfly@gmail.com> arsdragonfly <arsdragonfly@gmail.com> Esteban I. Ruiz Moreno <exio4.com@gmail.com>
Pavel Elagin <elagin.pasha@gmail.com> elagin <elagin.pasha@gmail.com> Esteban I. Ruiz Moreno <exio4.com@gmail.com> <me@exio4.xyz>
Esteban I. Ruiz Moreno <exio4.com@gmail.com> Esteban I. RM <exio4.com@gmail.com> Lord James <neftali_dtctv@hotmail.com>
manuel duarte <ffrogger0@yahoo.com> manuel joaquim <ffrogger0@yahoo.com> BlockMen <nmuelll@web.de>
manuel duarte <ffrogger0@yahoo.com> sweetbomber <ffrogger _zero_ at yahoo dot com> sfan5 <sfan5@live.de>
Diego Martínez <kaeza@users.sf.net> kaeza <kaeza@users.sf.net> DannyDark <the_skeleton_of_a_child@yahoo.co.uk>
Diego Martínez <kaeza@users.sf.net> Diego Martinez <kaeza@users.sf.net> Ilya Pavlov <TTChangeTheWorld@gmail.com>
Lord James <neftali_dtctv@hotmail.com> Lord89James <neftali_dtctv@hotmail.com>
BlockMen <nmuelll@web.de> Block Men <nmuelll@web.de>
sfan5 <sfan5@live.de> Sfan5 <sfan5@live.de>
DannyDark <the_skeleton_of_a_child@yahoo.co.uk> dannydark <the_skeleton_of_a_child@yahoo.co.uk>
Ilya Pavlov <TTChangeTheWorld@gmail.com> Ilya <TTChangeTheWorld@gmail.com>
Ilya Zhuravlev <zhuravlevilya@ya.ru> xyzz <zhuravlevilya@ya.ru>
sapier <Sapier at GMX dot net> sapier <sapier AT gmx DOT net> sapier <Sapier at GMX dot net> sapier <sapier AT gmx DOT net>
sapier <Sapier at GMX dot net> sapier <sapier at gmx dot net> sapier <Sapier at GMX dot net> sapier <sapier at gmx dot net>
SmallJoker <SmallJoker@users.noreply.github.com> <mk939@ymail.com>
Loïc Blot <nerzhul@users.noreply.github.com>
Loïc Blot <nerzhul@users.noreply.github.com> <loic.blot@unix-experience.fr>
numzero <numzer0@yandex.ru> Vitaliy <numzer0@yandex.ru>
numzero <numzer0@yandex.ru> <silverunicorn2011@yandex.ru>
Jean-Patrick Guerrero <kilbith@users.noreply.github.com>
Jean-Patrick Guerrero <kilbith@users.noreply.github.com> <jeanpatrick.guerrero@gmail.com>
HybridDog <3192173+HybridDog@users.noreply.github.com> <ovvv@web.de>
srfqi <muhammadrifqipriyosusanto@gmail.com>
Dániel Juhász <juhdanad@gmail.com>
rubenwardy <rw@rubenwardy.com>
rubenwardy <rw@rubenwardy.com> <rubenwardy@gmail.com>
Paul Ouellette <oue.paul18@gmail.com>
Vanessa Dannenberg <vanessa.e.dannenberg@gmail.com> <vanessaezekowitz@gmail.com>
ClobberXD <ClobberXD@gmail.com>
ClobberXD <ClobberXD@gmail.com> <ClobberXD@protonmail.com>
ClobberXD <ClobberXD@gmail.com> <36130650+ClobberXD@users.noreply.github.com>
Auke Kok <sofar+github@foo-projects.org>
Auke Kok <sofar+github@foo-projects.org> <sofar@foo-projects.org>
Desour <vorunbekannt75@web.de>
Nathanaël Courant <Ekdohibs@users.noreply.github.com> <nathanael.courant@laposte.net>
Ezhh <owlecho@live.com>
paramat <paramat@users.noreply.github.com>
paramat <paramat@users.noreply.github.com> <mat.gregory@virginmedia.com>
lhofhansl <lhofhansl@yahoo.com> <larsh@apache.org>
red-001 <red-001@outlook.ie> <red-001@openmailbox.org>
Wuzzy <wuzzy2@mail.ru> <Wuzzy2@mail.ru>
Wuzzy <wuzzy2@mail.ru> <almikes@aol.com>
Jordach <jordach.snelling@gmail.com>
MoNTE48 <MoNTE48@mail.ua>
v-rob <robinsonvincent89@gmail.com>
v-rob <robinsonvincent89@gmail.com> <31123645+v-rob@users.noreply.github.com>
EvidenceB <49488517+EvidenceBKidscode@users.noreply.github.com>
gregorycu <gregory.currie@gmail.com>
Rogier <rogier777@gmail.com>
Rogier <rogier777@gmail.com> <Rogier-5@users.noreply.github.com>

51
AppImageBuilder.yml Normal file
View File

@ -0,0 +1,51 @@
version: 1
AppDir:
path: ./AppDir
app_info:
id: minetest
name: Minetest
icon: minetest
version: !ENV ${VERSION}
exec: usr/bin/minetest
exec_args: $@
runtime:
env:
APPDIR_LIBRARY_PATH: $APPDIR/usr/lib/x86_64-linux-gnu
apt:
arch: amd64
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic main universe
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32'
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates main universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main universe
include:
- libirrlicht1.8
- libxxf86vm1
- libgl1-mesa-glx
- libsqlite3-0
- libogg0
- libvorbis0a
- libopenal1
- libcurl3-gnutls
- libfreetype6
- zlib1g
- libgmp10
- libjsoncpp1
files:
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
AppImage:
update-information: None
sign-key: None
arch: x86_64

View File

@ -1,15 +1,11 @@
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 3.5)
if(${CMAKE_VERSION} STREQUAL "2.8.2") cmake_policy(SET CMP0025 OLD)
# Bug http://vtk.org/Bug/view.php?id=11020
message(WARNING "CMake/CPack version 2.8.2 will not create working .deb packages!")
endif()
# This can be read from ${PROJECT_NAME} after project() is called # This can be read from ${PROJECT_NAME} after project() is called
project(multicraft) project(multicraft)
set(PROJECT_NAME_CAPITALIZED "MultiCraft") set(PROJECT_NAME_CAPITALIZED "MultiCraft")
# Works only for cmake 3.1 and greater
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(GCC_MINIMUM_VERSION "4.8") set(GCC_MINIMUM_VERSION "4.8")
set(CLANG_MINIMUM_VERSION "3.4") set(CLANG_MINIMUM_VERSION "3.4")
@ -250,15 +246,15 @@ cpack_add_component(Docs
cpack_add_component(SUBGAME_MINETEST_GAME cpack_add_component(SUBGAME_MINETEST_GAME
DISPLAY_NAME "Minetest Game" DISPLAY_NAME "Minetest Game"
DESCRIPTION "The official subgame for the Minetest engine, that can easily extended by mods." DESCRIPTION "The default game bundled in the Minetest engine. Mainly used as a modding base."
GROUP "Subgames" GROUP "Games"
) )
cpack_add_component(SUBGAME_MINIMAL cpack_add_component(SUBGAME_MINIMAL
DISPLAY_NAME "Development Test" DISPLAY_NAME "Development Test"
DESCRIPTION "A minimal test game helping to develop the engine." DESCRIPTION "A basic testing environment used for engine development and sometimes for testing mods."
DISABLED #DISABLED does not mean it is disabled, and is just not selected by default. DISABLED #DISABLED does not mean it is disabled, and is just not selected by default.
GROUP "Subgames" GROUP "Games"
) )
cpack_add_component_group(Subgames cpack_add_component_group(Subgames
@ -279,11 +275,12 @@ if(WIN32)
set(CPACK_GENERATOR ZIP) set(CPACK_GENERATOR ZIP)
else() else()
set(CPACK_GENERATOR WIX ZIP) set(CPACK_GENERATOR WIX)
set(CPACK_PACKAGE_NAME "${PROJECT_NAME_CAPITALIZED}") set(CPACK_PACKAGE_NAME "${PROJECT_NAME_CAPITALIZED}")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME_CAPITALIZED}") set(CPACK_PACKAGE_INSTALL_DIRECTORY ".")
set(CPACK_PACKAGE_EXECUTABLES ${PROJECT_NAME} "${PROJECT_NAME_CAPITALIZED}") set(CPACK_PACKAGE_EXECUTABLES ${PROJECT_NAME} "${PROJECT_NAME_CAPITALIZED}")
set(CPACK_CREATE_DESKTOP_LINKS ${PROJECT_NAME}) set(CPACK_CREATE_DESKTOP_LINKS ${PROJECT_NAME})
set(CPACK_PACKAGING_INSTALL_PREFIX "/${PROJECT_NAME_CAPITALIZED}")
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/misc/multicraft-icon.ico") set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/misc/multicraft-icon.ico")
# Supported languages can be found at # Supported languages can be found at

View File

@ -21,7 +21,7 @@ WORKDIR /usr/src/multicraft
RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \ RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \
jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \ jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \
libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \ libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \
gmp-dev jsoncpp-dev postgresql-dev ca-certificates && \ gmp-dev jsoncpp-dev postgresql-dev luajit-dev ca-certificates && \
git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \ git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
rm -fr ./games/minetest_game/.git rm -fr ./games/minetest_game/.git
@ -51,7 +51,7 @@ RUN mkdir build && \
FROM alpine:3.11 FROM alpine:3.11
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq && \ RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit && \
adduser -D multicraft --uid 30000 -h /var/lib/multicraft && \ adduser -D multicraft --uid 30000 -h /var/lib/multicraft && \
chown -R multicraft:multicraft /var/lib/multicraft chown -R multicraft:multicraft /var/lib/multicraft

View File

@ -10,6 +10,9 @@ http://creativecommons.org/licenses/by-sa/3.0/
textures/base/pack/refresh.png is under the Apache 2 license textures/base/pack/refresh.png is under the Apache 2 license
https://www.apache.org/licenses/LICENSE-2.0.html https://www.apache.org/licenses/LICENSE-2.0.html
Textures by Zughy are under CC BY-SA 4.0
https://creativecommons.org/licenses/by-sa/4.0/
Authors of media files Authors of media files
----------------------- -----------------------
Everything not listed in here: Everything not listed in here:
@ -22,6 +25,8 @@ paramat:
textures/base/pack/menu_header.png textures/base/pack/menu_header.png
textures/base/pack/next_icon.png textures/base/pack/next_icon.png
textures/base/pack/prev_icon.png textures/base/pack/prev_icon.png
textures/base/pack/clear.png
textures/base/pack/search.png
rubenwardy, paramat: rubenwardy, paramat:
textures/base/pack/start_icon.png textures/base/pack/start_icon.png
@ -44,6 +49,14 @@ srifqi
textures/base/pack/joystick_off.png textures/base/pack/joystick_off.png
textures/base/pack/minimap_btn.png textures/base/pack/minimap_btn.png
Zughy:
textures/base/pack/cdb_add.png
textures/base/pack/cdb_clear.png
textures/base/pack/cdb_downloading.png
textures/base/pack/cdb_queued.png
textures/base/pack/cdb_update.png
textures/base/pack/cdb_viewonline.png
License of Minetest source code License of Minetest source code
------------------------------- -------------------------------

View File

@ -25,10 +25,10 @@ Table of Contents
Further documentation Further documentation
---------------------- ----------------------
- Minetest Website: http://minetest.net/ - Minetest Website: https://minetest.net/
- Minetest Wiki: http://wiki.minetest.net/ - Minetest Wiki: https://wiki.minetest.net/
- Minetest Developer wiki: http://dev.minetest.net/ - Minetest Developer wiki: https://dev.minetest.net/
- Minetest Forum: http://forum.minetest.net/ - Minetest Forum: https://forum.minetest.net/
- Minetest GitHub: https://github.com/minetest/minetest/ - Minetest GitHub: https://github.com/minetest/minetest/
- [doc/](doc/) directory of source distribution - [doc/](doc/) directory of source distribution
@ -141,7 +141,7 @@ For Debian/Ubuntu users:
For Fedora users: For Fedora users:
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel bzip2-libs gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel doxygen spatialindex-devel bzip2-devel sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel bzip2-libs gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel doxygen spatialindex-devel bzip2-devel
For Arch users: For Arch users:
sudo pacman -S base-devel libcurl-gnutls cmake libxxf86vm irrlicht libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses sudo pacman -S base-devel libcurl-gnutls cmake libxxf86vm irrlicht libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses
@ -296,13 +296,14 @@ It is highly recommended to use vcpkg as package manager.
After you successfully built vcpkg you can easily install the required libraries: After you successfully built vcpkg you can easily install the required libraries:
```powershell ```powershell
vcpkg install irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit --triplet x64-windows vcpkg install irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit gmp jsoncpp --triplet x64-windows
``` ```
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store. - `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound. - `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
- `freetype` is optional, it allows true-type font rendering. - `freetype` is optional, it allows true-type font rendering.
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter. - `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
There are other optional libraries, but they are not tested if they can build and link correctly. There are other optional libraries, but they are not tested if they can build and link correctly.
@ -335,7 +336,7 @@ This is outdated and not recommended. Follow the instructions on https://dev.min
Run the following script in PowerShell: Run the following script in PowerShell:
```powershell ```powershell
cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=0 -DENABLE_CURSES=0 cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=OFF -DENABLE_CURSES=OFF -DENABLE_SYSTEM_JSONCPP=ON
cmake --build . --config Release cmake --build . --config Release
``` ```
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct. Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.

View File

@ -26,6 +26,8 @@ import android.view.inputmethod.InputMethodManager;
import androidx.appcompat.widget.AppCompatEditText; import androidx.appcompat.widget.AppCompatEditText;
import java.util.Objects;
public class CustomEditText extends AppCompatEditText { public class CustomEditText extends AppCompatEditText {
public CustomEditText(Context context) { public CustomEditText(Context context) {
super(context); super(context);
@ -36,7 +38,7 @@ public class CustomEditText extends AppCompatEditText {
if (keyCode == KeyEvent.KEYCODE_BACK) { if (keyCode == KeyEvent.KEYCODE_BACK) {
InputMethodManager mgr = (InputMethodManager) InputMethodManager mgr = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE); getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.hideSoftInputFromWindow(this.getWindowToken(), 0); Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
} }
return false; return false;
} }

View File

@ -23,6 +23,11 @@ core.register_on_sending_chat_message(function(message)
return true return true
end end
-- Run core.registered_on_chatcommand callbacks.
if core.run_callbacks(core.registered_on_chatcommand, 5, cmd, param) then
return true
end
local cmd_def = core.registered_chatcommands[cmd] local cmd_def = core.registered_chatcommands[cmd]
if cmd_def then if cmd_def then
core.set_last_run_mod(cmd_def.mod_origin) core.set_last_run_mod(cmd_def.mod_origin)

View File

@ -4,6 +4,13 @@ core.callback_origins = {}
local getinfo = debug.getinfo local getinfo = debug.getinfo
debug.getinfo = nil debug.getinfo = nil
--- Runs given callbacks.
--
-- Note: this function is also called from C++
-- @tparam table callbacks a table with registered callbacks, like `core.registered_on_*`
-- @tparam number mode a RunCallbacksMode, as defined in src/script/common/c_internal.h
-- @param ... arguments for the callback
-- @return depends on mode
function core.run_callbacks(callbacks, mode, ...) function core.run_callbacks(callbacks, mode, ...)
assert(type(callbacks) == "table") assert(type(callbacks) == "table")
local cb_len = #callbacks local cb_len = #callbacks
@ -63,6 +70,7 @@ core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration
core.registered_on_shutdown, core.register_on_shutdown = make_registration() core.registered_on_shutdown, core.register_on_shutdown = make_registration()
core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration() core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration()
core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration() core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration()
core.registered_on_chatcommand, core.register_on_chatcommand = make_registration()
core.registered_on_death, core.register_on_death = make_registration() core.registered_on_death, core.register_on_death = make_registration()
core.registered_on_hp_modification, core.register_on_hp_modification = make_registration() core.registered_on_hp_modification, core.register_on_hp_modification = make_registration()
core.registered_on_damage_taken, core.register_on_damage_taken = make_registration() core.registered_on_damage_taken, core.register_on_damage_taken = make_registration()

View File

@ -31,11 +31,13 @@ function core.after(after, func, ...)
assert(tonumber(after) and type(func) == "function", assert(tonumber(after) and type(func) == "function",
"Invalid minetest.after invocation") "Invalid minetest.after invocation")
local expire = time + after local expire = time + after
jobs[#jobs + 1] = { local new_job = {
func = func, func = func,
expire = expire, expire = expire,
arg = {...}, arg = {...},
mod_origin = core.get_last_run_mod() mod_origin = core.get_last_run_mod(),
} }
jobs[#jobs + 1] = new_job
time_next = math.min(time_next, expire) time_next = math.min(time_next, expire)
return { cancel = function() new_job.func = function() end end }
end end

View File

@ -118,7 +118,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
return return
end end
local event = minetest.explode_table_event(fields.list) local event = core.explode_table_event(fields.list)
if event.type ~= "INV" then if event.type ~= "INV" then
local name = player:get_player_name() local name = player:get_player_name()
core.show_formspec(name, "__builtin:help_cmds", core.show_formspec(name, "__builtin:help_cmds",

View File

@ -696,3 +696,7 @@ function core.privs_to_string(privs, delim)
end end
return table.concat(list, delim) return table.concat(list, delim)
end end
function core.is_nan(number)
return number ~= number
end

View File

@ -44,6 +44,10 @@ describe("vector", function()
assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 })) assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 }))
end) end)
it("offset()", function()
assert.same({ x = 41, y = 52, z = 63 }, vector.offset(vector.new(1, 2, 3), 40, 50, 60))
end)
-- This function is needed because of floating point imprecision. -- This function is needed because of floating point imprecision.
local function almost_equal(a, b) local function almost_equal(a, b)
if type(a) == "number" then if type(a) == "number" then

View File

@ -139,6 +139,12 @@ function vector.divide(a, b)
end end
end end
function vector.offset(v, x, y, z)
return {x = v.x + x,
y = v.y + y,
z = v.z + z}
end
function vector.sort(a, b) function vector.sort(a, b)
return {x = min(a.x, b.x), y = min(a.y, b.y), z = min(a.z, b.z)}, return {x = min(a.x, b.x), y = min(a.y, b.y), z = min(a.z, b.z)},
{x = max(a.x, b.x), y = max(a.y, b.y), z = max(a.z, b.z)} {x = max(a.x, b.x), y = max(a.y, b.y), z = max(a.z, b.z)}

View File

@ -18,6 +18,8 @@
ui = {} ui = {}
ui.childlist = {} ui.childlist = {}
ui.default = nil ui.default = nil
-- Whether fstk is currently showing its own formspec instead of active ui elements.
ui.overridden = false
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function ui.add(child) function ui.add(child)
@ -58,6 +60,7 @@ local maintab = core.settings:get("maintab_LAST")
local connect_time = tonumber(core.settings:get("connect_time")) local connect_time = tonumber(core.settings:get("connect_time"))
function ui.update() function ui.update()
ui.overridden = false
local formspec = {} local formspec = {}
-- attempt auto restart -- attempt auto restart
@ -93,6 +96,7 @@ function ui.update()
"button[2,6.6;4,1;btn_reconnect_yes;" .. fgettext("Reconnect") .. "]", "button[2,6.6;4,1;btn_reconnect_yes;" .. fgettext("Reconnect") .. "]",
"button[8,6.6;4,1;btn_reconnect_no;" .. fgettext("Close") .. "]" "button[8,6.6;4,1;btn_reconnect_no;" .. fgettext("Close") .. "]"
} }
ui.overridden = true
elseif gamedata ~= nil and gamedata.errormessage ~= nil then elseif gamedata ~= nil and gamedata.errormessage ~= nil then
local error_message = core.formspec_escape(gamedata.errormessage) local error_message = core.formspec_escape(gamedata.errormessage)
@ -119,6 +123,7 @@ function ui.update()
error_title, error_message), error_title, error_message),
restart_btn restart_btn
} }
ui.overridden = true
else else
local active_toplevel_ui_elements = 0 local active_toplevel_ui_elements = 0
for key,value in pairs(ui.childlist) do for key,value in pairs(ui.childlist) do
@ -221,6 +226,16 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
core.event_handler = function(event) core.event_handler = function(event)
-- Handle error messages
if ui.overridden then
if event == "MenuQuit" then
gamedata.errormessage = nil
gamedata.reconnect_requested = false
ui.update()
end
return
end
if ui.handle_events(event) then if ui.handle_events(event) then
ui.update() ui.update()
return return

View File

@ -58,6 +58,11 @@ core.register_on_chat_message(function(name, message)
param = param or "" param = param or ""
-- Run core.registered_on_chatcommands callbacks.
if core.run_callbacks(core.registered_on_chatcommands, 5, name, cmd, param) then
return true
end
local cmd_def = core.registered_chatcommands[cmd] local cmd_def = core.registered_chatcommands[cmd]
if not cmd_def then if not cmd_def then
core.chat_send_player(name, "-!- Invalid command: " .. cmd) core.chat_send_player(name, "-!- Invalid command: " .. cmd)
@ -66,8 +71,17 @@ core.register_on_chat_message(function(name, message)
local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs) local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs)
if has_privs then if has_privs then
core.set_last_run_mod(cmd_def.mod_origin) core.set_last_run_mod(cmd_def.mod_origin)
local _, result = cmd_def.func(name, param) local success, result = cmd_def.func(name, param)
if result then if success == false and result == nil then
core.chat_send_player(name, "-!- Invalid command usage")
local help_def = core.registered_chatcommands["help"]
if help_def then
local _, helpmsg = help_def.func(name, cmd)
if helpmsg then
core.chat_send_player(name, helpmsg)
end
end
elseif result then
core.chat_send_player(name, result) core.chat_send_player(name, result)
end end
else else
@ -1093,10 +1107,10 @@ core.register_chatcommand("last-login", {
local pauth = core.get_auth_handler().get_auth(param) local pauth = core.get_auth_handler().get_auth(param)
if pauth and pauth.last_login and pauth.last_login ~= -1 then if pauth and pauth.last_login and pauth.last_login ~= -1 then
-- Time in UTC, ISO 8601 format -- Time in UTC, ISO 8601 format
return true, "Last login time was " .. return true, param.."'s last login time was " ..
os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login) os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login)
end end
return false, "Last login time is unknown" return false, param.."'s last login time is unknown"
end, end,
}) })

View File

@ -1,28 +1,5 @@
-- Minetest: builtin/deprecated.lua -- Minetest: builtin/deprecated.lua
--
-- Default material types
--
local function digprop_err()
core.log("deprecated", "The core.digprop_* functions are obsolete and need to be replaced by item groups.")
end
core.digprop_constanttime = digprop_err
core.digprop_stonelike = digprop_err
core.digprop_dirtlike = digprop_err
core.digprop_gravellike = digprop_err
core.digprop_woodlike = digprop_err
core.digprop_leaveslike = digprop_err
core.digprop_glasslike = digprop_err
function core.node_metadata_inventory_move_allow_all()
core.log("deprecated", "core.node_metadata_inventory_move_allow_all is obsolete and does nothing.")
end
function core.add_to_creative_inventory(itemstring)
core.log("deprecated", "core.add_to_creative_inventory is obsolete and does nothing.")
end
-- --
-- EnvRef -- EnvRef
-- --
@ -77,7 +54,7 @@ core.setting_save = setting_proxy("write")
function core.register_on_auth_fail(func) function core.register_on_auth_fail(func)
core.log("deprecated", "core.register_on_auth_fail " .. core.log("deprecated", "core.register_on_auth_fail " ..
"is obsolete and should be replaced by " .. "is deprecated and should be replaced by " ..
"core.register_on_authplayer instead.") "core.register_on_authplayer instead.")
core.register_on_authplayer(function (player_name, ip, is_success) core.register_on_authplayer(function (player_name, ip, is_success)

View File

@ -90,6 +90,9 @@ core.register_entity(":__builtin:falling_node", {
local textures local textures
if def.tiles and def.tiles[1] then if def.tiles and def.tiles[1] then
local tile = def.tiles[1] local tile = def.tiles[1]
if def.drawtype == "torchlike" and def.paramtype2 ~= "wallmounted" then
tile = def.tiles[2] or def.tiles[1]
end
if type(tile) == "table" then if type(tile) == "table" then
tile = tile.name tile = tile.name
end end
@ -133,7 +136,7 @@ core.register_entity(":__builtin:falling_node", {
-- Set collision box (certain nodeboxes only for now) -- Set collision box (certain nodeboxes only for now)
local nb_types = {fixed=true, leveled=true, connected=true} local nb_types = {fixed=true, leveled=true, connected=true}
if def.drawtype == "nodebox" and def.node_box and if def.drawtype == "nodebox" and def.node_box and
nb_types[def.node_box.type] then nb_types[def.node_box.type] and def.node_box.fixed then
local box = table.copy(def.node_box.fixed) local box = table.copy(def.node_box.fixed)
if type(box[1]) == "table" then if type(box[1]) == "table" then
box = #box == 1 and box[1] or nil -- We can only use a single box box = #box == 1 and box[1] or nil -- We can only use a single box
@ -150,9 +153,13 @@ core.register_entity(":__builtin:falling_node", {
-- Rotate entity -- Rotate entity
if def.drawtype == "torchlike" then if def.drawtype == "torchlike" then
self.object:set_yaw(pi*0.25) if def.paramtype2 == "wallmounted" then
elseif (node.param2 ~= 0 and (def.wield_image == "" self.object:set_yaw(pi*0.25)
or def.wield_image == nil)) else
self.object:set_yaw(-pi*0.25)
end
elseif ((node.param2 ~= 0 or def.drawtype == "nodebox" or def.drawtype == "mesh")
and (def.wield_image == "" or def.wield_image == nil))
or def.drawtype == "signlike" or def.drawtype == "signlike"
or def.drawtype == "mesh" or def.drawtype == "mesh"
or def.drawtype == "normal" or def.drawtype == "normal"
@ -167,16 +174,30 @@ core.register_entity(":__builtin:falling_node", {
elseif (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted") then elseif (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted") then
local rot = node.param2 % 8 local rot = node.param2 % 8
local pitch, yaw, roll = 0, 0, 0 local pitch, yaw, roll = 0, 0, 0
if rot == 1 then if def.drawtype == "nodebox" or def.drawtype == "mesh" then
pitch, yaw = pi, pi if rot == 0 then
elseif rot == 2 then pitch, yaw = pi/2, 0
pitch, yaw = pi/2, pi/2 elseif rot == 1 then
elseif rot == 3 then pitch, yaw = -pi/2, pi
pitch, yaw = pi/2, -pi/2 elseif rot == 2 then
elseif rot == 4 then pitch, yaw = 0, pi/2
pitch, yaw = pi/2, pi elseif rot == 3 then
elseif rot == 5 then pitch, yaw = 0, -pi/2
pitch, yaw = pi/2, 0 elseif rot == 4 then
pitch, yaw = 0, pi
end
else
if rot == 1 then
pitch, yaw = pi, pi
elseif rot == 2 then
pitch, yaw = pi/2, pi/2
elseif rot == 3 then
pitch, yaw = pi/2, -pi/2
elseif rot == 4 then
pitch, yaw = pi/2, pi
elseif rot == 5 then
pitch, yaw = pi/2, 0
end
end end
if def.drawtype == "signlike" then if def.drawtype == "signlike" then
pitch = pitch - pi/2 pitch = pitch - pi/2
@ -185,7 +206,7 @@ core.register_entity(":__builtin:falling_node", {
elseif rot == 1 then elseif rot == 1 then
yaw = yaw - pi/2 yaw = yaw - pi/2
end end
elseif def.drawtype == "mesh" or def.drawtype == "normal" then elseif def.drawtype == "mesh" or def.drawtype == "normal" or def.drawtype == "nodebox" then
if rot >= 0 and rot <= 1 then if rot >= 0 and rot <= 1 then
roll = roll + pi roll = roll + pi
else else

View File

@ -17,6 +17,8 @@ core.features = {
area_store_persistent_ids = true, area_store_persistent_ids = true,
pathfinder_works = true, pathfinder_works = true,
object_step_has_moveresult = true, object_step_has_moveresult = true,
direct_velocity_on_players = true,
use_texture_alpha_string_modes = true,
} }
function core.has_feature(arg) function core.has_feature(arg)

View File

@ -676,12 +676,13 @@ function core.node_dig(pos, node, digger)
local diggername = user_name(digger) local diggername = user_name(digger)
local log = make_log(diggername) local log = make_log(diggername)
local def = core.registered_nodes[node.name] local def = core.registered_nodes[node.name]
-- Copy pos because the callback could modify it
if def and (not def.diggable or if def and (not def.diggable or
(def.can_dig and not def.can_dig(pos, digger))) then (def.can_dig and not def.can_dig(vector.new(pos), digger))) then
log("info", diggername .. " tried to dig " log("info", diggername .. " tried to dig "
.. node.name .. " which is not diggable " .. node.name .. " which is not diggable "
.. core.pos_to_string(pos)) .. core.pos_to_string(pos))
return return false
end end
if core.is_protected(pos, diggername) then if core.is_protected(pos, diggername) then
@ -690,7 +691,7 @@ function core.node_dig(pos, node, digger)
.. " at protected position " .. " at protected position "
.. core.pos_to_string(pos)) .. core.pos_to_string(pos))
core.record_protection_violation(pos, diggername) core.record_protection_violation(pos, diggername)
return return false
end end
log('action', diggername .. " digs " log('action', diggername .. " digs "
@ -773,6 +774,8 @@ function core.node_dig(pos, node, digger)
local node_copy = {name=node.name, param1=node.param1, param2=node.param2} local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
callback(pos_copy, node_copy, digger) callback(pos_copy, node_copy, digger)
end end
return true
end end
function core.itemstring_with_palette(item, palette_index) function core.itemstring_with_palette(item, palette_index)
@ -800,7 +803,7 @@ end
-- Item definition defaults -- Item definition defaults
-- --
local default_stack_max = tonumber(minetest.settings:get("default_stack_max")) or 64 local default_stack_max = tonumber(core.settings:get("default_stack_max")) or 64
core.nodedef_default = { core.nodedef_default = {
-- Item properties -- Item properties
@ -829,10 +832,6 @@ core.nodedef_default = {
on_receive_fields = nil, on_receive_fields = nil,
on_metadata_inventory_move = core.node_metadata_inventory_move_allow_all,
on_metadata_inventory_offer = core.node_metadata_inventory_offer_allow_all,
on_metadata_inventory_take = core.node_metadata_inventory_take_allow_all,
-- Node properties -- Node properties
drawtype = "normal", drawtype = "normal",
visual_scale = 1.0, visual_scale = 1.0,
@ -843,7 +842,6 @@ core.nodedef_default = {
-- {name="", backface_culling=true}, -- {name="", backface_culling=true},
-- {name="", backface_culling=true}, -- {name="", backface_culling=true},
--}, --},
alpha = 255,
post_effect_color = {a=0, r=0, g=0, b=0}, post_effect_color = {a=0, r=0, g=0, b=0},
paramtype = "none", paramtype = "none",
paramtype2 = "none", paramtype2 = "none",

View File

@ -100,8 +100,9 @@ core.register_entity(":__builtin:item", {
local max_count = stack:get_stack_max() local max_count = stack:get_stack_max()
local count = min(stack:get_count(), max_count) local count = min(stack:get_count(), max_count)
local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3) local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3)
local def = core.registered_nodes[itemname] local def = core.registered_items[itemname]
local glow = def and floor(def.light_source / 2 + 0.5) local glow = def and def.light_source and
floor(def.light_source / 2 + 0.5)
self.object:set_properties({ self.object:set_properties({
is_visible = true, is_visible = true,

View File

@ -43,5 +43,5 @@ core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool
return -- barely noticeable, so don't even send return -- barely noticeable, so don't even send
end end
player:add_player_velocity(kdir) player:add_velocity(kdir)
end) end)

View File

@ -285,3 +285,26 @@ end
function core.cancel_shutdown_requests() function core.cancel_shutdown_requests()
core.request_shutdown("", false, -1) core.request_shutdown("", false, -1)
end end
-- Callback handling for dynamic_add_media
local dynamic_add_media_raw = core.dynamic_add_media_raw
core.dynamic_add_media_raw = nil
function core.dynamic_add_media(filepath, callback)
local ret = dynamic_add_media_raw(filepath)
if ret == false then
return ret
end
if callback == nil then
core.log("deprecated", "Calling minetest.dynamic_add_media without "..
"a callback is deprecated and will stop working in future versions.")
else
-- At the moment async loading is not actually implemented, so we
-- immediately call the callback ourselves
for _, name in ipairs(ret) do
callback(name)
end
end
return true
end

View File

@ -332,13 +332,6 @@ for name in pairs(forbidden_item_names) do
register_alias_raw(name, "") register_alias_raw(name, "")
end end
-- Obsolete:
-- Aliases for core.register_alias (how ironic...)
-- core.alias_node = core.register_alias
-- core.alias_tool = core.register_alias
-- core.alias_craftitem = core.register_alias
-- --
-- Built-in node definitions. Also defined in C. -- Built-in node definitions. Also defined in C.
-- --
@ -611,6 +604,7 @@ core.unregister_biome = make_wrap_deregistration(core.register_biome,
core.clear_registered_biomes, core.registered_biomes) core.clear_registered_biomes, core.registered_biomes)
core.registered_on_chat_messages, core.register_on_chat_message = make_registration() core.registered_on_chat_messages, core.register_on_chat_message = make_registration()
core.registered_on_chatcommands, core.register_on_chatcommand = make_registration()
core.registered_globalsteps, core.register_globalstep = make_registration() core.registered_globalsteps, core.register_globalstep = make_registration()
core.registered_playerevents, core.register_playerevent = make_registration() core.registered_playerevents, core.register_playerevent = make_registration()
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration() core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
@ -639,6 +633,7 @@ core.registered_can_bypass_userlimit, core.register_can_bypass_userlimit = make_
core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration() core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration()
core.registered_on_player_inventory_actions, core.register_on_player_inventory_action = make_registration() core.registered_on_player_inventory_actions, core.register_on_player_inventory_action = make_registration()
core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration() core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration()
core.registered_on_rightclickplayers, core.register_on_rightclickplayer = make_registration()
-- --

View File

@ -41,9 +41,20 @@ if INIT == "game" then
assert(not core.get_http_api) assert(not core.get_http_api)
elseif INIT == "mainmenu" then elseif INIT == "mainmenu" then
local mm_script = core.settings:get("main_menu_script") local mm_script = core.settings:get("main_menu_script")
local custom_loaded = false
if mm_script and mm_script ~= "" then if mm_script and mm_script ~= "" then
dofile(mm_script) local testfile = io.open(mm_script, "r")
else if testfile then
testfile:close()
dofile(mm_script)
custom_loaded = true
core.log("info", "Loaded custom main menu script: "..mm_script)
else
core.log("error", "Failed to load custom main menu script: "..mm_script)
core.log("info", "Falling back to default main menu script")
end
end
if not custom_loaded then
dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua") dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua")
end end
elseif INIT == "async" then elseif INIT == "async" then

View File

@ -56,29 +56,6 @@ function image_column(tooltip)
"11=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png") "11=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png")
end end
--------------------------------------------------------------------------------
function order_favorite_list(list, mobile)
local res = {}
local non_mobile_servers = {}
-- orders the multicraft list before support
for i = 1, #list do
local fav = list[i]
if mobile and not fav.mobile_friendly then
non_mobile_servers[("%s:%s"):format(fav.address, fav.port)] = fav
elseif fav.server_id == "multicraft" then
res[#res + 1] = fav
end
end
for i = 1, #list do
local fav = list[i]
if (mobile and fav.mobile_friendly or not mobile) and
is_server_protocol_compat(fav.proto_min, fav.proto_max) and
fav.server_id ~= "multicraft" then
res[#res + 1] = fav
end
end
return res, non_mobile_servers
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function render_serverlist_row(spec, is_favorite, is_approved) function render_serverlist_row(spec, is_favorite, is_approved)
@ -93,7 +70,7 @@ function render_serverlist_row(spec, is_favorite, is_approved)
if spec.name then if spec.name then
text = text .. core.formspec_escape(spec.name:trim()) text = text .. core.formspec_escape(spec.name:trim())
elseif spec.address then elseif spec.address then
text = text .. spec.address:trim() text = text .. core.formspec_escape(spec.address:trim())
if spec.port then if spec.port then
text = text .. ":" .. spec.port text = text .. ":" .. spec.port
end end
@ -166,35 +143,15 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
os.tempfolder = function() os.tempfolder = function()
if core.settings:get("TMPFolder") then local temp = core.get_temp_path()
return core.settings:get("TMPFolder") .. DIR_DELIM .. "MT_" .. math.random(0,10000) return temp .. DIR_DELIM .. "MT_" .. math.random(0, 10000)
end end
local filetocheck = os.tmpname() --------------------------------------------------------------------------------
os.remove(filetocheck) os.tmpname = function()
local path = os.tempfolder()
-- luacheck: ignore io.open(path, "w"):close()
-- https://blogs.msdn.microsoft.com/vcblog/2014/06/18/c-runtime-crt-features-fixes-and-breaking-changes-in-visual-studio-14-ctp1/ return path
-- The C runtime (CRT) function called by os.tmpname is tmpnam.
-- Microsofts tmpnam implementation in older CRT / MSVC releases is defective.
-- tmpnam return values starting with a backslash characterize this behavior.
-- https://sourceforge.net/p/mingw-w64/bugs/555/
-- MinGW tmpnam implementation is forwarded to the CRT directly.
-- https://sourceforge.net/p/mingw-w64/discussion/723797/thread/55520785/
-- MinGW links to an older CRT release (msvcrt.dll).
-- Due to legal concerns MinGW will never use a newer CRT.
--
-- Make use of TEMP to compose the temporary filename if an old
-- style tmpnam return value is detected.
if filetocheck:sub(1, 1) == "\\" then
local tempfolder = os.getenv("TEMP")
return tempfolder .. filetocheck
end
local randname = "MTTempModFolder_" .. math.random(0,10000)
local backstring = filetocheck:reverse()
return filetocheck:sub(0, filetocheck:len() - backstring:find(DIR_DELIM) + 1) ..
randname
end end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -226,42 +183,6 @@ function menu_handle_key_up_down(fields, textlist, settingname)
return false return false
end end
--------------------------------------------------------------------------------
function asyncOnlineFavourites(mobile)
if not menudata.public_known then
menudata.public_known = {{
name = fgettext("Loading..."),
description = fgettext_ne("Try reenabling public serverlist and check your internet connection.")
}}
end
menudata.favorites = menudata.public_known
menudata.favorites_is_public = true
if not menudata.public_downloading then
menudata.public_downloading = true
else
return
end
core.handle_async(
function(param)
return core.get_favorites("online")
end,
nil,
function(result)
menudata.public_downloading = nil
local favs, non_mobile = order_favorite_list(result, mobile)
if favs[1] then
menudata.public_known = favs
menudata.favorites = menudata.public_known
menudata.favorites_is_public = true
menudata.non_mobile_servers = non_mobile
end
core.event_handler("Refresh")
end
)
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency) function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency)
local textlines = core.wrap_text(text, textlen, true) local textlines = core.wrap_text(text, textlen, true)

View File

@ -76,7 +76,7 @@ local function get_formspec(data)
"label[1.75,0;" .. data.worldspec.name .. "]" "label[1.75,0;" .. data.worldspec.name .. "]"
if mod.is_modpack or mod.type == "game" then if mod.is_modpack or mod.type == "game" then
local info = minetest.formspec_escape( local info = core.formspec_escape(
core.get_content_info(mod.path).description) core.get_content_info(mod.path).description)
if info == "" then if info == "" then
if mod.is_modpack then if mod.is_modpack then

View File

@ -15,7 +15,7 @@
--with this program; if not, write to the Free Software Foundation, Inc., --with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
if not minetest.get_http_api then if not core.get_http_api then
function create_store_dlg() function create_store_dlg()
return messagebox("store", return messagebox("store",
fgettext("ContentDB is not available when Minetest was compiled without cURL")) fgettext("ContentDB is not available when Minetest was compiled without cURL"))
@ -23,9 +23,11 @@ if not minetest.get_http_api then
return return
end end
local store = { packages = {}, packages_full = {} } -- 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 http = minetest.get_http_api() local http = core.get_http_api()
-- Screenshot -- Screenshot
local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb" local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb"
@ -45,6 +47,9 @@ local filter_types_titles = {
fgettext("Texture packs"), fgettext("Texture packs"),
} }
local number_downloading = 0
local download_queue = {}
local filter_types_type = { local filter_types_type = {
nil, nil,
"game", "game",
@ -67,12 +72,14 @@ local function download_package(param)
end end
end end
local function start_install(calling_dialog, package) local function start_install(package)
local params = { local params = {
package = package, package = package,
filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip", filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip",
} }
number_downloading = number_downloading + 1
local function callback(result) local function callback(result)
if result.successful then if result.successful then
local path, msg = pkgmgr.install(package.type, local path, msg = pkgmgr.install(package.type,
@ -124,9 +131,20 @@ local function start_install(calling_dialog, package)
end end
package.downloading = false package.downloading = false
number_downloading = number_downloading - 1
local next = download_queue[1]
if next then
table.remove(download_queue, 1)
start_install(next)
end
ui.update() ui.update()
end end
package.queued = false
package.downloading = true package.downloading = true
if not core.handle_async(download_package, params, callback) then if not core.handle_async(download_package, params, callback) then
@ -136,6 +154,341 @@ local function start_install(calling_dialog, package)
end end
end end
local function queue_download(package)
local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads"))
if number_downloading < max_concurrent_downloads then
start_install(package)
else
table.insert(download_queue, package)
package.queued = true
end
end
local function get_raw_dependencies(package)
if package.raw_deps then
return package.raw_deps
end
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 response = http.fetch_sync({ url = url })
if not response.succeeded then
return
end
local data = core.parse_json(response.data) or {}
local content_lookup = {}
for _, pkg in pairs(store.packages_full) do
content_lookup[pkg.id] = pkg
end
for id, raw_deps in pairs(data) do
local package2 = content_lookup[id:lower()]
if package2 and not package2.raw_deps then
package2.raw_deps = raw_deps
for _, dep in pairs(raw_deps) do
local packages = {}
for i=1, #dep.packages do
packages[#packages + 1] = content_lookup[dep.packages[i]:lower()]
end
dep.packages = packages
end
end
end
return package.raw_deps
end
local function has_hard_deps(raw_deps)
for i=1, #raw_deps do
if not raw_deps[i].is_optional then
return true
end
end
return false
end
-- Recursively resolve dependencies, given the installed mods
local function resolve_dependencies_2(raw_deps, installed_mods, out)
local function resolve_dep(dep)
-- Check whether it's already installed
if installed_mods[dep.name] then
return {
is_optional = dep.is_optional,
name = dep.name,
installed = true,
}
end
-- Find exact name matches
local fallback
for _, package in pairs(dep.packages) do
if package.type ~= "game" then
if package.name == dep.name then
return {
is_optional = dep.is_optional,
name = dep.name,
installed = false,
package = package,
}
elseif not fallback then
fallback = package
end
end
end
-- Otherwise, find the first mod that fulfils it
if fallback then
return {
is_optional = dep.is_optional,
name = dep.name,
installed = false,
package = fallback,
}
end
return {
is_optional = dep.is_optional,
name = dep.name,
installed = false,
}
end
for _, dep in pairs(raw_deps) do
if not dep.is_optional and not out[dep.name] then
local result = resolve_dep(dep)
out[dep.name] = result
if result and result.package and not result.installed then
local raw_deps2 = get_raw_dependencies(result.package)
if raw_deps2 then
resolve_dependencies_2(raw_deps2, installed_mods, out)
end
end
end
end
return true
end
-- Resolve dependencies for a package, calls the recursive version.
local function resolve_dependencies(raw_deps, game)
assert(game)
local installed_mods = {}
local mods = {}
pkgmgr.get_game_mods(game, mods)
for _, mod in pairs(mods) do
installed_mods[mod.name] = true
end
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
installed_mods[mod.name] = true
end
local out = {}
if not resolve_dependencies_2(raw_deps, installed_mods, out) then
return nil
end
local retval = {}
for _, dep in pairs(out) do
retval[#retval + 1] = dep
end
table.sort(retval, function(a, b)
return a.name < b.name
end)
return retval
end
local install_dialog = {}
function install_dialog.get_formspec()
local package = install_dialog.package
local raw_deps = install_dialog.raw_deps
local will_install_deps = install_dialog.will_install_deps
local selected_game_idx = 1
local selected_gameid = core.settings:get("menu_last_game")
local games = table.copy(pkgmgr.games)
for i=1, #games do
if selected_gameid and games[i].id == selected_gameid then
selected_game_idx = i
end
games[i] = core.formspec_escape(games[i].name)
end
local selected_game = pkgmgr.games[selected_game_idx]
local deps_to_install = 0
local deps_not_found = 0
install_dialog.dependencies = resolve_dependencies(raw_deps, selected_game)
local formatted_deps = {}
for _, dep in pairs(install_dialog.dependencies) do
formatted_deps[#formatted_deps + 1] = "#fff"
formatted_deps[#formatted_deps + 1] = core.formspec_escape(dep.name)
if dep.installed then
formatted_deps[#formatted_deps + 1] = "#ccf"
formatted_deps[#formatted_deps + 1] = fgettext("Already installed")
elseif dep.package then
formatted_deps[#formatted_deps + 1] = "#cfc"
formatted_deps[#formatted_deps + 1] = fgettext("$1 by $2", dep.package.title, dep.package.author)
deps_to_install = deps_to_install + 1
else
formatted_deps[#formatted_deps + 1] = "#f00"
formatted_deps[#formatted_deps + 1] = fgettext("Not found")
deps_not_found = deps_not_found + 1
end
end
local message_bg = "#3333"
local message
if will_install_deps then
message = fgettext("$1 and $2 dependencies will be installed.", package.title, deps_to_install)
else
message = fgettext("$1 will be installed, and $2 dependencies will be skipped.", package.title, deps_to_install)
end
if deps_not_found > 0 then
message = fgettext("$1 required dependencies could not be found.", deps_not_found) ..
" " .. fgettext("Please check that the base game is correct.", deps_not_found) ..
"\n" .. message
message_bg = mt_color_orange
end
local formspec = {
"formspec_version[3]",
"size[7,7.85]",
"style[title;border=false]",
"box[0,0;7,0.5;#3333]",
"button[0,0;7,0.5;title;", fgettext("Install $1", package.title) , "]",
"container[0.375,0.70]",
"label[0,0.25;", fgettext("Base Game:"), "]",
"dropdown[2,0;4.25,0.5;gameid;", table.concat(games, ","), ";", selected_game_idx, "]",
"label[0,0.8;", fgettext("Dependencies:"), "]",
"tablecolumns[color;text;color;text]",
"table[0,1.1;6.25,3;packages;", table.concat(formatted_deps, ","), "]",
"container_end[]",
"checkbox[0.375,5.1;will_install_deps;",
fgettext("Install missing dependencies"), ";",
will_install_deps and "true" or "false", "]",
"box[0,5.4;7,1.2;", message_bg, "]",
"textarea[0.375,5.5;6.25,1;;;", message, "]",
"container[1.375,6.85]",
"button[0,0;2,0.8;install_all;", fgettext("Install"), "]",
"button[2.25,0;2,0.8;cancel;", fgettext("Cancel"), "]",
"container_end[]",
}
return table.concat(formspec, "")
end
function install_dialog.handle_submit(this, fields)
if fields.cancel then
this:delete()
return true
end
if fields.will_install_deps ~= nil then
install_dialog.will_install_deps = core.is_yes(fields.will_install_deps)
return true
end
if fields.install_all then
queue_download(install_dialog.package)
if install_dialog.will_install_deps then
for _, dep in pairs(install_dialog.dependencies) do
if not dep.is_optional and not dep.installed and dep.package then
queue_download(dep.package)
end
end
end
this:delete()
return true
end
if fields.gameid then
for _, game in pairs(pkgmgr.games) do
if game.name == fields.gameid then
core.settings:set("menu_last_game", game.id)
break
end
end
return true
end
return false
end
function install_dialog.create(package, raw_deps)
install_dialog.dependencies = nil
install_dialog.package = package
install_dialog.raw_deps = raw_deps
install_dialog.will_install_deps = true
return dialog_create("install_dialog",
install_dialog.get_formspec,
install_dialog.handle_submit,
nil)
end
local confirm_overwrite = {}
function confirm_overwrite.get_formspec()
local package = confirm_overwrite.package
return "size[11.5,4.5,true]" ..
"label[2,2;" ..
fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name) .. "]"..
"style[install;bgcolor=red]" ..
"button[3.25,3.5;2.5,0.5;install;" .. fgettext("Overwrite") .. "]" ..
"button[5.75,3.5;2.5,0.5;cancel;" .. fgettext("Cancel") .. "]"
end
function confirm_overwrite.handle_submit(this, fields)
if fields.cancel then
this:delete()
return true
end
if fields.install then
this:delete()
confirm_overwrite.callback()
return true
end
return false
end
function confirm_overwrite.create(package, callback)
assert(type(package) == "table")
assert(type(callback) == "function")
confirm_overwrite.package = package
confirm_overwrite.callback = callback
return dialog_create("confirm_overwrite",
confirm_overwrite.get_formspec,
confirm_overwrite.handle_submit,
nil)
end
local function get_file_extension(path) local function get_file_extension(path)
local parts = path:split(".") local parts = path:split(".")
return parts[#parts] return parts[#parts]
@ -203,7 +556,7 @@ function store.load()
end end
end end
local timeout = tonumber(minetest.settings:get("curl_file_download_timeout")) 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, timeout = timeout })
if not response.succeeded then if not response.succeeded then
return return
@ -224,6 +577,7 @@ function store.load()
end end
end end
store.packages_full_unordered = store.packages_full
store.packages = store.packages_full store.packages = store.packages_full
store.loaded = true store.loaded = true
end end
@ -232,7 +586,7 @@ function store.update_paths()
local mod_hash = {} local mod_hash = {}
pkgmgr.refresh_globals() pkgmgr.refresh_globals()
for _, mod in pairs(pkgmgr.global_mods:get_list()) do for _, mod in pairs(pkgmgr.global_mods:get_list()) do
if mod.author then if mod.author and mod.release > 0 then
mod_hash[mod.author:lower() .. "/" .. mod.name] = mod mod_hash[mod.author:lower() .. "/" .. mod.name] = mod
end end
end end
@ -240,14 +594,14 @@ function store.update_paths()
local game_hash = {} local game_hash = {}
pkgmgr.update_gamelist() pkgmgr.update_gamelist()
for _, game in pairs(pkgmgr.games) do for _, game in pairs(pkgmgr.games) do
if game.author ~= "" then if game.author ~= "" and game.release > 0 then
game_hash[game.author:lower() .. "/" .. game.id] = game game_hash[game.author:lower() .. "/" .. game.id] = game
end end
end end
local txp_hash = {} local txp_hash = {}
for _, txp in pairs(pkgmgr.get_texture_packs()) do for _, txp in pairs(pkgmgr.get_texture_packs()) do
if txp.author then if txp.author and txp.release > 0 then
txp_hash[txp.author:lower() .. "/" .. txp.name] = txp txp_hash[txp.author:lower() .. "/" .. txp.name] = txp
end end
end end
@ -271,6 +625,33 @@ function store.update_paths()
end end
end end
function store.sort_packages()
local ret = {}
-- Add installed content
for i=1, #store.packages_full_unordered do
local package = store.packages_full_unordered[i]
if package.path then
ret[#ret + 1] = package
end
end
-- Sort installed content by title
table.sort(ret, function(a, b)
return a.title < b.title
end)
-- Add uninstalled content
for i=1, #store.packages_full_unordered do
local package = store.packages_full_unordered[i]
if not package.path then
ret[#ret + 1] = package
end
end
store.packages_full = ret
end
function store.filter_packages(query) function store.filter_packages(query)
if query == "" and filter_type == 1 then if query == "" and filter_type == 1 then
store.packages = store.packages_full store.packages = store.packages_full
@ -282,7 +663,7 @@ function store.filter_packages(query)
table.insert(keywords, word) table.insert(keywords, word)
end end
local function matches_keywords(package, keywords) local function matches_keywords(package)
for k = 1, #keywords do for k = 1, #keywords do
local keyword = keywords[k] local keyword = keywords[k]
@ -299,12 +680,11 @@ function store.filter_packages(query)
store.packages = {} store.packages = {}
for _, package in pairs(store.packages_full) do for _, package in pairs(store.packages_full) do
if (query == "" or matches_keywords(package, keywords)) and if (query == "" or matches_keywords(package)) and
(filter_type == 1 or package.type == filter_types_type[filter_type]) then (filter_type == 1 or package.type == filter_types_type[filter_type]) then
store.packages[#store.packages + 1] = package store.packages[#store.packages + 1] = package
end end
end end
end end
function store.get_formspec(dlgdata) function store.get_formspec(dlgdata)
@ -317,7 +697,6 @@ function store.get_formspec(dlgdata)
local W = 15.75 local W = 15.75
local H = 9.5 local H = 9.5
local formspec local formspec
if #store.packages_full > 0 then if #store.packages_full > 0 then
formspec = { formspec = {
@ -326,11 +705,15 @@ function store.get_formspec(dlgdata)
"background[0,0;0,0;" .. core.formspec_escape(defaulttexturedir .. "background[0,0;0,0;" .. core.formspec_escape(defaulttexturedir ..
"bg_common.png") .. ";true;32]", "bg_common.png") .. ";true;32]",
"position[0.5,0.55]", "position[0.5,0.55]",
"style[status,downloading,queued;border=false]",
"container[0.375,0.375]", "container[0.375,0.375]",
"field[0,0;10.225,0.8;search_string;;", core.formspec_escape(search_string), "]", "field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]",
"field_close_on_enter[search_string;false]", "field_close_on_enter[search_string;false]",
"button[10.225,0;2,0.8;search;", fgettext("Search"), "]", "image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
"dropdown[12.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]", "image_button[8.125,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";clear;]",
"dropdown[9.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]",
"container_end[]", "container_end[]",
-- Page nav buttons -- Page nav buttons
@ -349,6 +732,35 @@ function store.get_formspec(dlgdata)
"container_end[]", "container_end[]",
} }
if number_downloading > 0 then
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;downloading;"
if #download_queue > 0 then
formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", number_downloading, #download_queue)
else
formspec[#formspec + 1] = fgettext("$1 downloading...", number_downloading)
end
formspec[#formspec + 1] = "]"
else
local num_avail_updates = 0
for i=1, #store.packages_full do
local package = store.packages_full[i]
if package.path and package.installed_release < package.release and
not (package.downloading or package.queued) then
num_avail_updates = num_avail_updates + 1
end
end
if num_avail_updates == 0 then
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;status;"
formspec[#formspec + 1] = fgettext("No updates")
formspec[#formspec + 1] = "]"
else
formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;update_all;"
formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates)
formspec[#formspec + 1] = "]"
end
end
if #store.packages == 0 then if #store.packages == 0 then
formspec[#formspec + 1] = "label[4,3;" formspec[#formspec + 1] = "label[4,3;"
formspec[#formspec + 1] = fgettext("No results") formspec[#formspec + 1] = fgettext("No results")
@ -367,11 +779,17 @@ function store.get_formspec(dlgdata)
} }
end end
-- download/queued tooltips always have the same message
local tooltip_colors = ";#dff6f5;#302c2e]"
formspec[#formspec + 1] = "tooltip[downloading;" .. fgettext("Downloading...") .. tooltip_colors
formspec[#formspec + 1] = "tooltip[queued;" .. fgettext("Queued") .. tooltip_colors
local start_idx = (cur_page - 1) * num_per_page + 1 local start_idx = (cur_page - 1) * num_per_page + 1
for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do
local package = store.packages[i] local package = store.packages[i]
local container_y = (i - start_idx) * 1.375 + (2*0.375 + 0.8)
formspec[#formspec + 1] = "container[0.375," formspec[#formspec + 1] = "container[0.375,"
formspec[#formspec + 1] = (i - start_idx) * 1.375 + (2*0.375 + 0.8) formspec[#formspec + 1] = container_y
formspec[#formspec + 1] = "]" formspec[#formspec + 1] = "]"
-- image -- image
@ -382,55 +800,55 @@ function store.get_formspec(dlgdata)
-- title -- title
formspec[#formspec + 1] = "label[1.875,0.1;" formspec[#formspec + 1] = "label[1.875,0.1;"
formspec[#formspec + 1] = core.formspec_escape( formspec[#formspec + 1] = core.formspec_escape(
minetest.colorize(mt_color_green, package.title) .. core.colorize(mt_color_green, package.title) ..
minetest.colorize("#BFBFBF", " by " .. package.author)) core.colorize("#BFBFBF", " by " .. package.author))
formspec[#formspec + 1] = "]" formspec[#formspec + 1] = "]"
-- buttons -- buttons
local description_width = W - 0.375*5 - 1 - 2*1.5 local left_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "container[" formspec[#formspec + 1] = "container["
formspec[#formspec + 1] = W - 0.375*2 formspec[#formspec + 1] = W - 0.375*2
formspec[#formspec + 1] = ",0.1]" formspec[#formspec + 1] = ",0.1]"
if package.downloading then if package.downloading then
formspec[#formspec + 1] = "style[download;border=false]" formspec[#formspec + 1] = "animated_image[-1.7,-0.15;1,1;downloading;"
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "button[-3.5,0;2,0.8;download;" formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
formspec[#formspec + 1] = fgettext("Downloading...") elseif package.queued then
formspec[#formspec + 1] = "]" formspec[#formspec + 1] = left_base
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "cdb_queued.png;queued]"
elseif not package.path then elseif not package.path then
formspec[#formspec + 1] = "button[-3,0;1.5,0.8;install_" local elem_name = "install_" .. i .. ";"
formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
formspec[#formspec + 1] = ";" formspec[#formspec + 1] = left_base .. "cdb_add.png;" .. elem_name .. "]"
formspec[#formspec + 1] = fgettext("Install") formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Install") .. tooltip_colors
formspec[#formspec + 1] = "]"
else else
if package.installed_release < package.release then if package.installed_release < package.release then
description_width = description_width - 1.5
-- The install_ action also handles updating -- The install_ action also handles updating
formspec[#formspec + 1] = "button[-4.5,0;1.5,0.8;install_" local elem_name = "install_" .. i .. ";"
formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#28ccdf]"
formspec[#formspec + 1] = ";" formspec[#formspec + 1] = left_base .. "cdb_update.png;" .. elem_name .. "]"
formspec[#formspec + 1] = fgettext("Update") formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Update") .. tooltip_colors
formspec[#formspec + 1] = "]" else
end
formspec[#formspec + 1] = "button[-3,0;1.5,0.8;uninstall_" local elem_name = "uninstall_" .. i .. ";"
formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]"
formspec[#formspec + 1] = ";" formspec[#formspec + 1] = left_base .. "cdb_clear.png;" .. elem_name .. "]"
formspec[#formspec + 1] = fgettext("Uninstall") formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors
formspec[#formspec + 1] = "]" end
end end
formspec[#formspec + 1] = "button[-1.5,0;1.5,0.8;view_" local web_elem_name = "view_" .. i .. ";"
formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = "image_button[-0.7,0;0.7,0.7;" ..
formspec[#formspec + 1] = ";" core.formspec_escape(defaulttexturedir) .. "cdb_viewonline.png;" .. web_elem_name .. "]"
formspec[#formspec + 1] = fgettext("View") formspec[#formspec + 1] = "tooltip[" .. web_elem_name ..
formspec[#formspec + 1] = "]" fgettext("View more information in a web browser") .. tooltip_colors
formspec[#formspec + 1] = "container_end[]" formspec[#formspec + 1] = "container_end[]"
-- description -- description
local description_width = W - 0.375*5 - 0.85 - 2*0.7
formspec[#formspec + 1] = "textarea[1.855,0.3;" formspec[#formspec + 1] = "textarea[1.855,0.3;"
formspec[#formspec + 1] = tostring(description_width) formspec[#formspec + 1] = tostring(description_width)
formspec[#formspec + 1] = ",0.8;;;" formspec[#formspec + 1] = ",0.8;;;"
@ -451,6 +869,13 @@ function store.handle_submit(this, fields)
return true return true
end end
if fields.clear then
search_string = ""
cur_page = 1
store.filter_packages("")
return true
end
if fields.back then if fields.back then
this:delete() this:delete()
return true return true
@ -492,6 +917,17 @@ function store.handle_submit(this, fields)
end end
end end
if fields.update_all then
for i=1, #store.packages_full do
local package = store.packages_full[i]
if package.path and package.installed_release < package.release and
not (package.downloading or package.queued) then
queue_download(package)
end
end
return true
end
local start_idx = (cur_page - 1) * num_per_page + 1 local start_idx = (cur_page - 1) * num_per_page + 1
assert(start_idx ~= nil) assert(start_idx ~= nil)
for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do
@ -499,21 +935,54 @@ function store.handle_submit(this, fields)
assert(package) assert(package)
if fields["install_" .. i] then if fields["install_" .. i] then
start_install(this, package) local install_parent
if package.type == "mod" then
install_parent = core.get_modpath()
elseif package.type == "game" then
install_parent = core.get_gamepath()
elseif package.type == "txp" then
install_parent = core.get_texturepath()
else
error("Unknown package type: " .. package.type)
end
local function on_confirm()
local deps = get_raw_dependencies(package)
if deps and has_hard_deps(deps) then
local dlg = install_dialog.create(package, deps)
dlg:set_parent(this)
this:hide()
dlg:show()
else
queue_download(package)
end
end
if not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then
local dlg = confirm_overwrite.create(package, on_confirm)
dlg:set_parent(this)
this:hide()
dlg:show()
else
on_confirm()
end
return true return true
end end
if fields["uninstall_" .. i] then if fields["uninstall_" .. i] then
local dlg_delmod = create_delete_content_dlg(package) local dlg = create_delete_content_dlg(package)
dlg_delmod:set_parent(this) dlg:set_parent(this)
this:hide() this:hide()
dlg_delmod:show() dlg:show()
return true return true
end end
if fields["view_" .. i] then if fields["view_" .. i] then
local url = ("%s/packages/%s?protocol_version=%d"):format( local url = ("%s/packages/%s/%s?protocol_version=%d"):format(
core.settings:get("contentdb_url"), package.id, core.get_max_supp_proto()) core.settings:get("contentdb_url"),
package.author, package.name, core.get_max_supp_proto())
core.open_url(url) core.open_url(url)
return true return true
end end
@ -527,6 +996,9 @@ function create_store_dlg(type)
store.load() store.load()
end end
store.update_paths()
store.sort_packages()
search_string = "" search_string = ""
cur_page = 1 cur_page = 1

View File

@ -61,6 +61,7 @@ local flag_checkboxes = {
fgettext("Low humidity and high heat causes shallow or dry rivers") }, fgettext("Low humidity and high heat causes shallow or dry rivers") },
}, },
flat = { flat = {
cb_caverns,
{ "hills", fgettext("Hills"), "hills" }, { "hills", fgettext("Hills"), "hills" },
{ "lakes", fgettext("Lakes"), "lakes" }, { "lakes", fgettext("Lakes"), "lakes" },
}, },
@ -366,8 +367,18 @@ local function create_world_buttonhandler(this, fields)
local gameindex = core.get_textlist_index("games") local gameindex = core.get_textlist_index("games")
if gameindex ~= nil then if gameindex ~= 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.
if worldname == "" then if worldname == "" then
worldname = "World " .. math.random(1000, 9999) local worldnum_max = 0
for _, world in ipairs(menudata.worldlist:get_list()) do
if world.name:match("^World %d+$") then
local worldnum = tonumber(world.name:sub(6))
worldnum_max = math.max(worldnum_max, worldnum)
end
end
worldname = "World " .. worldnum_max + 1
end end
core.settings:set("fixed_map_seed", fields["te_seed"]) core.settings:set("fixed_map_seed", fields["te_seed"])
@ -436,7 +447,7 @@ local function create_world_buttonhandler(this, fields)
end end
if fields["mgv6_biomes"] then if fields["mgv6_biomes"] then
local entry = minetest.formspec_escape(fields["mgv6_biomes"]) local entry = core.formspec_escape(fields["mgv6_biomes"])
for b=1, #mgv6_biomes do for b=1, #mgv6_biomes do
if entry == mgv6_biomes[b][1] then if entry == mgv6_biomes[b][1] then
local ftable = core.settings:get_flags("mgv6_spflags") local ftable = core.settings:get_flags("mgv6_spflags")

View File

@ -19,6 +19,7 @@ mt_color_grey = "#AAAAAA"
mt_color_blue = "#6389FF" mt_color_blue = "#6389FF"
mt_color_green = "#72FF63" mt_color_green = "#72FF63"
mt_color_dark_green = "#25C191" mt_color_dark_green = "#25C191"
mt_color_orange = "#FF8800"
local menupath = core.get_mainmenu_path() local menupath = core.get_mainmenu_path()
local basepath = core.get_builtin_path() local basepath = core.get_builtin_path()
@ -34,6 +35,7 @@ dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua")
dofile(menupath .. DIR_DELIM .. "async_event.lua") dofile(menupath .. DIR_DELIM .. "async_event.lua")
dofile(menupath .. DIR_DELIM .. "common.lua") dofile(menupath .. DIR_DELIM .. "common.lua")
dofile(menupath .. DIR_DELIM .. "pkgmgr.lua") dofile(menupath .. DIR_DELIM .. "pkgmgr.lua")
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
dofile(menupath .. DIR_DELIM .. "textures.lua") dofile(menupath .. DIR_DELIM .. "textures.lua")
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua") dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
@ -77,7 +79,34 @@ local function main_event_handler(tabview, event)
end end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function menudata.init_tabs() local function init_globals()
-- Init gamedata
gamedata.worldindex = 0
menudata.worldlist = filterlist.create(
core.get_worlds,
compare_worlds,
-- Unique id comparison function
function(element, uid)
return element.name == uid
end,
-- Filter function
function(element, gameid)
return element.gameid == gameid
end
)
menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic)
menudata.worldlist:set_sortmode("alphabetic")
if not core.settings:get("menu_last_game") then
local default_game = core.settings:get("default_game") or "minetest"
core.settings:set("menu_last_game", default_game)
end
mm_texture.init()
-- Create main tabview
local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0}) local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0})
for i = 1, #pkgmgr.games do for i = 1, #pkgmgr.games do
@ -116,6 +145,15 @@ function menudata.init_tabs()
end end
end end
-- In case the folder of the last selected game has been deleted,
-- display "Minetest" as a header
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()
end
end
ui.set_default("maintab") ui.set_default("maintab")
tv_main:show() tv_main:show()

View File

@ -72,6 +72,34 @@ local function cleanup_path(temppath)
return temppath return temppath
end end
local function load_texture_packs(txtpath, retval)
local list = core.get_dir_list(txtpath, true)
local current_texture_path = core.settings:get("texture_path")
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")
retval[#retval + 1] = {
name = item,
author = conf:get("author"),
release = tonumber(conf:get("release")) or 0,
list_name = name,
type = "txp",
path = path,
enabled = path == current_texture_path,
}
end
end
end
function get_mods(path,retval,modpack) function get_mods(path,retval,modpack)
local mods = core.get_dir_list(path, true) local mods = core.get_dir_list(path, true)
@ -107,12 +135,12 @@ function get_mods(path,retval,modpack)
-- Read from config -- Read from config
toadd.name = name toadd.name = name
toadd.author = mod_conf.author toadd.author = mod_conf.author
toadd.release = tonumber(mod_conf.release or "0") toadd.release = tonumber(mod_conf.release) or 0
toadd.path = prefix toadd.path = prefix
toadd.type = "mod" toadd.type = "mod"
-- Check modpack.txt -- Check modpack.txt
-- Note: modpack.conf is already checked above -- Note: modpack.conf is already checked above
local modpackfile = io.open(prefix .. DIR_DELIM .. "modpack.txt") local modpackfile = io.open(prefix .. DIR_DELIM .. "modpack.txt")
if modpackfile then if modpackfile then
modpackfile:close() modpackfile:close()
@ -136,32 +164,13 @@ pkgmgr = {}
function pkgmgr.get_texture_packs() function pkgmgr.get_texture_packs()
local txtpath = core.get_texturepath() local txtpath = core.get_texturepath()
local list = core.get_dir_list(txtpath, true) local txtpath_system = core.get_texturepath_share()
local retval = {} local retval = {}
local current_texture_path = core.settings:get("texture_path") load_texture_packs(txtpath, retval)
-- on portable versions these two paths coincide. It avoids loading the path twice
for _, item in ipairs(list) do if txtpath ~= txtpath_system then
if item ~= "base" then load_texture_packs(txtpath_system, retval)
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")
retval[#retval + 1] = {
name = item,
author = conf:get("author"),
release = tonumber(conf:get("release") or "0"),
list_name = name,
type = "txp",
path = path,
enabled = path == current_texture_path,
}
end
end end
table.sort(retval, function(a, b) table.sort(retval, function(a, b)
@ -404,18 +413,7 @@ function pkgmgr.is_modpack_entirely_enabled(data, name)
end end
---------- toggles or en/disables a mod or modpack and its dependencies -------- ---------- toggles or en/disables a mod or modpack and its dependencies --------
function pkgmgr.enable_mod(this, toset) local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
local list = this.data.list:get_list()
local mod = list[this.data.selected_mod]
-- Game mods can't be enabled or disabled
if mod.is_game_content then
return
end
local toggled_mods = {}
local enabled_mods = {}
if not mod.is_modpack then if not mod.is_modpack then
-- Toggle or en/disable the mod -- Toggle or en/disable the mod
if toset == nil then if toset == nil then
@ -434,23 +432,29 @@ function pkgmgr.enable_mod(this, toset)
-- interleaved unsupported -- interleaved unsupported
for i = 1, #list do for i = 1, #list do
if list[i].modpack == mod.name then if list[i].modpack == mod.name then
if toset == nil then toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, list[i])
toset = not list[i].enabled
end
if list[i].enabled ~= toset then
list[i].enabled = toset
toggled_mods[#toggled_mods+1] = list[i].name
end
if toset then
enabled_mods[list[i].name] = true
end
end end
end end
end end
end
function pkgmgr.enable_mod(this, toset)
local list = this.data.list:get_list()
local mod = list[this.data.selected_mod]
-- Game mods can't be enabled or disabled
if mod.is_game_content then
return
end
local toggled_mods = {}
local enabled_mods = {}
toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
if not toset then if not toset then
-- Mod(s) were disabled, so no dependencies need to be enabled -- Mod(s) were disabled, so no dependencies need to be enabled
table.sort(toggled_mods) table.sort(toggled_mods)
minetest.log("info", "Following mods were disabled: " .. core.log("info", "Following mods were disabled: " ..
table.concat(toggled_mods, ", ")) table.concat(toggled_mods, ", "))
return return
end end
@ -487,7 +491,7 @@ function pkgmgr.enable_mod(this, toset)
enabled_mods[name] = true enabled_mods[name] = true
local mod_to_enable = list[mod_ids[name]] local mod_to_enable = list[mod_ids[name]]
if not mod_to_enable then if not mod_to_enable then
minetest.log("warning", "Mod dependency \"" .. name .. core.log("warning", "Mod dependency \"" .. name ..
"\" not found!") "\" not found!")
else else
if mod_to_enable.enabled == false then if mod_to_enable.enabled == false then
@ -508,7 +512,7 @@ function pkgmgr.enable_mod(this, toset)
-- Log the list of enabled mods -- Log the list of enabled mods
table.sort(toggled_mods) table.sort(toggled_mods)
minetest.log("info", "Following mods were enabled: " .. core.log("info", "Following mods were enabled: " ..
table.concat(toggled_mods, ", ")) table.concat(toggled_mods, ", "))
end end

View File

@ -0,0 +1,252 @@
--Minetest
--Copyright (C) 2020 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--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.
serverlistmgr = {}
--------------------------------------------------------------------------------
local function order_server_list(list)
local res = {}
--orders the favorite list after support
for i = 1, #list do
local fav = list[i]
if is_server_protocol_compat(fav.proto_min, fav.proto_max) then
res[#res + 1] = fav
end
end
for i = 1, #list do
local fav = list[i]
if not is_server_protocol_compat(fav.proto_min, fav.proto_max) then
res[#res + 1] = fav
end
end
return res
end
local public_downloading = false
--------------------------------------------------------------------------------
function serverlistmgr.sync()
if not serverlistmgr.servers then
serverlistmgr.servers = {{
name = fgettext("Loading..."),
description = fgettext_ne("Try reenabling public serverlist and check your internet connection.")
}}
end
local serverlist_url = core.settings:get("serverlist_url") or ""
if not core.get_http_api or serverlist_url == "" then
serverlistmgr.servers = {{
name = fgettext("Public server list is disabled"),
description = ""
}}
return
end
if public_downloading then
return
end
public_downloading = true
core.handle_async(
function(param)
local http = core.get_http_api()
local url = ("%s/list?proto_version_min=%d&proto_version_max=%d"):format(
core.settings:get("serverlist_url"),
core.get_min_supp_proto(),
core.get_max_supp_proto())
local response = http.fetch_sync({ url = url })
if not response.succeeded then
return {}
end
local retval = core.parse_json(response.data)
return retval and retval.list or {}
end,
nil,
function(result)
public_downloading = nil
local favs = order_server_list(result)
if favs[1] then
serverlistmgr.servers = favs
end
core.event_handler("Refresh")
end
)
end
--------------------------------------------------------------------------------
local function get_favorites_path(folder)
local base = core.get_user_path() .. DIR_DELIM .. "client" .. DIR_DELIM .. "serverlist" .. DIR_DELIM
if folder then
return base
end
return base .. core.settings:get("serverlist_file")
end
--------------------------------------------------------------------------------
local function save_favorites(favorites)
local filename = core.settings:get("serverlist_file")
-- If setting specifies legacy format change the filename to the new one
if filename:sub(#filename - 3):lower() == ".txt" then
core.settings:set("serverlist_file", filename:sub(1, #filename - 4) .. ".json")
end
assert(core.create_dir(get_favorites_path(true)))
core.safe_file_write(get_favorites_path(), core.write_json(favorites))
end
--------------------------------------------------------------------------------
function serverlistmgr.read_legacy_favorites(path)
local file = io.open(path, "r")
if not file then
return nil
end
local lines = {}
for line in file:lines() do
lines[#lines + 1] = line
end
file:close()
local favorites = {}
local i = 1
while i < #lines do
local function pop()
local line = lines[i]
i = i + 1
return line and line:trim()
end
if pop():lower() == "[server]" then
local name = pop()
local address = pop()
local port = tonumber(pop())
local description = pop()
if name == "" then
name = nil
end
if description == "" then
description = nil
end
if not address or #address < 3 then
core.log("warning", "Malformed favorites file, missing address at line " .. i)
elseif not port or port < 1 or port > 65535 then
core.log("warning", "Malformed favorites file, missing port at line " .. i)
elseif (name and name:upper() == "[SERVER]") or
(address and address:upper() == "[SERVER]") or
(description and description:upper() == "[SERVER]") then
core.log("warning", "Potentially malformed favorites file, overran at line " .. i)
else
favorites[#favorites + 1] = {
name = name,
address = address,
port = port,
description = description
}
end
end
end
return favorites
end
--------------------------------------------------------------------------------
local function read_favorites()
local path = get_favorites_path()
-- If new format configured fall back to reading the legacy file
if path:sub(#path - 4):lower() == ".json" then
local file = io.open(path, "r")
if file then
local json = file:read("*all")
file:close()
return core.parse_json(json)
end
path = path:sub(1, #path - 5) .. ".txt"
end
local favs = serverlistmgr.read_legacy_favorites(path)
if favs then
save_favorites(favs)
os.remove(path)
end
return favs
end
--------------------------------------------------------------------------------
local function delete_favorite(favorites, del_favorite)
for i=1, #favorites do
local fav = favorites[i]
if fav.address == del_favorite.address and fav.port == del_favorite.port then
table.remove(favorites, i)
return
end
end
end
--------------------------------------------------------------------------------
function serverlistmgr.get_favorites()
if serverlistmgr.favorites then
return serverlistmgr.favorites
end
serverlistmgr.favorites = {}
-- Add favorites, removing duplicates
local seen = {}
for _, fav in ipairs(read_favorites() or {}) do
local key = ("%s:%d"):format(fav.address:lower(), fav.port)
if not seen[key] then
seen[key] = true
serverlistmgr.favorites[#serverlistmgr.favorites + 1] = fav
end
end
return serverlistmgr.favorites
end
--------------------------------------------------------------------------------
function serverlistmgr.add_favorite(new_favorite)
assert(type(new_favorite.port) == "number")
-- Whitelist favorite keys
new_favorite = {
name = new_favorite.name,
address = new_favorite.address,
port = new_favorite.port,
description = new_favorite.description,
}
local favorites = serverlistmgr.get_favorites()
delete_favorite(favorites, new_favorite)
table.insert(favorites, 1, new_favorite)
save_favorites(favorites)
end
--------------------------------------------------------------------------------
function serverlistmgr.delete_favorite(del_favorite)
local favorites = serverlistmgr.get_favorites()
delete_favorite(favorites, del_favorite)
save_favorites(favorites)
end

View File

@ -36,28 +36,37 @@ local core_developers = {
"Nathanaël Courant (Nore/Ekdohibs) <nore@mesecons.net>", "Nathanaël Courant (Nore/Ekdohibs) <nore@mesecons.net>",
"Loic Blot (nerzhul/nrz) <loic.blot@unix-experience.fr>", "Loic Blot (nerzhul/nrz) <loic.blot@unix-experience.fr>",
"paramat", "paramat",
"Auke Kok (sofar) <sofar@foo-projects.org>",
"Andrew Ward (rubenwardy) <rw@rubenwardy.com>", "Andrew Ward (rubenwardy) <rw@rubenwardy.com>",
"Krock/SmallJoker <mk939@ymail.com>", "Krock/SmallJoker <mk939@ymail.com>",
"Lars Hofhansl <larsh@apache.org>", "Lars Hofhansl <larsh@apache.org>",
"Pierre-Yves Rollo <dev@pyrollo.com>",
"v-rob <robinsonvincent89@gmail.com>",
} }
-- For updating active/previous contributors, see the script in ./util/gather_git_credits.py
local active_contributors = { local active_contributors = {
"Hugues Ross [Formspecs]", "Wuzzy [devtest game, visual corrections]",
"Zughy [Visual improvements, various fixes]",
"Maksim (MoNTE48) [Android]", "Maksim (MoNTE48) [Android]",
"DS [Formspecs]", "numzero [Graphics and rendering]",
"pyrollo [Formspecs: Hypertext]", "appgurueu [Various internal fixes]",
"v-rob [Formspecs]", "Desour [Formspec and vector API changes]",
"Jordach [set_sky]", "HybridDog [Rendering fixes and documentation]",
"random-geek [Formspecs]", "Hugues Ross [Graphics-related improvements]",
"Wuzzy [Pathfinder, builtin, translations]", "ANAND (ClobberXD) [Mouse buttons rebinding]",
"ANAND (ClobberXD) [Fixes, per-player FOV]", "luk3yx [Fixes]",
"Warr1024 [Fixes]", "hecks [Audiovisuals, Lua API]",
"Paul Ouellette (pauloue) [Fixes, Script API]", "LoneWolfHT [Object crosshair, documentation fixes]",
"Jean-Patrick G (kilbith) <jeanpatrick.guerrero@gmail.com> [Audiovisuals]", "Lejo [Server-related improvements]",
"HybridDog [Script API]", "EvidenceB [Compass HUD element]",
"Paul Ouellette (pauloue) [Lua API, documentation]",
"TheTermos [Collision detection, physics]",
"David CARLIER [Unix & Haiku build fixes]",
"dcbrwn [Object shading]", "dcbrwn [Object shading]",
"srifqi [Fixes]", "Elias Fleckenstein [API features/fixes]",
"Jean-Patrick Guerrero (kilbith) [model element, visual fixes]",
"k.h.lai [Memory leak fixes, documentation]",
} }
local previous_core_developers = { local previous_core_developers = {
@ -73,30 +82,23 @@ local previous_core_developers = {
"sapier", "sapier",
"Zeno", "Zeno",
"ShadowNinja <shadowninja@minetest.net>", "ShadowNinja <shadowninja@minetest.net>",
"Auke Kok (sofar) <sofar@foo-projects.org>",
} }
local previous_contributors = { local previous_contributors = {
"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net> [Minetest Logo]", "Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net> [Minetest Logo]",
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
"red-001 <red-001@outlook.ie>", "red-001 <red-001@outlook.ie>",
"numberZero [Audiovisuals: meshgen]",
"Giuseppe Bilotta", "Giuseppe Bilotta",
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
"MirceaKitsune <mirceakitsune@gmail.com>", "MirceaKitsune <mirceakitsune@gmail.com>",
"Constantin Wenger (SpeedProg)", "Constantin Wenger (SpeedProg)",
"Ciaran Gultnieks (CiaranG)", "Ciaran Gultnieks (CiaranG)",
"stujones11 [Android UX improvements]", "stujones11 [Android UX improvements]",
"Jeija <jeija@mesecons.net> [HTTP, particles]",
"Vincent Glize (Dumbeldor) [Cleanups, CSM APIs]",
"Ben Deutsch [Rendering, Fixes, SQLite auth]",
"TeTpaAka [Hand overriding, nametag colors]",
"Rui [Sound Pitch]",
"Duane Robertson <duane@duanerobertson.com> [MGValleys]",
"Raymoo [Tool Capabilities]",
"Rogier <rogier777@gmail.com> [Fixes]", "Rogier <rogier777@gmail.com> [Fixes]",
"Gregory Currie (gregorycu) [optimisation]", "Gregory Currie (gregorycu) [optimisation]",
"TriBlade9 <triblade9@mail.com> [Audiovisuals]", "srifqi [Fixes]",
"T4im [Profiler]", "JacobF",
"Jurgen Doser (doserj) <jurgen.doser@gmail.com>", "Jeija <jeija@mesecons.net> [HTTP, particles]",
} }
local function buildCreditList(source) local function buildCreditList(source)
@ -116,7 +118,7 @@ return {
"MultiCraft Open Source Project, ver. " .. version.string .. "\n" .. "MultiCraft Open Source Project, ver. " .. version.string .. "\n" ..
"Copyright (C) 2014-2021 MultiCraft Development Team\n" .. "Copyright (C) 2014-2021 MultiCraft Development Team\n" ..
"Licence: LGPLv3.0+ and CC-BY-SA 4.0, Home page: http://multicraft.world\n" .. "Licence: LGPLv3.0+ and CC-BY-SA 4.0, Home page: http://multicraft.world\n" ..
"Created and Powered by Minetest Engine, ver. 5.3.0]" .. "Created and Powered by Minetest Engine, ver. 5.4.1]" ..
-- "button[10,-0.5;2,2;homepage;multicraft.world]" .. -- "button[10,-0.5;2,2;homepage;multicraft.world]" ..
"tablecolumns[color;text]" .. "tablecolumns[color;text]" ..
"tableoptions[background=#999999;highlight=#00000000;border=true]" .. "tableoptions[background=#999999;highlight=#00000000;border=true]" ..
@ -132,10 +134,23 @@ return {
"#FFFF00," .. fgettext("Previous Contributors") .. ",," .. "#FFFF00," .. fgettext("Previous Contributors") .. ",," ..
buildCreditList(previous_contributors) .. "," .. buildCreditList(previous_contributors) .. "," ..
";1]" ";1]"
if PLATFORM ~= "Android" then
fs = fs .. "tooltip[userdata;" ..
fgettext("Opens the directory that contains user-provided worlds, games, mods,\n" ..
"and texture packs in a file manager / explorer.") .. "]"
fs = fs .. "button[0,4.75;3.5,1;userdata;" .. fgettext("Open User Data Directory") .. "]"
end
return fs
end, end,
cbf_button_handler = function(this, fields, name, tabdata) cbf_button_handler = function(this, fields, name, tabdata)
if fields.homepage then if fields.homepage then
core.open_url("http://multicraft.world") core.open_url("http://multicraft.world")
end end
if fields.userdata then
core.open_dir(core.get_user_path())
end
end, end,
} }

View File

@ -19,9 +19,13 @@ local lang = core.settings:get("language")
if not lang or lang == "" then lang = os.getenv("LANG") end if not lang or lang == "" then lang = os.getenv("LANG") end
local mobile = PLATFORM == "Android" or PLATFORM == "iOS" local mobile = PLATFORM == "Android" or PLATFORM == "iOS"
local function current_game() local enable_gamebar = PLATFORM ~= "Android"
local last_game_id = core.settings:get("menu_last_game") local current_game, singleplayer_refresh_gamebar
local game = pkgmgr.find_by_gameid(last_game_id)
if enable_gamebar then
function current_game()
local last_game_id = core.settings:get("menu_last_game")
local game = pkgmgr.find_by_gameid(last_game_id)
return game return game
end end

View File

@ -24,11 +24,11 @@ local function get_formspec(tabview, name, tabdata)
-- Update the cached supported proto info, -- Update the cached supported proto info,
-- it may have changed after a change by the settings menu. -- it may have changed after a change by the settings menu.
common_update_cached_supp_proto() common_update_cached_supp_proto()
local fav_selected local selected
if menudata.search_result then if menudata.search_result then
fav_selected = menudata.search_result[tabdata.fav_selected] selected = menudata.search_result[tabdata.selected]
else else
fav_selected = menudata.favorites[tabdata.fav_selected] selected = serverlistmgr.servers[tabdata.selected]
end end
if not tabdata.search_for then if not tabdata.search_for then
@ -88,7 +88,7 @@ local function get_formspec(tabview, name, tabdata)
-- Password -- Password
retval = retval .. "pwdfield[10.45,1.8;1.95,0.39;te_pwd;;" .. pwd .. "]" retval = retval .. "pwdfield[10.45,1.8;1.95,0.39;te_pwd;;" .. pwd .. "]"
if tabdata.fav_selected and fav_selected then if tabdata.selected and selected then
if gamedata.fav then if gamedata.fav then
retval = retval .. "image_button[7.1,4.91;0.83,0.83;" .. esc(defaulttexturedir .. "trash.png") retval = retval .. "image_button[7.1,4.91;0.83,0.83;" .. esc(defaulttexturedir .. "trash.png")
.. ";btn_delete_favorite;;true;false]" .. ";btn_delete_favorite;;true;false]"
@ -115,10 +115,9 @@ local function get_formspec(tabview, name, tabdata)
"table[-0.09,0.7;6.99,4.93;favourites;" "table[-0.09,0.7;6.99,4.93;favourites;"
if menudata.search_result then if menudata.search_result then
local favs = serverlistmgr.get_favorites()
for i = 1, #menudata.search_result do for i = 1, #menudata.search_result do
local favs = core.get_favorites("local")
local server = menudata.search_result[i] local server = menudata.search_result[i]
for fav_id = 1, #favs do for fav_id = 1, #favs do
if server.address == favs[fav_id].address and if server.address == favs[fav_id].address and
server.port == favs[fav_id].port then server.port == favs[fav_id].port then
@ -133,18 +132,18 @@ local function get_formspec(tabview, name, tabdata)
retval = retval .. render_serverlist_row(server, server.is_favorite, retval = retval .. render_serverlist_row(server, server.is_favorite,
server.server_id == "multicraft") server.server_id == "multicraft")
end end
elseif #menudata.favorites > 0 then elseif #serverlistmgr.servers > 0 then
local favs = core.get_favorites("local") local favs = serverlistmgr.get_favorites()
if #favs > 0 then if #favs > 0 then
for i = 1, #favs do for i = 1, #favs do
for j = 1, #menudata.favorites do for j = 1, #serverlistmgr.servers do
if menudata.favorites[j].address == favs[i].address and if serverlistmgr.servers[j].address == favs[i].address and
menudata.favorites[j].port == favs[i].port then serverlistmgr.servers[j].port == favs[i].port then
table.insert(menudata.favorites, i, table.remove(menudata.favorites, j)) table.insert(serverlistmgr.servers, i, table.remove(serverlistmgr.servers, j))
end
end end
end if favs[i].address ~= serverlistmgr.servers[i].address then
if favs[i].address ~= menudata.favorites[i].address then table.insert(serverlistmgr.servers, i, favs[i])
table.insert(menudata.favorites, i, favs[i])
end end
end end
end end
@ -156,8 +155,8 @@ local function get_formspec(tabview, name, tabdata)
end end
end end
if tabdata.fav_selected then if tabdata.selected then
retval = retval .. ";" .. tabdata.fav_selected .. "]" retval = retval .. ";" .. tabdata.selected .. "]"
else else
retval = retval .. ";0]" retval = retval .. ";0]"
end end
@ -167,7 +166,7 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
local function main_button_handler(tabview, fields, name, tabdata) local function main_button_handler(tabview, fields, name, tabdata)
local serverlist = menudata.search_result or menudata.favorites local serverlist = menudata.search_result or serverlistmgr.servers
if fields.te_name then if fields.te_name then
gamedata.playername = fields.te_name gamedata.playername = fields.te_name
@ -188,8 +187,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
if event.type == "DCL" then if event.type == "DCL" then
if event.row <= #serverlist then if event.row <= #serverlist then
if menudata.favorites_is_public and if not is_server_protocol_compat_or_error(
not is_server_protocol_compat_or_error(
fav.proto_min, fav.proto_max) then fav.proto_min, fav.proto_max) then
return true return true
end end
@ -218,7 +216,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
if event.type == "CHG" then if event.type == "CHG" then
if event.row <= #serverlist then if event.row <= #serverlist then
gamedata.fav = false gamedata.fav = false
local favs = core.get_favorites("local") local favs = serverlistmgr.get_favorites()
local address = fav.address local address = fav.address
local port = fav.port local port = fav.port
gamedata.serverdescription = fav.description gamedata.serverdescription = fav.description
@ -234,28 +232,28 @@ local function main_button_handler(tabview, fields, name, tabdata)
core.settings:set("address", address) core.settings:set("address", address)
core.settings:set("remote_port", port) core.settings:set("remote_port", port)
end end
tabdata.fav_selected = event.row tabdata.selected = event.row
end end
return true return true
end end
end end
if fields.key_up or fields.key_down then if fields.key_up or fields.key_down then
local fav_idx = core.get_table_index("favourites") local fav_idx = core.get_table_index("favorites")
local fav = serverlist[fav_idx] local fav = serverlist[fav_idx]
if fav_idx then if fav_idx then
if fields.key_up and fav_idx > 1 then if fields.key_up and fav_idx > 1 then
fav_idx = fav_idx - 1 fav_idx = fav_idx - 1
elseif fields.key_down and fav_idx < #menudata.favorites then elseif fields.key_down and fav_idx < #serverlistmgr.servers then
fav_idx = fav_idx + 1 fav_idx = fav_idx + 1
end end
else else
fav_idx = 1 fav_idx = 1
end end
if not menudata.favorites or not fav then if not serverlistmgr.servers or not fav then
tabdata.fav_selected = 0 tabdata.selected = 0
return true return true
end end
@ -267,27 +265,33 @@ local function main_button_handler(tabview, fields, name, tabdata)
core.settings:set("remote_port", port) core.settings:set("remote_port", port)
end end
tabdata.fav_selected = fav_idx tabdata.selected = fav_idx
return true return true
end end
if fields.btn_delete_favorite then if fields.btn_delete_favorite then
local current_favourite = core.get_table_index("favourites") local current_favorite = core.get_table_index("favorites")
if not current_favourite then return end if not current_favorite then return end
core.delete_favorite(current_favourite) serverlistmgr.delete_favorite(serverlistmgr.servers[current_favorite])
asyncOnlineFavourites(mobile_only) serverlistmgr.sync()
tabdata.fav_selected = nil tabdata.selected = nil
return true return true
end end
if fields.btn_mp_clear then
tabdata.search_for = ""
menudata.search_result = nil
return true
end
if fields.btn_mp_search or fields.key_enter_field == "te_search" then if fields.btn_mp_search or fields.key_enter_field == "te_search" then
tabdata.fav_selected = 1 tabdata.selected = 1
local input = fields.te_search:lower() local input = fields.te_search:lower()
tabdata.search_for = fields.te_search tabdata.search_for = fields.te_search
if #menudata.favorites < 2 then if #serverlistmgr.servers < 2 then
return true return true
end end
@ -307,8 +311,8 @@ local function main_button_handler(tabview, fields, name, tabdata)
-- Search the serverlist -- Search the serverlist
local search_result = {} local search_result = {}
for i = 1, #menudata.favorites do for i = 1, #serverlistmgr.servers do
local server = menudata.favorites[i] local server = serverlistmgr.servers[i]
local found = 0 local found = 0
for k = 1, #keywords do for k = 1, #keywords do
local keyword = keywords[k] local keyword = keywords[k]
@ -325,7 +329,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
end end
end end
if found > 0 then if found > 0 then
local points = (#menudata.favorites - i) / 5 + found local points = (#serverlistmgr.servers - i) / 5 + found
server.points = points server.points = points
table.insert(search_result, server) table.insert(search_result, server)
end end
@ -344,13 +348,13 @@ local function main_button_handler(tabview, fields, name, tabdata)
end end
if fields.btn_mp_refresh then if fields.btn_mp_refresh then
asyncOnlineFavourites(mobile_only) serverlistmgr.sync()
return true return true
end end
if fields.btn_mp_mobile then if fields.btn_mp_mobile then
mobile_only = not mobile_only mobile_only = not mobile_only
asyncOnlineFavourites(mobile_only) serverlistmgr.sync()
return true return true
end end
@ -359,26 +363,32 @@ local function main_button_handler(tabview, fields, name, tabdata)
gamedata.playername = fields.te_name gamedata.playername = fields.te_name
gamedata.password = fields.te_pwd gamedata.password = fields.te_pwd
gamedata.address = fields.te_address gamedata.address = fields.te_address
gamedata.port = fields.te_port gamedata.port = tonumber(fields.te_port)
gamedata.selected_world = 0 gamedata.selected_world = 0
local fav_idx = core.get_table_index("favourites") local fav_idx = core.get_table_index("favorites")
local fav = serverlist[fav_idx] local fav = serverlist[fav_idx]
if fav_idx and fav_idx <= #serverlist and if fav_idx and fav_idx <= #serverlist and
fav.address == fields.te_address and fav.address == gamedata.address and
fav.port == fields.te_port then fav.port == gamedata.port then
serverlistmgr.add_favorite(fav)
gamedata.servername = fav.name gamedata.servername = fav.name
gamedata.serverdescription = fav.description gamedata.serverdescription = fav.description
if menudata.favorites_is_public and if not is_server_protocol_compat_or_error(
not is_server_protocol_compat_or_error(
fav.proto_min, fav.proto_max) then fav.proto_min, fav.proto_max) then
return true return true
end end
else else
gamedata.servername = "" gamedata.servername = ""
gamedata.serverdescription = "" gamedata.serverdescription = ""
serverlistmgr.add_favorite({
address = gamedata.address,
port = gamedata.port,
})
end end
local auto_connect = false local auto_connect = false
@ -403,7 +413,7 @@ end
local function on_change(type, old_tab, new_tab) local function on_change(type, old_tab, new_tab)
if type == "LEAVE" then return end if type == "LEAVE" then return end
asyncOnlineFavourites(mobile_only) serverlistmgr.sync()
end end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -172,8 +172,8 @@ local function formspec(tabview, name, tabdata)
end end
tab_string = tab_string .. tab_string = tab_string ..
"button[8,4.75;3.95,1;btn_change_keys;" "button[8,4.75;3.95,1;btn_change_keys;"
.. fgettext("Change Keys") .. "]" .. fgettext("Change Keys") .. "]"
tab_string = tab_string .. tab_string = tab_string ..
"button[0,4.75;3.95,1;btn_advanced_settings;" "button[0,4.75;3.95,1;btn_advanced_settings;"

View File

@ -0,0 +1,29 @@
[server]
127.0.0.1
30000
[server]
localhost
30000
[server]
vps.rubenwardy.com
30001
[server]
gundul.ddnss.de
39155
[server]
VanessaE's Dreambuilder creative Server
daconcepts.com
30000
VanessaE's Dreambuilder creative-mode server. Lots of mods, whitelisted buckets.

View File

@ -0,0 +1,36 @@
_G.core = {}
_G.unpack = table.unpack
_G.serverlistmgr = {}
dofile("builtin/common/misc_helpers.lua")
dofile("builtin/mainmenu/serverlistmgr.lua")
local base = "builtin/mainmenu/tests/"
describe("legacy favorites", function()
it("loads well-formed correctly", function()
local favs = serverlistmgr.read_legacy_favorites(base .. "favorites_wellformed.txt")
local expected = {
{
address = "127.0.0.1",
port = 30000,
},
{ address = "localhost", port = 30000 },
{ address = "vps.rubenwardy.com", port = 30001 },
{ address = "gundul.ddnss.de", port = 39155 },
{
address = "daconcepts.com",
port = 30000,
name = "VanessaE's Dreambuilder creative Server",
description = "VanessaE's Dreambuilder creative-mode server. Lots of mods, whitelisted buckets."
},
}
assert.same(expected, favs)
end)
end)

View File

@ -160,6 +160,7 @@ local function init()
-- Simple iteration would ignore lookup via __index. -- Simple iteration would ignore lookup via __index.
local entity_instrumentation = { local entity_instrumentation = {
"on_activate", "on_activate",
"on_deactivate",
"on_step", "on_step",
"on_punch", "on_punch",
"on_rightclick", "on_rightclick",

View File

@ -110,9 +110,9 @@ doubletap_jump (Double tap jump for fly) bool false
# enabled. # enabled.
always_fly_fast (Always fly and fast) bool true always_fly_fast (Always fly and fast) bool true
# The time in seconds it takes between repeated right clicks when holding the right # The time in seconds it takes between repeated node placements when holding
# mouse button. # the place button.
repeat_rightclick_time (Rightclick repetition interval) float 0.25 0.001 repeat_place_time (Place repetition interval) float 0.25 0.001
# Automatically jump up single-node obstacles. # Automatically jump up single-node obstacles.
autojump (Automatic jumping) bool false autojump (Automatic jumping) bool false
@ -152,6 +152,9 @@ joystick_type (Joystick type) enum auto auto,generic,xbox
# when holding down a joystick button combination. # when holding down a joystick button combination.
repeat_joystick_button_time (Joystick button repetition interval) float 0.17 0.001 repeat_joystick_button_time (Joystick button repetition interval) float 0.17 0.001
# The deadzone of the joystick
joystick_deadzone (Joystick deadzone) int 2048
# The sensitivity of the joystick axes for moving the # The sensitivity of the joystick axes for moving the
# ingame view frustum around. # ingame view frustum around.
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170 joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170
@ -182,6 +185,14 @@ keymap_jump (Jump key) key KEY_SPACE
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_sneak (Sneak key) key KEY_LSHIFT keymap_sneak (Sneak key) key KEY_LSHIFT
# Key for digging.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_dig (Dig key) key KEY_LBUTTON
# Key for placing.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_place (Place key) key KEY_RBUTTON
# Key for opening the inventory. # Key for opening the inventory.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_inventory (Inventory key) key KEY_KEY_I keymap_inventory (Inventory key) key KEY_KEY_I
@ -440,6 +451,10 @@ keymap_decrease_viewing_range_min (View range decrease key) key -
[**Basic] [**Basic]
# Whether nametag backgrounds should be shown by default.
# Mods may still set a background.
show_nametag_backgrounds (Show nametag backgrounds by default) bool true
# Enable vertex buffer objects. # Enable vertex buffer objects.
# This should greatly improve graphics performance. # This should greatly improve graphics performance.
enable_vbo (VBO) bool true enable_vbo (VBO) bool true
@ -505,8 +520,13 @@ texture_clean_transparent (Clean transparent textures) bool false
# texture autoscaling. # texture autoscaling.
texture_min_size (Minimum texture size) int 64 texture_min_size (Minimum texture size) int 64
# Experimental option, might cause visible spaces between blocks # Use multi-sample antialiasing (MSAA) to smooth out block edges.
# when set to higher number than 0. # This algorithm smooths out the 3D viewport while keeping the image sharp,
# but it doesn't affect the insides of textures
# (which is especially noticeable with transparent textures).
# Visible spaces appear between nodes when shaders are disabled.
# If set to 0, MSAA is disabled.
# A restart is required after changing this option.
fsaa (FSAA) enum 0 0,1,2,4,8,16 fsaa (FSAA) enum 0 0,1,2,4,8,16
# Undersampling is similar to using a lower screen resolution, but it applies # Undersampling is similar to using a lower screen resolution, but it applies
@ -573,15 +593,15 @@ arm_inertia (Arm inertia) bool true
# to not waste CPU power for no benefit. # to not waste CPU power for no benefit.
fps_max (Maximum FPS) int 60 1 fps_max (Maximum FPS) int 60 1
# Maximum FPS when game is paused. # Maximum FPS when the window is not focused, or when the game is paused.
pause_fps_max (FPS in pause menu) int 20 1 fps_max_unfocused (FPS when unfocused or paused) int 20 1
# Open the pause menu when the window's focus is lost. Does not pause if a formspec is # Open the pause menu when the window's focus is lost. Does not pause if a formspec is
# open. # open.
pause_on_lost_focus (Pause on lost window focus) bool false pause_on_lost_focus (Pause on lost window focus) bool false
# View distance in nodes. # View distance in nodes.
viewing_range (Viewing range) int 100 20 4000 viewing_range (Viewing range) int 190 20 4000
# Camera 'near clipping plane' distance in nodes, between 0 and 0.25 # Camera 'near clipping plane' distance in nodes, between 0 and 0.25
# Only works on GLES platforms. Most users will not need to change this. # Only works on GLES platforms. Most users will not need to change this.
@ -704,9 +724,11 @@ selectionbox_color (Selection box color) string (0,0,0)
selectionbox_width (Selection box width) int 2 1 5 selectionbox_width (Selection box width) int 2 1 5
# Crosshair color (R,G,B). # Crosshair color (R,G,B).
# Also controls the object crosshair color
crosshair_color (Crosshair color) string (255,255,255) crosshair_color (Crosshair color) string (255,255,255)
# Crosshair alpha (opaqueness, between 0 and 255). # Crosshair alpha (opaqueness, between 0 and 255).
# Also controls the object crosshair color
crosshair_alpha (Crosshair alpha) int 255 0 255 crosshair_alpha (Crosshair alpha) int 255 0 255
# Maximum number of recent chat messages to show # Maximum number of recent chat messages to show
@ -780,7 +802,8 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc
autoscale_mode (Autoscaling mode) enum disable disable,enable,force autoscale_mode (Autoscaling mode) enum disable disable,enable,force
# Show entity selection boxes # Show entity selection boxes
show_entity_selectionbox (Show entity selection boxes) bool true # A restart is required after changing this.
show_entity_selectionbox (Show entity selection boxes) bool false
[*Menus] [*Menus]
@ -945,7 +968,7 @@ serverlist_url (Serverlist URL) string servers.minetest.net
# File in client/serverlist/ that contains your favorite servers displayed in the # File in client/serverlist/ that contains your favorite servers displayed in the
# Multiplayer Tab. # Multiplayer Tab.
serverlist_file (Serverlist file) string favoriteservers.txt serverlist_file (Serverlist file) string favoriteservers.json
# Maximum size of the out chat queue. # Maximum size of the out chat queue.
# 0 to disable queueing and -1 to make the queue size unlimited. # 0 to disable queueing and -1 to make the queue size unlimited.
@ -962,7 +985,7 @@ client_unload_unused_data_timeout (Mapblock unload timeout) int 600
# Maximum number of mapblocks for client to be kept in memory. # Maximum number of mapblocks for client to be kept in memory.
# Set to -1 for unlimited amount. # Set to -1 for unlimited amount.
client_mapblock_limit (Mapblock limit) int 5000 client_mapblock_limit (Mapblock limit) int 7500
# Whether to show the client debug info (has the same effect as hitting F5). # Whether to show the client debug info (has the same effect as hitting F5).
show_debug (Show debug info) bool false show_debug (Show debug info) bool false
@ -1032,6 +1055,13 @@ full_block_send_enable_min_time_from_building (Delay in sending blocks after bui
# client number. # client number.
max_packets_per_iteration (Max. packets per iteration) int 1024 max_packets_per_iteration (Max. packets per iteration) int 1024
# ZLib compression level to use when sending mapblocks to the client.
# -1 - Zlib's default compression level
# 0 - no compresson, fastest
# 9 - best compression, slowest
# (levels 1-3 use Zlib's "fast" method, 4-9 use the normal method)
map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9
[*Game] [*Game]
# Default game when creating a new world. # Default game when creating a new world.
@ -1059,7 +1089,7 @@ default_stack_max (Default stack size) int 99
# Enable players getting damage and dying. # Enable players getting damage and dying.
enable_damage (Damage) bool false enable_damage (Damage) bool false
# Enable creative mode for new created maps. # Enable creative mode for all players
creative_mode (Creative) bool false creative_mode (Creative) bool false
# A chosen map seed for a new map, leave empty for random. # A chosen map seed for a new map, leave empty for random.
@ -1121,17 +1151,17 @@ ask_reconnect_on_crash (Ask to reconnect after crash) bool false
# Setting this larger than active_block_range will also cause the server # Setting this larger than active_block_range will also cause the server
# to maintain active objects up to this distance in the direction the # to maintain active objects up to this distance in the direction the
# player is looking. (This can avoid mobs suddenly disappearing from view) # player is looking. (This can avoid mobs suddenly disappearing from view)
active_object_send_range_blocks (Active object send range) int 4 active_object_send_range_blocks (Active object send range) int 8
# The radius of the volume of blocks around every player that is subject to the # The radius of the volume of blocks around every player that is subject to the
# active block stuff, stated in mapblocks (16 nodes). # active block stuff, stated in mapblocks (16 nodes).
# In active blocks objects are loaded and ABMs run. # In active blocks objects are loaded and ABMs run.
# This is also the minimum range in which active objects (mobs) are maintained. # This is also the minimum range in which active objects (mobs) are maintained.
# This should be configured together with active_object_send_range_blocks. # This should be configured together with active_object_send_range_blocks.
active_block_range (Active block range) int 3 active_block_range (Active block range) int 4
# From how far blocks are sent to clients, stated in mapblocks (16 nodes). # From how far blocks are sent to clients, stated in mapblocks (16 nodes).
max_block_send_distance (Max block send distance) int 10 max_block_send_distance (Max block send distance) int 12
# Maximum number of forceloaded mapblocks. # Maximum number of forceloaded mapblocks.
max_forceloaded_blocks (Maximum forceloaded blocks) int 16 max_forceloaded_blocks (Maximum forceloaded blocks) int 16
@ -1204,10 +1234,10 @@ movement_gravity (Gravity) float 9.81
[**Advanced] [**Advanced]
# Handling for deprecated Lua API calls: # Handling for deprecated Lua API calls:
# - legacy: (try to) mimic old behaviour (default for release). # - none: Do not log deprecated calls
# - log: mimic and log backtrace of deprecated call (default for debug). # - log: mimic and log backtrace of deprecated call (default).
# - error: abort on usage of deprecated call (suggested for mod developers). # - error: abort on usage of deprecated call (suggested for mod developers).
deprecated_lua_api_handling (Deprecated Lua API handling) enum legacy legacy,log,error deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error
# Number of extra blocks that can be loaded by /clearobjects at once. # Number of extra blocks that can be loaded by /clearobjects at once.
# This is a trade-off between sqlite transaction overhead and # This is a trade-off between sqlite transaction overhead and
@ -1224,6 +1254,13 @@ max_objects_per_block (Maximum objects per block) int 64
# See https://www.sqlite.org/pragma.html#pragma_synchronous # See https://www.sqlite.org/pragma.html#pragma_synchronous
sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2 sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2
# ZLib compression level to use when saving mapblocks to disk.
# -1 - Zlib's default compression level
# 0 - no compresson, fastest
# 9 - best compression, slowest
# (levels 1-3 use Zlib's "fast" method, 4-9 use the normal method)
map_compression_level_disk (Map Compression Level for Disk Storage) int 3 -1 9
# Length of a server tick and the interval at which objects are generally updated over # Length of a server tick and the interval at which objects are generally updated over
# network. # network.
dedicated_server_step (Dedicated server step) float 0.09 dedicated_server_step (Dedicated server step) float 0.09
@ -1234,6 +1271,10 @@ active_block_mgmt_interval (Active block management interval) float 2.0
# Length of time between Active Block Modifier (ABM) execution cycles # Length of time between Active Block Modifier (ABM) execution cycles
abm_interval (ABM interval) float 1.0 abm_interval (ABM interval) float 1.0
# The time budget allowed for ABMs to execute on each step
# (as a fraction of the ABM Interval)
abm_time_budget (ABM time budget) float 0.2 0.1 0.9
# Length of time between NodeTimer execution cycles # Length of time between NodeTimer execution cycles
nodetimer_interval (NodeTimer interval) float 0.2 nodetimer_interval (NodeTimer interval) float 0.2
@ -1394,12 +1435,6 @@ curl_file_download_timeout (cURL file download timeout) int 300000
# Makes DirectX work with LuaJIT. Disable if it causes troubles. # Makes DirectX work with LuaJIT. Disable if it causes troubles.
high_precision_fpu (High-precision FPU) bool true high_precision_fpu (High-precision FPU) bool true
# Changes the main menu UI:
# - Full: Multiple singleplayer worlds, game choice, texture pack chooser, etc.
# - Simple: One singleplayer world, no game or texture pack choosers. May be
# necessary for smaller screens.
main_menu_style (Main menu style) enum full full,simple
# Replaces the default main menu with a custom one. # Replaces the default main menu with a custom one.
main_menu_script (Main menu script) string main_menu_script (Main menu script) string
@ -1419,7 +1454,7 @@ mg_name (Mapgen name) enum v7 v7,valleys,carpathian,v5,flat,fractal,singlenode,v
water_level (Water level) int 1 water_level (Water level) int 1
# From how far blocks are generated for clients, stated in mapblocks (16 nodes). # From how far blocks are generated for clients, stated in mapblocks (16 nodes).
max_block_generate_distance (Max block generate distance) int 8 max_block_generate_distance (Max block generate distance) int 10
# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0). # Limit of map generation, in nodes, in all 6 directions from (0, 0, 0).
# Only mapchunks completely within the mapgen limit are generated. # Only mapchunks completely within the mapgen limit are generated.
@ -1429,7 +1464,7 @@ mapgen_limit (Map generation limit) int 31000 0 31000
# Global map generation attributes. # Global map generation attributes.
# In Mapgen v6 the 'decorations' flag controls all decorations except trees # In Mapgen v6 the 'decorations' flag controls all decorations except trees
# and junglegrass, in all other mapgens this flag controls all decorations. # and junglegrass, in all other mapgens this flag controls all decorations.
mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes caves,dungeons,light,decorations,biomes,nocaves,nodungeons,nolight,nodecorations,nobiomes mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores,nocaves,nodungeons,nolight,nodecorations,nobiomes,noores
[*Biome API temperature and humidity noise parameters] [*Biome API temperature and humidity noise parameters]
@ -1820,7 +1855,7 @@ mgcarpathian_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 50
# Map generation attributes specific to Mapgen Flat. # Map generation attributes specific to Mapgen Flat.
# Occasional lakes and hills can be added to the flat world. # Occasional lakes and hills can be added to the flat world.
mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills lakes,hills,nolakes,nohills mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills,nocaverns lakes,hills,caverns,nolakes,nohills,nocaverns
# Y of flat ground. # Y of flat ground.
mgflat_ground_level (Ground level) int 8 mgflat_ground_level (Ground level) int 8
@ -1864,6 +1899,15 @@ mgflat_hill_threshold (Hill threshold) float 0.45
# Controls steepness/height of hills. # Controls steepness/height of hills.
mgflat_hill_steepness (Hill steepness) float 64.0 mgflat_hill_steepness (Hill steepness) float 64.0
# Y-level of cavern upper limit.
mgflat_cavern_limit (Cavern limit) int -256
# Y-distance over which caverns expand to full size.
mgflat_cavern_taper (Cavern taper) int 256
# Defines full size of caverns, smaller values create larger caverns.
mgflat_cavern_threshold (Cavern threshold) float 0.7
# Lower Y limit of dungeons. # Lower Y limit of dungeons.
mgflat_dungeon_ymin (Dungeon minimum Y) int -31000 mgflat_dungeon_ymin (Dungeon minimum Y) int -31000
@ -1884,6 +1928,9 @@ mgflat_np_cave1 (Cave1 noise) noise_params_3d 0, 12, (61, 61, 61), 52534, 3, 0.5
# Second of two 3D noises that together define tunnels. # Second of two 3D noises that together define tunnels.
mgflat_np_cave2 (Cave2 noise) noise_params_3d 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 mgflat_np_cave2 (Cave2 noise) noise_params_3d 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0
# 3D noise defining giant caverns.
mgflat_np_cavern (Cavern noise) noise_params_3d 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0
# 3D noise that determines number of dungeons per mapchunk. # 3D noise that determines number of dungeons per mapchunk.
mgflat_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, 2, 0.8, 2.0 mgflat_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, 2, 0.8, 2.0
@ -2126,15 +2173,15 @@ chunksize (Chunk size) int 5
enable_mapgen_debug_info (Mapgen debug) bool false enable_mapgen_debug_info (Mapgen debug) bool false
# Maximum number of blocks that can be queued for loading. # Maximum number of blocks that can be queued for loading.
emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 512 emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 1024
# Maximum number of blocks to be queued that are to be loaded from file. # Maximum number of blocks to be queued that are to be loaded from file.
# This limit is enforced per player. # This limit is enforced per player.
emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 64 emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 128
# Maximum number of blocks to be queued that are to be generated. # Maximum number of blocks to be queued that are to be generated.
# This limit is enforced per player. # This limit is enforced per player.
emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 64 emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 128
# Number of emerge threads to use. # Number of emerge threads to use.
# Value 0: # Value 0:
@ -2160,3 +2207,7 @@ contentdb_url (ContentDB URL) string https://content.minetest.net
# These flags are independent from Minetest versions, # These flags are independent from Minetest versions,
# so see a full list at https://content.minetest.net/help/content_flags/ # so see a full list at https://content.minetest.net/help/content_flags/
contentdb_flag_blacklist (ContentDB Flag Blacklist) string nonfree, desktop_default contentdb_flag_blacklist (ContentDB Flag Blacklist) string nonfree, desktop_default
# Maximum number of concurrent downloads. Downloads exceeding this limit will be queued.
# This should be lower than curl_parallel_limit.
contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3

View File

@ -15,13 +15,17 @@ varying vec3 vPosition;
// precision must be considered). // precision must be considered).
varying vec3 worldPosition; varying vec3 worldPosition;
varying lowp vec4 varColor; varying lowp vec4 varColor;
#ifdef GL_ES
varying mediump vec2 varTexCoord; varying mediump vec2 varTexCoord;
varying mediump vec3 eyeVec; // divided by fogDistance #else
centroid varying vec2 varTexCoord;
#endif
varying vec3 eyeVec;
const float fogStart = FOG_START; const float fogStart = FOG_START;
const float fogShadingParameter = 1.0 / ( 1.0 - fogStart); const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
#ifdef ENABLE_TONE_MAPPING #if ENABLE_TONE_MAPPING
/* Hable's UC2 Tone mapping parameters /* Hable's UC2 Tone mapping parameters
A = 0.22; A = 0.22;
@ -45,7 +49,7 @@ vec4 applyToneMapping(vec4 color)
const float gamma = 1.6; const float gamma = 1.6;
const float exposureBias = 5.5; const float exposureBias = 5.5;
color.rgb = uncharted2Tonemap(exposureBias * color.rgb); color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
// Precalculated white_scale from // Precalculated white_scale from
//vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
vec3 whiteScale = vec3(1.036015346); vec3 whiteScale = vec3(1.036015346);
color.rgb *= whiteScale; color.rgb *= whiteScale;
@ -71,8 +75,8 @@ void main(void)
color = base.rgb; color = base.rgb;
vec4 col = vec4(color.rgb * varColor.rgb, 1.0); vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
#ifdef ENABLE_TONE_MAPPING #if ENABLE_TONE_MAPPING
col = applyToneMapping(col); col = applyToneMapping(col);
#endif #endif

View File

@ -17,8 +17,15 @@ varying vec3 vPosition;
// precision must be considered). // precision must be considered).
varying vec3 worldPosition; varying vec3 worldPosition;
varying lowp vec4 varColor; varying lowp vec4 varColor;
// The centroid keyword ensures that after interpolation the texture coordinates
// lie within the same bounds when MSAA is en- and disabled.
// This fixes the stripes problem with nearest-neighbour textures and MSAA.
#ifdef GL_ES
varying mediump vec2 varTexCoord; varying mediump vec2 varTexCoord;
varying mediump vec3 eyeVec; #else
centroid varying vec2 varTexCoord;
#endif
varying vec3 eyeVec;
// Color of the light emitted by the light sources. // Color of the light emitted by the light sources.
const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
@ -144,7 +151,9 @@ void main(void)
color.xyz = color.zyx; // swap RGB order color.xyz = color.zyx; // swap RGB order
#endif #endif
// The alpha gives the ratio of sunlight in the incoming light. // The alpha gives the ratio of sunlight in the incoming light.
color.rgb *= 2.0 * mix(artificialLight.rgb, dayLight.rgb, color.a); float nightRatio = 1.0 - inVertexColor.a;
color.rgb = inVertexColor.rgb * (inVertexColor.a * dayLight.rgb +
nightRatio * artificialLight.rgb) * 2.0;
color.a = 1.0; color.a = 1.0;
// Emphase blue a bit in darker places // Emphase blue a bit in darker places

View File

@ -9,7 +9,11 @@ varying vec3 vNormal;
varying vec3 vPosition; varying vec3 vPosition;
varying vec3 worldPosition; varying vec3 worldPosition;
varying lowp vec4 varColor; varying lowp vec4 varColor;
#ifdef GL_ES
varying mediump vec2 varTexCoord; varying mediump vec2 varTexCoord;
#else
centroid varying vec2 varTexCoord;
#endif
varying vec3 eyeVec; varying vec3 eyeVec;
varying float vIDiff; varying float vIDiff;
@ -19,7 +23,7 @@ const float BS = 10.0;
const float fogStart = FOG_START; const float fogStart = FOG_START;
const float fogShadingParameter = 1.0 / (1.0 - fogStart); const float fogShadingParameter = 1.0 / (1.0 - fogStart);
#ifdef ENABLE_TONE_MAPPING #if ENABLE_TONE_MAPPING
/* Hable's UC2 Tone mapping parameters /* Hable's UC2 Tone mapping parameters
A = 0.22; A = 0.22;
@ -43,7 +47,7 @@ vec4 applyToneMapping(vec4 color)
const float gamma = 1.6; const float gamma = 1.6;
const float exposureBias = 5.5; const float exposureBias = 5.5;
color.rgb = uncharted2Tonemap(exposureBias * color.rgb); color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
// Precalculated white_scale from // Precalculated white_scale from
//vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
vec3 whiteScale = vec3(1.036015346); vec3 whiteScale = vec3(1.036015346);
color.rgb *= whiteScale; color.rgb *= whiteScale;
@ -75,7 +79,7 @@ void main(void)
col.rgb *= emissiveColor.rgb * vIDiff; col.rgb *= emissiveColor.rgb * vIDiff;
#ifdef ENABLE_TONE_MAPPING #if ENABLE_TONE_MAPPING
col = applyToneMapping(col); col = applyToneMapping(col);
#endif #endif

View File

@ -7,7 +7,11 @@ varying vec3 vNormal;
varying vec3 vPosition; varying vec3 vPosition;
varying vec3 worldPosition; varying vec3 worldPosition;
varying lowp vec4 varColor; varying lowp vec4 varColor;
#ifdef GL_ES
varying mediump vec2 varTexCoord; varying mediump vec2 varTexCoord;
#else
centroid varying vec2 varTexCoord;
#endif
varying vec3 eyeVec; varying vec3 eyeVec;
varying float vIDiff; varying float vIDiff;

View File

@ -109,6 +109,10 @@ core.register_on_sending_chat_message(function(message)
return false return false
end) end)
core.register_on_chatcommand(function(command, params)
print("[PREVIEW] caught command '"..command.."'. Parameters: '"..params.."'")
end)
-- This is an example function to ensure it's working properly, should be removed before merge -- This is an example function to ensure it's working properly, should be removed before merge
core.register_on_hp_modification(function(hp) core.register_on_hp_modification(function(hp)
print("[PREVIEW] HP modified " .. hp) print("[PREVIEW] HP modified " .. hp)

101
doc/builtin_entities.txt Normal file
View File

@ -0,0 +1,101 @@
# Builtin Entities
Minetest registers two entities by default: Falling nodes and dropped items.
This document describes how they behave and what you can do with them.
## Falling node (`__builtin:falling_node`)
This entity is created by `minetest.check_for_falling` in place of a node
with the special group `falling_node=1`. Falling nodes can also be created
artificially with `minetest.spawn_falling_node`.
Needs manual initialization when spawned using `/spawnentity`.
Default behaviour:
* Falls down in a straight line (gravity = `movement_gravity` setting)
* Collides with `walkable` node
* Collides with all physical objects except players
* If the node group `float=1` is set, it also collides with liquid nodes
* When it hits a solid (=`walkable`) node, it will try to place itself as a
node, replacing the node above.
* If the falling node cannot replace the destination node, it is dropped.
* If the destination node is a leveled node (`paramtype2="leveled"`) of the
same node name, the levels of both are summed.
### Entity fields
* `set_node(self, node[, meta])`
* Function to initialize the falling node
* `node` and `meta` are explained below.
* The `meta` argument is optional.
* `node`: Node table of the node (`name`, `param1`, `param2`) that this
entity represents. Read-only.
* `meta`: Node metadata of the falling node. Will be used when the falling
nodes tries to place itself as a node. Read-only.
### Rendering / supported nodes
Falling nodes have visuals to look as close as possible to the original node.
This works for most drawtypes, but there are limitations.
Supported drawtypes:
* `normal`
* `signlike`
* `torchlike`
* `nodebox`
* `raillike`
* `glasslike`
* `glasslike_framed`
* `glasslike_framed_optional`
* `allfaces`
* `allfaces_optional`
* `firelike`
* `mesh`
* `fencelike`
* `liquid`
* `airlike` (not pointable)
Other drawtypes still kinda work, but they might look weird.
Supported `paramtype2` values:
* `wallmounted`
* `facedir`
* `colorwallmounted`
* `colorfacedir`
* `color`
## Dropped item stack (`__builtin:item`)
This is an item stack in a collectable form.
Common cases that spawn a dropped item:
* Item dropped by player
* The root node of a node with the group `attached_node=1` is removed
* `minetest.add_item` is called
Needs manual initialization when spawned using `/spawnentity`.
### Behavior
* Players can collect it by punching
* Lifespan is defined by the setting `item_entity_ttl`
* Slides on `slippery` nodes
* Subject to gravity (uses `movement_gravity` setting)
* Collides with `walkable` nodes
* Does not collide physical objects
* When it's inside a solid (`walkable=true`) node, it tries to escape to a
neighboring non-solid (`walkable=false`) node
### Entity fields
* `set_item(self, item)`:
* Function to initialize the dropped item
* `item` (type `ItemStack`) specifies the item to represent
* `age`: Age in seconds. Behaviour according to the setting `item_entity_ttl`
* `itemstring`: Itemstring of the item that this item entity represents.
Read-only.
Other fields are for internal use only.

View File

@ -1,4 +1,4 @@
Minetest Lua Client Modding API Reference 5.3.0 Minetest Lua Client Modding API Reference 5.4.0
================================================ ================================================
* More information at <http://www.minetest.net/> * More information at <http://www.minetest.net/>
* Developer Wiki: <http://dev.minetest.net/> * Developer Wiki: <http://dev.minetest.net/>
@ -620,7 +620,7 @@ Helper functions
* `minetest.is_yes(arg)` * `minetest.is_yes(arg)`
* returns whether `arg` can be interpreted as yes * returns whether `arg` can be interpreted as yes
* `minetest.is_nan(arg)` * `minetest.is_nan(arg)`
* returns true true when the passed number represents NaN. * returns true when the passed number represents NaN.
* `table.copy(table)`: returns a table * `table.copy(table)`: returns a table
* returns a deep copy of `table` * returns a deep copy of `table`
@ -686,6 +686,11 @@ Call these functions only at load time!
* Adds definition to minetest.registered_chatcommands * Adds definition to minetest.registered_chatcommands
* `minetest.unregister_chatcommand(name)` * `minetest.unregister_chatcommand(name)`
* Unregisters a chatcommands registered with register_chatcommand. * Unregisters a chatcommands registered with register_chatcommand.
* `minetest.register_on_chatcommand(function(command, params))`
* Called always when a chatcommand is triggered, before `minetest.registered_chatcommands`
is checked to see if that the command exists, but after the input is parsed.
* Return `true` to mark the command as handled, which means that the default
handlers will be prevented.
* `minetest.register_on_death(function())` * `minetest.register_on_death(function())`
* Called when the local player dies * Called when the local player dies
* `minetest.register_on_hp_modification(function(hp))` * `minetest.register_on_hp_modification(function(hp))`
@ -768,12 +773,15 @@ Call these functions only at load time!
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `search_center` is an optional boolean (default: `false`) * `search_center` is an optional boolean (default: `false`)
If true `pos` is also checked for the nodes If true `pos` is also checked for the nodes
* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of * `minetest.find_nodes_in_area(pos1, pos2, nodenames, [grouped])`
positions. * `pos1` and `pos2` are the min and max positions of the area to search.
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* First return value: Table with all node positions * If `grouped` is true the return value is a table indexed by node name
* Second return value: Table with the count of each node with the node name which contains lists of positions.
as index. * If `grouped` is false or absent the return values are as follows:
first value: Table with all node positions
second value: Table with the count of each node with the node name
as index
* Area volume is limited to 4,096,000 nodes * Area volume is limited to 4,096,000 nodes
* `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a * `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a
list of positions. list of positions.
@ -993,6 +1001,7 @@ Please do not try to access the reference until the camera is initialized, other
### LocalPlayer ### LocalPlayer
An interface to retrieve information about the player. An interface to retrieve information about the player.
This object will only be available after the client is initialized. Earlier accesses will yield a `nil` value.
Methods: Methods:
@ -1097,8 +1106,8 @@ Methods:
aux1 = boolean, aux1 = boolean,
sneak = boolean, sneak = boolean,
zoom = boolean, zoom = boolean,
LMB = boolean, dig = boolean,
RMB = boolean, place = boolean,
} }
``` ```

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
Minetest Lua Mainmenu API Reference 5.3.0 Minetest Lua Mainmenu API Reference 5.4.0
========================================= =========================================
Introduction Introduction
@ -43,10 +43,14 @@ core.get_max_supp_proto()
core.open_url(url) core.open_url(url)
^ opens the URL in a web browser, returns false on failure. ^ opens the URL in a web browser, returns false on failure.
^ Must begin with http:// or https:// ^ Must begin with http:// or https://
core.open_dir(path)
^ opens the path in the system file browser/explorer, returns false on failure.
^ Must be an existing directory.
core.get_version() (possible in async calls) core.get_version() (possible in async calls)
^ returns current core version ^ returns current core version
Filesystem Filesystem
---------- ----------
@ -63,6 +67,8 @@ core.copy_dir(source,destination,keep_soure) (possible in async calls)
^ destination folder ^ destination folder
^ keep_source DEFAULT true --> if set to false source is deleted after copying ^ keep_source DEFAULT true --> if set to false source is deleted after copying
^ returns true/false ^ returns true/false
core.is_dir(path) (possible in async calls)
^ returns true if path is a valid dir
core.extract_zip(zipfile,destination) [unzip within path required] core.extract_zip(zipfile,destination) [unzip within path required]
^ zipfile to extract ^ zipfile to extract
^ destination folder to extract to ^ destination folder to extract to
@ -79,6 +85,7 @@ core.get_video_drivers()
core.get_mapgen_names([include_hidden=false]) -> table of map generator algorithms core.get_mapgen_names([include_hidden=false]) -> table of map generator algorithms
registered in the core (possible in async calls) registered in the core (possible in async calls)
core.get_cache_path() -> path of cache core.get_cache_path() -> path of cache
core.get_temp_path() -> path of temp folder
HTTP Requests HTTP Requests
@ -207,6 +214,9 @@ Content and Packages
Content - an installed mod, modpack, game, or texture pack (txt) Content - an installed mod, modpack, game, or texture pack (txt)
Package - content which is downloadable from the content db, may or may not be installed. Package - content which is downloadable from the content db, may or may not be installed.
* core.get_user_path() (possible in async calls)
* 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) * core.get_modpath() (possible in async calls)
* returns path to global modpath * returns path to global modpath
* core.get_clientmodpath() (possible in async calls) * core.get_clientmodpath() (possible in async calls)
@ -244,32 +254,6 @@ Package - content which is downloadable from the content db, may or may not be i
} }
Favorites
---------
core.get_favorites(location) -> list of favorites (possible in async calls)
^ location: "local" or "online"
^ returns {
[1] = {
clients = <number of clients/nil>,
clients_max = <maximum number of clients/nil>,
version = <server version/nil>,
password = <true/nil>,
creative = <true/nil>,
damage = <true/nil>,
pvp = <true/nil>,
description = <server description/nil>,
name = <server name/nil>,
address = <address of server/nil>,
port = <port>
clients_list = <array of clients/nil>
mods = <array of mods/nil>
},
...
}
core.delete_favorite(id, location) -> success
Logging Logging
------- -------

View File

@ -72,7 +72,12 @@ by texture packs. All existing fallback textures can be found in the directory
* `crosshair.png` * `crosshair.png`
* the crosshair texture in the center of the screen. The settings * the crosshair texture in the center of the screen. The settings
`crosshair_color` and `crosshair_alpha` are used to create a cross `crosshair_color` and `crosshair_alpha` are used to create a cross
when no texture was found when no texture is found.
* `object_crosshair.png`
* the crosshair seen when pointing at an object. The settings
`crosshair_color` and `crosshair_alpha` are used to create a cross
when no texture is found.
* `halo.png`: used for the node highlighting mesh * `halo.png`: used for the node highlighting mesh
@ -85,6 +90,7 @@ by texture packs. All existing fallback textures can be found in the directory
* `minimap_mask_square.png`: mask used for the square minimap * `minimap_mask_square.png`: mask used for the square minimap
* `minimap_overlay_round.png`: overlay texture for the round minimap * `minimap_overlay_round.png`: overlay texture for the round minimap
* `minimap_overlay_square.png`: overlay texture for the square minimap * `minimap_overlay_square.png`: overlay texture for the square minimap
* `no_texture_airlike.png`: fallback inventory image for airlike nodes
* `object_marker_red.png`: texture for players on the minimap * `object_marker_red.png`: texture for players on the minimap
* `player_marker.png`: texture for the own player on the square minimap * `player_marker.png`: texture for the own player on the square minimap
@ -189,11 +195,27 @@ Here are targets you can choose from:
| bottom | y- face | | bottom | y- face |
| sides | x-, x+, z-, z+ faces | | sides | x-, x+, z-, z+ faces |
| all | All faces. You can also use '*' instead of 'all'. | | all | All faces. You can also use '*' instead of 'all'. |
| special1 | The first entry in the special_tiles list |
| special2 | The second entry in the special_tiles list |
| special3 | The third entry in the special_tiles list |
| special4 | The fourth entry in the special_tiles list |
| special5 | The fifth entry in the special_tiles list |
| special6 | The sixth entry in the special_tiles list |
| inventory | The inventory texture | | inventory | The inventory texture |
| wield | The texture used when held by the player | | wield | The texture used when held by the player |
Nodes support all targets, but other items only support 'inventory' Nodes support all targets, but other items only support 'inventory'
and 'wield' and 'wield'.
### Using the special targets
The special* targets only apply to specific drawtypes:
* `flowingliquid`: special1 sets the top texture, special2 sets the side texture
* `allfaces_optional`: special1 is used by simple mode, see below
* `glasslike_framed`: When containing a liquid, special1 sets the liquid texture
* `glasslike_framed_optional`: Same as `glasslike_framed`
* `plantlike_rooted`: special1 sets the plant's texture
Designing leaves textures for the leaves rendering options Designing leaves textures for the leaves rendering options
---------------------------------------------------------- ----------------------------------------------------------

View File

@ -493,19 +493,8 @@ Static objects are persistent freely moving objects in the world.
Object types: Object types:
1: Test object 1: Test object
2: Item
3: Rat (obsolete)
4: Oerkki (obsolete)
5: Firefly (obsolete)
6: MobV2 (obsolete)
7: LuaEntity 7: LuaEntity
1: Item:
u8 version
version 0:
u16 len
u8[len] itemstring
7: LuaEntity: 7: LuaEntity:
u8 compatibility_byte (always 1) u8 compatibility_byte (always 1)
u16 len u16 len

View File

@ -1,4 +1,4 @@
Copyright (C) 2008 The Android Open Source Project Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

Binary file not shown.

View File

@ -23,9 +23,8 @@ Basically, just create a world and start. A few important things to note:
* Use the `/infplace` command to toggle infinite node placement in-game * Use the `/infplace` command to toggle infinite node placement in-game
* Use the Param2 Tool to change the param2 of nodes; it's useful to experiment with the various drawtype test nodes * Use the Param2 Tool to change the param2 of nodes; it's useful to experiment with the various drawtype test nodes
* Check out the game settings and server commands for additional tests and features * Check out the game settings and server commands for additional tests and features
* Creative Mode does nothing (apart from default engine behavior)
Confused by a certain node or item? Check out for inline code comments. Confused by a certain node or item? Check out for inline code comments. The usages of most tools are explained in their tooltips.
### Example tests ### Example tests

View File

@ -1,4 +1,4 @@
local WATER_ALPHA = 160 local WATER_ALPHA = "^[opacity:" .. 160
local WATER_VISC = 1 local WATER_VISC = 1
local LAVA_VISC = 7 local LAVA_VISC = 7
@ -124,15 +124,16 @@ minetest.register_node("basenodes:pine_needles", {
}) })
minetest.register_node("basenodes:water_source", { minetest.register_node("basenodes:water_source", {
description = "Water Source", description = "Water Source".."\n"..
"Drowning damage: 1",
drawtype = "liquid", drawtype = "liquid",
waving = 3, waving = 3,
tiles = {"default_water.png"}, tiles = {"default_water.png"..WATER_ALPHA},
special_tiles = { special_tiles = {
{name = "default_water.png", backface_culling = false}, {name = "default_water.png"..WATER_ALPHA, backface_culling = false},
{name = "default_water.png", backface_culling = true}, {name = "default_water.png"..WATER_ALPHA, backface_culling = true},
}, },
alpha = WATER_ALPHA, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
walkable = false, walkable = false,
pointable = false, pointable = false,
@ -149,15 +150,18 @@ minetest.register_node("basenodes:water_source", {
}) })
minetest.register_node("basenodes:water_flowing", { minetest.register_node("basenodes:water_flowing", {
description = "Flowing Water", description = "Flowing Water".."\n"..
"Drowning damage: 1",
drawtype = "flowingliquid", drawtype = "flowingliquid",
waving = 3, waving = 3,
tiles = {"default_water_flowing.png"}, tiles = {"default_water_flowing.png"},
special_tiles = { special_tiles = {
{name = "default_water_flowing.png", backface_culling = false}, {name = "default_water_flowing.png"..WATER_ALPHA,
{name = "default_water_flowing.png", backface_culling = false}, backface_culling = false},
{name = "default_water_flowing.png"..WATER_ALPHA,
backface_culling = false},
}, },
alpha = WATER_ALPHA, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
paramtype2 = "flowingliquid", paramtype2 = "flowingliquid",
walkable = false, walkable = false,
@ -175,15 +179,16 @@ minetest.register_node("basenodes:water_flowing", {
}) })
minetest.register_node("basenodes:river_water_source", { minetest.register_node("basenodes:river_water_source", {
description = "River Water Source", description = "River Water Source".."\n"..
"Drowning damage: 1",
drawtype = "liquid", drawtype = "liquid",
waving = 3, waving = 3,
tiles = { "default_river_water.png" }, tiles = { "default_river_water.png"..WATER_ALPHA },
special_tiles = { special_tiles = {
{name = "default_river_water.png", backface_culling = false}, {name = "default_river_water.png"..WATER_ALPHA, backface_culling = false},
{name = "default_river_water.png", backface_culling = true}, {name = "default_river_water.png"..WATER_ALPHA, backface_culling = true},
}, },
alpha = WATER_ALPHA, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
walkable = false, walkable = false,
pointable = false, pointable = false,
@ -202,15 +207,18 @@ minetest.register_node("basenodes:river_water_source", {
}) })
minetest.register_node("basenodes:river_water_flowing", { minetest.register_node("basenodes:river_water_flowing", {
description = "Flowing River Water", description = "Flowing River Water".."\n"..
"Drowning damage: 1",
drawtype = "flowingliquid", drawtype = "flowingliquid",
waving = 3, waving = 3,
tiles = {"default_river_water_flowing.png"}, tiles = {"default_river_water_flowing.png"..WATER_ALPHA},
special_tiles = { special_tiles = {
{name = "default_river_water_flowing.png", backface_culling = false}, {name = "default_river_water_flowing.png"..WATER_ALPHA,
{name = "default_river_water_flowing.png", backface_culling = false}, backface_culling = false},
{name = "default_river_water_flowing.png"..WATER_ALPHA,
backface_culling = false},
}, },
alpha = WATER_ALPHA, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
paramtype2 = "flowingliquid", paramtype2 = "flowingliquid",
walkable = false, walkable = false,
@ -230,7 +238,9 @@ minetest.register_node("basenodes:river_water_flowing", {
}) })
minetest.register_node("basenodes:lava_flowing", { minetest.register_node("basenodes:lava_flowing", {
description = "Flowing Lava", description = "Flowing Lava".."\n"..
"4 damage per second".."\n"..
"Drowning damage: 1",
drawtype = "flowingliquid", drawtype = "flowingliquid",
tiles = {"default_lava_flowing.png"}, tiles = {"default_lava_flowing.png"},
special_tiles = { special_tiles = {
@ -255,7 +265,9 @@ minetest.register_node("basenodes:lava_flowing", {
}) })
minetest.register_node("basenodes:lava_source", { minetest.register_node("basenodes:lava_source", {
description = "Lava Source", description = "Lava Source".."\n"..
"4 damage per second".."\n"..
"Drowning damage: 1",
drawtype = "liquid", drawtype = "liquid",
tiles = { "default_lava.png" }, tiles = { "default_lava.png" },
special_tiles = { special_tiles = {
@ -294,7 +306,8 @@ minetest.register_node("basenodes:mossycobble", {
}) })
minetest.register_node("basenodes:apple", { minetest.register_node("basenodes:apple", {
description = "Apple", description = "Apple".."\n"..
"Food (+2)",
drawtype = "plantlike", drawtype = "plantlike",
tiles ={"default_apple.png"}, tiles ={"default_apple.png"},
inventory_image = "default_apple.png", inventory_image = "default_apple.png",
@ -334,5 +347,3 @@ minetest.register_node("basenodes:snowblock", {
tiles ={"default_snow.png"}, tiles ={"default_snow.png"},
groups = {crumbly=3}, groups = {crumbly=3},
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 B

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

View File

@ -0,0 +1,7 @@
The dirt_with_grass folder is for testing loading textures from subfolders.
If it works correctly, the default_grass_side.png file in the folder is used but
default_grass.png is not overwritten by the file in the folder.
default_dirt.png should be overwritten by the default_dirt.png in the unittests
mod which depends on basenodes.

View File

@ -6,7 +6,7 @@
Tool types: Tool types:
* Hand: basic tool/weapon (just for convenience, not optimized for testing) * Hand: basic tool/weapon (special capabilities in creative mode)
* Pickaxe: dig cracky * Pickaxe: dig cracky
* Axe: dig choppy * Axe: dig choppy
* Shovel: dig crumbly * Shovel: dig crumbly
@ -24,25 +24,54 @@ Tool materials:
]] ]]
-- The hand -- The hand
minetest.register_item(":", { if minetest.settings:get_bool("creative_mode") then
type = "none", local digtime = 42
wield_image = "wieldhand.png", local caps = {times = {digtime, digtime, digtime}, uses = 0, maxlevel = 256}
wield_scale = {x=1,y=1,z=2.5},
tool_capabilities = { minetest.register_item(":", {
full_punch_interval = 1.0, type = "none",
max_drop_level = 0, wield_image = "wieldhand.png",
groupcaps = { wield_scale = {x = 1, y = 1, z = 2.5},
crumbly = {times={[3]=1.50}, uses=0, maxlevel=0}, range = 10,
snappy = {times={[3]=1.50}, uses=0, maxlevel=0}, tool_capabilities = {
oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=2.00}, uses=0, maxlevel=0}, full_punch_interval = 0.5,
}, max_drop_level = 3,
damage_groups = {fleshy=1}, groupcaps = {
} crumbly = caps,
}) cracky = caps,
snappy = caps,
choppy = caps,
oddly_breakable_by_hand = caps,
-- dig_immediate group doesn't use value 1. Value 3 is instant dig
dig_immediate =
{times = {[2] = digtime, [3] = 0}, uses = 0, maxlevel = 256},
},
damage_groups = {fleshy = 10},
}
})
else
minetest.register_item(":", {
type = "none",
wield_image = "wieldhand.png",
wield_scale = {x = 1, y = 1, z = 2.5},
tool_capabilities = {
full_punch_interval = 0.9,
max_drop_level = 0,
groupcaps = {
crumbly = {times = {[2] = 3.00, [3] = 0.70}, uses = 0, maxlevel = 1},
snappy = {times = {[3] = 0.40}, uses = 0, maxlevel = 1},
oddly_breakable_by_hand =
{times = {[1] = 3.50, [2] = 2.00, [3] = 0.70}, uses = 0}
},
damage_groups = {fleshy = 1},
}
})
end
-- Mese Pickaxe: special tool that digs "everything" instantly -- Mese Pickaxe: special tool that digs "everything" instantly
minetest.register_tool("basetools:pick_mese", { minetest.register_tool("basetools:pick_mese", {
description = "Mese Pickaxe", description = "Mese Pickaxe".."\n"..
"Digs diggable nodes instantly",
inventory_image = "basetools_mesepick.png", inventory_image = "basetools_mesepick.png",
tool_capabilities = { tool_capabilities = {
full_punch_interval = 1.0, full_punch_interval = 1.0,
@ -65,7 +94,9 @@ minetest.register_tool("basetools:pick_mese", {
-- This should break after only 1 use -- This should break after only 1 use
minetest.register_tool("basetools:pick_dirt", { minetest.register_tool("basetools:pick_dirt", {
description = "Dirt Pickaxe", description = "Dirt Pickaxe".."\n"..
"Digs cracky=3".."\n"..
"1 use only",
inventory_image = "basetools_dirtpick.png", inventory_image = "basetools_dirtpick.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -76,7 +107,8 @@ minetest.register_tool("basetools:pick_dirt", {
}) })
minetest.register_tool("basetools:pick_wood", { minetest.register_tool("basetools:pick_wood", {
description = "Wooden Pickaxe", description = "Wooden Pickaxe".."\n"..
"Digs cracky=3",
inventory_image = "basetools_woodpick.png", inventory_image = "basetools_woodpick.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -86,7 +118,8 @@ minetest.register_tool("basetools:pick_wood", {
}, },
}) })
minetest.register_tool("basetools:pick_stone", { minetest.register_tool("basetools:pick_stone", {
description = "Stone Pickaxe", description = "Stone Pickaxe".."\n"..
"Digs cracky=2..3",
inventory_image = "basetools_stonepick.png", inventory_image = "basetools_stonepick.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -96,7 +129,8 @@ minetest.register_tool("basetools:pick_stone", {
}, },
}) })
minetest.register_tool("basetools:pick_steel", { minetest.register_tool("basetools:pick_steel", {
description = "Steel Pickaxe", description = "Steel Pickaxe".."\n"..
"Digs cracky=1..3",
inventory_image = "basetools_steelpick.png", inventory_image = "basetools_steelpick.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=1, max_drop_level=1,
@ -106,7 +140,9 @@ minetest.register_tool("basetools:pick_steel", {
}, },
}) })
minetest.register_tool("basetools:pick_steel_l1", { minetest.register_tool("basetools:pick_steel_l1", {
description = "Steel Pickaxe Level 1", description = "Steel Pickaxe Level 1".."\n"..
"Digs cracky=1..3".."\n"..
"maxlevel=1",
inventory_image = "basetools_steelpick_l1.png", inventory_image = "basetools_steelpick_l1.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=1, max_drop_level=1,
@ -116,7 +152,9 @@ minetest.register_tool("basetools:pick_steel_l1", {
}, },
}) })
minetest.register_tool("basetools:pick_steel_l2", { minetest.register_tool("basetools:pick_steel_l2", {
description = "Steel Pickaxe Level 2", description = "Steel Pickaxe Level 2".."\n"..
"Digs cracky=1..3".."\n"..
"maxlevel=2",
inventory_image = "basetools_steelpick_l2.png", inventory_image = "basetools_steelpick_l2.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=1, max_drop_level=1,
@ -131,7 +169,8 @@ minetest.register_tool("basetools:pick_steel_l2", {
-- --
minetest.register_tool("basetools:shovel_wood", { minetest.register_tool("basetools:shovel_wood", {
description = "Wooden Shovel", description = "Wooden Shovel".."\n"..
"Digs crumbly=3",
inventory_image = "basetools_woodshovel.png", inventory_image = "basetools_woodshovel.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -141,7 +180,8 @@ minetest.register_tool("basetools:shovel_wood", {
}, },
}) })
minetest.register_tool("basetools:shovel_stone", { minetest.register_tool("basetools:shovel_stone", {
description = "Stone Shovel", description = "Stone Shovel".."\n"..
"Digs crumbly=2..3",
inventory_image = "basetools_stoneshovel.png", inventory_image = "basetools_stoneshovel.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -151,7 +191,8 @@ minetest.register_tool("basetools:shovel_stone", {
}, },
}) })
minetest.register_tool("basetools:shovel_steel", { minetest.register_tool("basetools:shovel_steel", {
description = "Steel Shovel", description = "Steel Shovel".."\n"..
"Digs crumbly=1..3",
inventory_image = "basetools_steelshovel.png", inventory_image = "basetools_steelshovel.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=1, max_drop_level=1,
@ -166,7 +207,8 @@ minetest.register_tool("basetools:shovel_steel", {
-- --
minetest.register_tool("basetools:axe_wood", { minetest.register_tool("basetools:axe_wood", {
description = "Wooden Axe", description = "Wooden Axe".."\n"..
"Digs choppy=3",
inventory_image = "basetools_woodaxe.png", inventory_image = "basetools_woodaxe.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -176,7 +218,8 @@ minetest.register_tool("basetools:axe_wood", {
}, },
}) })
minetest.register_tool("basetools:axe_stone", { minetest.register_tool("basetools:axe_stone", {
description = "Stone Axe", description = "Stone Axe".."\n"..
"Digs choppy=2..3",
inventory_image = "basetools_stoneaxe.png", inventory_image = "basetools_stoneaxe.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -186,7 +229,8 @@ minetest.register_tool("basetools:axe_stone", {
}, },
}) })
minetest.register_tool("basetools:axe_steel", { minetest.register_tool("basetools:axe_steel", {
description = "Steel Axe", description = "Steel Axe".."\n"..
"Digs choppy=1..3",
inventory_image = "basetools_steelaxe.png", inventory_image = "basetools_steelaxe.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=1, max_drop_level=1,
@ -201,7 +245,8 @@ minetest.register_tool("basetools:axe_steel", {
-- --
minetest.register_tool("basetools:shears_wood", { minetest.register_tool("basetools:shears_wood", {
description = "Wooden Shears", description = "Wooden Shears".."\n"..
"Digs snappy=3",
inventory_image = "basetools_woodshears.png", inventory_image = "basetools_woodshears.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -211,7 +256,8 @@ minetest.register_tool("basetools:shears_wood", {
}, },
}) })
minetest.register_tool("basetools:shears_stone", { minetest.register_tool("basetools:shears_stone", {
description = "Stone Shears", description = "Stone Shears".."\n"..
"Digs snappy=2..3",
inventory_image = "basetools_stoneshears.png", inventory_image = "basetools_stoneshears.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=0, max_drop_level=0,
@ -221,7 +267,8 @@ minetest.register_tool("basetools:shears_stone", {
}, },
}) })
minetest.register_tool("basetools:shears_steel", { minetest.register_tool("basetools:shears_steel", {
description = "Steel Shears", description = "Steel Shears".."\n"..
"Digs snappy=1..3",
inventory_image = "basetools_steelshears.png", inventory_image = "basetools_steelshears.png",
tool_capabilities = { tool_capabilities = {
max_drop_level=1, max_drop_level=1,
@ -236,7 +283,8 @@ minetest.register_tool("basetools:shears_steel", {
-- --
minetest.register_tool("basetools:sword_wood", { minetest.register_tool("basetools:sword_wood", {
description = "Wooden Sword", description = "Wooden Sword".."\n"..
"Damage: fleshy=2",
inventory_image = "basetools_woodsword.png", inventory_image = "basetools_woodsword.png",
tool_capabilities = { tool_capabilities = {
full_punch_interval = 1.0, full_punch_interval = 1.0,
@ -244,7 +292,8 @@ minetest.register_tool("basetools:sword_wood", {
} }
}) })
minetest.register_tool("basetools:sword_stone", { minetest.register_tool("basetools:sword_stone", {
description = "Stone Sword", description = "Stone Sword".."\n"..
"Damage: fleshy=4",
inventory_image = "basetools_stonesword.png", inventory_image = "basetools_stonesword.png",
tool_capabilities = { tool_capabilities = {
full_punch_interval = 1.0, full_punch_interval = 1.0,
@ -253,7 +302,8 @@ minetest.register_tool("basetools:sword_stone", {
} }
}) })
minetest.register_tool("basetools:sword_steel", { minetest.register_tool("basetools:sword_steel", {
description = "Steel Sword", description = "Steel Sword".."\n"..
"Damage: fleshy=6",
inventory_image = "basetools_steelsword.png", inventory_image = "basetools_steelsword.png",
tool_capabilities = { tool_capabilities = {
full_punch_interval = 1.0, full_punch_interval = 1.0,
@ -264,7 +314,8 @@ minetest.register_tool("basetools:sword_steel", {
-- Fire/Ice sword: Deal damage to non-fleshy damage groups -- Fire/Ice sword: Deal damage to non-fleshy damage groups
minetest.register_tool("basetools:sword_fire", { minetest.register_tool("basetools:sword_fire", {
description = "Fire Sword", description = "Fire Sword".."\n"..
"Damage: icy=6",
inventory_image = "basetools_firesword.png", inventory_image = "basetools_firesword.png",
tool_capabilities = { tool_capabilities = {
full_punch_interval = 1.0, full_punch_interval = 1.0,
@ -273,12 +324,13 @@ minetest.register_tool("basetools:sword_fire", {
} }
}) })
minetest.register_tool("basetools:sword_ice", { minetest.register_tool("basetools:sword_ice", {
description = "Ice Sword", description = "Ice Sword".."\n"..
"Damage: fiery=6",
inventory_image = "basetools_icesword.png", inventory_image = "basetools_icesword.png",
tool_capabilities = { tool_capabilities = {
full_punch_interval = 1.0, full_punch_interval = 1.0,
max_drop_level=0, max_drop_level=0,
damage_groups = {firy=6}, damage_groups = {fiery=6},
} }
}) })
@ -286,7 +338,9 @@ minetest.register_tool("basetools:sword_ice", {
-- Dagger: Low damage, fast punch interval -- Dagger: Low damage, fast punch interval
-- --
minetest.register_tool("basetools:dagger_steel", { minetest.register_tool("basetools:dagger_steel", {
description = "Steel Dagger", description = "Steel Dagger".."\n"..
"Damage: fleshy=2".."\n"..
"Full Punch Interval: 0.5s",
inventory_image = "basetools_steeldagger.png", inventory_image = "basetools_steeldagger.png",
tool_capabilities = { tool_capabilities = {
full_punch_interval = 0.5, full_punch_interval = 0.5,

View File

@ -1,7 +1,8 @@
-- Bucket: Punch liquid source or flowing liquid to collect it -- Bucket: Punch liquid source or flowing liquid to collect it
minetest.register_tool("bucket:bucket", { minetest.register_tool("bucket:bucket", {
description = "Bucket", description = "Bucket".."\n"..
"Picks up liquid nodes",
inventory_image = "bucket.png", inventory_image = "bucket.png",
stack_max = 1, stack_max = 1,
liquids_pointable = true, liquids_pointable = true,

View File

@ -1,5 +1,6 @@
minetest.register_node("chest:chest", { minetest.register_node("chest:chest", {
description = "Chest", description = "Chest" .. "\n" ..
"32 inventory slots",
tiles ={"chest_chest.png^[sheet:2x2:0,0", "chest_chest.png^[sheet:2x2:0,0", tiles ={"chest_chest.png^[sheet:2x2:0,0", "chest_chest.png^[sheet:2x2:0,0",
"chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:1,0",
"chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:0,1"}, "chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:0,1"},
@ -22,6 +23,18 @@ minetest.register_node("chest:chest", {
local inv = meta:get_inventory() local inv = meta:get_inventory()
return inv:is_empty("main") return inv:is_empty("main")
end, end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.chat_send_player(player:get_player_name(), "Allow put: " .. stack:to_string())
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.chat_send_player(player:get_player_name(), "Allow take: " .. stack:to_string())
return stack:get_count()
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.chat_send_player(player:get_player_name(), "On put: " .. stack:to_string())
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.chat_send_player(player:get_player_name(), "On take: " .. stack:to_string())
end,
}) })

View File

@ -43,7 +43,8 @@ local function get_chest_formspec(page)
end end
minetest.register_node("chest_of_everything:chest", { minetest.register_node("chest_of_everything:chest", {
description = "Chest of Everything", description = "Chest of Everything" .. "\n" ..
"Grants access to all items",
tiles ={"chest_of_everything_chest.png^[sheet:2x2:0,0", "chest_of_everything_chest.png^[sheet:2x2:0,0", tiles ={"chest_of_everything_chest.png^[sheet:2x2:0,0", "chest_of_everything_chest.png^[sheet:2x2:0,0",
"chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0",
"chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:0,1"}, "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:0,1"},

View File

@ -214,3 +214,6 @@ minetest.register_chatcommand("test_place_nodes", {
end, end,
}) })
core.register_on_chatcommand(function(name, command, params)
minetest.log("caught command '"..command.."', issued by '"..name.."'. Parameters: '"..params.."'")
end)

View File

@ -44,7 +44,8 @@ minetest.register_node("experimental:callback_node", {
}) })
minetest.register_tool("experimental:privatizer", { minetest.register_tool("experimental:privatizer", {
description = "Node Meta Privatizer", description = "Node Meta Privatizer".."\n"..
"Punch: Marks 'infotext' and 'formspec' meta fields of chest as private",
inventory_image = "experimental_tester_tool_1.png", inventory_image = "experimental_tester_tool_1.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
@ -67,7 +68,8 @@ minetest.register_tool("experimental:privatizer", {
}) })
minetest.register_tool("experimental:particle_spawner", { minetest.register_tool("experimental:particle_spawner", {
description = "Particle Spawner", description = "Particle Spawner".."\n"..
"Punch: Spawn random test particle",
inventory_image = "experimental_tester_tool_1.png^[invert:g", inventory_image = "experimental_tester_tool_1.png^[invert:g",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)

View File

@ -60,11 +60,13 @@ minetest.register_node("soundstuff:footstep_liquid", {
description = "Liquid Footstep Sound Node", description = "Liquid Footstep Sound Node",
drawtype = "liquid", drawtype = "liquid",
tiles = { tiles = {
"soundstuff_node_sound.png^[colorize:#0000FF:127", "soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190",
}, },
special_tiles = { special_tiles = {
{name = "soundstuff_node_sound.png^[colorize:#0000FF:127", backface_culling = false}, {name = "soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190",
{name = "soundstuff_node_sound.png^[colorize:#0000FF:127", backface_culling = true}, backface_culling = false},
{name = "soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190",
backface_culling = true},
}, },
liquids_pointable = true, liquids_pointable = true,
liquidtype = "source", liquidtype = "source",
@ -73,7 +75,7 @@ minetest.register_node("soundstuff:footstep_liquid", {
liquid_renewable = false, liquid_renewable = false,
liquid_range = 0, liquid_range = 0,
liquid_viscosity = 0, liquid_viscosity = 0,
alpha = 190, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
walkable = false, walkable = false,
pointable = false, pointable = false,
@ -92,7 +94,6 @@ minetest.register_node("soundstuff:footstep_climbable", {
tiles = { tiles = {
"soundstuff_node_climbable.png", "soundstuff_node_climbable.png",
}, },
alpha = 120,
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
walkable = false, walkable = false,
@ -107,7 +108,8 @@ minetest.register_node("soundstuff:footstep_climbable", {
minetest.register_craftitem("soundstuff:eat", { minetest.register_craftitem("soundstuff:eat", {
description = "Eat Sound Item", description = "Eat Sound Item".."\n"..
"Makes a sound when 'eaten' (with punch key)",
inventory_image = "soundstuff_eat.png", inventory_image = "soundstuff_eat.png",
on_use = minetest.item_eat(0), on_use = minetest.item_eat(0),
sound = { sound = {
@ -116,7 +118,9 @@ minetest.register_craftitem("soundstuff:eat", {
}) })
minetest.register_tool("soundstuff:breaks", { minetest.register_tool("soundstuff:breaks", {
description = "Break Sound Tool", description = "Break Sound Tool".."\n"..
"Digs cracky=3 and more".."\n"..
"Makes a sound when it breaks",
inventory_image = "soundstuff_node_dug.png", inventory_image = "soundstuff_node_dug.png",
sound = { sound = {
breaks = { name = "soundstuff_mono", gain = 1.0 }, breaks = { name = "soundstuff_mono", gain = 1.0 },

View File

@ -3,7 +3,7 @@
local phasearmor = { local phasearmor = {
[0]={icy=100}, [0]={icy=100},
[1]={firy=100}, [1]={fiery=100},
[2]={fleshy=100}, [2]={fleshy=100},
[3]={immortal=1}, [3]={immortal=1},
[4]={punch_operable=1}, [4]={punch_operable=1},

View File

@ -31,6 +31,9 @@ minetest.register_entity("testentities:callback", {
on_activate = function(self, staticdata, dtime_s) on_activate = function(self, staticdata, dtime_s)
message("Callback entity: on_activate! pos="..spos(self).."; dtime_s="..dtime_s) message("Callback entity: on_activate! pos="..spos(self).."; dtime_s="..dtime_s)
end, end,
on_deactivate = function(self)
message("Callback entity: on_deactivate! pos="..spos(self))
end,
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
local name = get_object_name(puncher) local name = get_object_name(puncher)
message( message(

View File

@ -68,7 +68,7 @@ minetest.register_entity("testentities:mesh_unshaded", {
-- Advanced visual tests -- Advanced visual tests
-- A test entity for testing animated and yaw-modulated sprites -- An entity for testing animated and yaw-modulated sprites
minetest.register_entity("testentities:yawsprite", { minetest.register_entity("testentities:yawsprite", {
initial_properties = { initial_properties = {
selectionbox = {-0.3, -0.5, -0.3, 0.3, 0.3, 0.3}, selectionbox = {-0.3, -0.5, -0.3, 0.3, 0.3, 0.3},
@ -79,6 +79,59 @@ minetest.register_entity("testentities:yawsprite", {
initial_sprite_basepos = {x=0, y=0}, initial_sprite_basepos = {x=0, y=0},
}, },
on_activate = function(self, staticdata) on_activate = function(self, staticdata)
self.object:set_sprite({x=0, y=0}, 1, 0, true) self.object:set_sprite({x=0, y=0}, 3, 0.5, true)
end,
})
-- An entity for testing animated upright sprites
minetest.register_entity("testentities:upright_animated", {
initial_properties = {
visual = "upright_sprite",
textures = {"testnodes_anim.png"},
spritediv = {x = 1, y = 4},
},
on_activate = function(self)
self.object:set_sprite({x=0, y=0}, 4, 1.0, false)
end,
})
minetest.register_entity("testentities:nametag", {
initial_properties = {
visual = "sprite",
textures = { "testentities_sprite.png" },
},
on_activate = function(self, staticdata)
if staticdata ~= "" then
local data = minetest.deserialize(staticdata)
self.color = data.color
self.bgcolor = data.bgcolor
else
self.color = {
r = math.random(0, 255),
g = math.random(0, 255),
b = math.random(0, 255),
}
if math.random(0, 10) > 5 then
self.bgcolor = {
r = math.random(0, 255),
g = math.random(0, 255),
b = math.random(0, 255),
a = math.random(0, 255),
}
end
end
assert(self.color)
self.object:set_properties({
nametag = tostring(math.random(1000, 10000)),
nametag_color = self.color,
nametag_bgcolor = self.bgcolor,
})
end,
get_staticdata = function(self)
return minetest.serialize({ color = self.color, bgcolor = self.bgcolor })
end, end,
}) })

View File

@ -22,3 +22,10 @@ minetest.register_craftitem("testfood:bad5", {
on_use = minetest.item_eat(-5), on_use = minetest.item_eat(-5),
}) })
minetest.register_craftitem("testfood:replace1", {
description = S("Replacing Food (+1)").."\n"..
S("Replaced with 'Good Food (+1)' when eaten"),
inventory_image = "testfood_replace.png",
on_use = minetest.item_eat(1, "testfood:good1"),
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

View File

@ -0,0 +1,14 @@
License of media files
----------------------
Content imported from minetest_game.
BlockMen (CC BY-SA 3.0)
default_chest_front.png
default_chest_lock.png
default_chest_side.png
default_chest_top.png
stujones11 (CC BY-SA 3.0)
An0n3m0us (CC BY-SA 3.0)
testformspec_character.b3d

View File

@ -33,6 +33,34 @@ local tabheaders_fs = [[
tabheader[8,6;10,1.5;tabs_size2;Height=1.5;1;false;false] tabheader[8,6;10,1.5;tabs_size2;Height=1.5;1;false;false]
]] ]]
local inv_style_fs = [[
style_type[list;noclip=true]
list[current_player;main;-0.75,0.75;2,2]
real_coordinates[false]
list[current_player;main;1.5,0;3,2]
real_coordinates[true]
real_coordinates[false]
style_type[list;size=1.1;spacing=0.1]
list[current_player;main;5,0;3,2]
real_coordinates[true]
style_type[list;size=.001;spacing=0]
list[current_player;main;7,3.5;8,4]
box[3,3.5;1,1;#000000]
box[5,3.5;1,1;#000000]
box[4,4.5;1,1;#000000]
box[3,5.5;1,1;#000000]
box[5,5.5;1,1;#000000]
style_type[list;spacing=.25,.125;size=.75,.875]
list[current_player;main;3,3.5;3,3]
style_type[list;spacing=0;size=1.1]
list[current_player;main;.5,7;8,4]
]]
local hypertext_basic = [[ local hypertext_basic = [[
<bigger>Normal test</bigger> <bigger>Normal test</bigger>
This is a normal text. This is a normal text.
@ -164,7 +192,7 @@ local style_fs = [[
style[one_btn14:hovered+pressed;textcolor=purple] style[one_btn14:hovered+pressed;textcolor=purple]
image_button[0,9.6;1,1;testformspec_button_image.png;one_btn14;Bg] image_button[0,9.6;1,1;testformspec_button_image.png;one_btn14;Bg]
style[one_btn15;border=false;bgimg=testformspec_bg.png;bgimg_hovered=testformspec_bg_hovered.png;bgimg_pressed=testformspec_bg_pressed.png] style[one_btn15;border=false;bgcolor=#1cc;bgimg=testformspec_bg.png;bgimg_hovered=testformspec_bg_hovered.png;bgimg_pressed=testformspec_bg_pressed.png]
item_image_button[1.25,9.6;1,1;testformspec:item;one_btn15;Bg] item_image_button[1.25,9.6;1,1;testformspec:item;one_btn15;Bg]
style[one_btn16;border=false;bgimg=testformspec_bg_9slice.png;bgimg_hovered=testformspec_bg_9slice_hovered.png;bgimg_pressed=testformspec_bg_9slice_pressed.png;bgimg_middle=4,6] style[one_btn16;border=false;bgimg=testformspec_bg_9slice.png;bgimg_hovered=testformspec_bg_9slice_hovered.png;bgimg_pressed=testformspec_bg_9slice_pressed.png;bgimg_middle=4,6]
@ -199,6 +227,7 @@ local scroll_fs =
"box[1,1;8,6;#00aa]".. "box[1,1;8,6;#00aa]"..
"scroll_container[1,1;8,6;scrbar;vertical]".. "scroll_container[1,1;8,6;scrbar;vertical]"..
"button[0,1;1,1;lorem;Lorem]".. "button[0,1;1,1;lorem;Lorem]"..
"animated_image[0,1;4.5,1;clip_animated_image;testformspec_animation.png;4;100]" ..
"button[0,10;1,1;ipsum;Ipsum]".. "button[0,10;1,1;ipsum;Ipsum]"..
"pwdfield[2,2;1,1;lorem2;Lorem]".. "pwdfield[2,2;1,1;lorem2;Lorem]"..
"list[current_player;main;4,4;1,5;]".. "list[current_player;main;4,4;1,5;]"..
@ -211,6 +240,8 @@ local scroll_fs =
"tooltip[0,11;3,2;Buz;#f00;#000]".. "tooltip[0,11;3,2;Buz;#f00;#000]"..
"box[0,11;3,2;#00ff00]".. "box[0,11;3,2;#00ff00]"..
"hypertext[3,13;3,3;;" .. hypertext_basic .. "]" .. "hypertext[3,13;3,3;;" .. hypertext_basic .. "]" ..
"hypertext[3,17;3,3;;Hypertext with no scrollbar\\; the scroll container should scroll.]" ..
"textarea[3,21;3,1;textarea;;More scroll within scroll]" ..
"container[0,18]".. "container[0,18]"..
"box[1,2;3,2;#0a0a]".. "box[1,2;3,2;#0a0a]"..
"scroll_container[1,2;3,2;scrbar2;horizontal;0.06]".. "scroll_container[1,2;3,2;scrbar2;horizontal;0.06]"..
@ -310,6 +341,9 @@ local pages = {
"size[12,13]real_coordinates[true]" .. "size[12,13]real_coordinates[true]" ..
"container[0.5,1.5]" .. tabheaders_fs .. "container_end[]", "container[0.5,1.5]" .. tabheaders_fs .. "container_end[]",
-- Inv
"size[12,13]real_coordinates[true]" .. inv_style_fs,
-- Animation -- Animation
[[ [[
formspec_version[3] formspec_version[3]
@ -327,6 +361,10 @@ Number]
animated_image[3,4.25;1,1;;testformspec_animation.png;4;0;3] animated_image[3,4.25;1,1;;testformspec_animation.png;4;0;3]
animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100] animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100]
animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100] animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100]
style[m1;bgcolor=black]
model[0.5,6;4,4;m1;testformspec_character.b3d;testformspec_character.png]
model[5,6;4,4;m2;testformspec_chest.obj;default_chest_top.png,default_chest_top.png,default_chest_side.png,default_chest_side.png,default_chest_front.png,default_chest_inside.png;30,1;true;true]
]], ]],
-- Scroll containers -- Scroll containers
@ -337,7 +375,7 @@ Number]
local function show_test_formspec(pname, page_id) local function show_test_formspec(pname, page_id)
page_id = page_id or 2 page_id = page_id or 2
local fs = pages[page_id] .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Anim,ScrollC;" .. page_id .. ";false;false]" local fs = pages[page_id] .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Anim,ScrollC;" .. page_id .. ";false;false]"
minetest.show_formspec(pname, "testformspec:formspec", fs) minetest.show_formspec(pname, "testformspec:formspec", fs)
end end

View File

@ -0,0 +1,79 @@
# Blender v2.78 (sub 0) OBJ File: 'chest-open.blend'
# www.blender.org
o Top_Cube.002_None_Top_Cube.002_None_bottom
v -0.500000 0.408471 0.720970
v -0.500000 1.115578 0.013863
v -0.500000 0.894607 -0.207108
v -0.500000 0.187501 0.499999
v 0.500000 1.115578 0.013863
v 0.500000 0.408471 0.720970
v 0.500000 0.187501 0.499999
v 0.500000 0.894607 -0.207108
v -0.500000 0.187500 -0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 -0.500000 0.500000
v 0.500000 0.187500 -0.500000
v 0.500000 -0.500000 0.500000
v 0.500000 -0.500000 -0.500000
vt 0.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 1.0000
vt 0.0000 0.0000
vt 0.0000 1.0000
vt 1.0000 1.0000
vt 1.0000 0.6875
vt 0.0000 0.6875
vt 1.0000 1.0000
vt 0.0000 0.6875
vt 1.0000 0.6875
vt 1.0000 0.6875
vt 1.0000 0.0000
vt 0.0000 0.0000
vt 1.0000 0.6875
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 1.0000 0.6875
vt 1.0000 0.0000
vt 0.0000 1.0000
vt 0.0000 0.6875
vt 0.0000 0.6875
vt 0.0000 0.0000
vt 1.0000 0.5000
vt 1.0000 1.0000
vt 0.0000 1.0000
vt 0.0000 0.5000
vt 0.0000 0.0000
vt 1.0000 0.0000
vn 0.0000 0.7071 0.7071
vn -0.0000 -1.0000 -0.0000
vn -1.0000 0.0000 0.0000
vn 1.0000 0.0000 -0.0000
vn 0.0000 -0.7071 0.7071
vn 0.0000 0.0000 1.0000
vn -0.0000 0.7071 -0.7071
vn -0.0000 0.0000 -1.0000
vn -0.0000 -0.7071 -0.7071
vn -0.0000 1.0000 -0.0000
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Top
s off
f 6/1/1 5/2/1 2/3/1 1/4/1
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Bottom
f 11/5/2 10/6/2 14/7/2 13/8/2
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Right-Left
f 1/9/3 2/10/3 3/11/3 4/12/3
f 5/13/4 6/1/4 7/14/4 8/15/4
f 4/12/3 9/16/3 10/17/3 11/18/3
f 12/19/4 7/14/4 13/8/4 14/20/4
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Back
f 6/21/5 1/9/5 4/12/5 7/22/5
f 7/22/6 4/12/6 11/18/6 13/23/6
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Front
f 2/10/7 5/24/7 8/25/7 3/11/7
f 9/16/8 12/26/8 14/27/8 10/17/8
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Inside
f 4/28/9 3/29/9 8/30/9 7/31/9
f 7/31/10 12/32/10 9/33/10 4/28/10

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -145,6 +145,23 @@ minetest.register_node("testnodes:fencelike", {
}) })
minetest.register_node("testnodes:torchlike", { minetest.register_node("testnodes:torchlike", {
description = S("Torchlike Drawtype Test Node"),
drawtype = "torchlike",
paramtype = "light",
tiles = {
"testnodes_torchlike_floor.png",
"testnodes_torchlike_ceiling.png",
"testnodes_torchlike_wall.png",
},
walkable = false,
sunlight_propagates = true,
groups = { dig_immediate = 3 },
inventory_image = fallback_image("testnodes_torchlike_floor.png"),
})
minetest.register_node("testnodes:torchlike_wallmounted", {
description = S("Wallmounted Torchlike Drawtype Test Node"), description = S("Wallmounted Torchlike Drawtype Test Node"),
drawtype = "torchlike", drawtype = "torchlike",
paramtype = "light", paramtype = "light",
@ -162,6 +179,8 @@ minetest.register_node("testnodes:torchlike", {
inventory_image = fallback_image("testnodes_torchlike_floor.png"), inventory_image = fallback_image("testnodes_torchlike_floor.png"),
}) })
minetest.register_node("testnodes:signlike", { minetest.register_node("testnodes:signlike", {
description = S("Wallmounted Signlike Drawtype Test Node"), description = S("Wallmounted Signlike Drawtype Test Node"),
drawtype = "signlike", drawtype = "signlike",
@ -331,68 +350,72 @@ minetest.register_node("testnodes:plantlike_rooted_degrotate", {
}) })
-- Demonstrative liquid nodes, source and flowing form. -- Demonstrative liquid nodes, source and flowing form.
minetest.register_node("testnodes:liquid", { -- DRAWTYPE ONLY, NO LIQUID PHYSICS!
description = S("Source Liquid Drawtype Test Node"), -- Liquid ranges 0 to 8
drawtype = "liquid", for r = 0, 8 do
paramtype = "light", minetest.register_node("testnodes:liquid_"..r, {
tiles = { description = S("Source Liquid Drawtype Test Node, Range @1", r),
"testnodes_liquidsource.png", drawtype = "liquid",
}, paramtype = "light",
special_tiles = { tiles = {
{name="testnodes_liquidsource.png", backface_culling=false}, "testnodes_liquidsource_r"..r..".png^[colorize:#FFFFFF:100",
{name="testnodes_liquidsource.png", backface_culling=true}, },
}, special_tiles = {
use_texture_alpha = true, {name="testnodes_liquidsource_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=false},
{name="testnodes_liquidsource_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=true},
},
use_texture_alpha = "blend",
walkable = false, walkable = false,
liquidtype = "source", liquid_range = r,
liquid_range = 1, liquid_viscosity = 0,
liquid_viscosity = 0, liquid_alternative_flowing = "testnodes:liquid_flowing_"..r,
liquid_alternative_flowing = "testnodes:liquid_flowing", liquid_alternative_source = "testnodes:liquid_"..r,
liquid_alternative_source = "testnodes:liquid", groups = { dig_immediate = 3 },
groups = { dig_immediate = 3 }, })
}) minetest.register_node("testnodes:liquid_flowing_"..r, {
minetest.register_node("testnodes:liquid_flowing", { description = S("Flowing Liquid Drawtype Test Node, Range @1", r),
description = S("Flowing Liquid Drawtype Test Node"), drawtype = "flowingliquid",
drawtype = "flowingliquid", paramtype = "light",
paramtype = "light", paramtype2 = "flowingliquid",
paramtype2 = "flowingliquid", tiles = {
tiles = { "testnodes_liquidflowing_r"..r..".png^[colorize:#FFFFFF:100",
"testnodes_liquidflowing.png", },
}, special_tiles = {
special_tiles = { {name="testnodes_liquidflowing_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=false},
{name="testnodes_liquidflowing.png", backface_culling=false}, {name="testnodes_liquidflowing_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=false},
{name="testnodes_liquidflowing.png", backface_culling=false}, },
}, use_texture_alpha = "blend",
use_texture_alpha = true,
walkable = false, walkable = false,
liquidtype = "flowing", liquid_range = r,
liquid_range = 1, liquid_viscosity = 0,
liquid_viscosity = 0, liquid_alternative_flowing = "testnodes:liquid_flowing_"..r,
liquid_alternative_flowing = "testnodes:liquid_flowing", liquid_alternative_source = "testnodes:liquid_"..r,
liquid_alternative_source = "testnodes:liquid", groups = { dig_immediate = 3 },
groups = { dig_immediate = 3 }, })
})
end
-- Waving liquid test (drawtype only)
minetest.register_node("testnodes:liquid_waving", { minetest.register_node("testnodes:liquid_waving", {
description = S("Waving Source Liquid Drawtype Test Node"), description = S("Waving Source Liquid Drawtype Test Node"),
drawtype = "liquid", drawtype = "liquid",
paramtype = "light", paramtype = "light",
tiles = { tiles = {
"testnodes_liquidsource.png^[brighten", "testnodes_liquidsource.png^[colorize:#0000FF:127",
}, },
special_tiles = { special_tiles = {
{name="testnodes_liquidsource.png^[brighten", backface_culling=false}, {name="testnodes_liquidsource.png^[colorize:#0000FF:127", backface_culling=false},
{name="testnodes_liquidsource.png^[brighten", backface_culling=true}, {name="testnodes_liquidsource.png^[colorize:#0000FF:127", backface_culling=true},
}, },
use_texture_alpha = true, use_texture_alpha = "blend",
waving = 3, waving = 3,
walkable = false, walkable = false,
liquidtype = "source",
liquid_range = 1, liquid_range = 1,
liquid_viscosity = 0, liquid_viscosity = 0,
liquid_alternative_flowing = "testnodes:liquid_flowing_waving", liquid_alternative_flowing = "testnodes:liquid_flowing_waving",
@ -405,18 +428,17 @@ minetest.register_node("testnodes:liquid_flowing_waving", {
paramtype = "light", paramtype = "light",
paramtype2 = "flowingliquid", paramtype2 = "flowingliquid",
tiles = { tiles = {
"testnodes_liquidflowing.png^[brighten", "testnodes_liquidflowing.png^[colorize:#0000FF:127",
}, },
special_tiles = { special_tiles = {
{name="testnodes_liquidflowing.png^[brighten", backface_culling=false}, {name="testnodes_liquidflowing.png^[colorize:#0000FF:127", backface_culling=false},
{name="testnodes_liquidflowing.png^[brighten", backface_culling=false}, {name="testnodes_liquidflowing.png^[colorize:#0000FF:127", backface_culling=false},
}, },
use_texture_alpha = true, use_texture_alpha = "blend",
waving = 3, waving = 3,
walkable = false, walkable = false,
liquidtype = "flowing",
liquid_range = 1, liquid_range = 1,
liquid_viscosity = 0, liquid_viscosity = 0,
liquid_alternative_flowing = "testnodes:liquid_flowing_waving", liquid_alternative_flowing = "testnodes:liquid_flowing_waving",
@ -424,8 +446,6 @@ minetest.register_node("testnodes:liquid_flowing_waving", {
groups = { dig_immediate = 3 }, groups = { dig_immediate = 3 },
}) })
-- Invisible node -- Invisible node
minetest.register_node("testnodes:airlike", { minetest.register_node("testnodes:airlike", {
description = S("Airlike Drawtype Test Node"), description = S("Airlike Drawtype Test Node"),
@ -514,10 +534,19 @@ local scale = function(subname, desc_double, desc_half)
minetest.register_node("testnodes:"..subname.."_half", def) minetest.register_node("testnodes:"..subname.."_half", def)
end end
scale("allfaces",
S("Double-sized Allfaces Drawtype Test Node"),
S("Half-sized Allfaces Drawtype Test Node"))
scale("allfaces_optional",
S("Double-sized Allfaces Optional Drawtype Test Node"),
S("Half-sized Allfaces Optional Drawtype Test Node"))
scale("allfaces_optional_waving",
S("Double-sized Waving Allfaces Optional Drawtype Test Node"),
S("Half-sized Waving Allfaces Optional Drawtype Test Node"))
scale("plantlike", scale("plantlike",
S("Double-sized Plantlike Drawtype Test Node"), S("Double-sized Plantlike Drawtype Test Node"),
S("Half-sized Plantlike Drawtype Test Node")) S("Half-sized Plantlike Drawtype Test Node"))
scale("torchlike", scale("torchlike_wallmounted",
S("Double-sized Wallmounted Torchlike Drawtype Test Node"), S("Double-sized Wallmounted Torchlike Drawtype Test Node"),
S("Half-sized Wallmounted Torchlike Drawtype Test Node")) S("Half-sized Wallmounted Torchlike Drawtype Test Node"))
scale("signlike", scale("signlike",

View File

@ -22,7 +22,8 @@ end
-- Lets light through, but not sunlight, leading to a -- Lets light through, but not sunlight, leading to a
-- reduction in light level when light passes through -- reduction in light level when light passes through
minetest.register_node("testnodes:sunlight_filter", { minetest.register_node("testnodes:sunlight_filter", {
description = S("Sunlight Filter"), description = S("Sunlight Filter") .."\n"..
S("Lets light through, but weakens sunlight"),
paramtype = "light", paramtype = "light",
@ -35,7 +36,8 @@ minetest.register_node("testnodes:sunlight_filter", {
-- Lets light and sunlight through without obstruction -- Lets light and sunlight through without obstruction
minetest.register_node("testnodes:sunlight_propagator", { minetest.register_node("testnodes:sunlight_propagator", {
description = S("Sunlight Propagator"), description = S("Sunlight Propagator") .."\n"..
S("Lets all light through"),
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,

View File

@ -9,11 +9,9 @@ for d=0, 8 do
{name = "testnodes_liquidsource_r"..d..".png", backface_culling = false}, {name = "testnodes_liquidsource_r"..d..".png", backface_culling = false},
{name = "testnodes_liquidsource_r"..d..".png", backface_culling = true}, {name = "testnodes_liquidsource_r"..d..".png", backface_culling = true},
}, },
alpha = 192, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
walkable = false, walkable = false,
pointable = false,
diggable = false,
buildable_to = true, buildable_to = true,
is_ground_content = false, is_ground_content = false,
liquidtype = "source", liquidtype = "source",
@ -30,12 +28,10 @@ for d=0, 8 do
{name = "testnodes_liquidflowing_r"..d..".png", backface_culling = false}, {name = "testnodes_liquidflowing_r"..d..".png", backface_culling = false},
{name = "testnodes_liquidflowing_r"..d..".png", backface_culling = false}, {name = "testnodes_liquidflowing_r"..d..".png", backface_culling = false},
}, },
alpha = 192, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
paramtype2 = "flowingliquid", paramtype2 = "flowingliquid",
walkable = false, walkable = false,
pointable = false,
diggable = false,
buildable_to = true, buildable_to = true,
is_ground_content = false, is_ground_content = false,
liquidtype = "flowing", liquidtype = "flowing",
@ -53,11 +49,9 @@ for d=0, 8 do
{name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = false}, {name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = false},
{name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = true}, {name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = true},
}, },
alpha = 192, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
walkable = false, walkable = false,
pointable = false,
diggable = false,
buildable_to = true, buildable_to = true,
is_ground_content = false, is_ground_content = false,
liquidtype = "source", liquidtype = "source",
@ -74,12 +68,10 @@ for d=0, 8 do
{name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false}, {name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false},
{name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false}, {name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false},
}, },
alpha = 192, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
paramtype2 = "flowingliquid", paramtype2 = "flowingliquid",
walkable = false, walkable = false,
pointable = false,
diggable = false,
buildable_to = true, buildable_to = true,
is_ground_content = false, is_ground_content = false,
liquidtype = "flowing", liquidtype = "flowing",

View File

@ -1,2 +1,3 @@
name = testnodes name = testnodes
description = Contains a bunch of basic example nodes for demonstrative purposes, development and testing description = Contains a bunch of basic example nodes for demonstrative purposes, development and testing
depends = stairs

View File

@ -18,7 +18,7 @@ minetest.register_node("testnodes:nodebox_fixed", {
-- 50% higher than a regular node -- 50% higher than a regular node
minetest.register_node("testnodes:nodebox_overhigh", { minetest.register_node("testnodes:nodebox_overhigh", {
description = S("Overhigh Nodebox Test Node"), description = S("+50% high Nodebox Test Node"),
tiles = {"testnodes_nodebox.png"}, tiles = {"testnodes_nodebox.png"},
drawtype = "nodebox", drawtype = "nodebox",
paramtype = "light", paramtype = "light",
@ -30,15 +30,16 @@ minetest.register_node("testnodes:nodebox_overhigh", {
groups = {dig_immediate=3}, groups = {dig_immediate=3},
}) })
-- 100% higher than a regular node -- 95% higher than a regular node
minetest.register_node("testnodes:nodebox_overhigh2", { minetest.register_node("testnodes:nodebox_overhigh2", {
description = S("Double-height Nodebox Test Node"), description = S("+95% high Nodebox Test Node"),
tiles = {"testnodes_nodebox.png"}, tiles = {"testnodes_nodebox.png"},
drawtype = "nodebox", drawtype = "nodebox",
paramtype = "light", paramtype = "light",
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5}, -- Y max: more is possible, but glitchy
fixed = {-0.5, -0.5, -0.5, 0.5, 1.45, 0.5},
}, },
groups = {dig_immediate=3}, groups = {dig_immediate=3},

View File

@ -114,11 +114,10 @@ minetest.register_node("testnodes:liquid_nojump", {
{name = "testnodes_liquidsource.png^[colorize:#FF0000:127", backface_culling = false}, {name = "testnodes_liquidsource.png^[colorize:#FF0000:127", backface_culling = false},
{name = "testnodes_liquidsource.png^[colorize:#FF0000:127", backface_culling = true}, {name = "testnodes_liquidsource.png^[colorize:#FF0000:127", backface_culling = true},
}, },
use_texture_alpha = true, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
pointable = false, pointable = false,
liquids_pointable = true, liquids_pointable = true,
diggable = false,
buildable_to = true, buildable_to = true,
is_ground_content = false, is_ground_content = false,
post_effect_color = {a = 70, r = 255, g = 0, b = 200}, post_effect_color = {a = 70, r = 255, g = 0, b = 200},
@ -143,12 +142,11 @@ minetest.register_node("testnodes:liquidflowing_nojump", {
{name = "testnodes_liquidflowing.png^[colorize:#FF0000:127", backface_culling = false}, {name = "testnodes_liquidflowing.png^[colorize:#FF0000:127", backface_culling = false},
{name = "testnodes_liquidflowing.png^[colorize:#FF0000:127", backface_culling = false}, {name = "testnodes_liquidflowing.png^[colorize:#FF0000:127", backface_culling = false},
}, },
use_texture_alpha = true, use_texture_alpha = "blend",
paramtype = "light", paramtype = "light",
paramtype2 = "flowingliquid", paramtype2 = "flowingliquid",
pointable = false, pointable = false,
liquids_pointable = true, liquids_pointable = true,
diggable = false,
buildable_to = true, buildable_to = true,
is_ground_content = false, is_ground_content = false,
post_effect_color = {a = 70, r = 255, g = 0, b = 200}, post_effect_color = {a = 70, r = 255, g = 0, b = 200},

View File

@ -46,28 +46,22 @@ for a=1,#alphas do
tiles = { tiles = {
"testnodes_alpha"..alpha..".png", "testnodes_alpha"..alpha..".png",
}, },
use_texture_alpha = true, use_texture_alpha = "blend",
groups = { dig_immediate = 3 }, groups = { dig_immediate = 3 },
}) })
-- Transparency set via "alpha" parameter -- Transparency set via texture modifier
minetest.register_node("testnodes:alpha_"..alpha, { minetest.register_node("testnodes:alpha_"..alpha, {
description = S("Alpha Test Node (@1)", alpha), description = S("Alpha Test Node (@1)", alpha),
-- It seems that only the liquid drawtype supports the alpha parameter drawtype = "glasslike",
drawtype = "liquid",
paramtype = "light", paramtype = "light",
tiles = { tiles = {
"testnodes_alpha.png", "testnodes_alpha.png^[opacity:" .. alpha,
}, },
alpha = alpha, use_texture_alpha = "blend",
liquidtype = "source",
liquid_range = 0,
liquid_viscosity = 0,
liquid_alternative_source = "testnodes:alpha_"..alpha,
liquid_alternative_flowing = "testnodes:alpha_"..alpha,
groups = { dig_immediate = 3 }, groups = { dig_immediate = 3 },
}) })
end end

View File

@ -121,7 +121,11 @@ end
-- Sneak+punch: Select pathfinding algorithm -- Sneak+punch: Select pathfinding algorithm
-- Place: Select destination node -- Place: Select destination node
minetest.register_tool("testpathfinder:testpathfinder", { minetest.register_tool("testpathfinder:testpathfinder", {
description = S("Pathfinder Tester"), description = S("Pathfinder Tester") .."\n"..
S("Finds path between 2 points") .."\n"..
S("Place on node: Select destination") .."\n"..
S("Punch: Find path from here") .."\n"..
S("Sneak+Punch: Change algorithm"),
inventory_image = "testpathfinder_testpathfinder.png", inventory_image = "testpathfinder_testpathfinder.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = find_path_or_set_algorithm, on_use = find_path_or_set_algorithm,

View File

@ -5,13 +5,13 @@ dofile(minetest.get_modpath("testtools") .. "/light.lua")
-- TODO: Add a Node Metadata tool -- TODO: Add a Node Metadata tool
-- Param 2 Tool: Set param2 value of tools
-- Punch: +1
-- Punch+Shift: +8
-- Place: -1
-- Place+Shift: -8
minetest.register_tool("testtools:param2tool", { minetest.register_tool("testtools:param2tool", {
description = S("Param2 Tool"), description = S("Param2 Tool") .."\n"..
S("Modify param2 value of nodes") .."\n"..
S("Punch: +1") .."\n"..
S("Sneak+Punch: +8") .."\n"..
S("Place: -1") .."\n"..
S("Sneak+Place: -8"),
inventory_image = "testtools_param2tool.png", inventory_image = "testtools_param2tool.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
@ -49,7 +49,11 @@ minetest.register_tool("testtools:param2tool", {
}) })
minetest.register_tool("testtools:node_setter", { minetest.register_tool("testtools:node_setter", {
description = S("Node Setter"), description = S("Node Setter") .."\n"..
S("Replace pointed node with something else") .."\n"..
S("Punch: Select pointed node") .."\n"..
S("Place on node: Replace node with selected node") .."\n"..
S("Place in air: Manually select a node"),
inventory_image = "testtools_node_setter.png", inventory_image = "testtools_node_setter.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
@ -127,7 +131,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end) end)
minetest.register_tool("testtools:remover", { minetest.register_tool("testtools:remover", {
description = S("Remover"), description = S("Remover") .."\n"..
S("Punch: Remove pointed node or object"),
inventory_image = "testtools_remover.png", inventory_image = "testtools_remover.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
@ -138,13 +143,17 @@ minetest.register_tool("testtools:remover", {
local obj = pointed_thing.ref local obj = pointed_thing.ref
if not obj:is_player() then if not obj:is_player() then
obj:remove() obj:remove()
else
minetest.chat_send_player(user:get_player_name(), S("Can't remove players!"))
end end
end end
end, end,
}) })
minetest.register_tool("testtools:falling_node_tool", { minetest.register_tool("testtools:falling_node_tool", {
description = S("Falling Node Tool"), description = S("Falling Node Tool") .."\n"..
S("Punch: Make pointed node fall") .."\n"..
S("Place: Move pointed node 2 units upwards, then make it fall"),
inventory_image = "testtools_falling_node_tool.png", inventory_image = "testtools_falling_node_tool.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_place = function(itemstack, user, pointed_thing) on_place = function(itemstack, user, pointed_thing)
@ -193,7 +202,11 @@ minetest.register_tool("testtools:falling_node_tool", {
}) })
minetest.register_tool("testtools:rotator", { minetest.register_tool("testtools:rotator", {
description = S("Entity Rotator"), description = S("Entity Rotator") .. "\n" ..
S("Rotate pointed entity") .."\n"..
S("Punch: Yaw") .."\n"..
S("Sneak+Punch: Pitch") .."\n"..
S("Aux1+Punch: Roll"),
inventory_image = "testtools_entity_rotator.png", inventory_image = "testtools_entity_rotator.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
@ -246,7 +259,12 @@ local mover_config = function(itemstack, user, pointed_thing)
end end
minetest.register_tool("testtools:object_mover", { minetest.register_tool("testtools:object_mover", {
description = S("Object Mover"), description = S("Object Mover") .."\n"..
S("Move pointed object towards or away from you") .."\n"..
S("Punch: Move by distance").."\n"..
S("Sneak+Punch: Move by negative distance").."\n"..
S("Place: Increase distance").."\n"..
S("Sneak+Place: Decrease distance"),
inventory_image = "testtools_object_mover.png", inventory_image = "testtools_object_mover.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_place = mover_config, on_place = mover_config,
@ -289,7 +307,10 @@ minetest.register_tool("testtools:object_mover", {
minetest.register_tool("testtools:entity_scaler", { minetest.register_tool("testtools:entity_scaler", {
description = S("Entity Visual Scaler"), description = S("Entity Visual Scaler") .."\n"..
S("Scale visual size of entities") .."\n"..
S("Punch: Increase size") .."\n"..
S("Sneak+Punch: Decrease scale"),
inventory_image = "testtools_entity_scaler.png", inventory_image = "testtools_entity_scaler.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
@ -344,14 +365,21 @@ local function get_entity_list()
return entity_list return entity_list
end end
minetest.register_tool("testtools:entity_spawner", { minetest.register_tool("testtools:entity_spawner", {
description = S("Entity Spawner"), description = S("Entity Spawner") .."\n"..
S("Spawns entities") .."\n"..
S("Punch: Select entity to spawn") .."\n"..
S("Place: Spawn selected entity"),
inventory_image = "testtools_entity_spawner.png", inventory_image = "testtools_entity_spawner.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_place = function(itemstack, user, pointed_thing) on_place = function(itemstack, user, pointed_thing)
local name = user:get_player_name() local name = user:get_player_name()
if selections[name] and pointed_thing.type == "node" then if pointed_thing.type == "node" then
local pos = pointed_thing.above if selections[name] then
minetest.add_entity(pos, get_entity_list()[selections[name]]) local pos = pointed_thing.above
minetest.add_entity(pos, get_entity_list()[selections[name]])
else
minetest.chat_send_player(name, S("Select an entity first (with punch key)!"))
end
end end
end, end,
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
@ -437,7 +465,10 @@ local editor_formspec = function(playername, obj, value, sel)
end end
minetest.register_tool("testtools:object_editor", { minetest.register_tool("testtools:object_editor", {
description = S("Object Property Editor"), description = S("Object Property Editor") .."\n"..
S("Edit properties of objects") .."\n"..
S("Punch object: Edit object") .."\n"..
S("Punch air: Edit yourself"),
inventory_image = "testtools_object_editor.png", inventory_image = "testtools_object_editor.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
@ -517,7 +548,14 @@ local attacher_config = function(itemstack, user, pointed_thing)
end end
minetest.register_tool("testtools:object_attacher", { minetest.register_tool("testtools:object_attacher", {
description = S("Object Attacher"), description = S("Object Attacher") .."\n"..
S("Attach object to another") .."\n"..
S("Punch objects to first select parent object, then the child object to attach") .."\n"..
S("Punch air to select yourself") .."\n"..
S("Place: Incease attachment Y offset") .."\n"..
S("Sneak+Place: Decease attachment Y offset") .."\n"..
S("Aux1+Place: Incease attachment rotation") .."\n"..
S("Aux1+Sneak+Place: Decrease attachment rotation"),
inventory_image = "testtools_object_attacher.png", inventory_image = "testtools_object_attacher.png",
groups = { testtool = 1, disable_repair = 1 }, groups = { testtool = 1, disable_repair = 1 },
on_place = attacher_config, on_place = attacher_config,

View File

@ -5,10 +5,12 @@ dofile(modpath .. "/random.lua")
dofile(modpath .. "/player.lua") dofile(modpath .. "/player.lua")
dofile(modpath .. "/crafting_prepare.lua") dofile(modpath .. "/crafting_prepare.lua")
dofile(modpath .. "/crafting.lua") dofile(modpath .. "/crafting.lua")
dofile(modpath .. "/itemdescription.lua")
if minetest.settings:get_bool("devtest_unittests_autostart", false) then if minetest.settings:get_bool("devtest_unittests_autostart", false) then
unittests.test_random() unittests.test_random()
unittests.test_crafting() unittests.test_crafting()
unittests.test_short_desc()
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
unittests.test_player(player) unittests.test_player(player)
end) end)

View File

@ -0,0 +1,51 @@
local full_description = "Colorful Pickaxe\nThe best pick."
minetest.register_tool("unittests:colorful_pick", {
description = full_description,
inventory_image = "basetools_mesepick.png",
tool_capabilities = {
full_punch_interval = 1.0,
max_drop_level=3,
groupcaps={
cracky={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3},
crumbly={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3},
snappy={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}
},
damage_groups = {fleshy=4},
},
})
minetest.register_chatcommand("item_description", {
param = "",
description = "Show the short and full description of the wielded item.",
func = function(name)
local player = minetest.get_player_by_name(name)
local item = player:get_wielded_item()
return true, string.format("short_description: %s\ndescription: %s",
item:get_short_description(), item:get_description())
end
})
function unittests.test_short_desc()
local function get_short_description(item)
return ItemStack(item):get_short_description()
end
local stack = ItemStack("unittests:colorful_pick")
assert(stack:get_short_description() == "Colorful Pickaxe")
assert(get_short_description("unittests:colorful_pick") == "Colorful Pickaxe")
assert(minetest.registered_items["unittests:colorful_pick"].short_description == nil)
assert(stack:get_description() == full_description)
assert(stack:get_description() == minetest.registered_items["unittests:colorful_pick"].description)
stack:get_meta():set_string("description", "Hello World")
assert(stack:get_short_description() == "Hello World")
assert(stack:get_description() == "Hello World")
assert(get_short_description(stack) == "Hello World")
assert(get_short_description("unittests:colorful_pick") == "Colorful Pickaxe")
stack:get_meta():set_string("short_description", "Foo Bar")
assert(stack:get_short_description() == "Foo Bar")
assert(stack:get_description() == "Hello World")
return true
end

View File

@ -1,2 +1,3 @@
name = unittests name = unittests
description = Adds automated unit tests for the engine description = Adds automated unit tests for the engine
depends = basenodes

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

View File

@ -36,8 +36,12 @@ minetest.register_chatcommand("hp", {
end, end,
}) })
minetest.register_chatcommand("zoom", { minetest.register_on_joinplayer(function(player)
params = "[<zoom_fov>]", player:set_properties({zoom_fov = 15})
end)
minetest.register_chatcommand("zoomfov", {
params = "[<FOV>]",
description = "Set or display your zoom_fov", description = "Set or display your zoom_fov",
func = function(name, param) func = function(name, param)
local player = minetest.get_player_by_name(name) local player = minetest.get_player_by_name(name)
@ -58,8 +62,6 @@ minetest.register_chatcommand("zoom", {
end, end,
}) })
local s_infplace = minetest.settings:get("devtest_infplace") local s_infplace = minetest.settings:get("devtest_infplace")
if s_infplace == "true" then if s_infplace == "true" then
infplace = true infplace = true
@ -112,6 +114,62 @@ minetest.register_chatcommand("detach", {
end, end,
}) })
-- Use this to test waypoint capabilities
minetest.register_chatcommand("test_waypoints", {
params = "[change_immediate]",
description = "tests waypoint capabilities",
func = function(name, params)
local player = minetest.get_player_by_name(name)
local regular = player:hud_add {
hud_elem_type = "waypoint",
name = "regular waypoint",
text = "m",
number = 0xFF0000,
world_pos = vector.add(player:get_pos(), {x = 0, y = 1.5, z = 0})
}
local reduced_precision = player:hud_add {
hud_elem_type = "waypoint",
name = "better waypoint",
text = "m (0.5 steps, precision = 2)",
precision = 10,
number = 0xFFFF00,
world_pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0})
}
local function change()
if regular then
player:hud_change(regular, "world_pos", vector.add(player:get_pos(), {x = 0, y = 3, z = 0}))
end
if reduced_precision then
player:hud_change(reduced_precision, "precision", 2)
end
end
if params ~= "" then
-- change immediate
change()
else
minetest.after(0.5, change)
end
regular = regular or "error"
reduced_precision = reduced_precision or "error"
local hidden_distance = player:hud_add {
hud_elem_type = "waypoint",
name = "waypoint with hidden distance",
text = "this text is hidden as well (precision = 0)",
precision = 0,
number = 0x0000FF,
world_pos = vector.add(player:get_pos(), {x = 0, y = 0.5, z = 0})
} or "error"
local image_waypoint = player:hud_add {
hud_elem_type = "image_waypoint",
text = "wieldhand.png",
world_pos = player:get_pos(),
scale = {x = 10, y = 10},
offset = {x = 0, y = -32}
} or "error"
minetest.chat_send_player(name, "Waypoint IDs: regular: " .. regular .. ", reduced precision: " .. reduced_precision ..
", hidden distance: " .. hidden_distance .. ", image waypoint: " .. image_waypoint)
end
})
-- Unlimited node placement -- Unlimited node placement
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack) minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack)

Some files were not shown because too many files have changed in this diff Show More