Merge tag '5.4.1' into main
5.4.1
@ -29,3 +29,4 @@ AlignAfterOpenBracket: DontAlign
|
||||
ContinuationIndentWidth: 16
|
||||
ConstructorInitializerIndentWidth: 16
|
||||
BreakConstructorInitializers: AfterColon
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
6
.github/CONTRIBUTING.md
vendored
@ -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.
|
||||
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
|
||||
|
||||
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
@ -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
|
13
.github/workflows/build.yml
vendored
@ -55,9 +55,8 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install g++-8 gcc-8 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
install_linux_deps g++-8
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@ -99,11 +98,8 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install clang-9 valgrind -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
env:
|
||||
WITH_LUAJIT: 1
|
||||
install_linux_deps clang-9 valgrind libluajit-5.1-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@ -111,6 +107,7 @@ jobs:
|
||||
env:
|
||||
CC: clang-9
|
||||
CXX: clang++-9
|
||||
CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
@ -188,7 +185,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install compiler
|
||||
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
|
||||
sudo tar -xaf mingw.tar.xz -C /usr
|
||||
|
||||
@ -206,7 +203,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install compiler
|
||||
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
|
||||
sudo tar -xaf mingw.tar.xz -C /usr
|
||||
|
||||
|
193
.gitlab-ci.yml
@ -18,12 +18,12 @@ variables:
|
||||
- mkdir cmakebuild
|
||||
- mkdir -p artifact/minetest/usr/
|
||||
- cd cmakebuild
|
||||
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
||||
- 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 install
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 2h
|
||||
expire_in: 1h
|
||||
paths:
|
||||
- artifact/*
|
||||
|
||||
@ -34,15 +34,14 @@ variables:
|
||||
- apt-get install -y git
|
||||
- mkdir -p build/deb/minetest/DEBIAN/
|
||||
- cp misc/debpkg-control build/deb/minetest/DEBIAN/control
|
||||
- cp -Rp artifact/minetest/usr build/deb/minetest/
|
||||
- cp -a artifact/minetest/usr build/deb/minetest/
|
||||
script:
|
||||
- git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest
|
||||
- rm -Rf build/deb/minetest/usr/share/minetest/games/minetest/.git
|
||||
- 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
|
||||
- sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control
|
||||
- sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control
|
||||
- cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 90 day
|
||||
paths:
|
||||
- ./*.deb
|
||||
@ -51,44 +50,14 @@ variables:
|
||||
stage: deploy
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get install -y libc6 libcurl3-gnutls libfreetype6 libirrlicht1.8 $LEVELDB_PKG liblua5.1-0 libluajit-5.1-2 libopenal1 libstdc++6 libvorbisfile3 libx11-6 zlib1g
|
||||
script:
|
||||
- dpkg -i ./*.deb
|
||||
- apt-get install -y ./*.deb
|
||||
- minetest --version
|
||||
|
||||
##
|
||||
## 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
|
||||
|
||||
build:debian-9:
|
||||
@ -101,7 +70,7 @@ build:debian-9:
|
||||
package:debian-9:
|
||||
extends: .debpkg_template
|
||||
image: debian:9
|
||||
dependencies:
|
||||
needs:
|
||||
- build:debian-9
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
@ -109,12 +78,10 @@ package:debian-9:
|
||||
deploy:debian-9:
|
||||
extends: .debpkg_install
|
||||
image: debian:9
|
||||
dependencies:
|
||||
needs:
|
||||
- package:debian-9
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
|
||||
# Stretch
|
||||
# Buster
|
||||
|
||||
build:debian-10:
|
||||
extends: .build_template
|
||||
@ -126,7 +93,7 @@ build:debian-10:
|
||||
package:debian-10:
|
||||
extends: .debpkg_template
|
||||
image: debian:10
|
||||
dependencies:
|
||||
needs:
|
||||
- build:debian-10
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1d
|
||||
@ -134,44 +101,13 @@ package:debian-10:
|
||||
deploy:debian-10:
|
||||
extends: .debpkg_install
|
||||
image: debian:10
|
||||
dependencies:
|
||||
needs:
|
||||
- package:debian-10
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1d
|
||||
|
||||
##
|
||||
## 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
|
||||
|
||||
build:ubuntu-16.04:
|
||||
@ -184,7 +120,7 @@ build:ubuntu-16.04:
|
||||
package:ubuntu-16.04:
|
||||
extends: .debpkg_template
|
||||
image: ubuntu:xenial
|
||||
dependencies:
|
||||
needs:
|
||||
- build:ubuntu-16.04
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
@ -192,25 +128,45 @@ package:ubuntu-16.04:
|
||||
deploy:ubuntu-16.04:
|
||||
extends: .debpkg_install
|
||||
image: ubuntu:xenial
|
||||
dependencies:
|
||||
needs:
|
||||
- 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:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
|
||||
deploy:ubuntu-18.04:
|
||||
extends: .debpkg_install
|
||||
image: ubuntu:bionic
|
||||
needs:
|
||||
- package:ubuntu-18.04
|
||||
|
||||
##
|
||||
## Fedora
|
||||
##
|
||||
|
||||
# Do we need to support this old version ?
|
||||
build:fedora-24:
|
||||
# Fedora 28 <-> RHEL 8
|
||||
build:fedora-28:
|
||||
extends: .build_template
|
||||
image: fedora:24
|
||||
image: fedora:28
|
||||
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:
|
||||
@ -218,68 +174,63 @@ build:fedora-24:
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get install -y wget xz-utils unzip git cmake gettext
|
||||
- wget -q http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
- 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
|
||||
|
||||
.build_win_template:
|
||||
extends: .generic_win_template
|
||||
stage: build
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 2h
|
||||
expire_in: 1h
|
||||
paths:
|
||||
- build/*
|
||||
- build/minetest/_build/*
|
||||
|
||||
.package_win_template:
|
||||
extends: .generic_win_template
|
||||
stage: package
|
||||
script:
|
||||
- cd build/minetest/_build
|
||||
- make package
|
||||
- cd ../../../
|
||||
- mkdir minetest-win-${WIN_ARCH}
|
||||
- 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
|
||||
- unzip build/minetest/_build/minetest-*.zip
|
||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-*-win*/bin/
|
||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-*-win*/bin/
|
||||
- cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-*-win*/bin/
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 90 day
|
||||
paths:
|
||||
- minetest-win-*/*
|
||||
- minetest-*-win*/*
|
||||
|
||||
build:win32:
|
||||
extends: .build_win_template
|
||||
script:
|
||||
- ./util/buildbot/buildwin32.sh build
|
||||
variables:
|
||||
NO_PACKAGE: "1"
|
||||
WIN_ARCH: "i686"
|
||||
|
||||
package:win32:
|
||||
extends: .package_win_template
|
||||
dependencies:
|
||||
needs:
|
||||
- build:win32
|
||||
variables:
|
||||
NO_PACKAGE: "1"
|
||||
WIN_ARCH: "i686"
|
||||
|
||||
|
||||
build:win64:
|
||||
extends: .build_win_template
|
||||
script:
|
||||
- ./util/buildbot/buildwin64.sh build
|
||||
variables:
|
||||
NO_PACKAGE: "1"
|
||||
WIN_ARCH: "x86_64"
|
||||
|
||||
package:win64:
|
||||
extends: .package_win_template
|
||||
dependencies:
|
||||
needs:
|
||||
- build:win64
|
||||
variables:
|
||||
NO_PACKAGE: "1"
|
||||
WIN_ARCH: "x86_64"
|
||||
|
||||
##
|
||||
## Docker
|
||||
##
|
||||
|
||||
package:docker:
|
||||
stage: package
|
||||
image: docker:stable
|
||||
@ -293,6 +244,10 @@ package:docker:
|
||||
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME
|
||||
- docker push ${CONTAINER_IMAGE}/server:latest
|
||||
|
||||
##
|
||||
## Gitlab Pages (Lua API documentation)
|
||||
##
|
||||
|
||||
pages:
|
||||
stage: deploy
|
||||
image: python:3.8
|
||||
@ -308,3 +263,31 @@ pages:
|
||||
only:
|
||||
- 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
|
||||
|
@ -72,3 +72,11 @@ files["builtin/mainmenu"] = {
|
||||
"PLATFORM",
|
||||
},
|
||||
}
|
||||
|
||||
files["builtin/common/tests"] = {
|
||||
read_globals = {
|
||||
"describe",
|
||||
"it",
|
||||
"assert",
|
||||
},
|
||||
}
|
||||
|
74
.mailmap
@ -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>
|
||||
Calinou <calinou9999@gmail.com> <calinou9999spam@gmail.com>
|
||||
Perttu Ahola <celeron55@gmail.com> celeron55 <celeron55@gmail.com>
|
||||
Calinou <calinou@opmbx.org> <calinou9999@gmail.com>
|
||||
Calinou <calinou@opmbx.org> <calinou9999spam@gmail.com>
|
||||
Perttu Ahola <celeron55@gmail.com>
|
||||
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>
|
||||
Ilya Zhuravlev <zhuravlevilya@ya.ru>
|
||||
Ilya Zhuravlev <zhuravlevilya@ya.ru> <whatever@xyz.is>
|
||||
kwolekr <kwolekr@minetest.net> <mirrorisim@gmail.com>
|
||||
PilzAdam <pilzadam@minetest.net> PilzAdam <adam-k@outlook.com>
|
||||
PilzAdam <pilzadam@minetest.net> Pilz Adam <PilzAdam@gmx.de>
|
||||
PilzAdam <pilzadam@minetest.net> PilzAdam <PilzAdam@gmx.de>
|
||||
PilzAdam <pilzadam@minetest.net> <adam-k@outlook.com>
|
||||
PilzAdam <pilzadam@minetest.net> <PilzAdam@gmx.de>
|
||||
proller <proller@github.com> <proler@github.com>
|
||||
proller <proller@github.com> <proler@gmail.com>
|
||||
RealBadAngel <maciej.kasatkin@o2.pl> <mk@realbadangel.pl>
|
||||
RealBadAngel <maciej.kasatkin@o2.pl> <maciej.kasatkin@yahoo.com>
|
||||
Selat <LongExampleTestName@gmail.com> <LongExampletestName@gmail.com>
|
||||
ShadowNinja <shadowninja@minetest.net> ShadowNinja <noreply@gmail.com>
|
||||
Shen Zheyu <arsdragonfly@gmail.com> arsdragonfly <arsdragonfly@gmail.com>
|
||||
Pavel Elagin <elagin.pasha@gmail.com> elagin <elagin.pasha@gmail.com>
|
||||
Esteban I. Ruiz Moreno <exio4.com@gmail.com> Esteban I. RM <exio4.com@gmail.com>
|
||||
manuel duarte <ffrogger0@yahoo.com> manuel joaquim <ffrogger0@yahoo.com>
|
||||
manuel duarte <ffrogger0@yahoo.com> sweetbomber <ffrogger _zero_ at yahoo dot com>
|
||||
Diego Martínez <kaeza@users.sf.net> kaeza <kaeza@users.sf.net>
|
||||
Diego Martínez <kaeza@users.sf.net> Diego Martinez <kaeza@users.sf.net>
|
||||
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>
|
||||
Esteban I. Ruiz Moreno <exio4.com@gmail.com>
|
||||
Esteban I. Ruiz Moreno <exio4.com@gmail.com> <me@exio4.xyz>
|
||||
Lord James <neftali_dtctv@hotmail.com>
|
||||
BlockMen <nmuelll@web.de>
|
||||
sfan5 <sfan5@live.de>
|
||||
DannyDark <the_skeleton_of_a_child@yahoo.co.uk>
|
||||
Ilya Pavlov <TTChangeTheWorld@gmail.com>
|
||||
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
@ -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
|
@ -1,15 +1,11 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
if(${CMAKE_VERSION} STREQUAL "2.8.2")
|
||||
# Bug http://vtk.org/Bug/view.php?id=11020
|
||||
message(WARNING "CMake/CPack version 2.8.2 will not create working .deb packages!")
|
||||
endif()
|
||||
cmake_policy(SET CMP0025 OLD)
|
||||
|
||||
# This can be read from ${PROJECT_NAME} after project() is called
|
||||
project(multicraft)
|
||||
set(PROJECT_NAME_CAPITALIZED "MultiCraft")
|
||||
|
||||
# Works only for cmake 3.1 and greater
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(GCC_MINIMUM_VERSION "4.8")
|
||||
set(CLANG_MINIMUM_VERSION "3.4")
|
||||
@ -250,15 +246,15 @@ cpack_add_component(Docs
|
||||
|
||||
cpack_add_component(SUBGAME_MINETEST_GAME
|
||||
DISPLAY_NAME "Minetest Game"
|
||||
DESCRIPTION "The official subgame for the Minetest engine, that can easily extended by mods."
|
||||
GROUP "Subgames"
|
||||
DESCRIPTION "The default game bundled in the Minetest engine. Mainly used as a modding base."
|
||||
GROUP "Games"
|
||||
)
|
||||
|
||||
cpack_add_component(SUBGAME_MINIMAL
|
||||
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.
|
||||
GROUP "Subgames"
|
||||
GROUP "Games"
|
||||
)
|
||||
|
||||
cpack_add_component_group(Subgames
|
||||
@ -279,11 +275,12 @@ if(WIN32)
|
||||
set(CPACK_GENERATOR ZIP)
|
||||
|
||||
else()
|
||||
set(CPACK_GENERATOR WIX ZIP)
|
||||
set(CPACK_GENERATOR WIX)
|
||||
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_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")
|
||||
# Supported languages can be found at
|
||||
|
@ -21,7 +21,7 @@ WORKDIR /usr/src/multicraft
|
||||
RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \
|
||||
jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \
|
||||
libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \
|
||||
gmp-dev jsoncpp-dev postgresql-dev ca-certificates && \
|
||||
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 && \
|
||||
rm -fr ./games/minetest_game/.git
|
||||
|
||||
@ -51,7 +51,7 @@ RUN mkdir build && \
|
||||
|
||||
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 && \
|
||||
chown -R multicraft:multicraft /var/lib/multicraft
|
||||
|
||||
|
13
LICENSE.txt
@ -10,6 +10,9 @@ http://creativecommons.org/licenses/by-sa/3.0/
|
||||
textures/base/pack/refresh.png is under the Apache 2 license
|
||||
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
|
||||
-----------------------
|
||||
Everything not listed in here:
|
||||
@ -22,6 +25,8 @@ paramat:
|
||||
textures/base/pack/menu_header.png
|
||||
textures/base/pack/next_icon.png
|
||||
textures/base/pack/prev_icon.png
|
||||
textures/base/pack/clear.png
|
||||
textures/base/pack/search.png
|
||||
|
||||
rubenwardy, paramat:
|
||||
textures/base/pack/start_icon.png
|
||||
@ -44,6 +49,14 @@ srifqi
|
||||
textures/base/pack/joystick_off.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
|
||||
-------------------------------
|
||||
|
||||
|
13
README.md
@ -25,10 +25,10 @@ Table of Contents
|
||||
|
||||
Further documentation
|
||||
----------------------
|
||||
- Minetest Website: http://minetest.net/
|
||||
- Minetest Wiki: http://wiki.minetest.net/
|
||||
- Minetest Developer wiki: http://dev.minetest.net/
|
||||
- Minetest Forum: http://forum.minetest.net/
|
||||
- Minetest Website: https://minetest.net/
|
||||
- Minetest Wiki: https://wiki.minetest.net/
|
||||
- Minetest Developer wiki: https://dev.minetest.net/
|
||||
- Minetest Forum: https://forum.minetest.net/
|
||||
- Minetest GitHub: https://github.com/minetest/minetest/
|
||||
- [doc/](doc/) directory of source distribution
|
||||
|
||||
@ -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:
|
||||
```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.
|
||||
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
|
||||
- `freetype` is optional, it allows true-type font rendering.
|
||||
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
|
||||
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
|
||||
|
||||
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:
|
||||
|
||||
```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
|
||||
```
|
||||
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
|
||||
|
@ -26,6 +26,8 @@ import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class CustomEditText extends AppCompatEditText {
|
||||
public CustomEditText(Context context) {
|
||||
super(context);
|
||||
@ -36,7 +38,7 @@ public class CustomEditText extends AppCompatEditText {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
InputMethodManager mgr = (InputMethodManager)
|
||||
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);
|
||||
Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -23,6 +23,11 @@ core.register_on_sending_chat_message(function(message)
|
||||
return true
|
||||
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]
|
||||
if cmd_def then
|
||||
core.set_last_run_mod(cmd_def.mod_origin)
|
||||
|
@ -4,6 +4,13 @@ core.callback_origins = {}
|
||||
local getinfo = debug.getinfo
|
||||
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, ...)
|
||||
assert(type(callbacks) == "table")
|
||||
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_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_chatcommand, core.register_on_chatcommand = make_registration()
|
||||
core.registered_on_death, core.register_on_death = make_registration()
|
||||
core.registered_on_hp_modification, core.register_on_hp_modification = make_registration()
|
||||
core.registered_on_damage_taken, core.register_on_damage_taken = make_registration()
|
||||
|
@ -31,11 +31,13 @@ function core.after(after, func, ...)
|
||||
assert(tonumber(after) and type(func) == "function",
|
||||
"Invalid minetest.after invocation")
|
||||
local expire = time + after
|
||||
jobs[#jobs + 1] = {
|
||||
local new_job = {
|
||||
func = func,
|
||||
expire = expire,
|
||||
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)
|
||||
return { cancel = function() new_job.func = function() end end }
|
||||
end
|
||||
|
@ -118,7 +118,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
|
||||
return
|
||||
end
|
||||
|
||||
local event = minetest.explode_table_event(fields.list)
|
||||
local event = core.explode_table_event(fields.list)
|
||||
if event.type ~= "INV" then
|
||||
local name = player:get_player_name()
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
|
@ -696,3 +696,7 @@ function core.privs_to_string(privs, delim)
|
||||
end
|
||||
return table.concat(list, delim)
|
||||
end
|
||||
|
||||
function core.is_nan(number)
|
||||
return number ~= number
|
||||
end
|
||||
|
@ -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 }))
|
||||
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.
|
||||
local function almost_equal(a, b)
|
||||
if type(a) == "number" then
|
||||
|
@ -139,6 +139,12 @@ function vector.divide(a, b)
|
||||
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)
|
||||
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)}
|
||||
|
@ -18,6 +18,8 @@
|
||||
ui = {}
|
||||
ui.childlist = {}
|
||||
ui.default = nil
|
||||
-- Whether fstk is currently showing its own formspec instead of active ui elements.
|
||||
ui.overridden = false
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function ui.add(child)
|
||||
@ -58,6 +60,7 @@ local maintab = core.settings:get("maintab_LAST")
|
||||
local connect_time = tonumber(core.settings:get("connect_time"))
|
||||
|
||||
function ui.update()
|
||||
ui.overridden = false
|
||||
local formspec = {}
|
||||
|
||||
-- attempt auto restart
|
||||
@ -93,6 +96,7 @@ function ui.update()
|
||||
"button[2,6.6;4,1;btn_reconnect_yes;" .. fgettext("Reconnect") .. "]",
|
||||
"button[8,6.6;4,1;btn_reconnect_no;" .. fgettext("Close") .. "]"
|
||||
}
|
||||
ui.overridden = true
|
||||
elseif gamedata ~= nil and gamedata.errormessage ~= nil then
|
||||
local error_message = core.formspec_escape(gamedata.errormessage)
|
||||
|
||||
@ -119,6 +123,7 @@ function ui.update()
|
||||
error_title, error_message),
|
||||
restart_btn
|
||||
}
|
||||
ui.overridden = true
|
||||
else
|
||||
local active_toplevel_ui_elements = 0
|
||||
for key,value in pairs(ui.childlist) do
|
||||
@ -221,6 +226,16 @@ end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
ui.update()
|
||||
return
|
||||
|
@ -58,6 +58,11 @@ core.register_on_chat_message(function(name, message)
|
||||
|
||||
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]
|
||||
if not cmd_def then
|
||||
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)
|
||||
if has_privs then
|
||||
core.set_last_run_mod(cmd_def.mod_origin)
|
||||
local _, result = cmd_def.func(name, param)
|
||||
if result then
|
||||
local success, result = cmd_def.func(name, param)
|
||||
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)
|
||||
end
|
||||
else
|
||||
@ -1093,10 +1107,10 @@ core.register_chatcommand("last-login", {
|
||||
local pauth = core.get_auth_handler().get_auth(param)
|
||||
if pauth and pauth.last_login and pauth.last_login ~= -1 then
|
||||
-- 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)
|
||||
end
|
||||
return false, "Last login time is unknown"
|
||||
return false, param.."'s last login time is unknown"
|
||||
end,
|
||||
})
|
||||
|
||||
|
@ -1,28 +1,5 @@
|
||||
-- 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
|
||||
--
|
||||
@ -77,7 +54,7 @@ core.setting_save = setting_proxy("write")
|
||||
|
||||
function core.register_on_auth_fail(func)
|
||||
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(function (player_name, ip, is_success)
|
||||
|
@ -90,6 +90,9 @@ core.register_entity(":__builtin:falling_node", {
|
||||
local textures
|
||||
if def.tiles and def.tiles[1] then
|
||||
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
|
||||
tile = tile.name
|
||||
end
|
||||
@ -133,7 +136,7 @@ core.register_entity(":__builtin:falling_node", {
|
||||
-- Set collision box (certain nodeboxes only for now)
|
||||
local nb_types = {fixed=true, leveled=true, connected=true}
|
||||
if def.drawtype == "nodebox" and def.node_box and
|
||||
nb_types[def.node_box.type] then
|
||||
nb_types[def.node_box.type] and def.node_box.fixed then
|
||||
local box = table.copy(def.node_box.fixed)
|
||||
if type(box[1]) == "table" then
|
||||
box = #box == 1 and box[1] or nil -- We can only use a single box
|
||||
@ -150,9 +153,13 @@ core.register_entity(":__builtin:falling_node", {
|
||||
|
||||
-- Rotate entity
|
||||
if def.drawtype == "torchlike" then
|
||||
if def.paramtype2 == "wallmounted" then
|
||||
self.object:set_yaw(pi*0.25)
|
||||
elseif (node.param2 ~= 0 and (def.wield_image == ""
|
||||
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 == "mesh"
|
||||
or def.drawtype == "normal"
|
||||
@ -167,6 +174,19 @@ core.register_entity(":__builtin:falling_node", {
|
||||
elseif (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted") then
|
||||
local rot = node.param2 % 8
|
||||
local pitch, yaw, roll = 0, 0, 0
|
||||
if def.drawtype == "nodebox" or def.drawtype == "mesh" then
|
||||
if rot == 0 then
|
||||
pitch, yaw = pi/2, 0
|
||||
elseif rot == 1 then
|
||||
pitch, yaw = -pi/2, pi
|
||||
elseif rot == 2 then
|
||||
pitch, yaw = 0, pi/2
|
||||
elseif rot == 3 then
|
||||
pitch, yaw = 0, -pi/2
|
||||
elseif rot == 4 then
|
||||
pitch, yaw = 0, pi
|
||||
end
|
||||
else
|
||||
if rot == 1 then
|
||||
pitch, yaw = pi, pi
|
||||
elseif rot == 2 then
|
||||
@ -178,6 +198,7 @@ core.register_entity(":__builtin:falling_node", {
|
||||
elseif rot == 5 then
|
||||
pitch, yaw = pi/2, 0
|
||||
end
|
||||
end
|
||||
if def.drawtype == "signlike" then
|
||||
pitch = pitch - pi/2
|
||||
if rot == 0 then
|
||||
@ -185,7 +206,7 @@ core.register_entity(":__builtin:falling_node", {
|
||||
elseif rot == 1 then
|
||||
yaw = yaw - pi/2
|
||||
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
|
||||
roll = roll + pi
|
||||
else
|
||||
|
@ -17,6 +17,8 @@ core.features = {
|
||||
area_store_persistent_ids = true,
|
||||
pathfinder_works = true,
|
||||
object_step_has_moveresult = true,
|
||||
direct_velocity_on_players = true,
|
||||
use_texture_alpha_string_modes = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
@ -676,12 +676,13 @@ function core.node_dig(pos, node, digger)
|
||||
local diggername = user_name(digger)
|
||||
local log = make_log(diggername)
|
||||
local def = core.registered_nodes[node.name]
|
||||
-- Copy pos because the callback could modify it
|
||||
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 "
|
||||
.. node.name .. " which is not diggable "
|
||||
.. core.pos_to_string(pos))
|
||||
return
|
||||
return false
|
||||
end
|
||||
|
||||
if core.is_protected(pos, diggername) then
|
||||
@ -690,7 +691,7 @@ function core.node_dig(pos, node, digger)
|
||||
.. " at protected position "
|
||||
.. core.pos_to_string(pos))
|
||||
core.record_protection_violation(pos, diggername)
|
||||
return
|
||||
return false
|
||||
end
|
||||
|
||||
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}
|
||||
callback(pos_copy, node_copy, digger)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function core.itemstring_with_palette(item, palette_index)
|
||||
@ -800,7 +803,7 @@ end
|
||||
-- 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 = {
|
||||
-- Item properties
|
||||
@ -829,10 +832,6 @@ core.nodedef_default = {
|
||||
|
||||
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
|
||||
drawtype = "normal",
|
||||
visual_scale = 1.0,
|
||||
@ -843,7 +842,6 @@ core.nodedef_default = {
|
||||
-- {name="", backface_culling=true},
|
||||
-- {name="", backface_culling=true},
|
||||
--},
|
||||
alpha = 255,
|
||||
post_effect_color = {a=0, r=0, g=0, b=0},
|
||||
paramtype = "none",
|
||||
paramtype2 = "none",
|
||||
|
@ -100,8 +100,9 @@ core.register_entity(":__builtin:item", {
|
||||
local max_count = stack:get_stack_max()
|
||||
local count = min(stack:get_count(), max_count)
|
||||
local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3)
|
||||
local def = core.registered_nodes[itemname]
|
||||
local glow = def and floor(def.light_source / 2 + 0.5)
|
||||
local def = core.registered_items[itemname]
|
||||
local glow = def and def.light_source and
|
||||
floor(def.light_source / 2 + 0.5)
|
||||
|
||||
self.object:set_properties({
|
||||
is_visible = true,
|
||||
|
@ -43,5 +43,5 @@ core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool
|
||||
return -- barely noticeable, so don't even send
|
||||
end
|
||||
|
||||
player:add_player_velocity(kdir)
|
||||
player:add_velocity(kdir)
|
||||
end)
|
||||
|
@ -285,3 +285,26 @@ end
|
||||
function core.cancel_shutdown_requests()
|
||||
core.request_shutdown("", false, -1)
|
||||
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
|
||||
|
@ -332,13 +332,6 @@ for name in pairs(forbidden_item_names) do
|
||||
register_alias_raw(name, "")
|
||||
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.
|
||||
--
|
||||
@ -611,6 +604,7 @@ core.unregister_biome = make_wrap_deregistration(core.register_biome,
|
||||
core.clear_registered_biomes, core.registered_biomes)
|
||||
|
||||
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_playerevents, core.register_playerevent = 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_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_on_rightclickplayers, core.register_on_rightclickplayer = make_registration()
|
||||
|
||||
|
||||
--
|
||||
|
@ -41,9 +41,20 @@ if INIT == "game" then
|
||||
assert(not core.get_http_api)
|
||||
elseif INIT == "mainmenu" then
|
||||
local mm_script = core.settings:get("main_menu_script")
|
||||
local custom_loaded = false
|
||||
if mm_script and mm_script ~= "" then
|
||||
local testfile = io.open(mm_script, "r")
|
||||
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")
|
||||
end
|
||||
elseif INIT == "async" then
|
||||
|
@ -56,29 +56,6 @@ function image_column(tooltip)
|
||||
"11=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png")
|
||||
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)
|
||||
@ -93,7 +70,7 @@ function render_serverlist_row(spec, is_favorite, is_approved)
|
||||
if spec.name then
|
||||
text = text .. core.formspec_escape(spec.name:trim())
|
||||
elseif spec.address then
|
||||
text = text .. spec.address:trim()
|
||||
text = text .. core.formspec_escape(spec.address:trim())
|
||||
if spec.port then
|
||||
text = text .. ":" .. spec.port
|
||||
end
|
||||
@ -166,35 +143,15 @@ end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
os.tempfolder = function()
|
||||
if core.settings:get("TMPFolder") then
|
||||
return core.settings:get("TMPFolder") .. DIR_DELIM .. "MT_" .. math.random(0,10000)
|
||||
local temp = core.get_temp_path()
|
||||
return temp .. DIR_DELIM .. "MT_" .. math.random(0, 10000)
|
||||
end
|
||||
|
||||
local filetocheck = os.tmpname()
|
||||
os.remove(filetocheck)
|
||||
|
||||
-- luacheck: ignore
|
||||
-- https://blogs.msdn.microsoft.com/vcblog/2014/06/18/c-runtime-crt-features-fixes-and-breaking-changes-in-visual-studio-14-ctp1/
|
||||
-- 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
|
||||
--------------------------------------------------------------------------------
|
||||
os.tmpname = function()
|
||||
local path = os.tempfolder()
|
||||
io.open(path, "w"):close()
|
||||
return path
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -226,42 +183,6 @@ function menu_handle_key_up_down(fields, textlist, settingname)
|
||||
return false
|
||||
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)
|
||||
local textlines = core.wrap_text(text, textlen, true)
|
||||
|
@ -76,7 +76,7 @@ local function get_formspec(data)
|
||||
"label[1.75,0;" .. data.worldspec.name .. "]"
|
||||
|
||||
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)
|
||||
if info == "" then
|
||||
if mod.is_modpack then
|
||||
|
@ -15,7 +15,7 @@
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
if not minetest.get_http_api then
|
||||
if not core.get_http_api then
|
||||
function create_store_dlg()
|
||||
return messagebox("store",
|
||||
fgettext("ContentDB is not available when Minetest was compiled without cURL"))
|
||||
@ -23,9 +23,11 @@ if not minetest.get_http_api then
|
||||
return
|
||||
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
|
||||
local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb"
|
||||
@ -45,6 +47,9 @@ local filter_types_titles = {
|
||||
fgettext("Texture packs"),
|
||||
}
|
||||
|
||||
local number_downloading = 0
|
||||
local download_queue = {}
|
||||
|
||||
local filter_types_type = {
|
||||
nil,
|
||||
"game",
|
||||
@ -67,12 +72,14 @@ local function download_package(param)
|
||||
end
|
||||
end
|
||||
|
||||
local function start_install(calling_dialog, package)
|
||||
local function start_install(package)
|
||||
local params = {
|
||||
package = package,
|
||||
filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip",
|
||||
}
|
||||
|
||||
number_downloading = number_downloading + 1
|
||||
|
||||
local function callback(result)
|
||||
if result.successful then
|
||||
local path, msg = pkgmgr.install(package.type,
|
||||
@ -124,9 +131,20 @@ local function start_install(calling_dialog, package)
|
||||
end
|
||||
|
||||
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()
|
||||
end
|
||||
|
||||
package.queued = false
|
||||
package.downloading = true
|
||||
|
||||
if not core.handle_async(download_package, params, callback) then
|
||||
@ -136,6 +154,341 @@ local function start_install(calling_dialog, package)
|
||||
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 parts = path:split(".")
|
||||
return parts[#parts]
|
||||
@ -203,7 +556,7 @@ function store.load()
|
||||
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 })
|
||||
if not response.succeeded then
|
||||
return
|
||||
@ -224,6 +577,7 @@ function store.load()
|
||||
end
|
||||
end
|
||||
|
||||
store.packages_full_unordered = store.packages_full
|
||||
store.packages = store.packages_full
|
||||
store.loaded = true
|
||||
end
|
||||
@ -232,7 +586,7 @@ function store.update_paths()
|
||||
local mod_hash = {}
|
||||
pkgmgr.refresh_globals()
|
||||
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
|
||||
end
|
||||
end
|
||||
@ -240,14 +594,14 @@ function store.update_paths()
|
||||
local game_hash = {}
|
||||
pkgmgr.update_gamelist()
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
local txp_hash = {}
|
||||
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
|
||||
end
|
||||
end
|
||||
@ -271,6 +625,33 @@ function store.update_paths()
|
||||
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)
|
||||
if query == "" and filter_type == 1 then
|
||||
store.packages = store.packages_full
|
||||
@ -282,7 +663,7 @@ function store.filter_packages(query)
|
||||
table.insert(keywords, word)
|
||||
end
|
||||
|
||||
local function matches_keywords(package, keywords)
|
||||
local function matches_keywords(package)
|
||||
for k = 1, #keywords do
|
||||
local keyword = keywords[k]
|
||||
|
||||
@ -299,12 +680,11 @@ function store.filter_packages(query)
|
||||
|
||||
store.packages = {}
|
||||
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
|
||||
store.packages[#store.packages + 1] = package
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function store.get_formspec(dlgdata)
|
||||
@ -317,7 +697,6 @@ function store.get_formspec(dlgdata)
|
||||
|
||||
local W = 15.75
|
||||
local H = 9.5
|
||||
|
||||
local formspec
|
||||
if #store.packages_full > 0 then
|
||||
formspec = {
|
||||
@ -326,11 +705,15 @@ function store.get_formspec(dlgdata)
|
||||
"background[0,0;0,0;" .. core.formspec_escape(defaulttexturedir ..
|
||||
"bg_common.png") .. ";true;32]",
|
||||
"position[0.5,0.55]",
|
||||
|
||||
"style[status,downloading,queued;border=false]",
|
||||
|
||||
"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]",
|
||||
"button[10.225,0;2,0.8;search;", fgettext("Search"), "]",
|
||||
"dropdown[12.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]",
|
||||
"image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
|
||||
"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[]",
|
||||
|
||||
-- Page nav buttons
|
||||
@ -349,6 +732,35 @@ function store.get_formspec(dlgdata)
|
||||
"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
|
||||
formspec[#formspec + 1] = "label[4,3;"
|
||||
formspec[#formspec + 1] = fgettext("No results")
|
||||
@ -367,11 +779,17 @@ function store.get_formspec(dlgdata)
|
||||
}
|
||||
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
|
||||
for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do
|
||||
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] = (i - start_idx) * 1.375 + (2*0.375 + 0.8)
|
||||
formspec[#formspec + 1] = container_y
|
||||
formspec[#formspec + 1] = "]"
|
||||
|
||||
-- image
|
||||
@ -382,55 +800,55 @@ function store.get_formspec(dlgdata)
|
||||
-- title
|
||||
formspec[#formspec + 1] = "label[1.875,0.1;"
|
||||
formspec[#formspec + 1] = core.formspec_escape(
|
||||
minetest.colorize(mt_color_green, package.title) ..
|
||||
minetest.colorize("#BFBFBF", " by " .. package.author))
|
||||
core.colorize(mt_color_green, package.title) ..
|
||||
core.colorize("#BFBFBF", " by " .. package.author))
|
||||
formspec[#formspec + 1] = "]"
|
||||
|
||||
-- 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] = W - 0.375*2
|
||||
formspec[#formspec + 1] = ",0.1]"
|
||||
|
||||
if package.downloading then
|
||||
formspec[#formspec + 1] = "style[download;border=false]"
|
||||
|
||||
formspec[#formspec + 1] = "button[-3.5,0;2,0.8;download;"
|
||||
formspec[#formspec + 1] = fgettext("Downloading...")
|
||||
formspec[#formspec + 1] = "]"
|
||||
formspec[#formspec + 1] = "animated_image[-1.7,-0.15;1,1;downloading;"
|
||||
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
|
||||
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
|
||||
elseif package.queued then
|
||||
formspec[#formspec + 1] = left_base
|
||||
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
|
||||
formspec[#formspec + 1] = "cdb_queued.png;queued]"
|
||||
elseif not package.path then
|
||||
formspec[#formspec + 1] = "button[-3,0;1.5,0.8;install_"
|
||||
formspec[#formspec + 1] = tostring(i)
|
||||
formspec[#formspec + 1] = ";"
|
||||
formspec[#formspec + 1] = fgettext("Install")
|
||||
formspec[#formspec + 1] = "]"
|
||||
local elem_name = "install_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
|
||||
formspec[#formspec + 1] = left_base .. "cdb_add.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Install") .. tooltip_colors
|
||||
else
|
||||
if package.installed_release < package.release then
|
||||
description_width = description_width - 1.5
|
||||
|
||||
-- The install_ action also handles updating
|
||||
formspec[#formspec + 1] = "button[-4.5,0;1.5,0.8;install_"
|
||||
formspec[#formspec + 1] = tostring(i)
|
||||
formspec[#formspec + 1] = ";"
|
||||
formspec[#formspec + 1] = fgettext("Update")
|
||||
formspec[#formspec + 1] = "]"
|
||||
local elem_name = "install_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#28ccdf]"
|
||||
formspec[#formspec + 1] = left_base .. "cdb_update.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Update") .. tooltip_colors
|
||||
else
|
||||
|
||||
local elem_name = "uninstall_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]"
|
||||
formspec[#formspec + 1] = left_base .. "cdb_clear.png;" .. elem_name .. "]"
|
||||
formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors
|
||||
end
|
||||
end
|
||||
|
||||
formspec[#formspec + 1] = "button[-3,0;1.5,0.8;uninstall_"
|
||||
formspec[#formspec + 1] = tostring(i)
|
||||
formspec[#formspec + 1] = ";"
|
||||
formspec[#formspec + 1] = fgettext("Uninstall")
|
||||
formspec[#formspec + 1] = "]"
|
||||
end
|
||||
|
||||
formspec[#formspec + 1] = "button[-1.5,0;1.5,0.8;view_"
|
||||
formspec[#formspec + 1] = tostring(i)
|
||||
formspec[#formspec + 1] = ";"
|
||||
formspec[#formspec + 1] = fgettext("View")
|
||||
formspec[#formspec + 1] = "]"
|
||||
local web_elem_name = "view_" .. i .. ";"
|
||||
formspec[#formspec + 1] = "image_button[-0.7,0;0.7,0.7;" ..
|
||||
core.formspec_escape(defaulttexturedir) .. "cdb_viewonline.png;" .. web_elem_name .. "]"
|
||||
formspec[#formspec + 1] = "tooltip[" .. web_elem_name ..
|
||||
fgettext("View more information in a web browser") .. tooltip_colors
|
||||
formspec[#formspec + 1] = "container_end[]"
|
||||
|
||||
-- description
|
||||
local description_width = W - 0.375*5 - 0.85 - 2*0.7
|
||||
formspec[#formspec + 1] = "textarea[1.855,0.3;"
|
||||
formspec[#formspec + 1] = tostring(description_width)
|
||||
formspec[#formspec + 1] = ",0.8;;;"
|
||||
@ -451,6 +869,13 @@ function store.handle_submit(this, fields)
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.clear then
|
||||
search_string = ""
|
||||
cur_page = 1
|
||||
store.filter_packages("")
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.back then
|
||||
this:delete()
|
||||
return true
|
||||
@ -492,6 +917,17 @@ function store.handle_submit(this, fields)
|
||||
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
|
||||
assert(start_idx ~= nil)
|
||||
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)
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
if fields["uninstall_" .. i] then
|
||||
local dlg_delmod = create_delete_content_dlg(package)
|
||||
dlg_delmod:set_parent(this)
|
||||
local dlg = create_delete_content_dlg(package)
|
||||
dlg:set_parent(this)
|
||||
this:hide()
|
||||
dlg_delmod:show()
|
||||
dlg:show()
|
||||
return true
|
||||
end
|
||||
|
||||
if fields["view_" .. i] then
|
||||
local url = ("%s/packages/%s?protocol_version=%d"):format(
|
||||
core.settings:get("contentdb_url"), package.id, core.get_max_supp_proto())
|
||||
local url = ("%s/packages/%s/%s?protocol_version=%d"):format(
|
||||
core.settings:get("contentdb_url"),
|
||||
package.author, package.name, core.get_max_supp_proto())
|
||||
core.open_url(url)
|
||||
return true
|
||||
end
|
||||
@ -527,6 +996,9 @@ function create_store_dlg(type)
|
||||
store.load()
|
||||
end
|
||||
|
||||
store.update_paths()
|
||||
store.sort_packages()
|
||||
|
||||
search_string = ""
|
||||
cur_page = 1
|
||||
|
||||
|
@ -61,6 +61,7 @@ local flag_checkboxes = {
|
||||
fgettext("Low humidity and high heat causes shallow or dry rivers") },
|
||||
},
|
||||
flat = {
|
||||
cb_caverns,
|
||||
{ "hills", fgettext("Hills"), "hills" },
|
||||
{ "lakes", fgettext("Lakes"), "lakes" },
|
||||
},
|
||||
@ -366,8 +367,18 @@ local function create_world_buttonhandler(this, fields)
|
||||
local gameindex = core.get_textlist_index("games")
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
core.settings:set("fixed_map_seed", fields["te_seed"])
|
||||
@ -436,7 +447,7 @@ local function create_world_buttonhandler(this, fields)
|
||||
end
|
||||
|
||||
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
|
||||
if entry == mgv6_biomes[b][1] then
|
||||
local ftable = core.settings:get_flags("mgv6_spflags")
|
||||
|
@ -19,6 +19,7 @@ mt_color_grey = "#AAAAAA"
|
||||
mt_color_blue = "#6389FF"
|
||||
mt_color_green = "#72FF63"
|
||||
mt_color_dark_green = "#25C191"
|
||||
mt_color_orange = "#FF8800"
|
||||
|
||||
local menupath = core.get_mainmenu_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 .. "common.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "pkgmgr.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "textures.lua")
|
||||
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
|
||||
@ -77,7 +79,34 @@ local function main_event_handler(tabview, event)
|
||||
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})
|
||||
|
||||
for i = 1, #pkgmgr.games do
|
||||
@ -116,6 +145,15 @@ function menudata.init_tabs()
|
||||
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")
|
||||
tv_main:show()
|
||||
|
||||
|
@ -72,6 +72,34 @@ local function cleanup_path(temppath)
|
||||
return temppath
|
||||
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)
|
||||
local mods = core.get_dir_list(path, true)
|
||||
|
||||
@ -107,7 +135,7 @@ function get_mods(path,retval,modpack)
|
||||
-- Read from config
|
||||
toadd.name = name
|
||||
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.type = "mod"
|
||||
|
||||
@ -136,32 +164,13 @@ pkgmgr = {}
|
||||
|
||||
function pkgmgr.get_texture_packs()
|
||||
local txtpath = core.get_texturepath()
|
||||
local list = core.get_dir_list(txtpath, true)
|
||||
local txtpath_system = core.get_texturepath_share()
|
||||
local retval = {}
|
||||
|
||||
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
|
||||
load_texture_packs(txtpath, retval)
|
||||
-- on portable versions these two paths coincide. It avoids loading the path twice
|
||||
if txtpath ~= txtpath_system then
|
||||
load_texture_packs(txtpath_system, retval)
|
||||
end
|
||||
|
||||
table.sort(retval, function(a, b)
|
||||
@ -404,18 +413,7 @@ function pkgmgr.is_modpack_entirely_enabled(data, name)
|
||||
end
|
||||
|
||||
---------- toggles or en/disables a mod or modpack and its dependencies --------
|
||||
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 = {}
|
||||
local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
|
||||
if not mod.is_modpack then
|
||||
-- Toggle or en/disable the mod
|
||||
if toset == nil then
|
||||
@ -434,23 +432,29 @@ function pkgmgr.enable_mod(this, toset)
|
||||
-- interleaved unsupported
|
||||
for i = 1, #list do
|
||||
if list[i].modpack == mod.name then
|
||||
if toset == nil then
|
||||
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
|
||||
toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, list[i])
|
||||
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
|
||||
-- Mod(s) were disabled, so no dependencies need to be enabled
|
||||
table.sort(toggled_mods)
|
||||
minetest.log("info", "Following mods were disabled: " ..
|
||||
core.log("info", "Following mods were disabled: " ..
|
||||
table.concat(toggled_mods, ", "))
|
||||
return
|
||||
end
|
||||
@ -487,7 +491,7 @@ function pkgmgr.enable_mod(this, toset)
|
||||
enabled_mods[name] = true
|
||||
local mod_to_enable = list[mod_ids[name]]
|
||||
if not mod_to_enable then
|
||||
minetest.log("warning", "Mod dependency \"" .. name ..
|
||||
core.log("warning", "Mod dependency \"" .. name ..
|
||||
"\" not found!")
|
||||
else
|
||||
if mod_to_enable.enabled == false then
|
||||
@ -508,7 +512,7 @@ function pkgmgr.enable_mod(this, toset)
|
||||
|
||||
-- Log the list of enabled mods
|
||||
table.sort(toggled_mods)
|
||||
minetest.log("info", "Following mods were enabled: " ..
|
||||
core.log("info", "Following mods were enabled: " ..
|
||||
table.concat(toggled_mods, ", "))
|
||||
end
|
||||
|
||||
|
252
builtin/mainmenu/serverlistmgr.lua
Normal 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
|
@ -36,28 +36,37 @@ local core_developers = {
|
||||
"Nathanaël Courant (Nore/Ekdohibs) <nore@mesecons.net>",
|
||||
"Loic Blot (nerzhul/nrz) <loic.blot@unix-experience.fr>",
|
||||
"paramat",
|
||||
"Auke Kok (sofar) <sofar@foo-projects.org>",
|
||||
"Andrew Ward (rubenwardy) <rw@rubenwardy.com>",
|
||||
"Krock/SmallJoker <mk939@ymail.com>",
|
||||
"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 = {
|
||||
"Hugues Ross [Formspecs]",
|
||||
"Wuzzy [devtest game, visual corrections]",
|
||||
"Zughy [Visual improvements, various fixes]",
|
||||
"Maksim (MoNTE48) [Android]",
|
||||
"DS [Formspecs]",
|
||||
"pyrollo [Formspecs: Hypertext]",
|
||||
"v-rob [Formspecs]",
|
||||
"Jordach [set_sky]",
|
||||
"random-geek [Formspecs]",
|
||||
"Wuzzy [Pathfinder, builtin, translations]",
|
||||
"ANAND (ClobberXD) [Fixes, per-player FOV]",
|
||||
"Warr1024 [Fixes]",
|
||||
"Paul Ouellette (pauloue) [Fixes, Script API]",
|
||||
"Jean-Patrick G (kilbith) <jeanpatrick.guerrero@gmail.com> [Audiovisuals]",
|
||||
"HybridDog [Script API]",
|
||||
"numzero [Graphics and rendering]",
|
||||
"appgurueu [Various internal fixes]",
|
||||
"Desour [Formspec and vector API changes]",
|
||||
"HybridDog [Rendering fixes and documentation]",
|
||||
"Hugues Ross [Graphics-related improvements]",
|
||||
"ANAND (ClobberXD) [Mouse buttons rebinding]",
|
||||
"luk3yx [Fixes]",
|
||||
"hecks [Audiovisuals, Lua API]",
|
||||
"LoneWolfHT [Object crosshair, documentation fixes]",
|
||||
"Lejo [Server-related improvements]",
|
||||
"EvidenceB [Compass HUD element]",
|
||||
"Paul Ouellette (pauloue) [Lua API, documentation]",
|
||||
"TheTermos [Collision detection, physics]",
|
||||
"David CARLIER [Unix & Haiku build fixes]",
|
||||
"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 = {
|
||||
@ -73,30 +82,23 @@ local previous_core_developers = {
|
||||
"sapier",
|
||||
"Zeno",
|
||||
"ShadowNinja <shadowninja@minetest.net>",
|
||||
"Auke Kok (sofar) <sofar@foo-projects.org>",
|
||||
}
|
||||
|
||||
local previous_contributors = {
|
||||
"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net> [Minetest Logo]",
|
||||
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
|
||||
"red-001 <red-001@outlook.ie>",
|
||||
"numberZero [Audiovisuals: meshgen]",
|
||||
"Giuseppe Bilotta",
|
||||
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
|
||||
"MirceaKitsune <mirceakitsune@gmail.com>",
|
||||
"Constantin Wenger (SpeedProg)",
|
||||
"Ciaran Gultnieks (CiaranG)",
|
||||
"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]",
|
||||
"Gregory Currie (gregorycu) [optimisation]",
|
||||
"TriBlade9 <triblade9@mail.com> [Audiovisuals]",
|
||||
"T4im [Profiler]",
|
||||
"Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
|
||||
"srifqi [Fixes]",
|
||||
"JacobF",
|
||||
"Jeija <jeija@mesecons.net> [HTTP, particles]",
|
||||
}
|
||||
|
||||
local function buildCreditList(source)
|
||||
@ -116,7 +118,7 @@ return {
|
||||
"MultiCraft Open Source Project, ver. " .. version.string .. "\n" ..
|
||||
"Copyright (C) 2014-2021 MultiCraft Development Team\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]" ..
|
||||
"tablecolumns[color;text]" ..
|
||||
"tableoptions[background=#999999;highlight=#00000000;border=true]" ..
|
||||
@ -132,10 +134,23 @@ return {
|
||||
"#FFFF00," .. fgettext("Previous Contributors") .. ",," ..
|
||||
buildCreditList(previous_contributors) .. "," ..
|
||||
";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,
|
||||
cbf_button_handler = function(this, fields, name, tabdata)
|
||||
if fields.homepage then
|
||||
core.open_url("http://multicraft.world")
|
||||
end
|
||||
|
||||
if fields.userdata then
|
||||
core.open_dir(core.get_user_path())
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
@ -19,7 +19,11 @@ local lang = core.settings:get("language")
|
||||
if not lang or lang == "" then lang = os.getenv("LANG") end
|
||||
local mobile = PLATFORM == "Android" or PLATFORM == "iOS"
|
||||
|
||||
local function current_game()
|
||||
local enable_gamebar = PLATFORM ~= "Android"
|
||||
local current_game, singleplayer_refresh_gamebar
|
||||
|
||||
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)
|
||||
|
||||
|
@ -24,11 +24,11 @@ local function get_formspec(tabview, name, tabdata)
|
||||
-- Update the cached supported proto info,
|
||||
-- it may have changed after a change by the settings menu.
|
||||
common_update_cached_supp_proto()
|
||||
local fav_selected
|
||||
local selected
|
||||
if menudata.search_result then
|
||||
fav_selected = menudata.search_result[tabdata.fav_selected]
|
||||
selected = menudata.search_result[tabdata.selected]
|
||||
else
|
||||
fav_selected = menudata.favorites[tabdata.fav_selected]
|
||||
selected = serverlistmgr.servers[tabdata.selected]
|
||||
end
|
||||
|
||||
if not tabdata.search_for then
|
||||
@ -88,7 +88,7 @@ local function get_formspec(tabview, name, tabdata)
|
||||
-- Password
|
||||
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
|
||||
retval = retval .. "image_button[7.1,4.91;0.83,0.83;" .. esc(defaulttexturedir .. "trash.png")
|
||||
.. ";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;"
|
||||
|
||||
if menudata.search_result then
|
||||
local favs = serverlistmgr.get_favorites()
|
||||
for i = 1, #menudata.search_result do
|
||||
local favs = core.get_favorites("local")
|
||||
local server = menudata.search_result[i]
|
||||
|
||||
for fav_id = 1, #favs do
|
||||
if server.address == favs[fav_id].address and
|
||||
server.port == favs[fav_id].port then
|
||||
@ -133,18 +132,18 @@ local function get_formspec(tabview, name, tabdata)
|
||||
retval = retval .. render_serverlist_row(server, server.is_favorite,
|
||||
server.server_id == "multicraft")
|
||||
end
|
||||
elseif #menudata.favorites > 0 then
|
||||
local favs = core.get_favorites("local")
|
||||
elseif #serverlistmgr.servers > 0 then
|
||||
local favs = serverlistmgr.get_favorites()
|
||||
if #favs > 0 then
|
||||
for i = 1, #favs do
|
||||
for j = 1, #menudata.favorites do
|
||||
if menudata.favorites[j].address == favs[i].address and
|
||||
menudata.favorites[j].port == favs[i].port then
|
||||
table.insert(menudata.favorites, i, table.remove(menudata.favorites, j))
|
||||
for j = 1, #serverlistmgr.servers do
|
||||
if serverlistmgr.servers[j].address == favs[i].address and
|
||||
serverlistmgr.servers[j].port == favs[i].port then
|
||||
table.insert(serverlistmgr.servers, i, table.remove(serverlistmgr.servers, j))
|
||||
end
|
||||
end
|
||||
if favs[i].address ~= menudata.favorites[i].address then
|
||||
table.insert(menudata.favorites, i, favs[i])
|
||||
if favs[i].address ~= serverlistmgr.servers[i].address then
|
||||
table.insert(serverlistmgr.servers, i, favs[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -156,8 +155,8 @@ local function get_formspec(tabview, name, tabdata)
|
||||
end
|
||||
end
|
||||
|
||||
if tabdata.fav_selected then
|
||||
retval = retval .. ";" .. tabdata.fav_selected .. "]"
|
||||
if tabdata.selected then
|
||||
retval = retval .. ";" .. tabdata.selected .. "]"
|
||||
else
|
||||
retval = retval .. ";0]"
|
||||
end
|
||||
@ -167,7 +166,7 @@ end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
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.row <= #serverlist then
|
||||
if menudata.favorites_is_public and
|
||||
not is_server_protocol_compat_or_error(
|
||||
if not is_server_protocol_compat_or_error(
|
||||
fav.proto_min, fav.proto_max) then
|
||||
return true
|
||||
end
|
||||
@ -218,7 +216,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
||||
if event.type == "CHG" then
|
||||
if event.row <= #serverlist then
|
||||
gamedata.fav = false
|
||||
local favs = core.get_favorites("local")
|
||||
local favs = serverlistmgr.get_favorites()
|
||||
local address = fav.address
|
||||
local port = fav.port
|
||||
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("remote_port", port)
|
||||
end
|
||||
tabdata.fav_selected = event.row
|
||||
tabdata.selected = event.row
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
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]
|
||||
|
||||
if fav_idx then
|
||||
if fields.key_up and fav_idx > 1 then
|
||||
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
|
||||
end
|
||||
else
|
||||
fav_idx = 1
|
||||
end
|
||||
|
||||
if not menudata.favorites or not fav then
|
||||
tabdata.fav_selected = 0
|
||||
if not serverlistmgr.servers or not fav then
|
||||
tabdata.selected = 0
|
||||
return true
|
||||
end
|
||||
|
||||
@ -267,27 +265,33 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
||||
core.settings:set("remote_port", port)
|
||||
end
|
||||
|
||||
tabdata.fav_selected = fav_idx
|
||||
tabdata.selected = fav_idx
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.btn_delete_favorite then
|
||||
local current_favourite = core.get_table_index("favourites")
|
||||
if not current_favourite then return end
|
||||
local current_favorite = core.get_table_index("favorites")
|
||||
if not current_favorite then return end
|
||||
|
||||
core.delete_favorite(current_favourite)
|
||||
asyncOnlineFavourites(mobile_only)
|
||||
tabdata.fav_selected = nil
|
||||
serverlistmgr.delete_favorite(serverlistmgr.servers[current_favorite])
|
||||
serverlistmgr.sync()
|
||||
tabdata.selected = nil
|
||||
|
||||
return true
|
||||
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
|
||||
tabdata.fav_selected = 1
|
||||
tabdata.selected = 1
|
||||
local input = fields.te_search:lower()
|
||||
tabdata.search_for = fields.te_search
|
||||
|
||||
if #menudata.favorites < 2 then
|
||||
if #serverlistmgr.servers < 2 then
|
||||
return true
|
||||
end
|
||||
|
||||
@ -307,8 +311,8 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
||||
|
||||
-- Search the serverlist
|
||||
local search_result = {}
|
||||
for i = 1, #menudata.favorites do
|
||||
local server = menudata.favorites[i]
|
||||
for i = 1, #serverlistmgr.servers do
|
||||
local server = serverlistmgr.servers[i]
|
||||
local found = 0
|
||||
for k = 1, #keywords do
|
||||
local keyword = keywords[k]
|
||||
@ -325,7 +329,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
||||
end
|
||||
end
|
||||
if found > 0 then
|
||||
local points = (#menudata.favorites - i) / 5 + found
|
||||
local points = (#serverlistmgr.servers - i) / 5 + found
|
||||
server.points = points
|
||||
table.insert(search_result, server)
|
||||
end
|
||||
@ -344,13 +348,13 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
||||
end
|
||||
|
||||
if fields.btn_mp_refresh then
|
||||
asyncOnlineFavourites(mobile_only)
|
||||
serverlistmgr.sync()
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.btn_mp_mobile then
|
||||
mobile_only = not mobile_only
|
||||
asyncOnlineFavourites(mobile_only)
|
||||
serverlistmgr.sync()
|
||||
return true
|
||||
end
|
||||
|
||||
@ -359,26 +363,32 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
||||
gamedata.playername = fields.te_name
|
||||
gamedata.password = fields.te_pwd
|
||||
gamedata.address = fields.te_address
|
||||
gamedata.port = fields.te_port
|
||||
gamedata.port = tonumber(fields.te_port)
|
||||
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]
|
||||
|
||||
if fav_idx and fav_idx <= #serverlist and
|
||||
fav.address == fields.te_address and
|
||||
fav.port == fields.te_port then
|
||||
fav.address == gamedata.address and
|
||||
fav.port == gamedata.port then
|
||||
|
||||
serverlistmgr.add_favorite(fav)
|
||||
|
||||
gamedata.servername = fav.name
|
||||
gamedata.serverdescription = fav.description
|
||||
|
||||
if menudata.favorites_is_public and
|
||||
not is_server_protocol_compat_or_error(
|
||||
if not is_server_protocol_compat_or_error(
|
||||
fav.proto_min, fav.proto_max) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
gamedata.servername = ""
|
||||
gamedata.serverdescription = ""
|
||||
|
||||
serverlistmgr.add_favorite({
|
||||
address = gamedata.address,
|
||||
port = gamedata.port,
|
||||
})
|
||||
end
|
||||
|
||||
local auto_connect = false
|
||||
@ -403,7 +413,7 @@ end
|
||||
|
||||
local function on_change(type, old_tab, new_tab)
|
||||
if type == "LEAVE" then return end
|
||||
asyncOnlineFavourites(mobile_only)
|
||||
serverlistmgr.sync()
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
29
builtin/mainmenu/tests/favorites_wellformed.txt
Normal 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.
|
36
builtin/mainmenu/tests/serverlistmgr_spec.lua
Normal 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)
|
@ -160,6 +160,7 @@ local function init()
|
||||
-- Simple iteration would ignore lookup via __index.
|
||||
local entity_instrumentation = {
|
||||
"on_activate",
|
||||
"on_deactivate",
|
||||
"on_step",
|
||||
"on_punch",
|
||||
"on_rightclick",
|
||||
|
@ -110,9 +110,9 @@ doubletap_jump (Double tap jump for fly) bool false
|
||||
# enabled.
|
||||
always_fly_fast (Always fly and fast) bool true
|
||||
|
||||
# The time in seconds it takes between repeated right clicks when holding the right
|
||||
# mouse button.
|
||||
repeat_rightclick_time (Rightclick repetition interval) float 0.25 0.001
|
||||
# The time in seconds it takes between repeated node placements when holding
|
||||
# the place button.
|
||||
repeat_place_time (Place repetition interval) float 0.25 0.001
|
||||
|
||||
# Automatically jump up single-node obstacles.
|
||||
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.
|
||||
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
|
||||
# ingame view frustum around.
|
||||
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
|
||||
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.
|
||||
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
|
||||
keymap_inventory (Inventory key) key KEY_KEY_I
|
||||
@ -440,6 +451,10 @@ keymap_decrease_viewing_range_min (View range decrease key) key -
|
||||
|
||||
[**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.
|
||||
# This should greatly improve graphics performance.
|
||||
enable_vbo (VBO) bool true
|
||||
@ -505,8 +520,13 @@ texture_clean_transparent (Clean transparent textures) bool false
|
||||
# texture autoscaling.
|
||||
texture_min_size (Minimum texture size) int 64
|
||||
|
||||
# Experimental option, might cause visible spaces between blocks
|
||||
# when set to higher number than 0.
|
||||
# Use multi-sample antialiasing (MSAA) to smooth out block edges.
|
||||
# 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
|
||||
|
||||
# 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.
|
||||
fps_max (Maximum FPS) int 60 1
|
||||
|
||||
# Maximum FPS when game is paused.
|
||||
pause_fps_max (FPS in pause menu) int 20 1
|
||||
# Maximum FPS when the window is not focused, or when the game is paused.
|
||||
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.
|
||||
pause_on_lost_focus (Pause on lost window focus) bool false
|
||||
|
||||
# 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
|
||||
# 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
|
||||
|
||||
# Crosshair color (R,G,B).
|
||||
# Also controls the object crosshair color
|
||||
crosshair_color (Crosshair color) string (255,255,255)
|
||||
|
||||
# Crosshair alpha (opaqueness, between 0 and 255).
|
||||
# Also controls the object crosshair color
|
||||
crosshair_alpha (Crosshair alpha) int 255 0 255
|
||||
|
||||
# Maximum number of recent chat messages to show
|
||||
@ -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
|
||||
|
||||
# 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]
|
||||
|
||||
@ -945,7 +968,7 @@ serverlist_url (Serverlist URL) string servers.minetest.net
|
||||
|
||||
# File in client/serverlist/ that contains your favorite servers displayed in the
|
||||
# Multiplayer Tab.
|
||||
serverlist_file (Serverlist file) string favoriteservers.txt
|
||||
serverlist_file (Serverlist file) string favoriteservers.json
|
||||
|
||||
# Maximum size of the out chat queue.
|
||||
# 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.
|
||||
# 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).
|
||||
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.
|
||||
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]
|
||||
|
||||
# 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_damage (Damage) bool false
|
||||
|
||||
# Enable creative mode for new created maps.
|
||||
# Enable creative mode for all players
|
||||
creative_mode (Creative) bool false
|
||||
|
||||
# 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
|
||||
# to maintain active objects up to this distance in the direction the
|
||||
# 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
|
||||
# active block stuff, stated in mapblocks (16 nodes).
|
||||
# In active blocks objects are loaded and ABMs run.
|
||||
# This is also the minimum range in which active objects (mobs) are maintained.
|
||||
# This should be configured together with active_object_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).
|
||||
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.
|
||||
max_forceloaded_blocks (Maximum forceloaded blocks) int 16
|
||||
@ -1204,10 +1234,10 @@ movement_gravity (Gravity) float 9.81
|
||||
[**Advanced]
|
||||
|
||||
# Handling for deprecated Lua API calls:
|
||||
# - legacy: (try to) mimic old behaviour (default for release).
|
||||
# - log: mimic and log backtrace of deprecated call (default for debug).
|
||||
# - none: Do not log deprecated calls
|
||||
# - log: mimic and log backtrace of deprecated call (default).
|
||||
# - 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.
|
||||
# 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
|
||||
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
|
||||
# network.
|
||||
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
|
||||
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
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
# 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).
|
||||
# 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.
|
||||
# In Mapgen v6 the 'decorations' flag controls all decorations except trees
|
||||
# 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]
|
||||
|
||||
@ -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.
|
||||
# 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.
|
||||
mgflat_ground_level (Ground level) int 8
|
||||
@ -1864,6 +1899,15 @@ mgflat_hill_threshold (Hill threshold) float 0.45
|
||||
# Controls steepness/height of hills.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
# 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.
|
||||
# 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.
|
||||
# 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.
|
||||
# Value 0:
|
||||
@ -2160,3 +2207,7 @@ contentdb_url (ContentDB URL) string https://content.minetest.net
|
||||
# These flags are independent from Minetest versions,
|
||||
# so see a full list at https://content.minetest.net/help/content_flags/
|
||||
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
|
||||
|
@ -15,13 +15,17 @@ varying vec3 vPosition;
|
||||
// precision must be considered).
|
||||
varying vec3 worldPosition;
|
||||
varying lowp vec4 varColor;
|
||||
#ifdef GL_ES
|
||||
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 fogShadingParameter = 1.0 / ( 1.0 - fogStart);
|
||||
|
||||
#ifdef ENABLE_TONE_MAPPING
|
||||
#if ENABLE_TONE_MAPPING
|
||||
|
||||
/* Hable's UC2 Tone mapping parameters
|
||||
A = 0.22;
|
||||
@ -72,7 +76,7 @@ void main(void)
|
||||
|
||||
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
|
||||
|
||||
#ifdef ENABLE_TONE_MAPPING
|
||||
#if ENABLE_TONE_MAPPING
|
||||
col = applyToneMapping(col);
|
||||
#endif
|
||||
|
||||
|
@ -17,8 +17,15 @@ varying vec3 vPosition;
|
||||
// precision must be considered).
|
||||
varying vec3 worldPosition;
|
||||
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 vec3 eyeVec;
|
||||
#else
|
||||
centroid varying vec2 varTexCoord;
|
||||
#endif
|
||||
varying vec3 eyeVec;
|
||||
|
||||
// Color of the light emitted by the light sources.
|
||||
const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
|
||||
@ -144,7 +151,9 @@ void main(void)
|
||||
color.xyz = color.zyx; // swap RGB order
|
||||
#endif
|
||||
// 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;
|
||||
|
||||
// Emphase blue a bit in darker places
|
||||
|
@ -9,7 +9,11 @@ varying vec3 vNormal;
|
||||
varying vec3 vPosition;
|
||||
varying vec3 worldPosition;
|
||||
varying lowp vec4 varColor;
|
||||
#ifdef GL_ES
|
||||
varying mediump vec2 varTexCoord;
|
||||
#else
|
||||
centroid varying vec2 varTexCoord;
|
||||
#endif
|
||||
|
||||
varying vec3 eyeVec;
|
||||
varying float vIDiff;
|
||||
@ -19,7 +23,7 @@ const float BS = 10.0;
|
||||
const float fogStart = FOG_START;
|
||||
const float fogShadingParameter = 1.0 / (1.0 - fogStart);
|
||||
|
||||
#ifdef ENABLE_TONE_MAPPING
|
||||
#if ENABLE_TONE_MAPPING
|
||||
|
||||
/* Hable's UC2 Tone mapping parameters
|
||||
A = 0.22;
|
||||
@ -75,7 +79,7 @@ void main(void)
|
||||
|
||||
col.rgb *= emissiveColor.rgb * vIDiff;
|
||||
|
||||
#ifdef ENABLE_TONE_MAPPING
|
||||
#if ENABLE_TONE_MAPPING
|
||||
col = applyToneMapping(col);
|
||||
#endif
|
||||
|
||||
|
@ -7,7 +7,11 @@ varying vec3 vNormal;
|
||||
varying vec3 vPosition;
|
||||
varying vec3 worldPosition;
|
||||
varying lowp vec4 varColor;
|
||||
#ifdef GL_ES
|
||||
varying mediump vec2 varTexCoord;
|
||||
#else
|
||||
centroid varying vec2 varTexCoord;
|
||||
#endif
|
||||
|
||||
varying vec3 eyeVec;
|
||||
varying float vIDiff;
|
||||
|
@ -109,6 +109,10 @@ core.register_on_sending_chat_message(function(message)
|
||||
return false
|
||||
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
|
||||
core.register_on_hp_modification(function(hp)
|
||||
print("[PREVIEW] HP modified " .. hp)
|
||||
|
101
doc/builtin_entities.txt
Normal 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.
|
@ -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/>
|
||||
* Developer Wiki: <http://dev.minetest.net/>
|
||||
@ -620,7 +620,7 @@ Helper functions
|
||||
* `minetest.is_yes(arg)`
|
||||
* returns whether `arg` can be interpreted as yes
|
||||
* `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
|
||||
* returns a deep copy of `table`
|
||||
|
||||
@ -686,6 +686,11 @@ Call these functions only at load time!
|
||||
* Adds definition to minetest.registered_chatcommands
|
||||
* `minetest.unregister_chatcommand(name)`
|
||||
* 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())`
|
||||
* Called when the local player dies
|
||||
* `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"`
|
||||
* `search_center` is an optional boolean (default: `false`)
|
||||
If true `pos` is also checked for the nodes
|
||||
* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of
|
||||
positions.
|
||||
* `minetest.find_nodes_in_area(pos1, pos2, nodenames, [grouped])`
|
||||
* `pos1` and `pos2` are the min and max positions of the area to search.
|
||||
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
|
||||
* First return value: Table with all node positions
|
||||
* Second return value: Table with the count of each node with the node name
|
||||
as index.
|
||||
* If `grouped` is true the return value is a table indexed by node name
|
||||
which contains lists of positions.
|
||||
* If `grouped` is false or absent the return values are as follows:
|
||||
first value: Table with all node positions
|
||||
second value: Table with the count of each node with the node name
|
||||
as index
|
||||
* Area volume is limited to 4,096,000 nodes
|
||||
* `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a
|
||||
list of positions.
|
||||
@ -993,6 +1001,7 @@ Please do not try to access the reference until the camera is initialized, other
|
||||
|
||||
### LocalPlayer
|
||||
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:
|
||||
|
||||
@ -1097,8 +1106,8 @@ Methods:
|
||||
aux1 = boolean,
|
||||
sneak = boolean,
|
||||
zoom = boolean,
|
||||
LMB = boolean,
|
||||
RMB = boolean,
|
||||
dig = boolean,
|
||||
place = boolean,
|
||||
}
|
||||
```
|
||||
|
||||
|
676
doc/lua_api.txt
@ -1,4 +1,4 @@
|
||||
Minetest Lua Mainmenu API Reference 5.3.0
|
||||
Minetest Lua Mainmenu API Reference 5.4.0
|
||||
=========================================
|
||||
|
||||
Introduction
|
||||
@ -43,10 +43,14 @@ core.get_max_supp_proto()
|
||||
core.open_url(url)
|
||||
^ opens the URL in a web browser, returns false on failure.
|
||||
^ 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)
|
||||
^ returns current core version
|
||||
|
||||
|
||||
|
||||
Filesystem
|
||||
----------
|
||||
|
||||
@ -63,6 +67,8 @@ core.copy_dir(source,destination,keep_soure) (possible in async calls)
|
||||
^ destination folder
|
||||
^ keep_source DEFAULT true --> if set to false source is deleted after copying
|
||||
^ 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]
|
||||
^ zipfile to extract
|
||||
^ 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
|
||||
registered in the core (possible in async calls)
|
||||
core.get_cache_path() -> path of cache
|
||||
core.get_temp_path() -> path of temp folder
|
||||
|
||||
|
||||
HTTP Requests
|
||||
@ -207,6 +214,9 @@ Content and Packages
|
||||
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.
|
||||
|
||||
* 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)
|
||||
* returns path to global modpath
|
||||
* 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
|
||||
-------
|
||||
|
||||
|
@ -72,7 +72,12 @@ by texture packs. All existing fallback textures can be found in the directory
|
||||
* `crosshair.png`
|
||||
* the crosshair texture in the center of the screen. The settings
|
||||
`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
|
||||
|
||||
@ -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_overlay_round.png`: overlay texture for the round minimap
|
||||
* `minimap_overlay_square.png`: overlay texture for the square minimap
|
||||
* `no_texture_airlike.png`: fallback inventory image for airlike nodes
|
||||
* `object_marker_red.png`: texture for players on the minimap
|
||||
* `player_marker.png`: texture for the own player on the square minimap
|
||||
|
||||
@ -189,11 +195,27 @@ Here are targets you can choose from:
|
||||
| bottom | y- face |
|
||||
| sides | x-, x+, z-, z+ faces |
|
||||
| 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 |
|
||||
| wield | The texture used when held by the player |
|
||||
|
||||
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
|
||||
----------------------------------------------------------
|
||||
|
@ -493,19 +493,8 @@ Static objects are persistent freely moving objects in the world.
|
||||
|
||||
Object types:
|
||||
1: Test object
|
||||
2: Item
|
||||
3: Rat (obsolete)
|
||||
4: Oerkki (obsolete)
|
||||
5: Firefly (obsolete)
|
||||
6: MobV2 (obsolete)
|
||||
7: LuaEntity
|
||||
|
||||
1: Item:
|
||||
u8 version
|
||||
version 0:
|
||||
u16 len
|
||||
u8[len] itemstring
|
||||
|
||||
7: LuaEntity:
|
||||
u8 compatibility_byte (always 1)
|
||||
u16 len
|
||||
|
@ -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");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -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 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
|
||||
* 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
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
local WATER_ALPHA = 160
|
||||
local WATER_ALPHA = "^[opacity:" .. 160
|
||||
local WATER_VISC = 1
|
||||
local LAVA_VISC = 7
|
||||
|
||||
@ -124,15 +124,16 @@ minetest.register_node("basenodes:pine_needles", {
|
||||
})
|
||||
|
||||
minetest.register_node("basenodes:water_source", {
|
||||
description = "Water Source",
|
||||
description = "Water Source".."\n"..
|
||||
"Drowning damage: 1",
|
||||
drawtype = "liquid",
|
||||
waving = 3,
|
||||
tiles = {"default_water.png"},
|
||||
tiles = {"default_water.png"..WATER_ALPHA},
|
||||
special_tiles = {
|
||||
{name = "default_water.png", backface_culling = false},
|
||||
{name = "default_water.png", backface_culling = true},
|
||||
{name = "default_water.png"..WATER_ALPHA, backface_culling = false},
|
||||
{name = "default_water.png"..WATER_ALPHA, backface_culling = true},
|
||||
},
|
||||
alpha = WATER_ALPHA,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
@ -149,15 +150,18 @@ minetest.register_node("basenodes:water_source", {
|
||||
})
|
||||
|
||||
minetest.register_node("basenodes:water_flowing", {
|
||||
description = "Flowing Water",
|
||||
description = "Flowing Water".."\n"..
|
||||
"Drowning damage: 1",
|
||||
drawtype = "flowingliquid",
|
||||
waving = 3,
|
||||
tiles = {"default_water_flowing.png"},
|
||||
special_tiles = {
|
||||
{name = "default_water_flowing.png", backface_culling = false},
|
||||
{name = "default_water_flowing.png", backface_culling = false},
|
||||
{name = "default_water_flowing.png"..WATER_ALPHA,
|
||||
backface_culling = false},
|
||||
{name = "default_water_flowing.png"..WATER_ALPHA,
|
||||
backface_culling = false},
|
||||
},
|
||||
alpha = WATER_ALPHA,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
paramtype2 = "flowingliquid",
|
||||
walkable = false,
|
||||
@ -175,15 +179,16 @@ minetest.register_node("basenodes:water_flowing", {
|
||||
})
|
||||
|
||||
minetest.register_node("basenodes:river_water_source", {
|
||||
description = "River Water Source",
|
||||
description = "River Water Source".."\n"..
|
||||
"Drowning damage: 1",
|
||||
drawtype = "liquid",
|
||||
waving = 3,
|
||||
tiles = { "default_river_water.png" },
|
||||
tiles = { "default_river_water.png"..WATER_ALPHA },
|
||||
special_tiles = {
|
||||
{name = "default_river_water.png", backface_culling = false},
|
||||
{name = "default_river_water.png", backface_culling = true},
|
||||
{name = "default_river_water.png"..WATER_ALPHA, backface_culling = false},
|
||||
{name = "default_river_water.png"..WATER_ALPHA, backface_culling = true},
|
||||
},
|
||||
alpha = WATER_ALPHA,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
@ -202,15 +207,18 @@ minetest.register_node("basenodes:river_water_source", {
|
||||
})
|
||||
|
||||
minetest.register_node("basenodes:river_water_flowing", {
|
||||
description = "Flowing River Water",
|
||||
description = "Flowing River Water".."\n"..
|
||||
"Drowning damage: 1",
|
||||
drawtype = "flowingliquid",
|
||||
waving = 3,
|
||||
tiles = {"default_river_water_flowing.png"},
|
||||
tiles = {"default_river_water_flowing.png"..WATER_ALPHA},
|
||||
special_tiles = {
|
||||
{name = "default_river_water_flowing.png", backface_culling = false},
|
||||
{name = "default_river_water_flowing.png", backface_culling = false},
|
||||
{name = "default_river_water_flowing.png"..WATER_ALPHA,
|
||||
backface_culling = false},
|
||||
{name = "default_river_water_flowing.png"..WATER_ALPHA,
|
||||
backface_culling = false},
|
||||
},
|
||||
alpha = WATER_ALPHA,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
paramtype2 = "flowingliquid",
|
||||
walkable = false,
|
||||
@ -230,7 +238,9 @@ minetest.register_node("basenodes:river_water_flowing", {
|
||||
})
|
||||
|
||||
minetest.register_node("basenodes:lava_flowing", {
|
||||
description = "Flowing Lava",
|
||||
description = "Flowing Lava".."\n"..
|
||||
"4 damage per second".."\n"..
|
||||
"Drowning damage: 1",
|
||||
drawtype = "flowingliquid",
|
||||
tiles = {"default_lava_flowing.png"},
|
||||
special_tiles = {
|
||||
@ -255,7 +265,9 @@ minetest.register_node("basenodes:lava_flowing", {
|
||||
})
|
||||
|
||||
minetest.register_node("basenodes:lava_source", {
|
||||
description = "Lava Source",
|
||||
description = "Lava Source".."\n"..
|
||||
"4 damage per second".."\n"..
|
||||
"Drowning damage: 1",
|
||||
drawtype = "liquid",
|
||||
tiles = { "default_lava.png" },
|
||||
special_tiles = {
|
||||
@ -294,7 +306,8 @@ minetest.register_node("basenodes:mossycobble", {
|
||||
})
|
||||
|
||||
minetest.register_node("basenodes:apple", {
|
||||
description = "Apple",
|
||||
description = "Apple".."\n"..
|
||||
"Food (+2)",
|
||||
drawtype = "plantlike",
|
||||
tiles ={"default_apple.png"},
|
||||
inventory_image = "default_apple.png",
|
||||
@ -334,5 +347,3 @@ minetest.register_node("basenodes:snowblock", {
|
||||
tiles ={"default_snow.png"},
|
||||
groups = {crumbly=3},
|
||||
})
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 829 B |
Before Width: | Height: | Size: 796 B After Width: | Height: | Size: 796 B |
7
games/devtest/mods/basenodes/textures/info.txt
Normal 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.
|
@ -6,7 +6,7 @@
|
||||
|
||||
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
|
||||
* Axe: dig choppy
|
||||
* Shovel: dig crumbly
|
||||
@ -24,25 +24,54 @@ Tool materials:
|
||||
]]
|
||||
|
||||
-- The hand
|
||||
if minetest.settings:get_bool("creative_mode") then
|
||||
local digtime = 42
|
||||
local caps = {times = {digtime, digtime, digtime}, uses = 0, maxlevel = 256}
|
||||
|
||||
minetest.register_item(":", {
|
||||
type = "none",
|
||||
wield_image = "wieldhand.png",
|
||||
wield_scale = {x = 1, y = 1, z = 2.5},
|
||||
range = 10,
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 0.5,
|
||||
max_drop_level = 3,
|
||||
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 = 1.0,
|
||||
full_punch_interval = 0.9,
|
||||
max_drop_level = 0,
|
||||
groupcaps = {
|
||||
crumbly = {times={[3]=1.50}, uses=0, maxlevel=0},
|
||||
snappy = {times={[3]=1.50}, uses=0, maxlevel=0},
|
||||
oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=2.00}, uses=0, maxlevel=0},
|
||||
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
|
||||
minetest.register_tool("basetools:pick_mese", {
|
||||
description = "Mese Pickaxe",
|
||||
description = "Mese Pickaxe".."\n"..
|
||||
"Digs diggable nodes instantly",
|
||||
inventory_image = "basetools_mesepick.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
@ -65,7 +94,9 @@ minetest.register_tool("basetools:pick_mese", {
|
||||
|
||||
-- This should break after only 1 use
|
||||
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",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -76,7 +107,8 @@ minetest.register_tool("basetools:pick_dirt", {
|
||||
})
|
||||
|
||||
minetest.register_tool("basetools:pick_wood", {
|
||||
description = "Wooden Pickaxe",
|
||||
description = "Wooden Pickaxe".."\n"..
|
||||
"Digs cracky=3",
|
||||
inventory_image = "basetools_woodpick.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -86,7 +118,8 @@ minetest.register_tool("basetools:pick_wood", {
|
||||
},
|
||||
})
|
||||
minetest.register_tool("basetools:pick_stone", {
|
||||
description = "Stone Pickaxe",
|
||||
description = "Stone Pickaxe".."\n"..
|
||||
"Digs cracky=2..3",
|
||||
inventory_image = "basetools_stonepick.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -96,7 +129,8 @@ minetest.register_tool("basetools:pick_stone", {
|
||||
},
|
||||
})
|
||||
minetest.register_tool("basetools:pick_steel", {
|
||||
description = "Steel Pickaxe",
|
||||
description = "Steel Pickaxe".."\n"..
|
||||
"Digs cracky=1..3",
|
||||
inventory_image = "basetools_steelpick.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
@ -106,7 +140,9 @@ minetest.register_tool("basetools:pick_steel", {
|
||||
},
|
||||
})
|
||||
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",
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
@ -116,7 +152,9 @@ minetest.register_tool("basetools:pick_steel_l1", {
|
||||
},
|
||||
})
|
||||
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",
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
@ -131,7 +169,8 @@ minetest.register_tool("basetools:pick_steel_l2", {
|
||||
--
|
||||
|
||||
minetest.register_tool("basetools:shovel_wood", {
|
||||
description = "Wooden Shovel",
|
||||
description = "Wooden Shovel".."\n"..
|
||||
"Digs crumbly=3",
|
||||
inventory_image = "basetools_woodshovel.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -141,7 +180,8 @@ minetest.register_tool("basetools:shovel_wood", {
|
||||
},
|
||||
})
|
||||
minetest.register_tool("basetools:shovel_stone", {
|
||||
description = "Stone Shovel",
|
||||
description = "Stone Shovel".."\n"..
|
||||
"Digs crumbly=2..3",
|
||||
inventory_image = "basetools_stoneshovel.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -151,7 +191,8 @@ minetest.register_tool("basetools:shovel_stone", {
|
||||
},
|
||||
})
|
||||
minetest.register_tool("basetools:shovel_steel", {
|
||||
description = "Steel Shovel",
|
||||
description = "Steel Shovel".."\n"..
|
||||
"Digs crumbly=1..3",
|
||||
inventory_image = "basetools_steelshovel.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
@ -166,7 +207,8 @@ minetest.register_tool("basetools:shovel_steel", {
|
||||
--
|
||||
|
||||
minetest.register_tool("basetools:axe_wood", {
|
||||
description = "Wooden Axe",
|
||||
description = "Wooden Axe".."\n"..
|
||||
"Digs choppy=3",
|
||||
inventory_image = "basetools_woodaxe.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -176,7 +218,8 @@ minetest.register_tool("basetools:axe_wood", {
|
||||
},
|
||||
})
|
||||
minetest.register_tool("basetools:axe_stone", {
|
||||
description = "Stone Axe",
|
||||
description = "Stone Axe".."\n"..
|
||||
"Digs choppy=2..3",
|
||||
inventory_image = "basetools_stoneaxe.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -186,7 +229,8 @@ minetest.register_tool("basetools:axe_stone", {
|
||||
},
|
||||
})
|
||||
minetest.register_tool("basetools:axe_steel", {
|
||||
description = "Steel Axe",
|
||||
description = "Steel Axe".."\n"..
|
||||
"Digs choppy=1..3",
|
||||
inventory_image = "basetools_steelaxe.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
@ -201,7 +245,8 @@ minetest.register_tool("basetools:axe_steel", {
|
||||
--
|
||||
|
||||
minetest.register_tool("basetools:shears_wood", {
|
||||
description = "Wooden Shears",
|
||||
description = "Wooden Shears".."\n"..
|
||||
"Digs snappy=3",
|
||||
inventory_image = "basetools_woodshears.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -211,7 +256,8 @@ minetest.register_tool("basetools:shears_wood", {
|
||||
},
|
||||
})
|
||||
minetest.register_tool("basetools:shears_stone", {
|
||||
description = "Stone Shears",
|
||||
description = "Stone Shears".."\n"..
|
||||
"Digs snappy=2..3",
|
||||
inventory_image = "basetools_stoneshears.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
@ -221,7 +267,8 @@ minetest.register_tool("basetools:shears_stone", {
|
||||
},
|
||||
})
|
||||
minetest.register_tool("basetools:shears_steel", {
|
||||
description = "Steel Shears",
|
||||
description = "Steel Shears".."\n"..
|
||||
"Digs snappy=1..3",
|
||||
inventory_image = "basetools_steelshears.png",
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
@ -236,7 +283,8 @@ minetest.register_tool("basetools:shears_steel", {
|
||||
--
|
||||
|
||||
minetest.register_tool("basetools:sword_wood", {
|
||||
description = "Wooden Sword",
|
||||
description = "Wooden Sword".."\n"..
|
||||
"Damage: fleshy=2",
|
||||
inventory_image = "basetools_woodsword.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
@ -244,7 +292,8 @@ minetest.register_tool("basetools:sword_wood", {
|
||||
}
|
||||
})
|
||||
minetest.register_tool("basetools:sword_stone", {
|
||||
description = "Stone Sword",
|
||||
description = "Stone Sword".."\n"..
|
||||
"Damage: fleshy=4",
|
||||
inventory_image = "basetools_stonesword.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
@ -253,7 +302,8 @@ minetest.register_tool("basetools:sword_stone", {
|
||||
}
|
||||
})
|
||||
minetest.register_tool("basetools:sword_steel", {
|
||||
description = "Steel Sword",
|
||||
description = "Steel Sword".."\n"..
|
||||
"Damage: fleshy=6",
|
||||
inventory_image = "basetools_steelsword.png",
|
||||
tool_capabilities = {
|
||||
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
|
||||
minetest.register_tool("basetools:sword_fire", {
|
||||
description = "Fire Sword",
|
||||
description = "Fire Sword".."\n"..
|
||||
"Damage: icy=6",
|
||||
inventory_image = "basetools_firesword.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
@ -273,12 +324,13 @@ minetest.register_tool("basetools:sword_fire", {
|
||||
}
|
||||
})
|
||||
minetest.register_tool("basetools:sword_ice", {
|
||||
description = "Ice Sword",
|
||||
description = "Ice Sword".."\n"..
|
||||
"Damage: fiery=6",
|
||||
inventory_image = "basetools_icesword.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.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
|
||||
--
|
||||
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",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 0.5,
|
||||
|
@ -1,7 +1,8 @@
|
||||
-- Bucket: Punch liquid source or flowing liquid to collect it
|
||||
|
||||
minetest.register_tool("bucket:bucket", {
|
||||
description = "Bucket",
|
||||
description = "Bucket".."\n"..
|
||||
"Picks up liquid nodes",
|
||||
inventory_image = "bucket.png",
|
||||
stack_max = 1,
|
||||
liquids_pointable = true,
|
||||
|
@ -1,5 +1,6 @@
|
||||
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",
|
||||
"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"},
|
||||
@ -22,6 +23,18 @@ minetest.register_node("chest:chest", {
|
||||
local inv = meta:get_inventory()
|
||||
return inv:is_empty("main")
|
||||
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,
|
||||
})
|
||||
|
||||
|
||||
|
@ -43,7 +43,8 @@ local function get_chest_formspec(page)
|
||||
end
|
||||
|
||||
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",
|
||||
"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"},
|
||||
|
@ -214,3 +214,6 @@ minetest.register_chatcommand("test_place_nodes", {
|
||||
end,
|
||||
})
|
||||
|
||||
core.register_on_chatcommand(function(name, command, params)
|
||||
minetest.log("caught command '"..command.."', issued by '"..name.."'. Parameters: '"..params.."'")
|
||||
end)
|
||||
|
@ -44,7 +44,8 @@ minetest.register_node("experimental:callback_node", {
|
||||
})
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -67,7 +68,8 @@ minetest.register_tool("experimental:privatizer", {
|
||||
})
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
|
@ -60,11 +60,13 @@ minetest.register_node("soundstuff:footstep_liquid", {
|
||||
description = "Liquid Footstep Sound Node",
|
||||
drawtype = "liquid",
|
||||
tiles = {
|
||||
"soundstuff_node_sound.png^[colorize:#0000FF:127",
|
||||
"soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190",
|
||||
},
|
||||
special_tiles = {
|
||||
{name = "soundstuff_node_sound.png^[colorize:#0000FF:127", backface_culling = false},
|
||||
{name = "soundstuff_node_sound.png^[colorize:#0000FF:127", backface_culling = true},
|
||||
{name = "soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190",
|
||||
backface_culling = false},
|
||||
{name = "soundstuff_node_sound.png^[colorize:#0000FF:127^[opacity:190",
|
||||
backface_culling = true},
|
||||
},
|
||||
liquids_pointable = true,
|
||||
liquidtype = "source",
|
||||
@ -73,7 +75,7 @@ minetest.register_node("soundstuff:footstep_liquid", {
|
||||
liquid_renewable = false,
|
||||
liquid_range = 0,
|
||||
liquid_viscosity = 0,
|
||||
alpha = 190,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
@ -92,7 +94,6 @@ minetest.register_node("soundstuff:footstep_climbable", {
|
||||
tiles = {
|
||||
"soundstuff_node_climbable.png",
|
||||
},
|
||||
alpha = 120,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
@ -107,7 +108,8 @@ minetest.register_node("soundstuff:footstep_climbable", {
|
||||
|
||||
|
||||
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",
|
||||
on_use = minetest.item_eat(0),
|
||||
sound = {
|
||||
@ -116,7 +118,9 @@ minetest.register_craftitem("soundstuff:eat", {
|
||||
})
|
||||
|
||||
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",
|
||||
sound = {
|
||||
breaks = { name = "soundstuff_mono", gain = 1.0 },
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
local phasearmor = {
|
||||
[0]={icy=100},
|
||||
[1]={firy=100},
|
||||
[1]={fiery=100},
|
||||
[2]={fleshy=100},
|
||||
[3]={immortal=1},
|
||||
[4]={punch_operable=1},
|
||||
|
@ -31,6 +31,9 @@ minetest.register_entity("testentities:callback", {
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
message("Callback entity: on_activate! pos="..spos(self).."; dtime_s="..dtime_s)
|
||||
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)
|
||||
local name = get_object_name(puncher)
|
||||
message(
|
||||
|
@ -68,7 +68,7 @@ minetest.register_entity("testentities:mesh_unshaded", {
|
||||
|
||||
-- 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", {
|
||||
initial_properties = {
|
||||
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},
|
||||
},
|
||||
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,
|
||||
})
|
||||
|
@ -22,3 +22,10 @@ minetest.register_craftitem("testfood:bad5", {
|
||||
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"),
|
||||
})
|
||||
|
||||
|
BIN
games/devtest/mods/testfood/textures/testfood_replace.png
Normal file
After Width: | Height: | Size: 135 B |
14
games/devtest/mods/testformspec/LICENSE.txt
Normal 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
|
@ -33,6 +33,34 @@ local tabheaders_fs = [[
|
||||
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 = [[
|
||||
<bigger>Normal test</bigger>
|
||||
This is a normal text.
|
||||
@ -164,7 +192,7 @@ local style_fs = [[
|
||||
style[one_btn14:hovered+pressed;textcolor=purple]
|
||||
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]
|
||||
|
||||
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]"..
|
||||
"scroll_container[1,1;8,6;scrbar;vertical]"..
|
||||
"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]"..
|
||||
"pwdfield[2,2;1,1;lorem2;Lorem]"..
|
||||
"list[current_player;main;4,4;1,5;]"..
|
||||
@ -211,6 +240,8 @@ local scroll_fs =
|
||||
"tooltip[0,11;3,2;Buz;#f00;#000]"..
|
||||
"box[0,11;3,2;#00ff00]"..
|
||||
"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]"..
|
||||
"box[1,2;3,2;#0a0a]"..
|
||||
"scroll_container[1,2;3,2;scrbar2;horizontal;0.06]"..
|
||||
@ -310,6 +341,9 @@ local pages = {
|
||||
"size[12,13]real_coordinates[true]" ..
|
||||
"container[0.5,1.5]" .. tabheaders_fs .. "container_end[]",
|
||||
|
||||
-- Inv
|
||||
"size[12,13]real_coordinates[true]" .. inv_style_fs,
|
||||
|
||||
-- Animation
|
||||
[[
|
||||
formspec_version[3]
|
||||
@ -327,6 +361,10 @@ Number]
|
||||
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,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
|
||||
@ -337,7 +375,7 @@ Number]
|
||||
local function show_test_formspec(pname, page_id)
|
||||
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)
|
||||
end
|
||||
|
@ -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
|
BIN
games/devtest/mods/testformspec/textures/default_chest_front.png
Normal file
After Width: | Height: | Size: 423 B |
After Width: | Height: | Size: 102 B |
BIN
games/devtest/mods/testformspec/textures/default_chest_side.png
Normal file
After Width: | Height: | Size: 375 B |
BIN
games/devtest/mods/testformspec/textures/default_chest_top.png
Normal file
After Width: | Height: | Size: 423 B |
After Width: | Height: | Size: 2.7 KiB |
@ -145,6 +145,23 @@ minetest.register_node("testnodes:fencelike", {
|
||||
})
|
||||
|
||||
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"),
|
||||
drawtype = "torchlike",
|
||||
paramtype = "light",
|
||||
@ -162,6 +179,8 @@ minetest.register_node("testnodes:torchlike", {
|
||||
inventory_image = fallback_image("testnodes_torchlike_floor.png"),
|
||||
})
|
||||
|
||||
|
||||
|
||||
minetest.register_node("testnodes:signlike", {
|
||||
description = S("Wallmounted Signlike Drawtype Test Node"),
|
||||
drawtype = "signlike",
|
||||
@ -331,68 +350,72 @@ minetest.register_node("testnodes:plantlike_rooted_degrotate", {
|
||||
})
|
||||
|
||||
-- Demonstrative liquid nodes, source and flowing form.
|
||||
minetest.register_node("testnodes:liquid", {
|
||||
description = S("Source Liquid Drawtype Test Node"),
|
||||
-- DRAWTYPE ONLY, NO LIQUID PHYSICS!
|
||||
-- Liquid ranges 0 to 8
|
||||
for r = 0, 8 do
|
||||
minetest.register_node("testnodes:liquid_"..r, {
|
||||
description = S("Source Liquid Drawtype Test Node, Range @1", r),
|
||||
drawtype = "liquid",
|
||||
paramtype = "light",
|
||||
tiles = {
|
||||
"testnodes_liquidsource.png",
|
||||
"testnodes_liquidsource_r"..r..".png^[colorize:#FFFFFF:100",
|
||||
},
|
||||
special_tiles = {
|
||||
{name="testnodes_liquidsource.png", backface_culling=false},
|
||||
{name="testnodes_liquidsource.png", backface_culling=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 = true,
|
||||
use_texture_alpha = "blend",
|
||||
|
||||
|
||||
walkable = false,
|
||||
liquidtype = "source",
|
||||
liquid_range = 1,
|
||||
liquid_range = r,
|
||||
liquid_viscosity = 0,
|
||||
liquid_alternative_flowing = "testnodes:liquid_flowing",
|
||||
liquid_alternative_source = "testnodes:liquid",
|
||||
liquid_alternative_flowing = "testnodes:liquid_flowing_"..r,
|
||||
liquid_alternative_source = "testnodes:liquid_"..r,
|
||||
groups = { dig_immediate = 3 },
|
||||
})
|
||||
minetest.register_node("testnodes:liquid_flowing", {
|
||||
description = S("Flowing Liquid Drawtype Test Node"),
|
||||
minetest.register_node("testnodes:liquid_flowing_"..r, {
|
||||
description = S("Flowing Liquid Drawtype Test Node, Range @1", r),
|
||||
drawtype = "flowingliquid",
|
||||
paramtype = "light",
|
||||
paramtype2 = "flowingliquid",
|
||||
tiles = {
|
||||
"testnodes_liquidflowing.png",
|
||||
"testnodes_liquidflowing_r"..r..".png^[colorize:#FFFFFF:100",
|
||||
},
|
||||
special_tiles = {
|
||||
{name="testnodes_liquidflowing.png", 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_r"..r..".png^[colorize:#FFFFFF:100", backface_culling=false},
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
use_texture_alpha = "blend",
|
||||
|
||||
|
||||
walkable = false,
|
||||
liquidtype = "flowing",
|
||||
liquid_range = 1,
|
||||
liquid_range = r,
|
||||
liquid_viscosity = 0,
|
||||
liquid_alternative_flowing = "testnodes:liquid_flowing",
|
||||
liquid_alternative_source = "testnodes:liquid",
|
||||
liquid_alternative_flowing = "testnodes:liquid_flowing_"..r,
|
||||
liquid_alternative_source = "testnodes:liquid_"..r,
|
||||
groups = { dig_immediate = 3 },
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
-- Waving liquid test (drawtype only)
|
||||
minetest.register_node("testnodes:liquid_waving", {
|
||||
description = S("Waving Source Liquid Drawtype Test Node"),
|
||||
drawtype = "liquid",
|
||||
paramtype = "light",
|
||||
tiles = {
|
||||
"testnodes_liquidsource.png^[brighten",
|
||||
"testnodes_liquidsource.png^[colorize:#0000FF:127",
|
||||
},
|
||||
special_tiles = {
|
||||
{name="testnodes_liquidsource.png^[brighten", backface_culling=false},
|
||||
{name="testnodes_liquidsource.png^[brighten", backface_culling=true},
|
||||
{name="testnodes_liquidsource.png^[colorize:#0000FF:127", backface_culling=false},
|
||||
{name="testnodes_liquidsource.png^[colorize:#0000FF:127", backface_culling=true},
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
use_texture_alpha = "blend",
|
||||
waving = 3,
|
||||
|
||||
|
||||
walkable = false,
|
||||
liquidtype = "source",
|
||||
liquid_range = 1,
|
||||
liquid_viscosity = 0,
|
||||
liquid_alternative_flowing = "testnodes:liquid_flowing_waving",
|
||||
@ -405,18 +428,17 @@ minetest.register_node("testnodes:liquid_flowing_waving", {
|
||||
paramtype = "light",
|
||||
paramtype2 = "flowingliquid",
|
||||
tiles = {
|
||||
"testnodes_liquidflowing.png^[brighten",
|
||||
"testnodes_liquidflowing.png^[colorize:#0000FF:127",
|
||||
},
|
||||
special_tiles = {
|
||||
{name="testnodes_liquidflowing.png^[brighten", backface_culling=false},
|
||||
{name="testnodes_liquidflowing.png^[brighten", backface_culling=false},
|
||||
{name="testnodes_liquidflowing.png^[colorize:#0000FF:127", backface_culling=false},
|
||||
{name="testnodes_liquidflowing.png^[colorize:#0000FF:127", backface_culling=false},
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
use_texture_alpha = "blend",
|
||||
waving = 3,
|
||||
|
||||
|
||||
walkable = false,
|
||||
liquidtype = "flowing",
|
||||
liquid_range = 1,
|
||||
liquid_viscosity = 0,
|
||||
liquid_alternative_flowing = "testnodes:liquid_flowing_waving",
|
||||
@ -424,8 +446,6 @@ minetest.register_node("testnodes:liquid_flowing_waving", {
|
||||
groups = { dig_immediate = 3 },
|
||||
})
|
||||
|
||||
|
||||
|
||||
-- Invisible node
|
||||
minetest.register_node("testnodes:airlike", {
|
||||
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)
|
||||
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",
|
||||
S("Double-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("Half-sized Wallmounted Torchlike Drawtype Test Node"))
|
||||
scale("signlike",
|
||||
|
@ -22,7 +22,8 @@ end
|
||||
-- Lets light through, but not sunlight, leading to a
|
||||
-- reduction in light level when light passes through
|
||||
minetest.register_node("testnodes:sunlight_filter", {
|
||||
description = S("Sunlight Filter"),
|
||||
description = S("Sunlight Filter") .."\n"..
|
||||
S("Lets light through, but weakens sunlight"),
|
||||
paramtype = "light",
|
||||
|
||||
|
||||
@ -35,7 +36,8 @@ minetest.register_node("testnodes:sunlight_filter", {
|
||||
|
||||
-- Lets light and sunlight through without obstruction
|
||||
minetest.register_node("testnodes:sunlight_propagator", {
|
||||
description = S("Sunlight Propagator"),
|
||||
description = S("Sunlight Propagator") .."\n"..
|
||||
S("Lets all light through"),
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
|
||||
|
@ -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 = true},
|
||||
},
|
||||
alpha = 192,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
is_ground_content = false,
|
||||
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},
|
||||
},
|
||||
alpha = 192,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
paramtype2 = "flowingliquid",
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
is_ground_content = false,
|
||||
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 = true},
|
||||
},
|
||||
alpha = 192,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
is_ground_content = false,
|
||||
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},
|
||||
},
|
||||
alpha = 192,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
paramtype2 = "flowingliquid",
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
is_ground_content = false,
|
||||
liquidtype = "flowing",
|
||||
|
@ -1,2 +1,3 @@
|
||||
name = testnodes
|
||||
description = Contains a bunch of basic example nodes for demonstrative purposes, development and testing
|
||||
depends = stairs
|
||||
|
@ -18,7 +18,7 @@ minetest.register_node("testnodes:nodebox_fixed", {
|
||||
|
||||
-- 50% higher than a regular node
|
||||
minetest.register_node("testnodes:nodebox_overhigh", {
|
||||
description = S("Overhigh Nodebox Test Node"),
|
||||
description = S("+50% high Nodebox Test Node"),
|
||||
tiles = {"testnodes_nodebox.png"},
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
@ -30,15 +30,16 @@ minetest.register_node("testnodes:nodebox_overhigh", {
|
||||
groups = {dig_immediate=3},
|
||||
})
|
||||
|
||||
-- 100% higher than a regular node
|
||||
-- 95% higher than a regular node
|
||||
minetest.register_node("testnodes:nodebox_overhigh2", {
|
||||
description = S("Double-height Nodebox Test Node"),
|
||||
description = S("+95% high Nodebox Test Node"),
|
||||
tiles = {"testnodes_nodebox.png"},
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
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},
|
||||
|
@ -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 = true},
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
pointable = false,
|
||||
liquids_pointable = true,
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
is_ground_content = false,
|
||||
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},
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
use_texture_alpha = "blend",
|
||||
paramtype = "light",
|
||||
paramtype2 = "flowingliquid",
|
||||
pointable = false,
|
||||
liquids_pointable = true,
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
is_ground_content = false,
|
||||
post_effect_color = {a = 70, r = 255, g = 0, b = 200},
|
||||
|
@ -46,28 +46,22 @@ for a=1,#alphas do
|
||||
tiles = {
|
||||
"testnodes_alpha"..alpha..".png",
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
use_texture_alpha = "blend",
|
||||
|
||||
groups = { dig_immediate = 3 },
|
||||
})
|
||||
|
||||
-- Transparency set via "alpha" parameter
|
||||
-- Transparency set via texture modifier
|
||||
minetest.register_node("testnodes:alpha_"..alpha, {
|
||||
description = S("Alpha Test Node (@1)", alpha),
|
||||
-- It seems that only the liquid drawtype supports the alpha parameter
|
||||
drawtype = "liquid",
|
||||
drawtype = "glasslike",
|
||||
paramtype = "light",
|
||||
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 },
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -121,7 +121,11 @@ end
|
||||
-- Sneak+punch: Select pathfinding algorithm
|
||||
-- Place: Select destination node
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = find_path_or_set_algorithm,
|
||||
|
@ -5,13 +5,13 @@ dofile(minetest.get_modpath("testtools") .. "/light.lua")
|
||||
|
||||
-- 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", {
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -49,7 +49,11 @@ minetest.register_tool("testtools:param2tool", {
|
||||
})
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -127,7 +131,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
end)
|
||||
|
||||
minetest.register_tool("testtools:remover", {
|
||||
description = S("Remover"),
|
||||
description = S("Remover") .."\n"..
|
||||
S("Punch: Remove pointed node or object"),
|
||||
inventory_image = "testtools_remover.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -138,13 +143,17 @@ minetest.register_tool("testtools:remover", {
|
||||
local obj = pointed_thing.ref
|
||||
if not obj:is_player() then
|
||||
obj:remove()
|
||||
else
|
||||
minetest.chat_send_player(user:get_player_name(), S("Can't remove players!"))
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
@ -193,7 +202,11 @@ minetest.register_tool("testtools:falling_node_tool", {
|
||||
})
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -246,7 +259,12 @@ local mover_config = function(itemstack, user, pointed_thing)
|
||||
end
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = mover_config,
|
||||
@ -289,7 +307,10 @@ minetest.register_tool("testtools:object_mover", {
|
||||
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -344,14 +365,21 @@ local function get_entity_list()
|
||||
return entity_list
|
||||
end
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
local name = user:get_player_name()
|
||||
if selections[name] and pointed_thing.type == "node" then
|
||||
if pointed_thing.type == "node" then
|
||||
if selections[name] then
|
||||
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,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -437,7 +465,10 @@ local editor_formspec = function(playername, obj, value, sel)
|
||||
end
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@ -517,7 +548,14 @@ local attacher_config = function(itemstack, user, pointed_thing)
|
||||
end
|
||||
|
||||
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",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = attacher_config,
|
||||
|
@ -5,10 +5,12 @@ dofile(modpath .. "/random.lua")
|
||||
dofile(modpath .. "/player.lua")
|
||||
dofile(modpath .. "/crafting_prepare.lua")
|
||||
dofile(modpath .. "/crafting.lua")
|
||||
dofile(modpath .. "/itemdescription.lua")
|
||||
|
||||
if minetest.settings:get_bool("devtest_unittests_autostart", false) then
|
||||
unittests.test_random()
|
||||
unittests.test_crafting()
|
||||
unittests.test_short_desc()
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
unittests.test_player(player)
|
||||
end)
|
||||
|
51
games/devtest/mods/unittests/itemdescription.lua
Normal 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
|
@ -1,2 +1,3 @@
|
||||
name = unittests
|
||||
description = Adds automated unit tests for the engine
|
||||
depends = basenodes
|
||||
|
BIN
games/devtest/mods/unittests/textures/default_dirt.png
Normal file
After Width: | Height: | Size: 790 B |
@ -36,8 +36,12 @@ minetest.register_chatcommand("hp", {
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("zoom", {
|
||||
params = "[<zoom_fov>]",
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
player:set_properties({zoom_fov = 15})
|
||||
end)
|
||||
|
||||
minetest.register_chatcommand("zoomfov", {
|
||||
params = "[<FOV>]",
|
||||
description = "Set or display your zoom_fov",
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
@ -58,8 +62,6 @@ minetest.register_chatcommand("zoom", {
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
|
||||
local s_infplace = minetest.settings:get("devtest_infplace")
|
||||
if s_infplace == "true" then
|
||||
infplace = true
|
||||
@ -112,6 +114,62 @@ minetest.register_chatcommand("detach", {
|
||||
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
|
||||
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack)
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
cd ..
|
||||
git clone https://github.com/open-source-parsers/jsoncpp -b 1.8.3 --depth 1
|
||||
git clone https://github.com/open-source-parsers/jsoncpp -b 1.9.4 --depth 1
|
||||
cd jsoncpp
|
||||
python amalgamate.py
|
||||
cp -R dist/json ../json
|
||||
|