1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Merge branch 'master' into master

This commit is contained in:
DustyBagel 2025-05-24 11:19:16 -05:00 committed by GitHub
commit 80c0410442
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
769 changed files with 137450 additions and 85683 deletions

View file

@ -1,4 +1,4 @@
Checks: '-*,modernize-use-emplace,modernize-avoid-bind,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,performance-*'
Checks: '-*,modernize-use-emplace,modernize-avoid-bind,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,performance-*,-performance-avoid-endl,performance-inefficient-string-concatenation'
WarningsAsErrors: '-*,modernize-use-emplace,performance-type-promotion-in-math-fn,performance-faster-string-find,performance-implicit-cast-in-loop'
CheckOptions:
- key: performance-unnecessary-value-param.AllowedTypes

View file

@ -14,7 +14,7 @@ Contributions are welcome! Here's how you can help:
[clone](https://help.github.com/articles/cloning-a-repository/) your fork.
2. Before you start coding, consider opening an
[issue at Github](https://github.com/minetest/minetest/issues) to discuss the
[issue on Github](https://github.com/luanti-org/luanti/issues) to discuss the
suitability and implementation of your intended contribution with the core
developers.
@ -25,16 +25,16 @@ Contributions are welcome! Here's how you can help:
the work, to avoid disappointment.
You may also benefit from discussing on our IRC development channel
[#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client
[#luanti-dev](http://www.luanti.org/irc/). Note that a proper IRC client
is required to speak on this channel.
3. Start coding!
- Refer to the
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.md),
[Developer Wiki](http://dev.minetest.net/Main_Page) and other
[documentation](https://github.com/minetest/minetest/tree/master/doc).
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and
[Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
[Lua API](https://github.com/luanti-org/luanti/blob/master/doc/lua_api.md),
[Developer Wiki](https://dev.luanti.org/) and other
[documentation](https://github.com/luanti-org/luanti/tree/master/doc).
- Follow the [C/C++](https://dev.luanti.org/Code_style_guidelines) and
[Lua](https://dev.luanti.org/Lua_code_style_guidelines) code style guidelines.
- Check your code works as expected and document any changes to the Lua API.
- To avoid conflicting changes between contributions, do not do the following manually. They will be done before each release.
- Run `updatepo.sh` or update `luanti.po{,t}` even if your code adds new translatable strings.
@ -53,7 +53,7 @@ Contributions are welcome! Here's how you can help:
- The following lines should describe the commit, starting a new line for each point.
5. Once you are happy with your changes, submit a pull request.
- Open the [pull-request form](https://github.com/minetest/minetest/pull/new/master).
- Open the [pull-request form](https://github.com/luanti-org/luanti/pull/new/master).
- Add a description explaining what you've done (or if it's a
work-in-progress - what you need to do).
- Make sure to fill out the pull request template.
@ -64,8 +64,8 @@ Contributions are welcome! Here's how you can help:
picture of the project.
2. It works.
3. It follows the code style for
[C/C++](http://dev.minetest.net/Code_style_guidelines) or
[Lua](http://dev.minetest.net/Lua_code_style_guidelines).
[C/C++](https://dev.luanti.org/Code_style_guidelines) or
[Lua](https://dev.luanti.org/Lua_code_style_guidelines).
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.
@ -76,9 +76,9 @@ If you experience an issue, we would like to know the details - especially when
a stable release is on the way.
1. Do a quick search on GitHub to check if the issue has already been reported.
2. Is it an issue with the Minetest *engine*? If not, report it
[elsewhere](http://www.minetest.net/development/#reporting-issues).
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe
2. Is it an issue with the Luanti *engine*? If not, report it
[elsewhere](http://www.luanti.org/development/#reporting-issues).
3. [Open an issue](https://github.com/luanti-org/luanti/issues/new) and describe
the issue you are having - you could include:
- Error logs (check the bottom of the `debug.txt` file).
- Screenshots.
@ -106,21 +106,21 @@ the project page with a list of current languages
Builtin (the component which contains things like server messages, chat command
descriptions, privilege descriptions) is translated separately; it needs to be
translated by editing a `.tr` text file. See
[Translation](https://dev.minetest.net/Translation) for more information.
[Translation](https://dev.luanti.org/Translation) for more information.
## Donations
If you'd like to monetarily support Luanti development, you can find donation
methods on [our website](http://www.minetest.net/development/#donate).
methods on [our website](http://www.luanti.org/development/#donate).
# Maintaining
* This is a concise version of the
[Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
[Rules & Guidelines](https://dev.luanti.org/engine-dev-process/) on the developer wiki.*
These notes are for those who have push access Luanti (core developers / maintainers).
- See the [project organisation](http://dev.minetest.net/Organisation) for the people involved.
- See the [project organisation](https://dev.luanti.org/Organisation) for the people involved.
## Concept approvals and roadmaps
@ -159,14 +159,14 @@ Submit a :+1: (+1) or "Looks good" comment to show you believe the pull-request
- The title should follow the commit guidelines (title starts with a capital letter, present tense, descriptive).
- Don't modify history older than 10 minutes.
- Use rebase, not merge to get linear history:
- `curl https://github.com/minetest/minetest/pull/1.patch | git am`
- `curl -Ls https://github.com/luanti-org/luanti/pull/1.patch | git am`
## Reviewing issues and feature requests
- If an issue does not get a response from its author within 1 month (when requiring more details), it can be closed.
- When an issue is a duplicate, refer to the first ones and close the later ones.
- Tag issues with the appropriate [labels](https://github.com/minetest/minetest/labels) for devices, platforms etc.
- Tag issues with the appropriate [labels](https://github.com/luanti-org/luanti/labels) for devices, platforms etc.
## Releasing a new version
*Refer to [dev.minetest.net/Releasing_Luanti](https://dev.minetest.net/Releasing_Luanti)*
*Refer to [dev.luanti.org/Releasing_Luanti](https://dev.luanti.org/Releasing_Luanti)*

View file

@ -9,7 +9,7 @@ body:
1. **Please update Luanti to the latest stable or dev version** before submitting bug reports. Make sure the bug is still reproducible on the latest version.
2. This page is for reporting the bugs of **the engine itself**. For bugs in a particular game, please [search for the game in the ContentDB](https://content.luanti.org/packages/?type=game) and submit a bug report in their issue trackers.
* For example, you can submit issues about the Minetest Game [in its own repository](https://github.com/minetest/minetest_game/issues).
* For example, you can submit issues about the Minetest Game [in its own repository](https://github.com/luanti-org/minetest_game/issues).
3. Please provide as many details as possible for us to spot the problem quicker.
- type: textarea
attributes:

View file

@ -1,7 +1,7 @@
blank_issues_enabled: true
contact_links:
- name: Submit issues about Minetest Game
url: https://github.com/minetest/minetest_game/issues/new/choose
url: https://github.com/luanti-org/minetest_game/issues/new
about: Only submit issues of the engine in this repository's issue tracker. Submit those of Minetest Game in its own issue tracker.
- name: Search for issue trackers of third-party games
url: https://content.luanti.org/packages/?type=game

View file

@ -3,7 +3,7 @@ Add compact, short information about your PR for easier understanding:
- Goal of the PR
- How does the PR work?
- Does it resolve any reported issue?
- Does this relate to a goal in [the roadmap](https://github.com/minetest/minetest/blob/master/doc/direction.md)?
- Does this relate to a goal in [the roadmap](https://github.com/luanti-org/luanti/blob/master/doc/direction.md)?
- If not a bug fix, why is this PR needed? What usecases does it solve?
## To do

4
.github/SECURITY.md vendored
View file

@ -3,7 +3,7 @@
## Supported Versions
We only support the latest stable version for security issues.
See the [releases page](https://github.com/minetest/minetest/releases).
See the [releases page](https://github.com/luanti-org/luanti/releases).
## Reporting a Vulnerability
@ -11,7 +11,7 @@ We ask that you report vulnerabilities privately, by contacting a core developer
to give us time to fix them. You can do that by emailing one of the following addresses:
* celeron55@gmail.com
* rubenwardy@minetest.net
* rw@rubenwardy.com
Depending on severity, we will either create a private issue for the vulnerability
and release a patch version of Luanti, or give you permission to file the issue publicly.

View file

@ -34,28 +34,32 @@ env:
jobs:
# Older gcc version (should be close to our minimum supported version)
gcc_7:
runs-on: ubuntu-20.04
gcc_9:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps g++-7
install_linux_deps g++-9
- name: Build
run: |
./util/ci/build.sh
env:
CC: gcc-7
CXX: g++-7
# Test fallback SHA implementations
CMAKE_FLAGS: '-DENABLE_OPENSSL=0'
CC: gcc-9
CXX: g++-9
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
- name: Test
- name: Unittest
run: |
./bin/luanti --run-unittests
# Do this here because we have ASan and error paths are sensitive to dangling pointers
- name: Test error cases
run: |
./util/test_error_cases.sh
# Current gcc version
gcc_14:
runs-on: ubuntu-24.04
@ -81,32 +85,28 @@ jobs:
../bin/luanti --run-unittests
# Older clang version (should be close to our minimum supported version)
clang_7:
runs-on: ubuntu-20.04
clang_11:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps clang-7 llvm-7
install_linux_deps clang-11
- name: Build
run: |
./util/ci/build.sh
env:
CC: clang-7
CXX: clang++-7
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
CC: clang-11
CXX: clang++-11
# Test fallback SHA implementations
CMAKE_FLAGS: '-DENABLE_OPENSSL=0'
- name: Unittest
- name: Test
run: |
./bin/luanti --run-unittests
# Do this here because we have ASan and error paths are sensitive to dangling pointers
- name: Test error cases
run: |
./util/test_error_cases.sh
# Current clang version
clang_18:
runs-on: ubuntu-24.04

View file

@ -16,7 +16,7 @@ on:
jobs:
build:
if: github.repository == 'minetest/minetest'
if: github.repository == 'luanti-org/luanti'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

View file

@ -45,7 +45,7 @@ jobs:
mkdir build
cd build
cmake .. \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
-DCMAKE_OSX_DEPLOYMENT_TARGET=13 \
-DCMAKE_FIND_FRAMEWORK=LAST \
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE \

26
.github/workflows/png_file_checks.yml vendored Normal file
View file

@ -0,0 +1,26 @@
name: png_file_checks
# Check whether all png files are in a valid format
on:
push:
paths:
- '**.png'
- '.github/workflows/**.yml'
pull_request:
paths:
- '**.png'
- '.github/workflows/**.yml'
jobs:
png_optimized:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
sudo apt-get update
sudo apt install -y optipng
- name: Check whether all png files are optimized
run: |
./util/ci/check_png_optimized.sh

View file

@ -14,6 +14,7 @@ on:
- '**.sh'
- '**.cmake'
- '**.glsl'
- '**.lua'
pull_request:
paths:
- '**.txt'
@ -24,6 +25,7 @@ on:
- '**.sh'
- '**.cmake'
- '**.glsl'
- '**.lua'
jobs:
trailing_whitespaces:
@ -32,7 +34,72 @@ jobs:
- uses: actions/checkout@v4
# Line endings are already ensured by .gitattributes
- name: Check trailing whitespaces
run: if git ls-files | grep -E '\.txt$|\.md$|\.[ch]$|\.cpp$|\.hpp$|\.sh$|\.cmake$|\.glsl$' | xargs grep -n '\s$'; then echo -e "\033[0;31mFound trailing whitespace"; (exit 1); else (exit 0); fi
run: |
if git ls-files |\
grep -E '\.txt$|\.md$|\.[ch]$|\.cpp$|\.hpp$|\.sh$|\.cmake$|\.glsl$' |\
xargs grep -n '\s$';\
then\
echo -e "\033[0;31mFound trailing whitespace";\
(exit 1);\
else\
(exit 0);\
fi
indent_spaces:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Line endings are already ensured by .gitattributes
# Multiple multline comments in one line is not supported by this check
# and there is no reason to use them
# So lines like: "/* */ /*" or "*/ a = 5; /*" will result in error
- name: Check for unsupported multiline comments
run: |
if git ls-files |\
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$' |\
xargs grep -n '\*/.*/\*';\
then
echo -e "\033[0;31mUnsupported combination of multiline comments. New multiline comment should begin on new line.";\
(exit 1);\
else\
(exit 0);\
fi
if git ls-files |\
grep -E '\.lua$' |\
xargs grep -n -e '\]\].*--\[\[';\
then
echo -e "\033[0;31mUnsupported combination of multiline comments. New multiline comment should begin on new line.";\
(exit 1);\
else\
(exit 0);\
fi
# This prepare files for final check
# See python script ./util/ci/indent_tab_preprocess.py for details.
- name: Preprocess files
run: |
git ls-files |\
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$' |\
xargs -L 1 -P $(($(nproc) + 1)) \
python3 ./util/ci/indent_tab_preprocess.py "/*" "*/"
git ls-files |\
grep -E '\.lua$' |\
xargs -L 1 -P $(($(nproc) + 1)) \
python3 ./util/ci/indent_tab_preprocess.py "--[[" "]]"
# Check for bad indent.
# This runs over preprocessed files.
# If there is any remaining space on line beginning or after tab,
# error is generated
- name: Check indent spaces
run: |
if git ls-files |\
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$|\.lua$' |\
xargs grep -n -P '^\t*[ ]';\
then\
echo -e "\033[0;31mFound incorrect indent whitespaces";\
(exit 1);\
else\
(exit 0);\
fi
tabs_lua_api_files:
runs-on: ubuntu-latest
@ -40,6 +107,12 @@ jobs:
- uses: actions/checkout@v4
# Some files should not contain tabs
- name: Check tabs in Lua API files
run: if grep -n $'\t' doc/lua_api.md doc/client_lua_api.md; then echo -e "\033[0;31mFound tab in markdown file"; (exit 1); else (exit 0); fi
run: |
if grep -n $'\t' doc/lua_api.md doc/client_lua_api.md;\
then\
echo -e "\033[0;31mFound tab in markdown file";\
(exit 1);\
else\
(exit 0);\
fi

View file

@ -71,9 +71,7 @@ jobs:
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
VCPKG_VERSION: 01f602195983451bc83e72f4214af2cbc495aa94
# 2024.05.24
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
VCPKG_DEFAULT_TRIPLET: ${{matrix.config.vcpkg_triplet}}
strategy:
fail-fast: false
matrix:
@ -97,19 +95,17 @@ jobs:
- uses: actions/checkout@v4
- name: Restore from cache and run vcpkg
uses: lukka/run-vcpkg@v7
uses: lukka/run-vcpkg@v11
with:
vcpkgArguments: ${{env.vcpkg_packages}}
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
- name: CMake
# Note: See #15976 for why CMAKE_POLICY_VERSION_MINIMUM=3.5 is set.
run: |
cmake ${{matrix.config.generator}} `
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 `
-DENABLE_POSTGRESQL=OFF `
-DENABLE_LUAJIT=TRUE `
-DREQUIRE_LUAJIT=TRUE `

6
.gitignore vendored
View file

@ -49,13 +49,13 @@ AppDir
# Nix
/result
## Files related to Minetest development cycle
## Files related to Luanti development cycle
/*.patch
*.diff
# GNU Patch reject file
*.rej
## Non-static Minetest directories or symlinks to these
## Non-static Luanti directories or symlinks to these
/bin/
/games/*
!/games/devtest/
@ -80,7 +80,7 @@ minetest.conf
debug.txt
debug.txt.1
## Other files generated by Minetest
## Other files generated by Luanti
screenshot_*.png
testbm.txt

View file

@ -10,6 +10,7 @@ ignore = {
read_globals = {
"ItemStack",
"INIT",
"PLATFORM",
"DIR_DELIM",
"dump", "dump2",
"fgettext", "fgettext_ne",
@ -18,10 +19,10 @@ read_globals = {
"VoxelManip",
"profiler",
"Settings",
"PerlinNoise", "PerlinNoiseMap",
"ValueNoise", "ValueNoiseMap",
string = {fields = {"split", "trim"}},
table = {fields = {"copy", "getn", "indexof", "keyof", "insert_all"}},
table = {fields = {"copy", "copy_with_metatables", "getn", "indexof", "keyof", "insert_all"}},
math = {fields = {"hypot", "round"}},
}
@ -32,6 +33,13 @@ globals = {
"_",
}
stds.menu_common = {
globals = {
"mt_color_grey", "mt_color_blue", "mt_color_lightblue", "mt_color_green",
"mt_color_dark_green", "mt_color_orange", "mt_color_red",
},
}
files["builtin/client/register.lua"] = {
globals = {
debug = {fields={"getinfo"}},
@ -72,13 +80,14 @@ files["builtin/common/filterlist.lua"] = {
}
files["builtin/mainmenu"] = {
std = "+menu_common",
globals = {
"gamedata",
},
}
read_globals = {
"PLATFORM",
},
files["builtin/common/settings"] = {
std = "+menu_common",
}
files["builtin/common/tests"] = {

View file

@ -1,4 +1,7 @@
cmake_minimum_required(VERSION 3.12)
if(POLICY CMP0177)
cmake_policy(SET CMP0177 NEW)
endif()
# This can be read from ${PROJECT_NAME} after project() is called
project(luanti)
@ -11,7 +14,7 @@ set(CLANG_MINIMUM_VERSION "7.0.1")
# You should not need to edit these manually, use util/bump_version.sh
set(VERSION_MAJOR 5)
set(VERSION_MINOR 11)
set(VERSION_MINOR 13)
set(VERSION_PATCH 0)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
@ -48,7 +51,7 @@ endif()
# - win32/gcc: fails to link
# - win32/clang: works
# - macOS on x86: seems to be fine
# - macOS on ARM: crashes, see <https://github.com/minetest/minetest/issues/14397>
# - macOS on ARM: crashes, see <https://github.com/luanti-org/luanti/issues/14397>
# Note: since CMake has no easy architecture detection disabling for Mac entirely
#### ####
if((WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR APPLE)
@ -262,8 +265,8 @@ install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
if(UNIX AND NOT APPLE)
install(FILES "doc/luanti.6" "doc/luantiserver.6" DESTINATION "${MANDIR}/man6")
install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}")
install(FILES "misc/net.minetest.minetest.metainfo.xml" DESTINATION "${METAINFODIR}")
install(FILES "misc/org.luanti.luanti.desktop" DESTINATION "${XDG_APPS_DIR}")
install(FILES "misc/org.luanti.luanti.metainfo.xml" DESTINATION "${METAINFODIR}")
install(FILES "misc/luanti.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
install(FILES "misc/luanti-xorg-icon-128.png"
DESTINATION "${ICONDIR}/hicolor/128x128/apps"

View file

@ -14,6 +14,9 @@ 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/
Textures by cx384 are under CC BY-SA 4.0
https://creativecommons.org/licenses/by-sa/4.0/
Media files by DS are under CC BY-SA 4.0
https://creativecommons.org/licenses/by-sa/4.0/
@ -64,7 +67,13 @@ Zughy:
textures/base/pack/settings_info.png
textures/base/pack/settings_reset.png
textures/base/pack/server_url.png
textures/base/pack/server_url_unavailable.png
textures/base/pack/server_view_clients.png
textures/base/pack/server_view_clients_unavailable.png
cx384:
textures/base/pack/server_view_mods.png
textures/base/pack/server_view_mods_unavailable.png
appgurueu:
textures/base/pack/server_incompatible.png
@ -93,6 +102,14 @@ grorp:
using the font "undefined medium" (https://undefined-medium.com/),
which is licensed under the SIL Open Font License, Version 1.1
modified by DS
textures/base/pack/dig_btn.png
textures/base/pack/place_btn.png
derived by editing the text in aux1_btn.svg
Material Design, Google (Apache license v2.0):
textures/base/pack/contentdb_thumb_up.png
textures/base/pack/contentdb_thumb_down.png
textures/base/pack/contentdb_neutral.png
License of Luanti source code
-------------------------------

View file

@ -1,13 +1,15 @@
Luanti (formerly Minetest)
==========================
![Build Status](https://github.com/minetest/minetest/workflows/build/badge.svg)
[![Translation status](https://hosted.weblate.org/widgets/minetest/-/svg-badge.svg)](https://hosted.weblate.org/engage/minetest/?utm_source=widget)
[![License](https://img.shields.io/badge/license-LGPLv2.1%2B-blue.svg)](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)
<div align="center">
<img src="textures/base/pack/logo.png" width="32%">
<h1>Luanti (formerly Minetest)</h1>
<img src="https://github.com/luanti-org/luanti/workflows/build/badge.svg" alt="Build Status">
<a href="https://hosted.weblate.org/engage/minetest/?utm_source=widget"><img src="https://hosted.weblate.org/widgets/minetest/-/svg-badge.svg" alt="Translation status"></a>
<a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html"><img src="https://img.shields.io/badge/license-LGPLv2.1%2B-blue.svg" alt="License"></a>
</div>
<br>
Luanti is a free open-source voxel game engine with easy modding and game creation.
Copyright (C) 2010-2024 Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2025 Perttu Ahola <celeron55@gmail.com>
and contributors (see source file comments and the version control log)
Table of Contents
@ -28,7 +30,7 @@ Further documentation
- Website: https://www.luanti.org/
- Wiki: https://wiki.luanti.org/
- Forum: https://forum.luanti.org/
- GitHub: https://github.com/minetest/minetest/
- GitHub: https://github.com/luanti-org/luanti/
- [Developer documentation](doc/developing/)
- [doc/](doc/) directory of source distribution
@ -55,6 +57,7 @@ Some can be changed in the key config dialog in the settings tab.
| T | Chat |
| / | Command |
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
| Shift + Esc | Exit directly to main menu from anywhere, bypassing pause menu |
| + | Increase view range |
| - | Decrease view range |
| K | Enable/disable fly mode (needs fly privilege) |

View file

@ -1,5 +1,4 @@
diff --git a/android/app/src/main/java/org/libsdl/app/SDLActivity.java b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
index fd5a056e3..83e3cf657 100644
--- a/android/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -1345,7 +1345,12 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
@ -9,7 +8,7 @@ index fd5a056e3..83e3cf657 100644
- if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
+ if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ||
+ /*
+ * CUSTOM ADDITION FOR MINETEST
+ * CUSTOM ADDITION FOR LUANTI
+ * should be upstreamed
+ */
+ (source & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE) {

View file

@ -22,14 +22,19 @@ package net.minetest.minetest;
import org.libsdl.app.SDLActivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
@ -91,6 +96,9 @@ public class GameActivity extends SDLActivity {
saveSettings();
}
private NotificationManager mNotifyManager;
private boolean gameNotificationShown = false;
public void showTextInputDialog(String hint, String current, int editType) {
runOnUiThread(() -> showTextInputDialogUI(hint, current, editType));
}
@ -263,4 +271,67 @@ public class GameActivity extends SDLActivity {
public boolean hasPhysicalKeyboard() {
return getContext().getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
}
// TODO: share code with UnzipService.createNotification
private void updateGameNotification() {
if (mNotifyManager == null) {
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
if (!gameNotificationShown) {
mNotifyManager.cancel(MainActivity.NOTIFICATION_ID_GAME);
return;
}
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
} else {
builder = new Notification.Builder(this);
}
Intent notificationIntent = new Intent(this, GameActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
int pendingIntentFlag = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
pendingIntentFlag = PendingIntent.FLAG_MUTABLE;
}
PendingIntent intent = PendingIntent.getActivity(this, 0,
notificationIntent, pendingIntentFlag);
builder.setContentTitle(getString(R.string.game_notification_title))
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(intent)
.setOngoing(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// This avoids a stuck notification if the app is killed while
// in-game: (1) if the user closes the app from the "Recents" screen
// or (2) if the system kills the app while it is in background.
// onStop is called too early to remove the notification and
// onDestroy is often not called at all, so there's this hack instead.
builder.setTimeoutAfter(16000);
// Replace the notification just before it expires as long as the app is
// running (and we're still in-game).
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (gameNotificationShown) {
updateGameNotification();
}
}
}, 15000);
}
mNotifyManager.notify(MainActivity.NOTIFICATION_ID_GAME, builder.build());
}
public void setPlayingNowNotification(boolean show) {
gameNotificationShown = show;
updateGameNotification();
}
}

View file

@ -43,6 +43,8 @@ import static net.minetest.minetest.UnzipService.*;
public class MainActivity extends AppCompatActivity {
public static final String NOTIFICATION_CHANNEL_ID = "Minetest channel";
public static final int NOTIFICATION_ID_UNZIP = 1;
public static final int NOTIFICATION_ID_GAME = 2;
private final static int versionCode = BuildConfig.VERSION_CODE;
private static final String SETTINGS = "MinetestSettings";

View file

@ -51,7 +51,6 @@ public class UnzipService extends IntentService {
public static final int SUCCESS = -1;
public static final int FAILURE = -2;
public static final int INDETERMINATE = -3;
private final int id = 1;
private NotificationManager mNotifyManager;
private boolean isSuccess = true;
private String failureMessage;
@ -100,11 +99,14 @@ public class UnzipService extends IntentService {
}
}
// TODO: share code with GameActivity.updateGameNotification
@NonNull
private Notification.Builder createNotification() {
Notification.Builder builder;
if (mNotifyManager == null)
if (mNotifyManager == null) {
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
} else {
@ -128,7 +130,7 @@ public class UnzipService extends IntentService {
.setOngoing(true)
.setProgress(0, 0, true);
mNotifyManager.notify(id, builder.build());
mNotifyManager.notify(MainActivity.NOTIFICATION_ID_UNZIP, builder.build());
return builder;
}
@ -200,14 +202,14 @@ public class UnzipService extends IntentService {
} else {
notificationBuilder.setProgress(100, progress, false);
}
mNotifyManager.notify(id, notificationBuilder.build());
mNotifyManager.notify(MainActivity.NOTIFICATION_ID_UNZIP, notificationBuilder.build());
}
}
@Override
public void onDestroy() {
super.onDestroy();
mNotifyManager.cancel(id);
mNotifyManager.cancel(MainActivity.NOTIFICATION_ID_UNZIP);
publishProgress(null, R.string.loading, isSuccess ? SUCCESS : FAILURE);
}
}

View file

@ -60,8 +60,8 @@ import java.util.Locale;
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
private static final String TAG = "SDL";
private static final int SDL_MAJOR_VERSION = 2;
private static final int SDL_MINOR_VERSION = 30;
private static final int SDL_MICRO_VERSION = 8;
private static final int SDL_MINOR_VERSION = 32;
private static final int SDL_MICRO_VERSION = 0;
/*
// Display InputType.SOURCE/CLASS of events and devices
//
@ -790,6 +790,9 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
SDLActivity.mFullscreenModeActive = false;
}
if (Build.VERSION.SDK_INT >= 28 /* Android 9 (Pie) */) {
window.getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
}
} else {
Log.e(TAG, "error handling message, getContext() returned no Activity");
@ -1347,7 +1350,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ||
/*
* CUSTOM ADDITION FOR MINETEST
* CUSTOM ADDITION FOR LUANTI
* should be upstreamed
*/
(source & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE) {

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="unzip_notification_title">O kargañ Luanti</string>
<string name="label">Luanti</string>
<string name="loading">O kargañ…</string>
<string name="notification_channel_description">Evezhiadennoù gant Luanti</string>
<string name="unzip_notification_description">Nebeutoc\'h eget ur vunutenn…</string>
<string name="ime_dialog_done">Graet</string>
<string name="no_web_browser">Merdeer web ebet bet kavet</string>
<string name="notification_channel_name">Evezhiadennoù hollek</string>
</resources>

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="label">Luanti</string>
<string name="loading">Lädt</string>
<string name="loading">Laden </string>
<string name="unzip_notification_title">Luanti lädt</string>
<string name="unzip_notification_description">Weniger als 1 Minute…</string>
<string name="unzip_notification_description">Weniger als 1 Minute </string>
<string name="ime_dialog_done">Fertig</string>
<string name="no_web_browser">Kein Web-Browser gefunden</string>
<string name="no_web_browser">Keinen Web-Browser gefunden</string>
<string name="notification_channel_name">Allgemeine Benachrichtigung</string>
<string name="notification_channel_description">Benachrichtigungen von Luanti</string>
<string name="game_notification_title">Luanti läuft</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="unzip_notification_description">کمتر از 1 دقیقه…</string>
<string name="loading">در حال بارگذاری…</string>
<string name="ime_dialog_done">انجام شد</string>
<string name="label">لوآنتی</string>
<string name="unzip_notification_title">در حال بارگذاری لوآنتی</string>
<string name="notification_channel_name">نوتیفیکیشن عمومی</string>
<string name="notification_channel_description">نوتیفیکیشن از لوآنتی</string>
<string name="no_web_browser">هیچ مرورگری یافت نشد</string>
</resources>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="label">Luanti</string>
<string name="loading">Chargement…</string>
<string name="notification_channel_name">Notification générale</string>
<string name="notification_channel_description">Notifications de Luanti</string>
<string name="unzip_notification_title">Chargement de Luanti</string>
<string name="unzip_notification_description">Moins d\'une minute…</string>
<string name="ime_dialog_done">Terminé</string>
<string name="no_web_browser">Aucun navigateur web trouvé</string>
<string name="game_notification_title">Luanti est en cours d\'exécution</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="label">Luanti</string>
<string name="loading">Cargando…</string>
<string name="notification_channel_name">Notificación xeral</string>
<string name="notification_channel_description">Notificacións de Luanti</string>
<string name="unzip_notification_title">Cargando Luanti</string>
<string name="unzip_notification_description">Menos de 1 minuto…</string>
<string name="ime_dialog_done">Feito</string>
<string name="no_web_browser">Non se atopou ningún navegador web</string>
</resources>

View file

@ -8,4 +8,5 @@
<string name="unzip_notification_description">Kurang dari 1 menit…</string>
<string name="notification_channel_description">Pemberitahuan dari Luanti</string>
<string name="unzip_notification_title">Memuat Luanti…</string>
<string name="game_notification_title">Luanti sedang berjalan</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="label">Luanti</string>
<string name="loading">Se încarcă…</string>
<string name="notification_channel_name">Notificare generală</string>
<string name="notification_channel_description">Notificări de la Luanti</string>
<string name="unzip_notification_title">Luanti pornește</string>
<string name="unzip_notification_description">Sub 1 minut…</string>
<string name="ime_dialog_done">Gata</string>
<string name="no_web_browser">Niciun navigator web găsit</string>
</resources>

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="unzip_notification_title">Загрузка Luanti</string>
<string name="unzip_notification_description">Меньше чам за 1 минуту</string>
<string name="unzip_notification_description">Менее 1 минуты</string>
<string name="ime_dialog_done">Готово</string>
<string name="label">Luаnti</string>
<string name="notification_channel_description">Уведомления от Luanti</string>
<string name="notification_channel_name">Основные уведомления</string>
<string name="loading">Загрузка…</string>
<string name="no_web_browser">Не найдено веб-браузера</string>
<string name="game_notification_title">Luanti запущено</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="no_web_browser">Ni bil najden spletni brskalnik</string>
<string name="notification_channel_name">Glavno obvestilo</string>
<string name="loading">Nalaganje …</string>
<string name="unzip_notification_description">Manj kot 1 minuta …</string>
<string name="label">Luanti</string>
<string name="notification_channel_description">Obvestilo od Luantia</string>
<string name="ime_dialog_done">Končano!l</string>
<string name="unzip_notification_title">Nalaganje Luantia</string>
</resources>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="label">Luanti</string>
<string name="loading">Laddar…</string>
<string name="unzip_notification_description">Mindre än 1 minut…</string>
<string name="ime_dialog_done">Färdig</string>
<string name="no_web_browser">Ingen webbläsare hittades</string>
<string name="notification_channel_name">Allmän notifikation</string>
<string name="notification_channel_description">Notifikationer från Luanti</string>
<string name="unzip_notification_title">Laddar Luanti</string>
<string name="game_notification_title">Luanti är igång</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="label">Luanti</string>
<string name="no_web_browser">Браузерів не знайдено</string>
<string name="loading">Завантаження…</string>
<string name="notification_channel_name">Загальні сповіщення</string>
<string name="unzip_notification_title">Luanti завантажується</string>
<string name="unzip_notification_description">Менше за 1 хвилину…</string>
<string name="ime_dialog_done">Готово</string>
<string name="notification_channel_description">Сповіщення від Luanti</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="label">Luanti</string>
<string name="loading">加载中…</string>
<string name="notification_channel_name">一般通知</string>
<string name="notification_channel_description">Luanti 的通知</string>
<string name="unzip_notification_title">加载 Luanti 中</string>
<string name="unzip_notification_description">不到1分钟…</string>
<string name="ime_dialog_done">完成</string>
<string name="no_web_browser">未找到网页浏览器</string>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="loading">載入中…</string>
<string name="notification_channel_name">一般通知</string>
<string name="notification_channel_description">Luanti 的通知</string>
<string name="unzip_notification_description">不到1分鐘…</string>
<string name="ime_dialog_done">完畢</string>
<string name="no_web_browser">未找到任何網頁瀏覽器</string>
<string name="label">Luanti</string>
<string name="unzip_notification_title">載入Luanti中</string>
</resources>

View file

@ -6,6 +6,7 @@
<string name="notification_channel_description">Notifications from Luanti</string>
<string name="unzip_notification_title">Loading Luanti</string>
<string name="unzip_notification_description">Less than 1 minute&#8230;</string>
<string name="game_notification_title">Luanti is running</string>
<string name="ime_dialog_done">Done</string>
<string name="no_web_browser">No web browser found</string>
</resources>

View file

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
project.ext.set("versionMajor", 5) // Version Major
project.ext.set("versionMinor", 11) // Version Minor
project.ext.set("versionMinor", 13) // Version Minor
project.ext.set("versionPatch", 0) // Version Patch
// ^ keep in sync with cmake

148
android/icons/dig_btn.svg Normal file
View file

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
inkscape:export-ydpi="24.000002"
inkscape:export-xdpi="24.000002"
inkscape:export-filename="../../textures/base/pack/dig_btn.png"
sodipodi:docname="dig_btn.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
id="svg8"
version="1.1"
viewBox="0 0 135.46666 135.46667"
height="512"
width="512"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:document-rotation="0"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-others="true"
inkscape:snap-object-midpoints="false"
inkscape:snap-to-guides="true"
inkscape:snap-bbox="true"
showguides="true"
inkscape:snap-page="true"
inkscape:snap-grids="false"
inkscape:pagecheckerboard="false"
inkscape:window-maximized="1"
inkscape:window-y="32"
inkscape:window-x="0"
inkscape:window-height="1011"
inkscape:window-width="1920"
units="px"
showgrid="true"
inkscape:current-layer="layer2"
inkscape:document-units="mm"
inkscape:cy="266.84627"
inkscape:cx="201.24514"
inkscape:zoom="1.4633894"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#404040"
id="base"
inkscape:showpageshadow="2"
inkscape:deskcolor="#d1d1d1">
<inkscape:grid
empopacity="0.25098039"
empcolor="#40ff40"
opacity="0.1254902"
color="#40ff40"
empspacing="4"
spacingy="0.26458333"
spacingx="0.26458333"
id="grid16"
type="xygrid"
originx="0"
originy="0"
units="px"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
style="display:inline"
inkscape:label="Layer 2"
id="layer2"
inkscape:groupmode="layer">
<path
inkscape:connector-curvature="0"
id="path7055"
d=""
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path7035"
d=""
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path7005"
d=""
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5127"
d=""
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
transform="scale(1.0078883,0.99217343)"
id="text4716"
y="85.59491"
x="67.78315"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:48.4785px;line-height:1.25;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
y="85.59491"
x="67.78315"
id="tspan4714"
sodipodi:role="line">LMB</tspan></text>
<flowRoot
transform="scale(0.26458333)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
id="flowRoot4718"
xml:space="preserve"><flowRegion
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
id="flowRegion4720"><rect
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
y="124.10143"
x="264.65997"
height="136.37059"
width="157.5838"
id="rect4722" /></flowRegion><flowPara
id="flowPara4724" /></flowRoot>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

148
android/icons/place_btn.svg Normal file
View file

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
inkscape:export-ydpi="24.000002"
inkscape:export-xdpi="24.000002"
inkscape:export-filename="../../textures/base/pack/place_btn.png"
sodipodi:docname="place_btn.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
id="svg8"
version="1.1"
viewBox="0 0 135.46666 135.46667"
height="512"
width="512"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:document-rotation="0"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-others="true"
inkscape:snap-object-midpoints="false"
inkscape:snap-to-guides="true"
inkscape:snap-bbox="true"
showguides="true"
inkscape:snap-page="true"
inkscape:snap-grids="false"
inkscape:pagecheckerboard="false"
inkscape:window-maximized="1"
inkscape:window-y="32"
inkscape:window-x="0"
inkscape:window-height="1011"
inkscape:window-width="1920"
units="px"
showgrid="true"
inkscape:current-layer="layer2"
inkscape:document-units="mm"
inkscape:cy="266.84627"
inkscape:cx="201.24514"
inkscape:zoom="1.4633894"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#404040"
id="base"
inkscape:showpageshadow="2"
inkscape:deskcolor="#d1d1d1">
<inkscape:grid
empopacity="0.25098039"
empcolor="#40ff40"
opacity="0.1254902"
color="#40ff40"
empspacing="4"
spacingy="0.26458333"
spacingx="0.26458333"
id="grid16"
type="xygrid"
originx="0"
originy="0"
units="px"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
style="display:inline"
inkscape:label="Layer 2"
id="layer2"
inkscape:groupmode="layer">
<path
inkscape:connector-curvature="0"
id="path7055"
d=""
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path7035"
d=""
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path7005"
d=""
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5127"
d=""
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
transform="scale(1.0078883,0.99217343)"
id="text4716"
y="85.59491"
x="67.78315"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:48.4785px;line-height:1.25;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
y="85.59491"
x="67.78315"
id="tspan4714"
sodipodi:role="line">RMB</tspan></text>
<flowRoot
transform="scale(0.26458333)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
id="flowRoot4718"
xml:space="preserve"><flowRegion
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
id="flowRegion4720"><rect
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
y="124.10143"
x="264.65997"
height="136.37059"
width="157.5838"
id="rect4722" /></flowRegion><flowPara
id="flowPara4724" /></flowRoot>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -52,7 +52,7 @@ if (new File(depsDir, 'armeabi-v7a').exists()) {
task downloadDeps(type: Download) {
def depsZip = new File(buildDir, 'deps.zip')
src 'https://github.com/minetest/minetest_android_deps/releases/download/latest/deps-lite.zip'
src 'https://github.com/luanti-org/luanti_android_deps/releases/download/latest/deps-lite.zip'
dest depsZip
overwrite false

View file

@ -32,8 +32,8 @@ do
all.registered_craftitems = {}
all.registered_tools = {}
for k, v in pairs(all.registered_items) do
-- Disable further modification
setmetatable(v, {__newindex = {}})
-- Ignore new keys
setmetatable(v, {__newindex = function() end})
-- Reassemble the other tables
if v.type == "node" then
getmetatable(v).__index = all.nodedef_default
@ -59,6 +59,9 @@ end
local alias_metatable = {
__index = function(t, name)
return rawget(t, core.registered_aliases[name])
end,
__newindex = function()
error("table is read-only")
end
}
setmetatable(core.registered_items, alias_metatable)

View file

@ -1,5 +1,6 @@
core.log("info", "Initializing asynchronous environment")
function core.job_processor(func, serialized_param)
local param = core.deserialize(serialized_param)
@ -7,3 +8,15 @@ function core.job_processor(func, serialized_param)
return retval or core.serialize(nil)
end
function core.get_http_accept_languages()
local languages
local current_language = core.get_language()
if current_language ~= "" then
languages = { current_language, "en;q=0.8" }
else
languages = { "en" }
end
return "Accept-Language: " .. table.concat(languages, ", ")
end

View file

@ -2,7 +2,10 @@ local scriptpath = core.get_builtin_path()
local clientpath = scriptpath.."client"..DIR_DELIM
local commonpath = scriptpath.."common"..DIR_DELIM
dofile(clientpath .. "register.lua")
local builtin_shared = {}
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
assert(loadfile(clientpath .. "register.lua"))(builtin_shared)
dofile(commonpath .. "after.lua")
dofile(commonpath .. "mod_storage.lua")
dofile(commonpath .. "chatcommands.lua")

View file

@ -1,68 +1,6 @@
core.callback_origins = {}
local builtin_shared = ...
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
if cb_len == 0 then
if mode == 2 or mode == 3 then
return true
elseif mode == 4 or mode == 5 then
return false
end
end
local ret
for i = 1, cb_len do
local cb_ret = callbacks[i](...)
if mode == 0 and i == 1 or mode == 1 and i == cb_len then
ret = cb_ret
elseif mode == 2 then
if not cb_ret or i == 1 then
ret = cb_ret
end
elseif mode == 3 then
if cb_ret then
return cb_ret
end
ret = cb_ret
elseif mode == 4 then
if (cb_ret and not ret) or i == 1 then
ret = cb_ret
end
elseif mode == 5 and cb_ret then
return cb_ret
end
end
return ret
end
--
-- Callback registration
--
local function make_registration()
local t = {}
local registerfunc = function(func)
t[#t + 1] = func
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
name = getinfo(1, "n").name or "??"
}
--local origin = core.callback_origins[func]
--print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
end
return t, registerfunc
end
local make_registration = builtin_shared.make_registration
core.registered_globalsteps, core.register_globalstep = make_registration()
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2013 sapier
--
--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.
-- Luanti
-- Copyright (C) 2013 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
-- TODO improve doc --

View file

@ -90,7 +90,7 @@ local facedir_to_dir_map = {
1, 4, 3, 2,
}
function core.facedir_to_dir(facedir)
return facedir_to_dir[facedir_to_dir_map[facedir % 32]]
return vector.copy(facedir_to_dir[facedir_to_dir_map[facedir % 32]])
end
function core.dir_to_fourdir(dir)
@ -110,7 +110,7 @@ function core.dir_to_fourdir(dir)
end
function core.fourdir_to_dir(fourdir)
return facedir_to_dir[facedir_to_dir_map[fourdir % 4]]
return vector.copy(facedir_to_dir[facedir_to_dir_map[fourdir % 4]])
end
function core.dir_to_wallmounted(dir)
@ -147,7 +147,7 @@ local wallmounted_to_dir = {
vector.new( 0, -1, 0),
}
function core.wallmounted_to_dir(wallmounted)
return wallmounted_to_dir[wallmounted % 8]
return vector.copy(wallmounted_to_dir[wallmounted % 8])
end
function core.dir_to_yaw(dir)

15
builtin/common/menu.lua Normal file
View file

@ -0,0 +1,15 @@
-- Luanti
-- SPDX-License-Identifier: LGPL-2.1-or-later
-- These colors are used by the main menu and the settings menu
mt_color_grey = "#AAAAAA"
mt_color_blue = "#6389FF"
mt_color_lightblue = "#99CCFF"
mt_color_green = "#72FF63"
mt_color_dark_green = "#25C191"
mt_color_orange = "#FF8800"
mt_color_red = "#FF3300"
function core.are_keycodes_equal(k1, k2)
return core.normalize_keycode(k1) == core.normalize_keycode(k2)
end

View file

@ -7,18 +7,21 @@ local math = math
local function basic_dump(o)
local tp = type(o)
if tp == "number" then
return tostring(o)
local s = tostring(o)
if tonumber(s) == o then
return s
end
-- Prefer an exact representation over a compact representation.
-- e.g. basic_dump(0.3) == "0.3",
-- but basic_dump(0.1 + 0.2) == "0.30000000000000004"
-- so the user can see that 0.1 + 0.2 ~= 0.3
return string.format("%.17g", o)
elseif tp == "string" then
return string.format("%q", o)
elseif tp == "boolean" then
return tostring(o)
elseif tp == "nil" then
return "nil"
-- Uncomment for full function dumping support.
-- Not currently enabled because bytecode isn't very human-readable and
-- dump's output is intended for humans.
--elseif tp == "function" then
-- return string.format("loadstring(%q)", string.dump(o))
elseif tp == "userdata" then
return tostring(o)
else
@ -105,65 +108,141 @@ function dump2(o, name, dumped)
return string.format("%s = {}\n%s", name, table.concat(t))
end
--------------------------------------------------------------------------------
-- This dumps values in a one-statement format.
-- This dumps values in a human-readable expression format.
-- If possible, the resulting string should evaluate to an equivalent value if loaded and executed.
-- For example, {test = {"Testing..."}} becomes:
-- [[{
-- test = {
-- "Testing..."
-- }
-- }]]
-- This supports tables as keys, but not circular references.
-- It performs poorly with multiple references as it writes out the full
-- table each time.
-- The indent field specifies a indentation string, it defaults to a tab.
-- Use the empty string to disable indentation.
-- The dumped and level arguments are internal-only.
function dump(o, indent, nested, level)
local t = type(o)
if not level and t == "userdata" then
-- when userdata (e.g. player) is passed directly, print its metatable:
return "userdata metatable: " .. dump(getmetatable(o))
end
if t ~= "table" then
return basic_dump(o)
end
-- Contains table -> true/nil of currently nested tables
nested = nested or {}
if nested[o] then
return "<circular reference>"
end
nested[o] = true
function dump(value, indent)
indent = indent or "\t"
level = level or 1
local newline = indent == "" and "" or "\n"
local ret = {}
local dumped_indexes = {}
for i, v in ipairs(o) do
ret[#ret + 1] = dump(v, indent, nested, level + 1)
dumped_indexes[i] = true
end
for k, v in pairs(o) do
if not dumped_indexes[k] then
if type(k) ~= "string" or not is_valid_identifier(k) then
k = "["..dump(k, indent, nested, level + 1).."]"
end
v = dump(v, indent, nested, level + 1)
ret[#ret + 1] = k.." = "..v
local rope = {}
local write
do
-- Keeping the length of the table as a local variable is *much*
-- faster than invoking the length operator.
-- See https://gitspartv.github.io/LuaJIT-Benchmarks/#test12.
local i = 0
function write(str)
i = i + 1
rope[i] = str
end
end
nested[o] = nil
if indent ~= "" then
local indent_str = "\n"..string.rep(indent, level)
local end_indent_str = "\n"..string.rep(indent, level - 1)
return string.format("{%s%s%s}",
indent_str,
table.concat(ret, ","..indent_str),
end_indent_str)
local n_refs = {}
local function count_refs(val)
if type(val) ~= "table" then
return
end
return "{"..table.concat(ret, ", ").."}"
local tbl = val
if n_refs[tbl] then
n_refs[tbl] = n_refs[tbl] + 1
return
end
n_refs[tbl] = 1
for k, v in pairs(tbl) do
count_refs(k)
count_refs(v)
end
end
count_refs(value)
local refs = {}
local cur_ref = 1
local function write_value(val, level)
if type(val) ~= "table" then
write(basic_dump(val))
return
end
local tbl = val
if refs[tbl] then
write(refs[tbl])
return
end
if n_refs[val] > 1 then
refs[val] = ("getref(%d)"):format(cur_ref)
write(("setref(%d)"):format(cur_ref))
cur_ref = cur_ref + 1
end
write("{")
if next(tbl) == nil then
write("}")
return
end
write(newline)
local function write_entry(k, v)
write(indent:rep(level))
write("[")
write_value(k, level + 1)
write("] = ")
write_value(v, level + 1)
write(",")
write(newline)
end
local keys = {string = {}, number = {}}
for k in pairs(tbl) do
local t = type(k)
if keys[t] then
table.insert(keys[t], k)
end
end
-- Write string-keyed entries
table.sort(keys.string)
for _, k in ipairs(keys.string) do
local v = val[k]
if is_valid_identifier(k) then
write(indent:rep(level))
write(k)
write(" = ")
write_value(v, level + 1)
write(",")
write(newline)
else
write_entry(k, v)
end
end
-- Write number-keyed entries
local len = 0
for i in ipairs(tbl) do
len = i
end
if #keys.number == len then -- table is a list
for _, v in ipairs(tbl) do
write(indent:rep(level))
write_value(v, level + 1)
write(",")
write(newline)
end
else -- table harbors arbitrary number keys
table.sort(keys.number)
for _, k in ipairs(keys.number) do
write_entry(k, tbl[k])
end
end
-- Write all remaining entries
for k, v in pairs(val) do
if not keys[type(k)] then
write_entry(k, v)
end
end
write(indent:rep(level - 1))
write("}")
end
write_value(value, 1)
return table.concat(rope)
end
--------------------------------------------------------------------------------
@ -457,18 +536,37 @@ do
end
end
--------------------------------------------------------------------------------
function table.copy(t, seen)
local n = {}
seen = seen or {}
seen[t] = n
for k, v in pairs(t) do
n[(type(k) == "table" and (seen[k] or table.copy(k, seen))) or k] =
(type(v) == "table" and (seen[v] or table.copy(v, seen))) or v
local function table_copy(value, preserve_metatables)
local seen = {}
local function copy(val)
if type(val) ~= "table" then
return val
end
return n
local t = val
if seen[t] then
return seen[t]
end
local res = {}
seen[t] = res
for k, v in pairs(t) do
res[copy(k)] = copy(v)
end
if preserve_metatables then
setmetatable(res, getmetatable(t))
end
return res
end
return copy(value)
end
function table.copy(value)
return table_copy(value, false)
end
function table.copy_with_metatables(value)
return table_copy(value, true)
end
function table.insert_all(t, other)
if table.move then -- LuaJIT
@ -536,6 +634,10 @@ if core.gettext then -- for client and mainmenu
function fgettext(text, ...)
return core.formspec_escape(fgettext_ne(text, ...))
end
function hgettext(text, ...)
return core.hypertext_escape(fgettext_ne(text, ...))
end
end
local ESCAPE_CHAR = string.char(0x1b)

View file

@ -54,7 +54,8 @@ function builtin_shared.make_registration()
local registerfunc = function(func)
t[#t + 1] = func
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
-- may be nil or return nil
mod = core.get_current_modname and core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
}
end
@ -66,7 +67,8 @@ function builtin_shared.make_registration_reverse()
local registerfunc = function(func)
table.insert(t, 1, func)
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
-- may be nil or return nil
mod = core.get_current_modname and core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
}
end

View file

@ -190,11 +190,41 @@ local function serialize(value, write)
dump(value)
end
-- Whether `value` recursively contains a function
local function contains_function(value)
local seen = {}
local function check(val)
if type(val) == "function" then
return true
end
if type(val) == "table" then
if seen[val] then
return false
end
seen[val] = true
for k, v in pairs(val) do
if check(k) or check(v) then
return true
end
end
end
return false
end
return check(value)
end
function core.serialize(value)
if contains_function(value) then
core.log("deprecated", "Support for dumping functions in `core.serialize` is deprecated.")
end
local rope = {}
-- Keeping the length of the table as a local variable is *much*
-- faster than invoking the length operator.
-- See https://gitspartv.github.io/LuaJIT-Benchmarks/#test12.
local i = 0
serialize(value, function(text)
-- Faster than table.insert(rope, text) on PUC Lua 5.1
rope[#rope + 1] = text
i = i + 1
rope[i] = text
end)
return table_concat(rope)
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2022 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.
-- Luanti
-- Copyright (C) 2022 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local make = {}
@ -37,6 +24,7 @@ local make = {}
-- * `fs` is a string for the formspec.
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
-- * `used_height` is the space used by components in `fs`.
-- * `spacing`: (Optional) the vertical margin to be added before the component (default 0.25)
-- * `on_submit = function(self, fields, parent)`:
-- * `fields`: submitted formspec fields
-- * `parent`: the fstk element for the settings UI, use to show dialogs
@ -98,6 +86,7 @@ local function make_field(converter, validator, stringifier)
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name) -- for pause menu env
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
return fs, 1.1
@ -217,6 +206,8 @@ local function make_path(setting)
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 3, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name) -- for pause menu env
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
@ -249,8 +240,11 @@ local function make_path(setting)
}
end
if PLATFORM == "Android" then
if PLATFORM == "Android" or INIT == "pause_menu" then
-- The Irrlicht file picker doesn't work on Android.
-- Access to the Irrlicht file picker isn't implemented in the pause menu.
-- We want to delete the Irrlicht file picker anyway, so any time spent on
-- that would be wasted.
make.path = make.string
make.filepath = make.string
else
@ -282,6 +276,14 @@ function make.v3f(setting)
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_x")
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_y")
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_z")
-- for pause menu env
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_x")
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_y")
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_z")
fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))
return fs, 1.4
@ -428,8 +430,75 @@ local function make_noise_params(setting)
}
end
make.noise_params_2d = make_noise_params
make.noise_params_3d = make_noise_params
function make.key(setting)
local btn_bind = "bind_" .. setting.name
local btn_clear = "unbind_" .. setting.name
local function add_conflict_warnings(fs, height)
local value = core.settings:get(setting.name)
if value == "" then
return height
end
for _, o in ipairs(core.full_settingtypes) do
if o.type == "key" and o.name ~= setting.name and core.are_keycodes_equal(core.settings:get(o.name), value) then
table.insert(fs, ("label[0,%f;%s]"):format(height + 0.3,
core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext(o.readable_name)))))
height = height + 0.6
end
end
return height
end
return {
info_text = setting.comment,
setting = setting,
spacing = 0.1,
get_formspec = function(self, avail_w)
self.resettable = core.settings:has(setting.name)
local btn_bind_width = math.max(2.5, avail_w/2)
local value = core.settings:get(setting.name)
local fs = {
("label[0,0.4;%s]"):format(get_label(setting)),
("button_key[%f,0;%f,0.8;%s;%s]"):format(
btn_bind_width, btn_bind_width-0.8,
btn_bind, core.formspec_escape(value)),
("image_button[%f,0;0.8,0.8;%s;%s;]"):format(avail_w - 0.8,
core.formspec_escape(defaulttexturedir .. "clear.png"),
btn_clear),
("tooltip[%s;%s]"):format(btn_clear, fgettext("Remove keybinding")),
}
local height = 0.8
height = add_conflict_warnings(fs, height)
return table.concat(fs), height
end,
on_submit = function(self, fields)
if fields[btn_bind] then
core.settings:set(setting.name, fields[btn_bind])
return true
elseif fields[btn_clear] then
core.settings:set(setting.name, "")
return true
end
end,
}
end
if INIT == "pause_menu" then
-- Making the noise parameter dialog work in the pause menu settings would
-- require porting "FSTK" (at least the dialog API) from the mainmenu formspec
-- API to the in-game formspec API.
-- There's no reason you'd want to adjust mapgen noise parameter settings
-- in-game (they only apply to new worlds, hidden as [world_creation]),
-- so there's no reason to implement this.
local empty = function()
return { get_formspec = function() return "", 0 end }
end
make.noise_params_2d = empty
make.noise_params_3d = empty
else
make.noise_params_2d = make_noise_params
make.noise_params_3d = make_noise_params
end
return make

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2015 PilzAdam
--
--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.
-- Luanti
-- Copyright (C) 2015 PilzAdam
-- SPDX-License-Identifier: LGPL-2.1-or-later
local checkboxes = {}

View file

@ -1,29 +1,14 @@
--Luanti
--Copyright (C) 2022 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.
-- Luanti
-- Copyright (C) 2022 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "components.lua")
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "shadows_component.lua")
local component_funcs = dofile(path .. "components.lua")
local shadows_component = dofile(path .. "shadows_component.lua")
local loaded = false
local full_settings
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
local all_pages = {}
@ -33,7 +18,7 @@ local filtered_page_by_id = page_by_id
local function get_setting_info(name)
for _, entry in ipairs(full_settings) do
for _, entry in ipairs(core.full_settingtypes) do
if entry.type ~= "category" and entry.name == name then
return entry
end
@ -71,7 +56,7 @@ local function load_settingtypes()
end
end
for _, entry in ipairs(full_settings) do
for _, entry in ipairs(core.full_settingtypes) do
if entry.type == "category" then
if entry.level == 0 then
section = entry.name
@ -105,29 +90,14 @@ local function load()
end
loaded = true
full_settings = settingtypes.parse_config_file(false, true)
local change_keys = {
query_text = "Controls",
requires = {
touch_controls = false,
},
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 3)
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
end,
on_submit = function(self, fields)
if fields.btn_change_keys then
core.show_keys_menu()
end
end,
}
core.full_settingtypes = settingtypes.parse_config_file(false, true)
local touchscreen_layout = {
query_text = "Touchscreen layout",
requires = {
touchscreen = true,
},
context = "client",
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 6)
return ("button[0,0;%f,0.8;btn_touch_layout;%s]"):format(btn_w, fgettext("Touchscreen layout")), 0.8
@ -160,13 +130,11 @@ local function load()
{ heading = fgettext_ne("Movement") },
"arm_inertia",
"view_bobbing_amount",
"fall_bobbing_amount",
},
})
load_settingtypes()
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
-- insert after "touch_controls"
table.insert(page_by_id.controls_touchscreen.content, 2, touchscreen_layout)
do
@ -175,18 +143,24 @@ local function load()
table.insert(content, idx, shadows_component)
idx = table.indexof(content, "enable_auto_exposure") + 1
local setting_info = get_setting_info("enable_auto_exposure")
local note = component_funcs.note(fgettext_ne("(The game will need to enable automatic exposure as well)"))
note.requires = get_setting_info("enable_auto_exposure").requires
note.requires = setting_info.requires
note.context = setting_info.context
table.insert(content, idx, note)
idx = table.indexof(content, "enable_bloom") + 1
setting_info = get_setting_info("enable_bloom")
note = component_funcs.note(fgettext_ne("(The game will need to enable bloom as well)"))
note.requires = get_setting_info("enable_bloom").requires
note.requires = setting_info.requires
note.context = setting_info.context
table.insert(content, idx, note)
idx = table.indexof(content, "enable_volumetric_lighting") + 1
setting_info = get_setting_info("enable_volumetric_lighting")
note = component_funcs.note(fgettext_ne("(The game will need to enable volumetric lighting as well)"))
note.requires = get_setting_info("enable_volumetric_lighting").requires
note.requires = setting_info.requires
note.context = setting_info.context
table.insert(content, idx, note)
end
@ -261,6 +235,17 @@ local function load()
["true"] = fgettext_ne("Enabled"),
["false"] = fgettext_ne("Disabled"),
}
get_setting_info("touch_interaction_style").option_labels = {
["tap"] = fgettext_ne("Tap"),
["tap_crosshair"] = fgettext_ne("Tap with crosshair"),
["buttons_crosshair"] = fgettext("Buttons with crosshair"),
}
get_setting_info("touch_punch_gesture").option_labels = {
["short_tap"] = fgettext_ne("Short tap"),
["long_tap"] = fgettext_ne("Long tap"),
}
end
@ -353,7 +338,18 @@ local function update_filtered_pages(query)
end
local function check_requirements(name, requires)
local shown_contexts = {
common = true,
client = true,
server = INIT ~= "pause_menu" or core.is_internal_server(),
world_creation = INIT ~= "pause_menu",
}
local function check_requirements(name, requires, context)
if context and not shown_contexts[context] then
return false
end
if requires == nil then
return true
end
@ -361,6 +357,7 @@ local function check_requirements(name, requires)
local video_driver = core.get_active_driver()
local touch_support = core.irrlicht_device_supports_touch()
local touch_controls = core.settings:get("touch_controls")
local touch_interaction_style = core.settings:get("touch_interaction_style")
local special = {
android = PLATFORM == "Android",
desktop = PLATFORM ~= "Android",
@ -371,6 +368,7 @@ local function check_requirements(name, requires)
keyboard_mouse = not touch_support or (touch_controls == "auto" or not core.is_yes(touch_controls)),
opengl = (video_driver == "opengl" or video_driver == "opengl3"),
gles = video_driver:sub(1, 5) == "ogles",
touch_interaction_style_tap = touch_interaction_style ~= "buttons_crosshair",
}
for req_key, req_value in pairs(requires) do
@ -378,6 +376,9 @@ local function check_requirements(name, requires)
local required_setting = get_setting_info(req_key)
if required_setting == nil then
core.log("warning", "Unknown setting " .. req_key .. " required by " .. (name or "???"))
elseif required_setting.type ~= "bool" then
core.log("warning", "Setting " .. req_key .. " of type " .. required_setting.type ..
" used as requirement by " .. (name or "???") .. ", only bool is allowed")
end
local actual_value = core.settings:get_bool(req_key,
required_setting and core.is_yes(required_setting.default))
@ -409,11 +410,11 @@ function page_has_contents(page, actual_content)
elseif type(item) == "string" then
local setting = get_setting_info(item)
assert(setting, "Unknown setting: " .. item)
if check_requirements(setting.name, setting.requires) then
if check_requirements(setting.name, setting.requires, setting.context) then
return true
end
elseif item.get_formspec then
if check_requirements(item.id, item.requires) then
if check_requirements(item.id, item.requires, item.context) then
return true
end
else
@ -435,20 +436,22 @@ local function build_page_components(page)
elseif item.heading then
last_heading = item
else
local name, requires
local name, requires, context
if type(item) == "string" then
local setting = get_setting_info(item)
assert(setting, "Unknown setting: " .. item)
name = setting.name
requires = setting.requires
context = setting.context
elseif item.get_formspec then
name = item.id
requires = item.requires
context = item.context
else
error("Unknown content in page: " .. dump(item))
end
if check_requirements(name, requires) then
if check_requirements(name, requires, context) then
if last_heading then
content[#content + 1] = last_heading
last_heading = nil
@ -514,7 +517,8 @@ local function get_formspec(dialogdata)
"box[0,0;", tostring(tabsize.width), ",", tostring(tabsize.height), ";#0000008C]",
("button[0,%f;%f,0.8;back;%s]"):format(
tabsize.height + 0.2, back_w, fgettext("Back")),
tabsize.height + 0.2, back_w,
fgettext("Back")),
("box[%f,%f;%f,0.8;#0000008C]"):format(
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
@ -531,6 +535,7 @@ local function get_formspec(dialogdata)
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
core.formspec_escape(dialogdata.query or ""), "]",
"field_enter_after_edit[search_query;true]",
"field_close_on_enter[search_query;false]", -- for pause menu env
"container[", tostring(search_width + 0.25), ", 0.25]",
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
@ -628,7 +633,13 @@ local function get_formspec(dialogdata)
fs[#fs + 1] = "container_end[]"
if used_h > 0 then
y = y + used_h + 0.25
local spacing = 0.25
local next_comp = dialogdata.components[i + 1]
if next_comp and next_comp.spacing then
spacing = next_comp.spacing
end
y = y + used_h + spacing
end
end
@ -671,7 +682,8 @@ local function buttonhandler(this, fields)
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
dialogdata.query = fields.search_query
if fields.back then
-- "fields.quit" is for the pause menu env
if fields.back or fields.quit then
this:delete()
return true
end
@ -765,11 +777,44 @@ local function eventhandler(event)
end
function create_settings_dlg()
if INIT == "mainmenu" then
function create_settings_dlg(page_id)
load()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
dlg.data.page_id = update_filtered_pages("")
dlg.data.page_id = page_id or update_filtered_pages("")
return dlg
end
else
assert(INIT == "pause_menu")
local dialog
core.register_on_formspec_input(function(formname, fields)
if dialog and formname == "__builtin:settings" then
-- buttonhandler returning true means we should update the formspec.
-- dialog is re-checked since the buttonhandler may have closed it.
if buttonhandler(dialog, fields) and dialog then
core.show_formspec("__builtin:settings", get_formspec(dialog.data))
end
return true
end
end)
core.open_settings = function()
load()
dialog = {}
dialog.data = {}
dialog.data.page_id = update_filtered_pages("")
dialog.delete = function()
dialog = nil
-- only needed for the "fields.back" case, in the "fields.quit"
-- case it's a no-op
core.show_formspec("__builtin:settings", "")
end
core.show_formspec("__builtin:settings", get_formspec(dialog.data))
end
end

View file

@ -61,7 +61,7 @@ local function create_minetest_conf_example(settings)
end
end
if entry.type == "key" then
local line = "See https://github.com/minetest/irrlicht/blob/master/include/Keycodes.h"
local line = "See https://docs.luanti.org/for-players/controls/"
insert(result, "# " .. line .. "\n")
end
insert(result, "# type: " .. entry.type)

View file

@ -0,0 +1,15 @@
-- Luanti
-- Copyright (C) 2022 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM
dofile(path .. "settingtypes.lua")
dofile(path .. "dlg_change_mapgen_flags.lua")
dofile(path .. "dlg_settings.lua")
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
-- dofile(path .. DIR_DELIM .. "generate_from_settingtypes.lua")

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2015 PilzAdam
--
--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.
-- Luanti
-- Copyright (C) 2015 PilzAdam
-- SPDX-License-Identifier: LGPL-2.1-or-later
settingtypes = {}
@ -40,12 +27,24 @@ local CHAR_CLASSES = {
FLAGS = "[%w_%-%.,]",
}
local valid_contexts = {common = true, client = true, server = true, world_creation = true}
local function check_context_annotation(context, force_context)
if force_context then
return "Context annotations are not allowed, context is always " .. force_context
end
if not valid_contexts[context] then
return "Unknown context"
end
return nil
end
local function flags_to_table(flags)
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
end
-- returns error message, or nil
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
local function parse_setting_line(settings, line, read_all, base_level, allow_secure, force_context)
-- strip carriage returns (CR, /r)
line = line:gsub("\r", "")
@ -69,9 +68,32 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
-- category
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
local category_context
if not category then
stars, category, category_context = line:match("^%[([%*]*)([^%]]+)%] %[([^%]]+)%]$")
end
if category then
local category_level = stars:len() + base_level
if settings.current_context_level and
category_level <= settings.current_context_level then
-- The start of this category marks the end of the context annotation's scope.
settings.current_context_level = nil
settings.current_context = nil
end
if category_context then
local err = check_context_annotation(category_context, force_context)
if err then
return err
end
if settings.current_context_level then
return "Category context annotations cannot be nested"
end
settings.current_context_level = category_level
settings.current_context = category_context
end
if settings.current_hide_level then
if settings.current_hide_level < category_level then
-- Skip this category, it's inside a hidden category.
@ -102,7 +124,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
end
-- settings
local first_part, name, readable_name, setting_type = line:match("^"
local function make_pattern(include_context)
return "^"
-- this first capture group matches the whole first part,
-- so we can later strip it from the rest of the line
.. "("
@ -110,9 +133,19 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
.. CHAR_CLASSES.SPACE .. "*"
.. "%(([^%)]*)%)" -- readable name
.. CHAR_CLASSES.SPACE .. "*"
.. (include_context and (
"%[([^%]]+)%]" -- context annotation
.. CHAR_CLASSES.SPACE .. "*"
) or "")
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
.. CHAR_CLASSES.SPACE .. "*"
.. ")")
.. ")"
end
local first_part, name, readable_name, setting_type = line:match(make_pattern(false))
local setting_context
if not first_part then
first_part, name, readable_name, setting_context, setting_type = line:match(make_pattern(true))
end
if not first_part then
return "Invalid line"
@ -122,6 +155,26 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
return "Tried to add \"secure.\" setting"
end
if setting_context then
local err = check_context_annotation(setting_context, force_context)
if err then
return err
end
end
local context
if force_context then
context = force_context
else
if setting_context then
context = setting_context
elseif settings.current_context_level then
context = settings.current_context
else
return "Missing context annotation"
end
end
local requires = {}
local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
if last_line and last_line:lower():sub(1, 9) == "requires:" then
@ -170,6 +223,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
min = min,
max = max,
requires = requires,
context = context,
comment = comment,
})
return
@ -182,9 +236,9 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
if not default then
return "Invalid string setting"
end
if setting_type == "key" and not read_all then
-- ignore key type if read_all is false
return
if setting_type == "key" then
requires.keyboard_mouse = true
end
table.insert(settings, {
@ -193,6 +247,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
type = setting_type,
default = default,
requires = requires,
context = context,
comment = comment,
})
return
@ -245,6 +300,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
},
values = values,
requires = requires,
context = context,
comment = comment,
noise_params = true,
flags = flags_to_table("defaults,eased,absvalue")
@ -263,6 +319,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
type = "bool",
default = remaining_line,
requires = requires,
context = context,
comment = comment,
})
return
@ -290,6 +347,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
min = min,
max = max,
requires = requires,
context = context,
comment = comment,
})
return
@ -313,6 +371,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
default = default,
values = values:split(",", true),
requires = requires,
context = context,
comment = comment,
})
return
@ -331,6 +390,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
type = setting_type,
default = default,
requires = requires,
context = context,
comment = comment,
})
return
@ -361,6 +421,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
default = default,
possible = flags_to_table(possible),
requires = requires,
context = context,
comment = comment,
})
return
@ -369,14 +430,14 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
return "Invalid setting type \"" .. setting_type .. "\""
end
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure, force_context)
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
result.current_comment = {}
result.current_hide_level = nil
local line = file:read("*line")
while line do
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure, force_context)
if error_msg then
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
end
@ -408,7 +469,15 @@ function settingtypes.parse_config_file(read_all, parse_mods)
file:close()
end
if parse_mods then
-- TODO: Support game/mod settings in the pause menu too
-- Note that this will need to work different from how it's done in the
-- mainmenu:
-- * Only show settings for the active game and mods
-- (add API function to get them, can return nil if on a remote server)
-- (names are probably not enough, will need paths for uniqueness)
-- This means just making "pkgmgr.lua" work won't get you very far.
if INIT == "mainmenu" and parse_mods then
-- Parse games
local games_category_initialized = false
for _, game in ipairs(pkgmgr.games) do
@ -432,7 +501,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
parse_single_file(file, path, read_all, settings, 2, false, "server")
file:close()
end
@ -465,7 +534,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
parse_single_file(file, path, read_all, settings, 2, false, "server")
file:close()
end
@ -496,7 +565,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
parse_single_file(file, path, read_all, settings, 2, false, "client")
file:close()
end

View file

@ -1,20 +1,7 @@
--Luanti
--Copyright (C) 2021-2 x2048
--Copyright (C) 2022-3 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.
-- Luanti
-- Copyright (C) 2021-2 x2048
-- Copyright (C) 2022-3 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local shadow_levels_labels = {
@ -84,6 +71,7 @@ return {
requires = {
opengl = true,
},
context = "client",
get_formspec = function(self, avail_w)
local labels = table.copy(shadow_levels_labels)
local idx = detect_mapping_idx()

View file

@ -178,6 +178,35 @@ describe("table", function()
assert.equal(2, table.keyof({[2] = "foo", [3] = "bar"}, "foo"))
assert.equal(3, table.keyof({[1] = "foo", [3] = "bar"}, "bar"))
end)
describe("copy()", function()
it("strips metatables", function()
local v = vector.new(1, 2, 3)
local w = table.copy(v)
assert.are_not.equal(v, w)
assert.same(v, w)
assert.equal(nil, getmetatable(w))
end)
it("preserves referential structure", function()
local t = {{}, {}}
t[1][1] = t[2]
t[2][1] = t[1]
local copy = table.copy(t)
assert.same(t, copy)
assert.equal(copy[1][1], copy[2])
assert.equal(copy[2][1], copy[1])
end)
end)
describe("copy_with_metatables()", function()
it("preserves metatables", function()
local v = vector.new(1, 2, 3)
local w = table.copy_with_metatables(v)
assert.equal(getmetatable(v), getmetatable(w))
assert(vector.check(w))
assert.equal(v, w) -- vector overrides ==
end)
end)
end)
describe("formspec_escape", function()
@ -201,3 +230,124 @@ describe("math", function()
assert.equal(0, math.round(-0.49999999999999994))
end)
end)
describe("dump", function()
local function test_expression(expr)
local chunk = assert(loadstring("return " .. expr))
local refs = {}
setfenv(chunk, {
setref = function(id)
refs[id] = {}
return function(fields)
for k, v in pairs(fields) do
refs[id][k] = v
end
return refs[id]
end
end,
getref = function(id)
return assert(refs[id])
end,
})
assert.equal(expr, dump(chunk()))
end
it("nil", function()
test_expression("nil")
end)
it("booleans", function()
test_expression("false")
test_expression("true")
end)
describe("numbers", function()
it("formats integers nicely", function()
test_expression("42")
end)
it("avoids misleading rounding", function()
test_expression("0.3")
assert.equal("0.30000000000000004", dump(0.1 + 0.2))
end)
end)
it("strings", function()
test_expression('"hello world"')
test_expression([["hello \"world\""]])
end)
describe("tables", function()
it("empty", function()
test_expression("{}")
end)
it("lists", function()
test_expression([[
{
false,
true,
"foo",
1,
2,
}]])
end)
it("number keys", function()
test_expression([[
{
[0.5] = false,
[1.5] = true,
[2.5] = "foo",
}]])
end)
it("dicts", function()
test_expression([[{
a = 1,
b = 2,
c = 3,
}]])
end)
it("mixed", function()
test_expression([[{
a = 1,
b = 2,
c = 3,
["d e"] = true,
"foo",
"bar",
}]])
end)
it("nested", function()
test_expression([[{
a = {
1,
{},
},
b = "foo",
c = {
[0.5] = 0.1,
[1.5] = 0.2,
},
}]])
end)
it("circular references", function()
test_expression([[setref(1){
child = {
parent = getref(1),
},
other_child = {
parent = getref(1),
},
}]])
end)
it("supports variable indent", function()
assert.equal('{1,2,3,{foo = "bar",},}', dump({1, 2, 3, {foo = "bar"}}, ""))
assert.equal('{\n "x",\n "y",\n}', dump({"x", "y"}, " "))
end)
end)
end)

View file

@ -93,7 +93,16 @@ describe("serialize", function()
assert_preserves(test_in)
end)
it("strips functions in safe mode", function()
describe("safe mode", function()
setup(function()
assert(not core.log)
-- logging a deprecation warning will be attempted
function core.log() end
end)
teardown(function()
core.log = nil
end)
it("functions are stripped", function()
local test_in = {
func = function(a, b)
error("test")
@ -109,6 +118,25 @@ describe("serialize", function()
assert.is_nil(test_out.func)
assert.equals(test_out.foo, "bar")
end)
end)
describe("deprecation warnings", function()
before_each(function()
assert(not core.log)
core.log = spy.new(function(level)
assert(level == "deprecated")
end)
end)
after_each(function()
core.log = nil
end)
it("dumping functions", function()
local t = {f = function() end, g = function() end}
t.t = t
core.serialize(t)
assert.spy(core.log).was.called(1) -- should have been called exactly *once*
end)
end)
it("vectors work", function()
local v = vector.new(1, 2, 3)

View file

@ -33,7 +33,7 @@ function core.get_node(pos)
return core.vmanip:get_node_at(pos)
end
function core.get_perlin(seed, octaves, persist, spread)
function core.get_value_noise(seed, octaves, persist, spread)
local params
if type(seed) == "table" then
params = table.copy(seed)
@ -47,12 +47,18 @@ function core.get_perlin(seed, octaves, persist, spread)
}
end
params.seed = core.get_seed(params.seed) -- add mapgen seed
return PerlinNoise(params)
return ValueNoise(params)
end
function core.get_perlin_map(params, size)
function core.get_value_noise_map(params, size)
local params2 = table.copy(params)
params2.seed = core.get_seed(params.seed) -- add mapgen seed
return PerlinNoiseMap(params2, size)
return ValueNoiseMap(params2, size)
end
-- deprecated as of 5.12, as it was not Perlin noise
-- but with no warnings (yet) for compatibility
core.get_perlin = core.get_value_noise
core.get_perlin_map = core.get_value_noise_map
PerlinNoise = ValueNoise
PerlinNoiseMap = ValueNoiseMap

View file

@ -9,8 +9,8 @@ do
all.registered_craftitems = {}
all.registered_tools = {}
for k, v in pairs(all.registered_items) do
-- Disable further modification
setmetatable(v, {__newindex = {}})
-- Ignore new keys
setmetatable(v, {__newindex = function() end})
-- Reassemble the other tables
if v.type == "node" then
getmetatable(v).__index = all.nodedef_default
@ -36,6 +36,9 @@ end
local alias_metatable = {
__index = function(t, name)
return rawget(t, core.registered_aliases[name])
end,
__newindex = function()
error("table is read-only")
end
}
setmetatable(core.registered_items, alias_metatable)

View file

@ -1,20 +1,7 @@
--Luanti
--Copyright (C) 2014 sapier
--Copyright (C) 2023 Gregor Parzefall
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- Copyright (C) 2023 Gregor Parzefall
-- SPDX-License-Identifier: LGPL-2.1-or-later
local BASE_SPACING = 0.1

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function dialog_event_handler(self,event)
if self.user_eventhandler == nil or

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
ui = {}
ui.childlist = {}
@ -179,6 +166,10 @@ end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
core.button_handler = function(fields)
if fields["try_quit"] and not fields["key_enter"] then
core.event_handler("MenuQuit")
return
end
if fields["btn_reconnect_yes"] then
gamedata.reconnect_requested = false
gamedata.errormessage = nil

View file

@ -60,6 +60,8 @@ core.register_on_chat_message(function(name, message)
param = param or ""
core.log("verbose", string.format("Handling chat command %q with params %q", cmd, param))
-- Run core.registered_on_chatcommands callbacks.
if core.run_callbacks(core.registered_on_chatcommands, 5, name, cmd, param) then
return true
@ -1275,7 +1277,7 @@ core.register_chatcommand("msg", {
core.log("action", "DM from " .. name .. " to " .. sendto
.. ": " .. message)
core.chat_send_player(sendto, S("DM from @1: @2", name, message))
return true, S("Message sent.")
return true, S("DM sent to @1: @2", sendto, message)
end,
})

View file

@ -61,3 +61,10 @@ function core.register_on_auth_fail(func)
end
end)
end
-- deprecated as of 5.12, as it was not Perlin noise
-- but with no warnings (yet) for compatibility
core.get_perlin = core.get_value_noise
core.get_perlin_map = core.get_value_noise_map
PerlinNoise = ValueNoise
PerlinNoiseMap = ValueNoiseMap

View file

@ -1,5 +1,4 @@
local builtin_shared = ...
local SCALE = 0.667
local facedir_to_euler = {
{y = 0, x = 0, z = 0},
@ -36,9 +35,7 @@ local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
core.register_entity(":__builtin:falling_node", {
initial_properties = {
visual = "item",
visual_size = vector.new(SCALE, SCALE, SCALE),
textures = {},
visual = "node",
physical = true,
is_visible = false,
collide_with_objects = true,
@ -80,41 +77,15 @@ core.register_entity(":__builtin:falling_node", {
-- Save liquidtype for falling water
self.liquidtype = def.liquidtype
-- Set entity visuals
if def.drawtype == "torchlike" or def.drawtype == "signlike" then
local textures
if def.tiles and def.tiles[1] then
local tile = def.tiles[1]
if type(tile) == "table" then
tile = tile.name
end
if def.drawtype == "torchlike" then
textures = { "("..tile..")^[transformFX", tile }
else
textures = { tile, "("..tile..")^[transformFX" }
end
end
local vsize
if def.visual_scale then
local s = def.visual_scale
vsize = vector.new(s, s, s)
end
self.object:set_properties({
is_visible = true,
visual = "upright_sprite",
visual_size = vsize,
textures = textures,
glow = def.light_source,
})
elseif def.drawtype ~= "airlike" then
local itemstring = node.name
if core.is_colored_paramtype(def.paramtype2) then
itemstring = core.itemstring_with_palette(itemstring, node.param2)
end
-- FIXME: solution needed for paramtype2 == "leveled"
-- Set up entity visuals
-- For compatibility with older clients we continue to use "item" visual
-- for simple situations.
local drawtypes = {normal=true, glasslike=true, allfaces=true, nodebox=true}
local p2types = {none=true, facedir=true, ["4dir"]=true}
if drawtypes[def.drawtype] and p2types[def.paramtype2] and def.use_texture_alpha ~= "blend" then
-- Calculate size of falling node
local s = {}
s.x = (def.visual_scale or 1) * SCALE
local s = vector.zero()
s.x = (def.visual_scale or 1) * 0.667
s.y = s.x
s.z = s.x
-- Compensate for wield_scale
@ -125,10 +96,31 @@ core.register_entity(":__builtin:falling_node", {
end
self.object:set_properties({
is_visible = true,
wield_item = itemstring,
visual = "item",
wield_item = node.name,
visual_size = s,
glow = def.light_source,
})
-- Rotate as needed
if def.paramtype2 == "facedir" then
local fdir = node.param2 % 32 % 24
local euler = facedir_to_euler[fdir + 1]
if euler then
self.object:set_rotation(euler)
end
elseif def.paramtype2 == "4dir" then
local fdir = node.param2 % 4
local euler = facedir_to_euler[fdir + 1]
if euler then
self.object:set_rotation(euler)
end
end
elseif def.drawtype ~= "airlike" then
self.object:set_properties({
is_visible = true,
node = node,
glow = def.light_source,
})
end
-- Set collision box (certain nodeboxes only for now)
@ -148,111 +140,6 @@ core.register_entity(":__builtin:falling_node", {
})
end
end
-- Rotate entity
if def.drawtype == "torchlike" then
if (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted")
and node.param2 % 8 == 7 then
self.object:set_yaw(-math.pi*0.25)
else
self.object:set_yaw(math.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"
or def.drawtype == "nodebox" then
if (def.paramtype2 == "facedir" or def.paramtype2 == "colorfacedir") then
local fdir = node.param2 % 32 % 24
-- Get rotation from a precalculated lookup table
local euler = facedir_to_euler[fdir + 1]
if euler then
self.object:set_rotation(euler)
end
elseif (def.paramtype2 == "4dir" or def.paramtype2 == "color4dir") then
local fdir = node.param2 % 4
-- Get rotation from a precalculated lookup table
local euler = facedir_to_euler[fdir + 1]
if euler then
self.object:set_rotation(euler)
end
elseif (def.drawtype ~= "plantlike" and def.drawtype ~= "plantlike_rooted" and
(def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" or def.drawtype == "signlike")) then
local rot = node.param2 % 8
if (def.drawtype == "signlike" and def.paramtype2 ~= "wallmounted" and def.paramtype2 ~= "colorwallmounted") then
-- Change rotation to "floor" by default for non-wallmounted paramtype2
rot = 1
end
local pitch, yaw, roll = 0, 0, 0
if def.drawtype == "nodebox" or def.drawtype == "mesh" then
if rot == 0 then
pitch, yaw = math.pi/2, 0
elseif rot == 1 then
pitch, yaw = -math.pi/2, math.pi
elseif rot == 2 then
pitch, yaw = 0, math.pi/2
elseif rot == 3 then
pitch, yaw = 0, -math.pi/2
elseif rot == 4 then
pitch, yaw = 0, math.pi
elseif rot == 6 then
pitch, yaw = math.pi/2, 0
elseif rot == 7 then
pitch, yaw = -math.pi/2, math.pi
end
else
if rot == 1 then
pitch, yaw = math.pi, math.pi
elseif rot == 2 then
pitch, yaw = math.pi/2, math.pi/2
elseif rot == 3 then
pitch, yaw = math.pi/2, -math.pi/2
elseif rot == 4 then
pitch, yaw = math.pi/2, math.pi
elseif rot == 5 then
pitch, yaw = math.pi/2, 0
elseif rot == 6 then
pitch, yaw = math.pi, -math.pi/2
elseif rot == 7 then
pitch, yaw = 0, -math.pi/2
end
end
if def.drawtype == "signlike" then
pitch = pitch - math.pi/2
if rot == 0 then
yaw = yaw + math.pi/2
elseif rot == 1 then
yaw = yaw - math.pi/2
elseif rot == 6 then
yaw = yaw - math.pi/2
pitch = pitch + math.pi
elseif rot == 7 then
yaw = yaw + math.pi/2
pitch = pitch + math.pi
end
elseif def.drawtype == "mesh" or def.drawtype == "normal" or def.drawtype == "nodebox" then
if rot == 0 or rot == 1 then
roll = roll + math.pi
elseif rot == 6 or rot == 7 then
if def.drawtype ~= "normal" then
roll = roll - math.pi/2
end
else
yaw = yaw + math.pi
end
end
self.object:set_rotation({x=pitch, y=yaw, z=roll})
elseif (def.drawtype == "mesh" and def.paramtype2 == "degrotate") then
local p2 = (node.param2 - (def.place_param2 or 0)) % 240
local yaw = (p2 / 240) * (math.pi * 2)
self.object:set_yaw(yaw)
elseif (def.drawtype == "mesh" and def.paramtype2 == "colordegrotate") then
local p2 = (node.param2 % 32 - (def.place_param2 or 0) % 32) % 24
local yaw = (p2 / 24) * (math.pi * 2)
self.object:set_yaw(yaw)
end
end
end,
get_staticdata = function(self)
@ -308,23 +195,26 @@ core.register_entity(":__builtin:falling_node", {
core.remove_node(bcp)
else
-- We are placing on top so check what's there
np.y = np.y + 1
end
-- Check what's here
local n2 = core.get_node(np)
local nd = core.registered_nodes[n2.name]
-- If it's not air or liquid, remove node and replace it with
-- it's drops
if n2.name ~= "air" and (not nd or nd.liquidtype ~= "source") then
if nd and nd.buildable_to == false then
if not nd or nd.buildable_to then
core.remove_node(np)
else
-- 'walkable' is used to mean "falling nodes can't replace this"
-- here. Normally we would collide with the walkable node itself
-- and place our node on top (so `n2.name == "air"`), but we
-- re-check this in case we ended up inside a node.
if not nd.diggable or nd.walkable then
return false
end
nd.on_dig(np, n2, nil)
-- If it's still there, it might be protected
if core.get_node(np).name == n2.name then
return false
end
else
core.remove_node(np)
end
end

View file

@ -45,6 +45,8 @@ core.features = {
abm_without_neighbors = true,
biome_weights = true,
particle_blend_clip = true,
remove_item_match_meta = true,
httpfetch_additional_methods = true,
}
function core.has_feature(arg)

View file

@ -360,13 +360,12 @@ end
function core.item_drop(itemstack, dropper, pos)
local dropper_is_player = dropper and dropper:is_player()
local p = table.copy(pos)
local cnt = itemstack:get_count()
if dropper_is_player then
p.y = p.y + 1.2
end
local item = itemstack:take_item(cnt)
local obj = core.add_item(p, item)
local obj = core.add_item(p, ItemStack(itemstack))
if obj then
itemstack:clear()
if dropper_is_player then
local dir = dropper:get_look_dir()
dir.x = dir.x * 2.9
@ -375,7 +374,7 @@ function core.item_drop(itemstack, dropper, pos)
obj:set_velocity(dir)
obj:get_luaentity().dropped_by = dropper:get_player_name()
end
return itemstack
return itemstack, obj
end
-- If we reach this, adding the object to the
-- environment failed
@ -514,7 +513,8 @@ function core.node_dig(pos, node, digger)
.. node.name .. " at " .. core.pos_to_string(pos))
local wielded = digger and digger:get_wielded_item()
local drops = core.get_node_drops(node, wielded and wielded:get_name())
local drops = core.get_node_drops(node, wielded and wielded:get_name(),
wielded and ItemStack(wielded), digger, vector.copy(pos))
if wielded then
local wdef = wielded:get_definition()

View file

@ -112,3 +112,26 @@ if core.set_push_moveresult1 then
end)
core.set_push_moveresult1 = nil
end
-- Protocol version table
-- see also src/network/networkprotocol.cpp
core.protocol_versions = {
["5.0.0"] = 37,
["5.1.0"] = 38,
["5.2.0"] = 39,
["5.3.0"] = 39,
["5.4.0"] = 39,
["5.5.0"] = 40,
["5.6.0"] = 41,
["5.7.0"] = 42,
["5.8.0"] = 43,
["5.9.0"] = 44,
["5.9.1"] = 45,
["5.10.0"] = 46,
["5.11.0"] = 47,
["5.12.0"] = 48,
}
setmetatable(core.protocol_versions, {__newindex = function()
error("core.protocol_versions is read-only")
end})

View file

@ -61,7 +61,7 @@ local function check_modname_prefix(name)
return name:sub(2)
else
-- Enforce that the name starts with the correct mod name.
local expected_prefix = core.get_current_modname() .. ":"
local expected_prefix = (core.get_current_modname() or "") .. ":"
if name:sub(1, #expected_prefix) ~= expected_prefix then
error("Name " .. name .. " does not follow naming conventions: " ..
"\"" .. expected_prefix .. "\" or \":\" prefix required")
@ -95,6 +95,7 @@ function core.register_abm(spec)
check_node_list(spec.nodenames, "nodenames")
check_node_list(spec.neighbors, "neighbors")
assert(type(spec.action) == "function", "Required field 'action' of type function")
core.registered_abms[#core.registered_abms + 1] = spec
spec.mod_origin = core.get_current_modname() or "??"
end
@ -128,127 +129,51 @@ function core.register_entity(name, prototype)
prototype.mod_origin = core.get_current_modname() or "??"
end
function core.register_item(name, itemdef)
-- Check name
if name == nil then
error("Unable to register item: Name is nil")
end
name = check_modname_prefix(tostring(name))
if forbidden_item_names[name] then
error("Unable to register item: Name is forbidden: " .. name)
end
itemdef.name = name
-- Apply defaults and add to registered_* table
if itemdef.type == "node" then
local function preprocess_node(nodedef)
-- Use the nodebox as selection box if it's not set manually
if itemdef.drawtype == "nodebox" and not itemdef.selection_box then
itemdef.selection_box = itemdef.node_box
elseif itemdef.drawtype == "fencelike" and not itemdef.selection_box then
itemdef.selection_box = {
if nodedef.drawtype == "nodebox" and not nodedef.selection_box then
nodedef.selection_box = nodedef.node_box
elseif nodedef.drawtype == "fencelike" and not nodedef.selection_box then
nodedef.selection_box = {
type = "fixed",
fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
}
end
if itemdef.light_source and itemdef.light_source > core.LIGHT_MAX then
itemdef.light_source = core.LIGHT_MAX
if nodedef.light_source and nodedef.light_source > core.LIGHT_MAX then
nodedef.light_source = core.LIGHT_MAX
core.log("warning", "Node 'light_source' value exceeds maximum," ..
" limiting to maximum: " ..name)
end
setmetatable(itemdef, {__index = core.nodedef_default})
core.registered_nodes[itemdef.name] = itemdef
elseif itemdef.type == "craft" then
setmetatable(itemdef, {__index = core.craftitemdef_default})
core.registered_craftitems[itemdef.name] = itemdef
elseif itemdef.type == "tool" then
setmetatable(itemdef, {__index = core.tooldef_default})
core.registered_tools[itemdef.name] = itemdef
elseif itemdef.type == "none" then
setmetatable(itemdef, {__index = core.noneitemdef_default})
else
error("Unable to register item: Type is invalid: " .. dump(itemdef))
" limiting it: " .. nodedef.name)
end
-- Flowing liquid uses param2
if itemdef.type == "node" and itemdef.liquidtype == "flowing" then
itemdef.paramtype2 = "flowingliquid"
if nodedef.liquidtype == "flowing" then
nodedef.paramtype2 = "flowingliquid"
end
end
local function preprocess_craft(itemdef)
-- BEGIN Legacy stuff
if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
core.register_craft({
type="cooking",
output=itemdef.cookresult_itemstring,
recipe=itemdef.name,
cooktime=itemdef.furnace_cooktime
})
end
if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
core.register_craft({
type="fuel",
recipe=itemdef.name,
burntime=itemdef.furnace_burntime
})
if itemdef.inventory_image == nil and itemdef.image ~= nil then
core.log("deprecated", "The `image` field in craftitem definitions " ..
"is deprecated. Use `inventory_image` instead. " ..
"Craftitem name: " .. itemdef.name, 3)
itemdef.inventory_image = itemdef.image
end
-- END Legacy stuff
itemdef.mod_origin = core.get_current_modname() or "??"
-- Disable all further modifications
getmetatable(itemdef).__newindex = {}
--core.log("Registering item: " .. itemdef.name)
core.registered_items[itemdef.name] = itemdef
core.registered_aliases[itemdef.name] = nil
register_item_raw(itemdef)
end
function core.unregister_item(name)
if not core.registered_items[name] then
core.log("warning", "Not unregistering item " ..name..
" because it doesn't exist.")
return
end
-- Erase from registered_* table
local type = core.registered_items[name].type
if type == "node" then
core.registered_nodes[name] = nil
elseif type == "craft" then
core.registered_craftitems[name] = nil
elseif type == "tool" then
core.registered_tools[name] = nil
end
core.registered_items[name] = nil
unregister_item_raw(name)
end
function core.register_node(name, nodedef)
nodedef.type = "node"
core.register_item(name, nodedef)
end
function core.register_craftitem(name, craftitemdef)
craftitemdef.type = "craft"
-- BEGIN Legacy stuff
if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then
craftitemdef.inventory_image = craftitemdef.image
end
-- END Legacy stuff
core.register_item(name, craftitemdef)
end
function core.register_tool(name, tooldef)
tooldef.type = "tool"
local function preprocess_tool(tooldef)
tooldef.stack_max = 1
-- BEGIN Legacy stuff
if tooldef.inventory_image == nil and tooldef.image ~= nil then
core.log("deprecated", "The `image` field in tool definitions " ..
"is deprecated. Use `inventory_image` instead. " ..
"Tool name: " .. tooldef.name, 3)
tooldef.inventory_image = tooldef.image
end
if tooldef.tool_capabilities == nil and
(tooldef.full_punch_interval ~= nil or
tooldef.basetime ~= nil or
@ -261,6 +186,9 @@ function core.register_tool(name, tooldef)
tooldef.dd_crackiness ~= nil or
tooldef.dd_crumbliness ~= nil or
tooldef.dd_cuttability ~= nil) then
core.log("deprecated", "Specifying tool capabilities directly in the tool " ..
"definition is deprecated. Use the `tool_capabilities` field instead. " ..
"Tool name: " .. tooldef.name, 3)
tooldef.tool_capabilities = {
full_punch_interval = tooldef.full_punch_interval,
basetime = tooldef.basetime,
@ -277,7 +205,7 @@ function core.register_tool(name, tooldef)
end
-- END Legacy stuff
-- This isn't just legacy, but more of a convenience feature
-- Automatically set punch_attack_uses as a convenience feature
local toolcaps = tooldef.tool_capabilities
if toolcaps and toolcaps.punch_attack_uses == nil then
for _, cap in pairs(toolcaps.groupcaps or {}) do
@ -288,8 +216,126 @@ function core.register_tool(name, tooldef)
end
end
end
end
core.register_item(name, tooldef)
local default_tables = {
node = core.nodedef_default,
craft = core.craftitemdef_default,
tool = core.tooldef_default,
none = core.noneitemdef_default,
}
local preprocess_fns = {
node = preprocess_node,
craft = preprocess_craft,
tool = preprocess_tool,
}
function core.register_item(name, itemdef)
-- Check name
if name == nil then
error("Unable to register item: Name is nil")
end
name = check_modname_prefix(tostring(name))
if forbidden_item_names[name] then
error("Unable to register item: Name is forbidden: " .. name)
end
itemdef.name = name
-- Compatibility stuff depending on type
local fn = preprocess_fns[itemdef.type]
if fn then
fn(itemdef)
end
-- Apply defaults
local defaults = default_tables[itemdef.type]
if defaults == nil then
error("Unable to register item: Type is invalid: " .. dump(itemdef))
end
local old_mt = getmetatable(itemdef)
-- TODO most of these checks should become an error after a while (maybe in 2026?)
if old_mt ~= nil and next(old_mt) ~= nil then
-- Note that even registering multiple identical items with the same table
-- is not allowed, due to the 'name' property.
if old_mt.__index == defaults then
core.log("warning", "Item definition table was reused between registrations. "..
"This is unsupported and broken: " .. name)
else
core.log("warning", "Item definition has a metatable, this is "..
"unsupported and it will be overwritten: " .. name)
end
end
setmetatable(itemdef, {__index = defaults})
-- BEGIN Legacy stuff
if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
core.log("deprecated", "The `cookresult_itemstring` item definition " ..
"field is deprecated. Use `core.register_craft` instead. " ..
"Item name: " .. itemdef.name, 2)
core.register_craft({
type="cooking",
output=itemdef.cookresult_itemstring,
recipe=itemdef.name,
cooktime=itemdef.furnace_cooktime
})
end
if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
core.log("deprecated", "The `furnace_burntime` item definition " ..
"field is deprecated. Use `core.register_craft` instead. " ..
"Item name: " .. itemdef.name, 2)
core.register_craft({
type="fuel",
recipe=itemdef.name,
burntime=itemdef.furnace_burntime
})
end
-- END Legacy stuff
itemdef.mod_origin = core.get_current_modname() or "??"
-- Ignore new keys as a failsafe to prevent mistakes
getmetatable(itemdef).__newindex = function() end
-- Add to registered_* tables
if itemdef.type == "node" then
core.registered_nodes[itemdef.name] = itemdef
elseif itemdef.type == "craft" then
core.registered_craftitems[itemdef.name] = itemdef
elseif itemdef.type == "tool" then
core.registered_tools[itemdef.name] = itemdef
end
core.registered_items[itemdef.name] = itemdef
core.registered_aliases[itemdef.name] = nil
register_item_raw(itemdef)
end
local function make_register_item_wrapper(the_type)
return function(name, itemdef)
itemdef.type = the_type
return core.register_item(name, itemdef)
end
end
core.register_node = make_register_item_wrapper("node")
core.register_craftitem = make_register_item_wrapper("craft")
core.register_tool = make_register_item_wrapper("tool")
function core.unregister_item(name)
if not core.registered_items[name] then
core.log("warning", "Not unregistering item " ..name..
" because it doesn't exist.")
return
end
-- Erase from registered_* table
core.registered_nodes[name] = nil
core.registered_craftitems[name] = nil
core.registered_tools[name] = nil
core.registered_items[name] = nil
unregister_item_raw(name)
end
function core.register_alias(name, convert_to)
@ -300,7 +346,6 @@ function core.register_alias(name, convert_to)
core.log("warning", "Not registering alias, item with same name" ..
" is already defined: " .. name .. " -> " .. convert_to)
else
--core.log("Registering alias: " .. name .. " -> " .. convert_to)
core.registered_aliases[name] = convert_to
register_alias_raw(name, convert_to)
end
@ -315,7 +360,6 @@ function core.register_alias_force(name, convert_to)
core.log("info", "Removed item " ..name..
" while attempting to force add an alias")
end
--core.log("Registering alias: " .. name .. " -> " .. convert_to)
core.registered_aliases[name] = convert_to
register_alias_raw(name, convert_to)
end
@ -406,6 +450,7 @@ core.register_item(":", {
groups = {not_in_creative_inventory=1},
})
local itemdefs_finalized = false
function core.override_item(name, redefinition, del_fields)
if redefinition.name ~= nil then
@ -418,10 +463,16 @@ function core.override_item(name, redefinition, del_fields)
if not item then
error("Attempt to override non-existent item "..name, 2)
end
if itemdefs_finalized then
-- TODO: it's not clear if this needs to be allowed at all?
core.log("warning", "Overriding item " .. name .. " after server startup. " ..
"This is unsupported and can cause problems related to data inconsistency.")
end
for k, v in pairs(redefinition) do
rawset(item, k, v)
end
for _, field in ipairs(del_fields or {}) do
assert(field ~= "name" and field ~= "type")
rawset(item, field, nil)
end
register_item_raw(item)
@ -568,13 +619,57 @@ core.registered_on_rightclickplayers, core.register_on_rightclickplayer = make_r
core.registered_on_liquid_transformed, core.register_on_liquid_transformed = make_registration()
core.registered_on_mapblocks_changed, core.register_on_mapblocks_changed = make_registration()
-- A bunch of registrations are read by the C++ side once on env init, so we cannot
-- allow them to change afterwards (see s_env.cpp).
-- Nodes and items do not have this problem but there are obvious consistency
-- problems if this would be allowed.
local function freeze_table(t)
-- Freezing a Lua table is not actually possible without some very intrusive
-- metatable hackery, but we can trivially prevent new additions.
local mt = table.copy(getmetatable(t) or {})
mt.__newindex = function()
error("modification forbidden")
end
setmetatable(t, mt)
end
local function generic_reg_error(what)
return function(something)
local described = what
if type(something) == "table" and type(something.name) == "string" then
described = what .. " " .. something.name
elseif type(something) == "string" then
described = what .. " " .. something
end
error("Tried to register " .. described .. " after load time!")
end
end
core.register_on_mods_loaded(function()
core.after(0, function()
setmetatable(core.registered_on_mapblocks_changed, {
__newindex = function()
error("on_mapblocks_changed callbacks must be registered at load time")
end,
})
itemdefs_finalized = true
-- prevent direct modification
freeze_table(core.registered_abms)
freeze_table(core.registered_lbms)
freeze_table(core.registered_items)
freeze_table(core.registered_nodes)
freeze_table(core.registered_craftitems)
freeze_table(core.registered_tools)
freeze_table(core.registered_aliases)
freeze_table(core.registered_on_mapblocks_changed)
-- neutralize registration functions
core.register_abm = generic_reg_error("ABM")
core.register_lbm = generic_reg_error("LBM")
core.register_item = generic_reg_error("item")
core.unregister_item = function(name)
error("Refusing to unregister item " .. name .. " after load time")
end
core.register_alias = generic_reg_error("alias")
core.register_alias_force = generic_reg_error("alias")
core.register_on_mapblocks_changed = generic_reg_error("on_mapblocks_changed callback")
end)
end)

View file

@ -78,6 +78,8 @@ elseif INIT == "client" then
dofile(scriptdir .. "client" .. DIR_DELIM .. "init.lua")
elseif INIT == "emerge" then
dofile(scriptdir .. "emerge" .. DIR_DELIM .. "init.lua")
elseif INIT == "pause_menu" then
dofile(scriptdir .. "pause_menu" .. DIR_DELIM .. "init.lua")
else
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Global menu data
menudata = {}
@ -34,7 +21,6 @@ function check_cache_age(key, max_age)
end
function core.on_before_close()
-- called before the menu is closed, either exit or to join a game
cache_settings:write()
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-24 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.
-- Luanti
-- Copyright (C) 2018-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
if not core.get_http_api then
return
@ -41,6 +28,7 @@ contentdb = {
REASON_DEPENDENCY = "dependency",
}
-- API documentation: https://content.luanti.org/help/api/
local function get_download_url(package, reason)
local base_url = core.settings:get("contentdb_url")
@ -182,14 +170,16 @@ function contentdb.get_package_by_id(id)
end
function contentdb.calculate_package_id(type, author, name)
local id = author:lower() .. "/"
local function strip_game_suffix(type, name)
if (type == nil or type == "game") and #name > 5 and name:sub(#name - 4) == "_game" then
id = id .. name:sub(1, #name - 5)
return name:sub(1, #name - 5)
else
id = id .. name
return name
end
return id
end
function contentdb.calculate_package_id(type, author, name)
return author:lower() .. "/" .. strip_game_suffix(type, name)
end
@ -398,7 +388,6 @@ local function fetch_pkgs()
local url = base_url ..
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
item = item:trim()
if item ~= "" then
@ -406,19 +395,11 @@ local function fetch_pkgs()
end
end
local languages
local current_language = core.get_language()
if current_language ~= "" then
languages = { current_language, "en;q=0.8" }
else
languages = { "en" }
end
local http = core.get_http_api()
local response = http.fetch_sync({
url = url,
extra_headers = {
"Accept-Language: " .. table.concat(languages, ", ")
core.get_http_accept_languages()
},
})
if not response.succeeded then
@ -448,7 +429,7 @@ function contentdb.set_packages_from_api(packages)
-- We currently don't support name changing
local suffix = "/" .. package.name
if alias:sub(-#suffix) == suffix then
contentdb.aliases[alias:lower()] = package.id
contentdb.aliases[strip_game_suffix(packages.type, alias:lower())] = package.id
end
end
end
@ -596,27 +577,19 @@ function contentdb.filter_packages(query, by_type)
end
function contentdb.get_full_package_info(package, callback)
local function get_package_info(key, path)
return function(package, callback)
assert(package)
if package.full_info then
callback(package.full_info)
if package[key] then
callback(package[key])
return
end
local function fetch(params)
local version = core.get_version()
local base_url = core.settings:get("contentdb_url")
local languages
local current_language = core.get_language()
if current_language ~= "" then
languages = { current_language, "en;q=0.8" }
else
languages = { "en" }
end
local url = base_url ..
"/api/packages/" .. params.package.url_part .. "/for-client/?" ..
"/api/packages/" .. params.package.url_part .. params.path .. "?" ..
"protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
"&engine_version=" .. core.urlencode(version.string) ..
"&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
@ -625,7 +598,7 @@ function contentdb.get_full_package_info(package, callback)
local response = http.fetch_sync({
url = url,
extra_headers = {
"Accept-Language: " .. table.concat(languages, ", ")
core.get_http_accept_languages()
},
})
if not response.succeeded then
@ -636,17 +609,22 @@ function contentdb.get_full_package_info(package, callback)
end
local function my_callback(value)
package.full_info = value
package[key] = value
callback(value)
end
if not core.handle_async(fetch, { package = package }, my_callback) then
if not core.handle_async(fetch, { package = package, path = path }, my_callback) then
core.log("error", "ERROR: async event failed")
callback(nil)
end
end
end
contentdb.get_full_package_info = get_package_info("full_info", "/for-client/")
contentdb.get_package_reviews = get_package_info("reviews", "/for-client/reviews/")
function contentdb.get_formspec_padding()
-- Padding is increased on Android to account for notches
-- TODO: use Android API to determine size of cut outs

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-20 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--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.
-- Luanti
-- Copyright (C) 2018-20 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
if not core.get_http_api then
function create_contentdb_dlg()
@ -323,9 +310,17 @@ local function get_formspec(dlgdata)
})
local img_w = cell_h * 3 / 2
-- Use as much of the available space as possible (so no padding on the
-- right/bottom), but don't quite allow the text to touch the border.
local text_w = cell_w - img_w - 0.25 - 0.025
local text_h = cell_h - 0.25 - 0.025
local start_idx = (cur_page - 1) * num_per_page + 1
for i=start_idx, math.min(#contentdb.packages, start_idx+num_per_page-1) do
local package = contentdb.packages[i]
local text = core.colorize(mt_color_green, package.title) ..
core.colorize("#BFBFBF", " by " .. package.author) .. "\n" ..
package.short_description
table.insert_all(formspec, {
"container[",
@ -340,13 +335,14 @@ local function get_formspec(dlgdata)
"image[0,0;", img_w, ",", cell_h, ";",
core.formspec_escape(get_screenshot(package, package.thumbnail, 2)), "]",
"label[", img_w + 0.25 + 0.05, ",0.5;",
core.formspec_escape(
core.colorize(mt_color_green, package.title) ..
core.colorize("#BFBFBF", " by " .. package.author)), "]",
"label[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
core.formspec_escape(text), "]",
"textarea[", img_w + 0.25, ",0.75;", cell_w - img_w - 0.25, ",", cell_h - 0.75, ";;;",
core.formspec_escape(package.short_description), "]",
-- Add a tooltip in case the label overflows and the short description is cut off.
"tooltip[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
-- Text in tooltips doesn't wrap automatically, so we do it manually to
-- avoid everything being one long line.
core.formspec_escape(core.wrap_text(package.short_description, 80)), "]",
"style[view_", i, ";border=false]",
"style[view_", i, ":hovered;bgimg=", core.formspec_escape(defaulttexturedir .. "button_hover_semitrans.png"), "]",
@ -362,7 +358,7 @@ local function get_formspec(dlgdata)
end
table.insert_all(formspec, {
"container[", cell_w - 0.625,",", 0.25, "]",
"container[", cell_w - 0.625,",", 0.125, "]",
})
if package.downloading then

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-24 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.
-- Luanti
-- Copyright (C) 2018-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function is_still_visible(dlg)
local this = ui.find_by_name("install_dialog")

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-24 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.
-- Luanti
-- Copyright (C) 2018-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
function get_formspec(data)
local package = data.package

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-24 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.
-- Luanti
-- Copyright (C) 2018-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_info_formspec(size, padding, text)
@ -32,6 +19,7 @@ end
local function get_formspec(data)
local package = data.package
local window_padding = contentdb.get_formspec_padding()
local size = contentdb.get_formspec_size()
size.x = math.min(size.x, 20)
@ -42,7 +30,7 @@ local function get_formspec(data)
if not data.loading and not data.loading_error then
data.loading = true
contentdb.get_full_package_info(data.package, function(info)
contentdb.get_full_package_info(package, function(info)
data.loading = false
if info == nil then
@ -61,7 +49,7 @@ local function get_formspec(data)
-- check to see if that happened
if not data.info then
if data.loading_error then
return get_info_formspec(size, window_padding, fgettext("No packages could be retrieved"))
return get_info_formspec(size, window_padding, fgettext("Error loading package information"))
end
return get_info_formspec(size, window_padding, fgettext("Loading..."))
end
@ -103,15 +91,15 @@ local function get_formspec(data)
local left_button_rect = "0,0;2.875,1"
local right_button_rect = "3.125,0;2.875,1"
if data.package.downloading then
if package.downloading then
formspec[#formspec + 1] = "animated_image[5,0;1,1;downloading;"
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
elseif data.package.queued then
elseif package.queued then
formspec[#formspec + 1] = "style[queued;border=false]"
formspec[#formspec + 1] = "image_button[5,0;1,1;" .. core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
elseif not data.package.path then
elseif not package.path then
formspec[#formspec + 1] = "style[install;bgcolor=green]"
formspec[#formspec + 1] = "button["
formspec[#formspec + 1] = right_button_rect
@ -119,7 +107,7 @@ local function get_formspec(data)
formspec[#formspec + 1] = fgettext("Install [$1]", info.download_size)
formspec[#formspec + 1] = "]"
else
if data.package.installed_release < data.package.release then
if package.installed_release < package.release then
-- The install_ action also handles updating
formspec[#formspec + 1] = "style[install;bgcolor=#28ccdf]"
formspec[#formspec + 1] = "button["
@ -137,10 +125,12 @@ local function get_formspec(data)
formspec[#formspec + 1] = "]"
end
local review_count = info.reviews.positive + info.reviews.neutral + info.reviews.negative
local current_tab = data.current_tab or 1
local tab_titles = {
fgettext("Description"),
fgettext("Information"),
fgettext("Reviews") .. core.formspec_escape(" [" .. review_count .. "]"),
}
local tab_body_height = bottom_buttons_y - 2.8
@ -162,8 +152,8 @@ local function get_formspec(data)
local winfo = core.get_window_info()
local fs_to_px = winfo.size.x / winfo.max_formspec_size.x
for i, ss in ipairs(info.screenshots) do
local path = get_screenshot(data.package, ss.url, 2)
hypertext = hypertext .. "<action name=\"ss_" .. i .. "\"><img name=\"" ..
local path = get_screenshot(package, ss.url, 2)
hypertext = hypertext .. "<action name=\"ss_".. i .. "\"><img name=\"" ..
core.hypertext_escape(path) .. "\" width=" .. (3 * fs_to_px) ..
" height=" .. (2 * fs_to_px) .. "></action>"
if i ~= #info.screenshots then
@ -194,22 +184,54 @@ local function get_formspec(data)
hypertext = hypertext .. "\n\n" .. info.long_description.body
-- Fix the path to blank.png. This is needed for bullet indentation.
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
table.insert_all(formspec, {
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
";desc;", core.formspec_escape(hypertext), "]",
})
elseif current_tab == 2 then
local hypertext = info.info_hypertext.head .. info.info_hypertext.body
table.insert_all(formspec, {
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
";info;", core.formspec_escape(hypertext), "]",
})
elseif current_tab == 3 then
if not package.reviews and not data.reviews_error and not data.reviews_loading then
data.reviews_loading = true
contentdb.get_package_reviews(package, function(reviews)
if not reviews then
data.reviews_error = true
end
ui.update()
end)
end
if package.reviews then
local hypertext = package.reviews.head .. package.reviews.body
-- Provide correct path to blank.png image. This is needed for bullet indentation.
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
-- Placeholders in reviews hypertext for icons
hypertext = hypertext:gsub("<thumbsup>",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_thumb_up.png\" width=24>")
hypertext = hypertext:gsub("<thumbsdown>",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_thumb_down.png\" width=24>")
hypertext = hypertext:gsub("<neutral>",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_neutral.png\" width=24>")
table.insert_all(formspec, {
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
";reviews;", core.formspec_escape(hypertext), "]",
})
elseif data.reviews_error then
table.insert_all(formspec, {"label[2,2;", fgettext("Error loading reviews"), "]"} )
else
table.insert_all(formspec, {"label[2,2;", fgettext("Loading..."), "]"} )
end
else
error("Unknown tab " .. current_tab)
end
@ -269,9 +291,10 @@ local function handle_submit(this, fields)
end
if fields.open_contentdb then
local url = ("%s/packages/%s/?protocol_version=%d"):format(
core.settings:get("contentdb_url"), package.url_part,
core.get_max_supp_proto())
local version = core.get_version()
local url = core.settings:get("contentdb_url") .. "/packages/" .. package.url_part ..
"/?protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
"&engine_version=" .. core.urlencode(version.string)
core.open_url(url)
return true
end
@ -295,7 +318,8 @@ local function handle_submit(this, fields)
end
if handle_hypertext_event(this, fields.desc, info.long_description) or
handle_hypertext_event(this, fields.info, info.info_hypertext) then
handle_hypertext_event(this, fields.info, info.info_hypertext) or
(package.reviews and handle_hypertext_event(this, fields.reviews, package.reviews)) then
return true
end
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2023 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.
-- Luanti
-- Copyright (C) 2023 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2013 sapier
--
--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.
-- Luanti
-- Copyright (C) 2013 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
local function get_last_folder(text,count)

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2023-24 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.
-- Luanti
-- Copyright (C) 2023-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Screenshot

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2022 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.
-- Luanti
-- Copyright (C) 2022 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local mods_dir = "/tmp/.minetest/mods"
local games_dir = "/tmp/.minetest/games"

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2023 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.
-- Luanti
-- Copyright (C) 2023 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
update_detector = {}

View file

@ -1,5 +1,5 @@
{
"#": "https://github.com/orgs/minetest/teams/engine/members",
"#": "https://github.com/orgs/luanti-org/teams/engine/members",
"core_developers": [
"Perttu Ahola (celeron55) <celeron55@gmail.com> [Project founder]",
"sfan5 <sfan5@live.de>",
@ -15,7 +15,8 @@
"Gregor Parzefall (grorp)",
"Lars Müller (luatic)",
"cx384",
"sfence"
"sfence",
"y5nw"
],
"previous_core_developers": [
"BlockMen",
@ -38,7 +39,7 @@
"Hugues Ross <hugues.ross@gmail.com>",
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>"
],
"#": "Currently only https://github.com/orgs/minetest/teams/triagers/members",
"#": "Currently only https://github.com/orgs/luanti-org/teams/triagers/members",
"core_team": [
"Zughy [Issue triager]",
"wsor [Issue triager]",
@ -46,25 +47,20 @@
],
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
"contributors": [
"JosiahWI",
"1F616EMO",
"y5nw",
"Erich Schubert",
"numzero",
"red-001 <red-001@outlook.ie>",
"David Heidelberg",
"Wuzzy",
"paradust7",
"HybridDog",
"Zemtzov7",
"kromka-chleba",
"AFCMS",
"chmodsayshello",
"OgelGames"
"wrrrzr",
"siliconsniffer",
"JosiahWI",
"veprogames",
"Miguel P.L",
"AFCMS"
],
"previous_contributors": [
"Ælla Chiana Moskopp (erle) <erle@dieweltistgarnichtso.net> [Logo]",
"numzero",
"red-001 <red-001@outlook.ie>",
"Giuseppe Bilotta",
"HybridDog",
"ClobberXD",
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
"MirceaKitsune <mirceakitsune@gmail.com>",
@ -77,6 +73,7 @@
"stujones11",
"Rogier <rogier777@gmail.com>",
"Gregory Currie (gregorycu)",
"paradust7",
"JacobF",
"Jeija <jeija@mesecons.net>"
]

View file

@ -21,7 +21,7 @@ local function clients_list_formspec(dialogdata)
"size[6,9.5]",
TOUCH_GUI and "padding[0.01,0.01]" or "",
"hypertext[0,0;6,1.5;;<global margin=5 halign=center valign=middle>",
fgettext("This is the list of clients connected to\n$1",
fgettext("Players connected to\n$1",
"<b>" .. core.hypertext_escape(servername) .. "</b>") .. "]",
"textlist[0.5,1.5;5,6.8;;" .. fmt_formspec_list(clients_list) .. "]",
"button[1.5,8.5;3,0.8;quit;OK]"

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2013 sapier
--
--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.
-- Luanti
-- Copyright (C) 2013 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
@ -299,7 +286,7 @@ local function handle_buttons(this, fields)
worldfile:set("load_mod_" .. mod.name, mod.virtual_path)
was_set[mod.name] = true
elseif not was_set[mod.name] then
worldfile:set("load_mod_" .. mod.name, "false")
worldfile:remove("load_mod_" .. mod.name)
end
elseif mod.enabled then
gamedata.errormessage = fgettext_ne("Failed to enable mo" ..

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function table_to_flags(ftable)
-- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function delete_world_formspec(dialogdata)

View file

@ -0,0 +1,108 @@
-- Luanti
-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Modified based on dlg_reinstall_mtg.lua
-- Note that this is only needed for migrating from <5.11 to 5.12.
local doc_url = "https://docs.luanti.org/for-players/controls/"
local SETTING_NAME = "no_keycode_migration_warning"
local function get_formspec(dialogdata)
local markup = table.concat({
"<big>" .. hgettext("Keybindings changed") .. "</big>",
hgettext("The input handling system was reworked in Luanti 5.12.0."),
hgettext("As a result, your keybindings may have been changed."),
hgettext("Check out the key settings or refer to the documentation:"),
("<action name='doc_url'><style color='cyan' hovercolor='orangered'>%s</style></action>"):format(doc_url),
}, "\n")
return table.concat({
"formspec_version[6]",
"size[12,7]",
"hypertext[0.5,0.5;11,4.7;text;", core.formspec_escape(markup), "]",
"container[0.5,5.7]",
"button[0,0;4,0.8;dismiss;", fgettext("Close"), "]",
"button[4.5,0;6.5,0.8;reconfigure;", fgettext("Open settings"), "]",
"container_end[]",
})
end
local function close_dialog(this)
cache_settings:set_bool(SETTING_NAME, true)
this:delete()
end
local function buttonhandler(this, fields)
if fields.reconfigure then
close_dialog(this)
local maintab = ui.find_by_name("maintab")
local dlg = create_settings_dlg("controls_keyboard_and_mouse")
dlg:set_parent(maintab)
maintab:hide()
dlg:show()
return true
end
if fields.dismiss then
close_dialog(this)
return true
end
if fields.text == "action:doc_url" then
core.open_url(doc_url)
end
end
local function eventhandler(event)
if event == "DialogShow" then
mm_game_theme.set_engine()
return true
elseif event == "MenuQuit" then
-- Don't allow closing the dialog with ESC, but still allow exiting
-- Luanti
core.close()
return true
end
return false
end
local function create_rebind_keys_dlg()
local dlg = dialog_create("dlg_rebind_keys", get_formspec,
buttonhandler, eventhandler)
return dlg
end
function migrate_keybindings()
-- Show migration dialog if the user upgraded from an earlier version
-- and this has not yet been shown before, *or* if keys settings had to be changed
if core.is_first_run then
cache_settings:set_bool(SETTING_NAME, true)
end
local has_migration = not cache_settings:get_bool(SETTING_NAME)
-- normalize all existing key settings, this converts them from KEY_KEY_C to SYSTEM_SCANCODE_6
local settings = core.settings:to_table()
for name, value in pairs(settings) do
if name:match("^keymap_") then
local normalized = core.normalize_keycode(value)
if value ~= normalized then
has_migration = true
core.settings:set(name, normalized)
end
end
end
if not has_migration then
return
end
local maintab = ui.find_by_name("maintab")
local dlg = create_rebind_keys_dlg()
dlg:set_parent(maintab)
maintab:hide()
dlg:show()
ui.update()
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2022 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.
-- Luanti
-- Copyright (C) 2022 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------

View file

@ -1,25 +1,12 @@
--Luanti
--Copyright (C) 2023 Gregor Parzefall
--
--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.
-- Luanti
-- Copyright (C) 2023 Gregor Parzefall
-- SPDX-License-Identifier: LGPL-2.1-or-later
---- IMPORTANT ----
-- This whole file can be removed after a while.
-- It was only directly useful for upgrades from 5.7.0 to 5.8.0, but
-- maybe some odd fellow directly upgrades from 5.6.1 to 5.9.0 in the future...
-- see <https://github.com/minetest/minetest/pull/13850> in case it's not obvious
-- see <https://github.com/luanti-org/luanti/pull/13850> in case it's not obvious
---- ----
local SETTING_NAME = "no_mtg_notification"
@ -67,10 +54,10 @@ end
local function get_formspec(dialogdata)
local markup = table.concat({
"<big>", fgettext("Minetest Game is no longer installed by default"), "</big>\n",
fgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
"<big>", hgettext("Minetest Game is no longer installed by default"), "</big>\n",
hgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
"Since version 5.8.0, Luanti ships without a default game."), "\n",
fgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
hgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
})
return table.concat({

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------

View file

@ -0,0 +1,105 @@
-- Luanti
-- Copyright (C) 2024 cx384
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_formspec(dialogdata)
local TOUCH_GUI = core.settings:get_bool("touch_gui")
local server = dialogdata.server
local group_by_prefix = dialogdata.group_by_prefix
local expand_all = dialogdata.expand_all
-- A wrongly behaving server may send ill formed mod names
table.sort(server.mods)
local cells = {}
if group_by_prefix then
local function get_prefix(mod)
return mod:match("[^_]*")
end
local count = {}
for _, mod in ipairs(server.mods) do
local prefix = get_prefix(mod)
count[prefix] = (count[prefix] or 0) + 1
end
local last_prefix
local function add_row(depth, mod)
table.insert(cells, ("%d"):format(depth))
table.insert(cells, mod)
end
for i, mod in ipairs(server.mods) do
local prefix = get_prefix(mod)
if last_prefix == prefix then
add_row(1, mod)
elseif count[prefix] > 1 then
add_row(0, prefix)
add_row(1, mod)
else
add_row(0, mod)
end
last_prefix = prefix
end
else
cells = table.copy(server.mods)
end
for i, cell in ipairs(cells) do
cells[i] = core.formspec_escape(cell)
end
cells = table.concat(cells, ",")
local heading
if server.gameid then
heading = fgettext("The $1 server uses a game called $2 and the following mods:",
"<b>" .. core.hypertext_escape(server.name) .. "</b>",
"<style font=mono>" .. core.hypertext_escape(server.gameid) .. "</style>")
else
heading = fgettext("The $1 server uses the following mods:",
"<b>" .. core.hypertext_escape(server.name) .. "</b>")
end
local formspec = {
"formspec_version[8]",
"size[8,9.5]",
TOUCH_GUI and "padding[0.01,0.01]" or "",
"hypertext[0,0;8,1.5;;<global margin=5 halign=center valign=middle>", heading, "]",
"tablecolumns[", group_by_prefix and
(expand_all and "indent;text" or "tree;text") or "text", "]",
"table[0.5,1.5;7,6.8;mods;", cells, "]",
"checkbox[0.5,8.7;group_by_prefix;", fgettext("Group by prefix"), ";",
group_by_prefix and "true" or "false", "]",
group_by_prefix and ("checkbox[0.5,9.15;expand_all;" .. fgettext("Expand all") .. ";" ..
(expand_all and "true" or "false") .. "]") or "",
"button[5.5,8.5;2,0.8;quit;OK]"
}
return table.concat(formspec, "")
end
local function buttonhandler(this, fields)
if fields.quit then
this:delete()
return true
end
if fields.group_by_prefix then
this.data.group_by_prefix = core.is_yes(fields.group_by_prefix)
return true
end
if fields.expand_all then
this.data.expand_all = core.is_yes(fields.expand_all)
return true
end
return false
end
function create_server_list_mods_dialog(server)
local retval = dialog_create("dlg_server_list_mods",
get_formspec,
buttonhandler,
nil)
retval.data.group_by_prefix = false
retval.data.expand_all = false
retval.data.server = server
return retval
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2013 sapier
--
--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.
-- Luanti
-- Copyright (C) 2013 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
mm_game_theme = {}

View file

@ -1,27 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
mt_color_grey = "#AAAAAA"
mt_color_blue = "#6389FF"
mt_color_lightblue = "#99CCFF"
mt_color_green = "#72FF63"
mt_color_dark_green = "#25C191"
mt_color_orange = "#FF8800"
mt_color_red = "#FF3300"
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
MAIN_TAB_W = 15.5
MAIN_TAB_H = 7.1
@ -35,6 +14,7 @@ local basepath = core.get_builtin_path()
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
DIR_DELIM .. "pack" .. DIR_DELIM
dofile(basepath .. "common" .. DIR_DELIM .. "menu.lua")
dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua")
@ -47,7 +27,7 @@ dofile(menupath .. DIR_DELIM .. "game_theme.lua")
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
dofile(basepath .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
@ -55,7 +35,9 @@ dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
dofile(menupath .. DIR_DELIM .. "dlg_rebind_keys.lua")
dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua")
dofile(menupath .. DIR_DELIM .. "dlg_server_list_mods.lua")
local tabs = {
content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"),
@ -131,6 +113,7 @@ local function init_globals()
ui.update()
check_reinstall_mtg()
migrate_keybindings()
check_new_version()
end

View file

@ -1,19 +1,6 @@
--Luanti
--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.
-- Luanti
-- Copyright (C) 2020 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
serverlistmgr = {
-- continent code we detected for ourselves

View file

@ -1,28 +0,0 @@
--Luanti
--Copyright (C) 2022 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.
local path = core.get_mainmenu_path() .. DIR_DELIM .. "settings"
dofile(path .. DIR_DELIM .. "settingtypes.lua")
dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua")
dofile(path .. DIR_DELIM .. "dlg_settings.lua")
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
-- dofile(path .. DIR_DELIM .. "generate_from_settingtypes.lua")

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