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

Merge remote-tracking branch 'upstream/master' into Visuals-Vol-2

This commit is contained in:
Gefüllte Taubenbrust 2025-04-13 11:33:37 +02:00
commit fa212d19f7
572 changed files with 71629 additions and 67352 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

@ -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

@ -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,8 +71,8 @@ jobs:
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
VCPKG_VERSION: 01f602195983451bc83e72f4214af2cbc495aa94
# 2024.05.24
VCPKG_VERSION: d5ec528843d29e3a52d745a64b469f810b2cedbf
# 2025.02.14
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
strategy:
fail-fast: false
@ -106,10 +106,12 @@ jobs:
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"}},
}
@ -75,10 +76,6 @@ files["builtin/mainmenu"] = {
globals = {
"gamedata",
},
read_globals = {
"PLATFORM",
},
}
files["builtin/common/tests"] = {

View file

@ -11,7 +11,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 12)
set(VERSION_PATCH 0)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
@ -48,7 +48,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)

View file

@ -102,6 +102,9 @@ 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
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

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

@ -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_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,11 @@
<?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>
</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

@ -0,0 +1,11 @@
<?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 kunde hittas</string>
<string name="notification_channel_name">Generell notis</string>
<string name="notification_channel_description">Notiser från Luanti</string>
<string name="unzip_notification_title">Laddar Luanti</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

@ -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", 12) // 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

@ -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

@ -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)

View file

@ -457,18 +457,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

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

@ -98,6 +98,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 +218,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 +252,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 +288,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 +442,22 @@ local function make_noise_params(setting)
}
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

@ -16,11 +16,10 @@
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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
@ -110,8 +109,9 @@ local function load()
local change_keys = {
query_text = "Controls",
requires = {
touch_controls = false,
keyboard_mouse = true,
},
context = "client",
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
@ -128,6 +128,7 @@ local function load()
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,7 +161,6 @@ local function load()
{ heading = fgettext_ne("Movement") },
"arm_inertia",
"view_bobbing_amount",
"fall_bobbing_amount",
},
})
@ -175,18 +175,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 +267,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 +370,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 +389,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 +400,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 +408,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 +442,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 +468,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 +549,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,
INIT == "pause_menu" and fgettext("Exit") or fgettext("Back")),
("box[%f,%f;%f,0.8;#0000008C]"):format(
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
@ -531,6 +567,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;]",
@ -671,7 +708,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,6 +803,7 @@ local function eventhandler(event)
end
if INIT == "mainmenu" then
function create_settings_dlg()
load()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
@ -773,3 +812,35 @@ function create_settings_dlg()
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://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h"
insert(result, "# " .. line .. "\n")
end
insert(result, "# type: " .. entry.type)

View file

@ -15,11 +15,11 @@
--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"
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM
dofile(path .. DIR_DELIM .. "settingtypes.lua")
dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua")
dofile(path .. DIR_DELIM .. "dlg_settings.lua")
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.

View file

@ -40,12 +40,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 +81,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 +137,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 +146,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 +168,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 +236,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
@ -193,6 +260,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 +313,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 +332,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 +360,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 +384,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 +403,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 +434,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 +443,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 +482,17 @@ 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 if in singleplayer / on local server, not on remote servers~~
-- (done now: context annotations)
-- * 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 +516,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 +549,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 +580,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

@ -84,6 +84,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()

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

@ -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)

View file

@ -45,6 +45,7 @@ core.features = {
abm_without_neighbors = true,
biome_weights = true,
particle_blend_clip = true,
remove_item_match_meta = 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

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,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]",
@ -47,23 +48,20 @@
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
"contributors": [
"JosiahWI",
"1F616EMO",
"y5nw",
"Erich Schubert",
"numzero",
"wrrrzr",
"1F616EMO",
"red-001 <red-001@outlook.ie>",
"David Heidelberg",
"Wuzzy",
"veprogames",
"paradust7",
"HybridDog",
"Zemtzov7",
"kromka-chleba",
"AFCMS",
"chmodsayshello",
"OgelGames"
"siliconsniffer",
"Wuzzy",
"Zemtzov7"
],
"previous_contributors": [
"Ælla Chiana Moskopp (erle) <erle@dieweltistgarnichtso.net> [Logo]",
"numzero",
"Giuseppe Bilotta",
"ClobberXD",
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",

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

@ -299,7 +299,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

@ -19,7 +19,7 @@
-- 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"

View file

@ -47,7 +47,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")

View file

@ -190,10 +190,10 @@ local function get_formspec(tabview, name, tabdata)
local max_clients = 5
if #clients_list > max_clients then
retval = retval .. "tooltip[btn_view_clients;" ..
fgettext("Clients:\n$1", table.concat(clients_list, "\n", 1, max_clients)) .. "\n..." .. "]"
fgettext("Players:\n$1", table.concat(clients_list, "\n", 1, max_clients)) .. "\n..." .. "]"
else
retval = retval .. "tooltip[btn_view_clients;" ..
fgettext("Clients:\n$1", table.concat(clients_list, "\n")) .. "]"
fgettext("Players:\n$1", table.concat(clients_list, "\n")) .. "]"
end
retval = retval .. "style[btn_view_clients;padding=6]"
retval = retval .. "image_button[4.5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir ..
@ -391,12 +391,15 @@ local function matches_query(server, query)
return name_matches and 50 or description_matches and 0
end
local function search_server_list(input)
local function search_server_list(input, tabdata)
menudata.search_result = nil
if #serverlistmgr.servers < 2 then
return
end
tabdata.pre_search_selection = tabdata.pre_search_selection or find_selected_server()
-- setup the search query
local query = parse_search_input(input)
if not query then
@ -419,10 +422,32 @@ local function search_server_list(input)
return
end
local current_server = find_selected_server()
table.sort(search_result, function(a, b)
return a.points > b.points
end)
menudata.search_result = search_result
-- Keep current selection if it's in search results
if current_server then
for _, server in ipairs(search_result) do
if server.address == current_server.address and
server.port == current_server.port then
return
end
end
end
-- Find first compatible server (favorite or public)
for _, server in ipairs(search_result) do
if is_server_protocol_compat(server.proto_min, server.proto_max) then
set_selected_server(server)
return
end
end
-- If no compatible server found, clear selection
set_selected_server(nil)
end
local function main_button_handler(tabview, fields, name, tabdata)
@ -462,6 +487,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
end
if event.type == "CHG" then
set_selected_server(server)
tabdata.pre_search_selection = nil
return true
end
end
@ -475,11 +501,9 @@ local function main_button_handler(tabview, fields, name, tabdata)
if fields.btn_delete_favorite then
local idx = core.get_table_index("servers")
if not idx then return end
local server = tabdata.lookup[idx]
if not server then return end
serverlistmgr.delete_favorite(server)
set_selected_server(server)
serverlistmgr.delete_favorite(tabdata.lookup[idx])
set_selected_server(tabdata.lookup[idx+1])
return true
end
@ -507,17 +531,16 @@ local function main_button_handler(tabview, fields, name, tabdata)
if fields.btn_mp_clear then
tabdata.search_for = ""
menudata.search_result = nil
if tabdata.pre_search_selection then
set_selected_server(tabdata.pre_search_selection)
tabdata.pre_search_selection = nil
end
return true
end
if fields.btn_mp_search or fields.key_enter_field == "te_search" then
tabdata.search_for = fields.te_search
search_server_list(fields.te_search)
if menudata.search_result then
-- Note: This clears the selection if there are no results
set_selected_server(menudata.search_result[1])
end
search_server_list(fields.te_search, tabdata)
return true
end

View file

@ -0,0 +1,12 @@
local scriptpath = core.get_builtin_path()
local pausepath = scriptpath.."pause_menu"..DIR_DELIM
local commonpath = scriptpath.."common"..DIR_DELIM
-- we're in-game, so no absolute paths are needed
defaulttexturedir = ""
local builtin_shared = {}
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
assert(loadfile(pausepath .. "register.lua"))(builtin_shared)
dofile(commonpath .. "settings" .. DIR_DELIM .. "init.lua")

View file

@ -0,0 +1,5 @@
local builtin_shared = ...
local make_registration = builtin_shared.make_registration
core.registered_on_formspec_input, core.register_on_formspec_input = make_registration()

View file

@ -2,9 +2,27 @@
#
# General format:
# name (Readable name) type type_args
# name (Readable name) [context] type type_args
#
# Note that the parts are separated by exactly one space
#
# `context` (optional) is used to document where the setting is read. It can be:
# - common: Read by both client and server.
# - client: Read by the client.
# (Includes settings read by the mainmenu.)
# - server: Read by the server.
# - world_creation: Read at world creation, thus only applied to new worlds.
# (Worlds are commonly created in the mainmenu (part of the client), but
# world creation is conceptually a server-side thing...)
# If not specified, the value is inherited from the context value of the containing
# category instead.
# For the builtin/settingtypes.txt file, every setting needs to have a context defined,
# either via a category containing it or via the setting itself. In game/mod-provided
# settingtypes.txt files, context annotations are invalid.
# Note: For context annotations, it's irrelevant whether changes to a setting
# after startup/game-join will be read. A separate mechanism for declaring that
# is needed.
#
# `type` can be:
# - int
# - string
@ -77,6 +95,8 @@
# Sections are marked by a single line in the format: [Section Name]
# Sub-section are marked by adding * in front of the section name: [*Sub-section]
# Sub-sub-sections have two * etc.
# A context (see above) can be specified optionally: [Section Name] [context]
# Context annotations on categories cannot be nested.
# There shouldn't be too many settings per category.
#
# The top-level categories "Advanced", "Client and Server" and "Mapgen" are
@ -84,7 +104,7 @@
# They contain settings not intended for the "average user".
[Controls]
[Controls] [client]
[*General]
@ -111,6 +131,13 @@ doubletap_jump (Double tap jump for fly) bool false
# enabled.
always_fly_fast (Always fly fast) bool true
# If enabled, the "Sneak" key will toggle when pressed.
# This functionality is ignored when fly is enabled.
toggle_sneak_key (Toggle Sneak key) bool false
# If enabled, the "Aux1" key will toggle when pressed.
toggle_aux1_key (Toggle Aux1 key) bool false
# The time in seconds it takes between repeated node placements when holding
# the place button.
#
@ -160,6 +187,36 @@ invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
# Requires: touch_support
touch_controls (Touchscreen controls) enum auto auto,true,false
# The kind of digging/placing controls used.
#
# * Tap
# Long/short tap anywhere on the screen to interact.
# Interaction happens at finger position.
#
# * Tap with crosshair
# Long/short tap anywhere on the screen to interact.
# Interaction happens at crosshair position.
#
# * Buttons with crosshair
# Use dedicated dig/place buttons to interact.
# Interaction happens at crosshair position.
#
# Requires: touchscreen
touch_interaction_style (Interaction style) enum tap tap,tap_crosshair,buttons_crosshair
# The gesture for punching players/entities.
# This can be overridden by games and mods.
#
# * Short tap
# Easy to use and well-known from other games that shall not be named.
#
# * Long tap
# Known from the classic Luanti mobile controls.
# Combat is more or less impossible.
#
# Requires: touchscreen, touch_interaction_style_tap
touch_punch_gesture (Punch gesture) enum short_tap short_tap,long_tap
# Touchscreen sensitivity multiplier.
#
# Requires: touchscreen
@ -175,12 +232,6 @@ touchscreen_threshold (Movement threshold) int 20 0 100
# Requires: touchscreen
touch_long_tap_delay (Threshold for long taps) int 400 100 1000
# Use crosshair to select object instead of whole screen.
# If enabled, a crosshair will be shown and will be used for selecting object.
#
# Requires: touchscreen
touch_use_crosshair (Use crosshair for touch screen) bool false
# Fixes the position of virtual joystick.
# If disabled, virtual joystick will center to first-touch's position.
#
@ -193,21 +244,7 @@ fixed_virtual_joystick (Fixed virtual joystick) bool false
# Requires: touchscreen
virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false
# The gesture for punching players/entities.
# This can be overridden by games and mods.
#
# * short_tap
# Easy to use and well-known from other games that shall not be named.
#
# * long_tap
# Known from the classic Luanti mobile controls.
# Combat is more or less impossible.
#
# Requires: touchscreen
touch_punch_gesture (Punch gesture) enum short_tap short_tap,long_tap
[Graphics and Audio]
[Graphics and Audio] [client]
[*Graphics]
@ -256,8 +293,8 @@ fps_max (Maximum FPS) int 60 1 4294967295
# Vertical screen synchronization. Your system may still force VSync on even if this is disabled.
vsync (VSync) bool false
# Maximum FPS when the window is not focused, or when the game is paused.
fps_max_unfocused (FPS when unfocused or paused) int 20 1 4294967295
# Maximum FPS when the window is not focused.
fps_max_unfocused (FPS when unfocused) int 10 1 4294967295
# View distance in nodes.
viewing_range (Viewing range) int 190 20 4000
@ -295,10 +332,6 @@ arm_inertia (Arm inertia) bool true
# For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double.
view_bobbing_amount (View bobbing factor) float 1.0 0.0 7.9
# Multiplier for fall bobbing.
# For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double.
fall_bobbing_amount (Fall bobbing factor) float 0.03 0.0 100.0
[**Camera]
# Field of view in degrees.
@ -541,8 +574,8 @@ shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true
# Requires: enable_dynamic_shadows, opengl
shadow_filters (Shadow filter quality) enum 1 0,1,2
# Enable colored shadows.
# On true translucent nodes cast colored shadows. This is expensive.
# Enable colored shadows for transculent nodes.
# This is expensive.
#
# Requires: enable_dynamic_shadows, opengl
shadow_map_color (Colored shadows) bool false
@ -742,9 +775,6 @@ console_color (Console color) string (0,0,0)
# In-game chat console background alpha (opaqueness, between 0 and 255).
console_alpha (Console alpha) int 200 0 255
# Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output.
clickable_chat_weblinks (Chat weblinks) bool true
# Optional override for chat weblink color.
chat_weblink_color (Weblink color) string #8888FF
@ -777,26 +807,26 @@ contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3 1
[Client and Server]
[*Client]
[*Client] [client]
# Save the map received by the client on disk.
enable_local_map_saving (Saving map received from server) bool false
# URL to the server list displayed in the Multiplayer Tab.
serverlist_url (Serverlist URL) string https://servers.luanti.org
serverlist_url (Serverlist URL) [common] string https://servers.luanti.org
# If enabled, account registration is separate from login in the UI.
# If disabled, new accounts will be registered automatically when logging in.
# If enabled, server account registration is separate from login in the UI.
# If disabled, connecting to a server will automatically register a new account.
enable_split_login_register (Enable split login/register) bool true
# URL to JSON file which provides information about the newest Luanti release.
# If this is empty the engine will never check for updates.
update_information_url (Update information URL) string https://www.minetest.net/release_info.json
update_information_url (Update information URL) string https://www.luanti.org/release_info.json
[*Server]
[*Server] [server]
# Name of the player.
# When running a server, clients connecting with this name are admins.
# When running a server, a client connecting with this name is admin.
# When starting from the main menu, this is overridden.
name (Admin name) string
@ -821,7 +851,7 @@ server_announce (Announce server) bool false
server_announce_send_players (Send player names to the server list) bool true
# Announce to this serverlist.
serverlist_url (Serverlist URL) string https://servers.luanti.org
serverlist_url (Serverlist URL) [common] string https://servers.luanti.org
# Message of the day displayed to players connecting.
motd (Message of the day) string
@ -860,12 +890,14 @@ protocol_version_min (Protocol version minimum) int 1 1 65535
# Files that are not present will be fetched the usual way.
remote_media (Remote media) string
# Enable/disable running an IPv6 server.
# Enable IPv6 support for server.
# Note that clients will be able to connect with both IPv4 and IPv6.
# Ignored if bind_address is set.
# Needs enable_ipv6 to be enabled.
ipv6_server (IPv6 server) bool false
#
# Requires: enable_ipv6
ipv6_server (IPv6 server) bool true
[*Server Security]
[*Server Security] [server]
# New users need to input this password.
default_password (Default password) string
@ -925,7 +957,7 @@ chat_message_limit_per_10sec (Chat message count limit) float 8.0 1.0
# Kick players who sent more than X messages per 10 seconds.
chat_message_limit_trigger_kick (Chat message kick threshold) int 50 1 65535
[*Server Gameplay]
[*Server Gameplay] [server]
# Controls length of day/night cycle.
# Examples:
@ -933,7 +965,7 @@ chat_message_limit_trigger_kick (Chat message kick threshold) int 50 1 65535
time_speed (Time speed) int 72 0
# Time of day when a new world is started, in millihours (0-23999).
world_start_time (World start time) int 6125 0 23999
world_start_time (World start time) [world_creation] int 6125 0 23999
# Time in seconds for item entity (dropped items) to live.
# Setting it to -1 disables the feature.
@ -988,7 +1020,7 @@ movement_liquid_sink (Liquid sinking) float 10.0
movement_gravity (Gravity) float 9.81
[Mapgen]
[Mapgen] [world_creation]
# A chosen map seed for a new map, leave empty for random.
# Will be overridden when creating a new world in the main menu.
@ -1004,7 +1036,7 @@ mg_name (Mapgen name) enum v7 v7,valleys,carpathian,v5,flat,fractal,singlenode,v
water_level (Water level) int 1 -31000 31000
# From how far blocks are generated for clients, stated in mapblocks (16 nodes).
max_block_generate_distance (Max block generate distance) int 10 1 32767
max_block_generate_distance (Max block generate distance) [server] int 10 1 32767
# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0).
# Only mapchunks completely within the mapgen limit are generated.
@ -1717,12 +1749,12 @@ mgvalleys_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500),
# Enable Lua modding support on client.
# This support is experimental and API can change.
enable_client_modding (Client modding) bool false
enable_client_modding (Client modding) [client] bool false
# Replaces the default main menu with a custom one.
main_menu_script (Main menu script) string
main_menu_script (Main menu script) [client] string
[**Mod Security]
[**Mod Security] [server]
# Prevent mods from doing insecure things like running shell commands.
secure.enable_security (Enable mod security) bool true
@ -1746,33 +1778,33 @@ secure.http_mods (HTTP mods) string
# - info
# - verbose
# - trace
debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose,trace
debug_log_level (Debug log level) [common] enum action ,none,error,warning,action,info,verbose,trace
# If the file size of debug.txt exceeds the number of megabytes specified in
# this setting when it is opened, the file is moved to debug.txt.1,
# deleting an older debug.txt.1 if it exists.
# debug.txt is only moved if this setting is positive.
debug_log_size_max (Debug log file size threshold) int 50 1
debug_log_size_max (Debug log file size threshold) [common] int 50 1
# Minimal level of logging to be written to chat.
chat_log_level (Chat log level) enum error ,none,error,warning,action,info,verbose,trace
chat_log_level (Chat log level) [client] enum error ,none,error,warning,action,info,verbose,trace
# Handling for deprecated Lua API calls:
# - none: Do not log deprecated calls
# - log: mimic and log backtrace of deprecated call (default).
# - error: abort on usage of deprecated call (suggested for mod developers).
deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error
deprecated_lua_api_handling (Deprecated Lua API handling) [common] enum log none,log,error
# Enable random user input (only used for testing).
random_input (Random input) bool false
random_input (Random input) [client] bool false
# Enable random mod loading (mainly used for testing).
random_mod_load_order (Random mod load order) bool false
random_mod_load_order (Random mod load order) [server] bool false
# Enable mod channels support.
enable_mod_channels (Mod channels) bool false
enable_mod_channels (Mod channels) [server] bool false
[**Mod Profiler]
[**Mod Profiler] [server]
# Load the game profiler to collect game profiling data.
# Provides a /profiler command to access the compiled profile.
@ -1812,7 +1844,7 @@ instrument.builtin (Builtin) bool false
# * Instrument the sampler being used to update the statistics.
instrument.profiler (Profiler) bool false
[**Engine Profiler]
[**Engine Profiler] [common]
# Print the engine's profiling data in regular intervals (in seconds).
# 0 = disable. Useful for developers.
@ -1821,15 +1853,7 @@ profiler_print_interval (Engine profiling data print interval) int 0 0
[*Advanced]
# Enable IPv6 support (for both client and server).
# Required for IPv6 connections to work at all.
enable_ipv6 (IPv6) bool true
# If enabled, invalid world data won't cause the server to shut down.
# Only enable this if you know what you are doing.
ignore_world_load_errors (Ignore world errors) bool false
[**Graphics]
[**Graphics] [client]
# Enables debug and error-checking in the OpenGL driver.
opengl_debug (OpenGL debug) bool false
@ -1866,7 +1890,7 @@ mesh_generation_threads (Mapblock mesh generation threads) int 0 0 8
# All mesh buffers with less than this number of vertices will be merged
# during map rendering. This improves rendering performance.
mesh_buffer_min_vertices (Minimum vertex count for mesh buffers) int 100 0 1000
mesh_buffer_min_vertices (Minimum vertex count for mesh buffers) int 300 0 1000
# True = 256
# False = 128
@ -1906,8 +1930,8 @@ texture_min_size (Base texture size) int 64 1 32768
client_mesh_chunk (Client Mesh Chunksize) int 1 1 16
# Decide the color depth of the texture used for the post-processing pipeline.
# Reducing this can improve performance, but might cause some effects (e.g. bloom)
# to not work.
# Reducing this can improve performance, but some effects (e.g. debanding)
# require more than 8 bits to work.
#
# Requires: enable_post_processing
post_processing_texture_bits (Color depth for post-processing texture) enum 16 8,10,16
@ -1918,10 +1942,9 @@ post_processing_texture_bits (Color depth for post-processing texture) enum 16 8
# Requires: enable_dynamic_shadows, opengl
shadow_poisson_filter (Poisson filtering) bool true
# Spread a complete update of shadow map over given number of frames.
# Spread a complete update of the shadow map over a given number of frames.
# Higher values might make shadows laggy, lower values
# will consume more resources.
# Minimum value: 1; maximum value: 16
#
# Requires: enable_dynamic_shadows, opengl
shadow_update_frames (Map shadows update frames) int 16 1 32
@ -1934,12 +1957,12 @@ shadow_update_frames (Map shadows update frames) int 16 1 32
# Requires: enable_post_processing, enable_bloom
enable_bloom_debug (Enable Bloom Debug) bool false
[**Sound]
[**Sound] [client]
# Comma-separated list of AL and ALC extensions that should not be used.
# Useful for testing. See al_extensions.[h,cpp] for details.
sound_extensions_blacklist (Sound Extensions Blacklist) string
[**Font]
[**Font] [client]
font_bold (Font bold by default) bool false
@ -1989,7 +2012,7 @@ mono_font_path_bold_italic (Bold and italic monospace font path) filepath fonts/
# This font will be used for certain languages or if the default font is unavailable.
fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf
[**Lighting]
[**Lighting] [client]
# Gradient of light curve at minimum light level.
# Controls the contrast of the lowest light levels.
@ -2015,43 +2038,49 @@ lighting_boost_spread (Light curve boost spread) float 0.2 0.0 0.4
[**Networking]
# Enable IPv6 support (for both client and server).
# Required for IPv6 connections to work at all.
enable_ipv6 (IPv6) [common] bool true
# Prometheus listener address.
# If Luanti is compiled with ENABLE_PROMETHEUS option enabled,
# enable metrics listener for Prometheus on that address.
# Metrics can be fetched on http://127.0.0.1:30000/metrics
prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000
prometheus_listener_address (Prometheus listener address) [server] string 127.0.0.1:30000
# Maximum size of the outgoing chat queue.
# Maximum size of the client's outgoing chat queue.
# 0 to disable queueing and -1 to make the queue size unlimited.
max_out_chat_queue_size (Maximum size of the outgoing chat queue) int 20 -1 32767
max_out_chat_queue_size (Maximum size of the client's outgoing chat queue) [client] int 20 -1 32767
# Timeout for client to remove unused map data from memory, in seconds.
client_unload_unused_data_timeout (Mapblock unload timeout) float 600.0 0.0
client_unload_unused_data_timeout (Mapblock unload timeout) [client] float 600.0 0.0
# Maximum number of mapblocks for client to be kept in memory.
# Set to -1 for unlimited amount.
client_mapblock_limit (Mapblock limit) int 7500 -1 2147483647
# Note that there is an internal dynamic minimum number of blocks that
# won't be deleted, depending on the current view range.
# Set to -1 for no limit.
client_mapblock_limit (Mapblock limit) [client] int 7500 -1 2147483647
# Maximum number of blocks that are simultaneously sent per client.
# The maximum total count is calculated dynamically:
# max_total = ceil((#clients + max_users) * per_client / 4)
max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) int 40 1 4294967295
max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) [server] int 40 1 4294967295
# To reduce lag, block transfers are slowed down when a player is building something.
# This determines how long they are slowed down after placing or removing a node.
full_block_send_enable_min_time_from_building (Delay in sending blocks after building) float 2.0 0.0
full_block_send_enable_min_time_from_building (Delay in sending blocks after building) [server] float 2.0 0.0
# Maximum number of packets sent per send step in the low-level networking code.
# You generally don't need to change this, however busy servers may benefit from a higher number.
max_packets_per_iteration (Max. packets per iteration) int 1024 1 65535
max_packets_per_iteration (Max. packets per iteration) [common] int 1024 1 65535
# Compression level to use when sending mapblocks to the client.
# -1 - use default compression level
# 0 - least compression, fastest
# 9 - best compression, slowest
map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9
map_compression_level_net (Map Compression Level for Network Transfer) [server] int -1 -1 9
[**Server]
[**Server] [server]
# Format of player chat messages. The following strings are valid placeholders:
# @name, @message, @timestamp (optional)
@ -2071,7 +2100,7 @@ kick_msg_crash (Crash message) string This server has experienced an internal er
# Set this to true if your server is set up to restart automatically.
ask_reconnect_on_crash (Ask to reconnect after crash) bool false
[**Server/Env Performance]
[**Server/Env Performance] [server]
# Length of a server tick (the interval at which everything is generally updated),
# stated in seconds.
@ -2122,14 +2151,14 @@ max_objects_per_block (Maximum objects per block) int 256 256 65535
active_block_mgmt_interval (Active block management interval) float 2.0 0.0
# Length of time between Active Block Modifier (ABM) execution cycles, stated in seconds.
abm_interval (ABM interval) float 1.0 0.0
abm_interval (ABM interval) float 1.0 0.1 30.0
# The time budget allowed for ABMs to execute on each step
# (as a fraction of the ABM Interval)
abm_time_budget (ABM time budget) float 0.2 0.1 0.9
# Length of time between NodeTimer execution cycles, stated in seconds.
nodetimer_interval (NodeTimer interval) float 0.2 0.0
nodetimer_interval (NodeTimer interval) float 0.2 0.1 1.0
# Max liquids processed per step.
liquid_loop_max (Liquid loop max) int 100000 1 4294967295
@ -2164,7 +2193,7 @@ server_side_occlusion_culling (Server-side occlusion culling) bool true
# Stated in MapBlocks (16 nodes).
block_cull_optimize_distance (Block cull optimize distance) int 25 2 2047
[**Mapgen]
[**Mapgen] [server]
# Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).
# WARNING: There is no benefit, and there are several dangers, in
@ -2172,7 +2201,7 @@ block_cull_optimize_distance (Block cull optimize distance) int 25 2 2047
# Reducing this value increases cave and dungeon density.
# Altering this value is for special usage, leaving it unchanged is
# recommended.
chunksize (Chunk size) int 5 1 10
chunksize (Chunk size) [world_creation] int 5 1 10
# Dump the mapgen debug information.
enable_mapgen_debug_info (Mapgen debug) bool false
@ -2200,7 +2229,7 @@ emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 1
# 'on_generated'. For many users the optimum setting may be '1'.
num_emerge_threads (Number of emerge threads) int 1 0 32767
[**cURL]
[**cURL] [common]
# Maximum time an interactive request (e.g. server list fetch) may take, stated in milliseconds.
curl_timeout (cURL interactive timeout) int 20000 1000 2147483647
@ -2217,42 +2246,49 @@ curl_file_download_timeout (cURL file download timeout) int 300000 5000 21474836
[**Miscellaneous]
# Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output.
clickable_chat_weblinks (Chat weblinks) [client] bool true
# If enabled, invalid world data won't cause the server to shut down.
# Only enable this if you know what you are doing.
ignore_world_load_errors (Ignore world errors) [server] bool false
# Adjust the detected display density, used for scaling UI elements.
display_density_factor (Display Density Scaling Factor) float 1 0.5 5.0
display_density_factor (Display Density Scaling Factor) [client] float 1 0.5 5.0
# Windows systems only: Start Luanti with the command line window in the background.
# Contains the same information as the file debug.txt (default name).
enable_console (Enable console window) bool false
enable_console (Enable console window) [common] bool false
# Number of extra blocks that can be loaded by /clearobjects at once.
# This is a trade-off between SQLite transaction overhead and
# memory consumption (4096=100MB, as a rule of thumb).
max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096 0 4294967295
max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) [server] int 4096 0 4294967295
# World directory (everything in the world is stored here).
# Not needed if starting from the main menu.
map-dir (Map directory) path
map-dir (Map directory) [server] path
# See https://www.sqlite.org/pragma.html#pragma_synchronous
sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2
sqlite_synchronous (Synchronous SQLite) [server] enum 2 0,1,2
# Compression level to use when saving mapblocks to disk.
# -1 - use default compression level
# 0 - least compression, fastest
# 9 - best compression, slowest
map_compression_level_disk (Map Compression Level for Disk Storage) int -1 -1 9
map_compression_level_disk (Map Compression Level for Disk Storage) [server] int -1 -1 9
# Enable usage of remote media server (if provided by server).
# Remote servers offer a significantly faster way to download media (e.g. textures)
# when connecting to the server.
enable_remote_media_server (Connect to external media server) bool true
enable_remote_media_server (Connect to external media server) [client] bool true
# File in client/serverlist/ that contains your favorite servers displayed in the
# Multiplayer Tab.
serverlist_file (Serverlist file) string favoriteservers.json
serverlist_file (Serverlist file) [client] string favoriteservers.json
[*Gamepads]
[*Gamepads] [client]
# Enable joysticks. Requires a restart to take effect
enable_joysticks (Enable joysticks) bool false
@ -2275,7 +2311,7 @@ joystick_deadzone (Joystick dead zone) int 2048 0 65535
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170.0 0.001
[*Hide: Temporary Settings]
[*Hide: Temporary Settings] [common]
# Path to texture directory. All textures are first searched from here.
texture_path (Texture path) path

View file

@ -44,8 +44,6 @@ centroid varying float nightRatio;
varying float perspective_factor;
#endif
varying float area_enable_parallax;
varying highp vec3 eyeVec;
varying vec3 sunTint;
varying float nightFactor;

View file

@ -70,7 +70,7 @@ vec4 applyBloom(vec4 color, vec2 uv)
equation used: ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
*/
// see https://github.com/minetest/minetest/pull/14688
// highp for GLES, see <https://github.com/luanti-org/luanti/pull/14688>
highp vec3 uncharted2Tonemap(highp vec3 x)
{
return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;

View file

@ -1,7 +1,7 @@
# Project properties
PROJECT_NAME = @PROJECT_NAME_CAPITALIZED@
PROJECT_NUMBER = @VERSION_STRING@
PROJECT_LOGO = @CMAKE_CURRENT_SOURCE_DIR@/misc/minetest.svg
PROJECT_LOGO = @CMAKE_CURRENT_SOURCE_DIR@/misc/luanti.svg
# Parsing
JAVADOC_AUTOBRIEF = YES

View file

@ -10,15 +10,16 @@ This list is largely advisory and items may be reevaluated once the time comes.
* `game.conf` name/id mess
* remove `depends.txt` / `description.txt` (would simplify ContentDB and Luanti code a little)
* rotate moon texture by 180°, making it coherent with the sun
* https://github.com/minetest/minetest/pull/11902
* https://github.com/luanti-org/luanti/pull/11902
* remove undocumented `set_physics_override(num, num, num)`
* remove special handling of `${key}` syntax in metadata values
* remove old_move
* change physics_override `sneak` to disable the speed change and not just the node clipping
* https://github.com/minetest/minetest/issues/13699
* https://github.com/luanti-org/luanti/issues/13699
* migrate from player names to UUIDs, this would enable renaming of accounts and unicode player names (if desired)
* harmonize use_texture_alpha between entities & nodes, change default to 'opaque' and remove bool compat code
* merge `sound` and `sounds` table in itemdef
* remove `DIR_DELIM` from Lua
* stop reading initial properties from bare entity def
* change particle default blend mode to `clip`
* remove built-in knockback and related functions entirely

View file

@ -1,12 +1,12 @@
Luanti Lua Client Modding API Reference 5.11.0
Luanti Lua Client Modding API Reference 5.12.0
==============================================
**WARNING**: if you're looking for the `minetest` namespace (e.g. `minetest.something`),
it's now called `core` due to the renaming of Luanti (formerly Minetest).
`minetest` will keep existing as an alias, so that old code won't break.
* More information at <http://www.minetest.net/>
* Developer Wiki: <http://dev.minetest.net/>
* More information at <http://www.luanti.org/>
* Developer Wiki: <https://dev.luanti.org/>
Introduction
------------
@ -23,7 +23,7 @@ Transferring client-sided mods from the server to the client is planned, but not
If you see a deficiency in the API, feel free to attempt to add the
functionality in the engine and API. You can send such improvements as
source code patches on GitHub (https://github.com/minetest/minetest).
source code patches on GitHub.
Programming in Lua
------------------

View file

@ -70,14 +70,14 @@ For Void users:
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
git clone --depth 1 https://github.com/minetest/minetest.git
cd minetest
git clone --depth 1 https://github.com/luanti-org/luanti
cd luanti
Download source, without using Git:
wget https://github.com/minetest/minetest/archive/master.tar.gz
wget https://github.com/luanti-org/luanti/archive/master.tar.gz
tar xf master.tar.gz
cd minetest-master
cd luanti-master
## Build
@ -88,7 +88,7 @@ Build a version that runs directly from the source directory:
Run it:
./bin/minetest
./bin/luanti
- Use `cmake . -LH` to see all CMake options and their current state.
- If you want to install it system-wide (or are making a distribution package),

View file

@ -16,7 +16,7 @@ brew install cmake freetype gettext gmp hiredis jpeg-turbo jsoncpp leveldb libog
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
```bash
git clone --depth 1 https://github.com/minetest/minetest.git luanti
git clone --depth 1 https://github.com/luanti-org/luanti luanti
cd luanti
```

View file

@ -33,8 +33,8 @@ Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-window
### a) Using the vcpkg toolchain and CMake GUI
1. Start up the CMake GUI
2. Select **Browse Source...** and select DIR/minetest
3. Select **Browse Build...** and select DIR/minetest-build
2. Select **Browse Source...** and select DIR/luanti
3. Select **Browse Build...** and select DIR/luanti-build
4. Select **Configure**
5. Choose the right visual Studio version and target platform. It has to match the version of the installed dependencies
6. Choose **Specify toolchain file for cross-compiling**

View file

@ -2,15 +2,15 @@
## Wiki
Some important development docs are found in the wiki: https://dev.minetest.net/
Some important development docs are found in the wiki: https://dev.luanti.org/
Notable pages:
- [Releasing Luanti](https://dev.minetest.net/Releasing_Minetest)
- [Engine translations](https://dev.minetest.net/Translation#Maintaining_engine_translations)
- [Changelog](https://dev.minetest.net/Changelog)
- [Organisation](https://dev.minetest.net/Organisation)
- [Code style guidelines](https://dev.minetest.net/Code_style_guidelines)
- [Releasing Luanti](https://dev.luanti.org/Releasing_Luanti)
- [Engine translations](https://dev.luanti.org/Translation#Maintaining_engine_translations)
- [Changelog](https://dev.luanti.org/Changelog)
- [Organisation](https://dev.luanti.org/Organisation)
- [Code style guidelines](https://dev.luanti.org/Code_style_guidelines)
## In this folder

View file

@ -2,7 +2,7 @@
## Sign the Android APK from CI
The [Github Actions Workflow](https://github.com/minetest/minetest/actions?query=workflow%3Aandroid+event%3Apush)
The [Github Actions Workflow](https://github.com/luanti-org/luanti/actions?query=workflow%3Aandroid+event%3Apush)
automatically produces an APK for each architecture.
Before installing them onto a device they however need to be signed.

View file

@ -9,7 +9,7 @@ To get usable results you need to build Luanti with debug symbols
Run the client (or server) like this and do whatever you wanted to test:
```bash
perf record -z --call-graph dwarf -- ./bin/minetest
perf record -z --call-graph dwarf -- ./bin/luanti
```
This will leave a file called "perf.data".

View file

@ -7,7 +7,7 @@ following documents:
* [What is Minetest?](http://c55.me/blog/?p=1491)
* [celeron55's roadmap](https://forum.luanti.org/viewtopic.php?t=9177)
* [celeron55's comment in "A clear mission statement for Minetest is missing"](https://github.com/minetest/minetest/issues/3476#issuecomment-167399287)
* [celeron55's comment in "A clear mission statement for Minetest is missing"](https://github.com/luanti-org/luanti/issues/3476#issuecomment-167399287)
* [Core developer to-do/wish lists](https://forum.luanti.org/viewforum.php?f=7)
## 2. Medium-term Roadmap
@ -16,7 +16,7 @@ These are the current medium-term goals for Luanti development, in no
particular order.
These goals were created from the top points in a
[roadmap brainstorm](https://github.com/minetest/minetest/issues/10461).
[roadmap brainstorm](https://github.com/luanti-org/luanti/issues/10461).
This should be reviewed approximately yearly, or when goals are achieved.
Pull requests that address one of these goals will be labeled as "Roadmap".
@ -32,9 +32,9 @@ Once that is done, fancier features can be worked on, such as water shaders,
shadows, and improved lighting.
Examples include
[transparency sorting](https://github.com/minetest/minetest/issues/95),
[particle performance](https://github.com/minetest/minetest/issues/1414),
[general view distance](https://github.com/minetest/minetest/issues/7222).
[transparency sorting](https://github.com/luanti-org/luanti/issues/95),
[particle performance](https://github.com/luanti-org/luanti/issues/1414),
[general view distance](https://github.com/luanti-org/luanti/issues/7222).
This includes work on maintaining
[our Irrlicht fork](https://github.com/minetest/irrlicht), and switching to
@ -43,16 +43,16 @@ alternative libraries to replace Irrlicht functionality as needed
### 2.2 Internal code refactoring
To ensure sustainable development, Luanti's code needs to be
[refactored and improved](https://github.com/minetest/minetest/pulls?q=is%3Aopen+sort%3Aupdated-desc+label%3A%22Code+quality%22+).
[refactored and improved](https://github.com/luanti-org/luanti/pulls?q=is%3Aopen+sort%3Aupdated-desc+label%3A%22Code+quality%22).
This will remove code rot and allow for more efficient development.
### 2.3 UI Improvements
A [formspec replacement](https://github.com/minetest/minetest/issues/6527) is
A [formspec replacement](https://github.com/luanti-org/luanti/issues/6527) is
needed to make GUIs better and easier to create. This replacement could also
be a replacement for HUDs, allowing for a unified API.
A [new mainmenu](https://github.com/minetest/minetest/issues/6733) is needed to
A [new mainmenu](https://github.com/luanti-org/luanti/issues/6733) is needed to
improve user experience. First impressions matter, and the current main menu
doesn't do a very good job at selling Luanti or explaining what it is.
A new main menu should promote games to users, allowing Minetest Game to no
@ -65,5 +65,5 @@ an issue for any large changes before spending lots of time.
There are still a significant number of issues with objects.
Collisions,
[performance](https://github.com/minetest/minetest/issues/6453),
[performance](https://github.com/luanti-org/luanti/issues/6453),
API convenience, and discrepancies between players and entities.

View file

@ -4,22 +4,25 @@ We provide Luanti server Docker images using the GitHub container registry.
Images are built on each commit and available using the following tag scheme:
* `ghcr.io/minetest/minetest:master` (latest build)
* `ghcr.io/minetest/minetest:<tag>` (specific Git tag)
* `ghcr.io/minetest/minetest:latest` (latest Git tag, which is the stable release)
* `ghcr.io/luanti-org/luanti:master` (latest build)
* `ghcr.io/luanti-org/luanti:<tag>` (specific Git tag)
* `ghcr.io/luanti-org/luanti:latest` (latest Git tag, which is the stable release)
See [here](https://github.com/minetest/minetest/pkgs/container/minetest) for all available tags.
See [here](https://github.com/luanti-org/luanti/pkgs/container/luanti) for all available tags.
Versions released before the project was renamed are available with the same tag scheme at `ghcr.io/minetest/minetest`.
See [here](https://github.com/orgs/minetest/packages/container/package/minetest) for all available tags.
For a quick test you can easily run:
```shell
docker run ghcr.io/minetest/minetest:master
docker run ghcr.io/luanti-org/luanti:master
```
To use it in a production environment, you should use volumes bound to the Docker host to persist data and modify the configuration:
```shell
docker create -v /home/minetest/data/:/var/lib/minetest/ -v /home/minetest/conf/:/etc/minetest/ ghcr.io/minetest/minetest:master
docker create -v /home/minetest/data/:/var/lib/minetest/ -v /home/minetest/conf/:/etc/minetest/ ghcr.io/luanti-org/luanti:master
```
You may also want to use [Docker Compose](https://docs.docker.com/compose):
@ -29,7 +32,7 @@ You may also want to use [Docker Compose](https://docs.docker.com/compose):
version: "2"
services:
minetest_server:
image: ghcr.io/minetest/minetest:master
image: ghcr.io/luanti-org/luanti:master
restart: always
networks:
- default

View file

@ -5,10 +5,10 @@ Luanti Lua Modding API Reference
it's now called `core` due to the renaming of Luanti (formerly Minetest).
`minetest` will keep existing as an alias, so that old code won't break.
* More information at <http://www.minetest.net/>
* Developer Wiki: <http://dev.minetest.net/>
* More information at <http://www.luanti.org/>
* Developer Wiki: <https://dev.luanti.org/>
* (Unofficial) Minetest Modding Book by rubenwardy: <https://rubenwardy.com/minetest_modding_book/>
* Modding tools: <https://github.com/minetest/modtools>
* Modding tools: <https://github.com/luanti-org/modtools>
Introduction
------------
@ -194,7 +194,8 @@ Mod directory structure
│   │   │ └── another_subfolder
│   │   └── bar_subfolder
│   ├── sounds
│   ├── media
│   ├── fonts
│ ├── media
│   ├── locale
│   └── <custom data>
└── another
@ -265,7 +266,7 @@ The main Lua script. Running this script should register everything it
wants to register. Subsequent execution depends on Luanti calling the
registered callbacks.
### `textures`, `sounds`, `media`, `models`, `locale`
### `textures`, `sounds`, `media`, `models`, `locale`, `fonts`
Media files (textures, sounds, whatever) that will be transferred to the
client and will be available for use by the mod and translation files for
@ -278,6 +279,7 @@ Accepted formats are:
images: .png, .jpg, .tga
sounds: .ogg vorbis
models: .x, .b3d, .obj, (since version 5.10:) .gltf, .glb
fonts: .ttf, .woff (both since version 5.11, see notes below)
Other formats won't be sent to the client (e.g. you can store .blend files
in a folder for convenience, without the risk that such files are transferred)
@ -325,7 +327,7 @@ Many glTF features are not supported *yet*, including:
* Double-sided materials don't work
* Alternative means of supplying data
* Embedded images. You can use `gltfutil.py` from the
[modding tools](https://github.com/minetest/modtools) to strip or extract embedded images.
[modding tools](https://github.com/luanti-org/modtools) to strip or extract embedded images.
* References to files via URIs
Textures are supplied solely via the same means as for the other model file formats:
@ -342,6 +344,28 @@ For example, if your model used an emissive material,
you should expect that a future version of Luanti may respect this,
and thus cause your model to render differently there.
#### Custom fonts
You can supply custom fonts in TrueType Font (`.ttf`) or Web Open Font Format (`.woff`) format.
The former is supported primarily for convenience. The latter is preferred due to its compression.
In the future, having multiple custom fonts and the ability to switch between them is planned,
but for now this feature is limited to the ability to override Luanti's default fonts via mods.
It is recommended that this only be used by game mods to set a look and feel.
The stems (file names without extension) are self-explanatory:
* Regular variants:
* `regular`
* `bold`
* `italic`
* `bold_italic`
* Monospaced variants:
* `mono`
* `mono_bold`
* `mono_italic`
* `mono_bold_italic`
Naming conventions
------------------
@ -1460,8 +1484,6 @@ Node drawtypes
There are a bunch of different looking node types.
Look for examples in `games/devtest` or `games/minetest_game`.
* `normal`
* A node-sized cube.
* `airlike`
@ -1500,7 +1522,7 @@ Look for examples in `games/devtest` or `games/minetest_game`.
* `allfaces`
* Often used for partially-transparent nodes.
* External sides of textures, and unlike other drawtypes, the external sides
of other blocks, are visible from the inside.
of other nodes, are visible from the inside.
* `allfaces_optional`
* Often used for leaves nodes.
* This switches between `normal`, `glasslike` and `allfaces` according to
@ -1574,7 +1596,8 @@ Look for examples in `games/devtest` or `games/minetest_game`.
Node boxes
----------
Node selection boxes are defined using "node boxes".
Node selection boxes and collision boxes, and the appearance of the `nodebox`
drawtype, are defined using "node boxes".
A nodebox is defined as any of:
@ -1659,7 +1682,9 @@ roughly 1x1x1 meters in size.
A 'mapblock' (often abbreviated to 'block') is 16x16x16 nodes and is the
fundamental region of a world that is stored in the world database, sent to
clients and handled by many parts of the engine.
clients and handled by many parts of the engine. This size is available as the
constant `core.MAP_BLOCKSIZE` (=16).
'mapblock' is preferred terminology to 'block' to help avoid confusion with
'node', however 'block' often appears in the API.
@ -1668,6 +1693,38 @@ A 'mapchunk' (sometimes abbreviated to 'chunk') is usually 5x5x5 mapblocks
the map generator.
The size in mapblocks has been chosen to optimize map generation.
### Mapblock status
A mapblock being "loaded" means that is in memory. These are the mapblocks that
API functions like `core.get_node` or `core.set_node` can operate on. To reach
this state, the mapblock must first go through the process of being "emerged".
This means that it is loaded from disk, and/or, if it isn't yet generated,
generated by the map generator.
Mapblocks are loaded in a broad area around each player. They become "unloaded"
again if no player is close enough. The engine commonly represents the contents
of unloaded mapblocks as `"ignore"` nodes.
A mapblock being "active" means that it is not only in memory, but also affected
by world simulation:
* Entities are active
* They are in memory as `ServerActiveObject`, exposed to Lua as `ObjectRef`
* They exist in Lua as luaentity tables
* ABMs are executed
* Node timers are executed
Also, when a mapblock is "activated", LBMs are executed. Mapblocks are active
in a smaller area around each player, and are "deactivated" again if no player
is close enough.
Related API functions:
* `core.compare_block_status`
* `core.forceload_block`
* `core.load_area`
* `core.emerge_area`
Coordinates
-----------
@ -1691,7 +1748,7 @@ node position (0,0,0) to node position (15,15,15).
To calculate the blockpos of the mapblock that contains the node at 'nodepos',
for each axis:
* blockpos = math.floor(nodepos / 16)
* blockpos = math.floor(nodepos / core.MAP_BLOCKSIZE)
#### Converting blockpos to min/max node positions
@ -1699,9 +1756,9 @@ To calculate the min/max node positions contained in the mapblock at 'blockpos',
for each axis:
* Minimum:
nodepos = blockpos * 16
nodepos = blockpos * core.MAP_BLOCKSIZE
* Maximum:
nodepos = blockpos * 16 + 15
nodepos = (blockpos + 1) * core.MAP_BLOCKSIZE - 1
@ -2002,6 +2059,21 @@ that acts as tool in a gameplay sense as a craftitem, and vice-versa.
Craftitems can be used for items that neither need to be a node
nor a tool.
Special Items
-------------
The following items are predefined and have special properties.
* `"unknown"`: An item that represents every item which has not been registered
* `"air"`: The node which appears everywhere where no other node is
* `"ignore"`: Mapblocks that are not loaded are represented using this node.
* Also used for nodes that have not yet been set by the map generator.
* This is also what appears outside of the map boundary.
* `""`: The player's hand, which is in use whenever the player wields no item.
* Its range and tool capabilities are also used as a fallback for the wielded item.
* It can be overridden to change those properties:
* globally using `core.override_item`
* per-player using the special `"hand"` inventory list
Amount and wear
---------------
@ -3744,7 +3816,8 @@ Player Inventory lists
* `craftresult`: list containing the crafted output
* `hand`: list containing an override for the empty hand
* Is not created automatically, use `InvRef:set_size`
* Is only used to enhance the empty hand's tool capabilities
* Players use the first item in this list as their hand
* It behaves as if the default hand `""` has been overridden for this specific player
Custom lists can be added and deleted with `InvRef:set_size(name, size)` like
any other inventory.
@ -4120,6 +4193,11 @@ Helper functions
* returns time with microsecond precision. May not return wall time.
* `table.copy(table)`: returns a table
* returns a deep copy of `table`
* strips metatables, but this may change in the future
* `table.copy_with_metatables(table)`
* since 5.12
* `table` can also be non-table value, which will be returned as-is
* preserves metatables as they are
* `table.indexof(list, val)`: returns the smallest numerical index containing
the value `val` in the table `list`. Non-numerical indices are ignored.
If `val` could not be found, `-1` is returned. `list` must not have
@ -4346,7 +4424,7 @@ Hello @1, how are you today?=Hallo @1, wie geht es dir heute?
```
For old translation files, consider using the script `mod_translation_updater.py`
in the Luanti [modtools](https://github.com/minetest/modtools) repository to
in the Luanti [modtools](https://github.com/luanti-org/modtools) repository to
generate and update translation files automatically from the Lua sources.
Gettext translation file format
@ -4442,22 +4520,25 @@ textdomain match the mod name, but this isn't required.
Perlin noise
============
Fractal value noise
===================
Value noise creates a continuously-varying value depending on the input values.
It is similar to Perlin noise, but may exhibit more geometric artifacts,
as it interpolates between values and not between gradients as in Perlin noise.
Perlin noise creates a continuously-varying value depending on the input values.
Usually in Luanti the input values are either 2D or 3D coordinates in nodes.
The result is used during map generation to create the terrain shape, vary heat
and humidity to distribute biomes, vary the density of decorations or vary the
structure of ores.
Structure of perlin noise
-------------------------
Structure of fractal value noise
--------------------------------
An 'octave' is a simple noise generator that outputs a value between -1 and 1.
The smooth wavy noise it generates has a single characteristic scale, almost
like a 'wavelength', so on its own does not create fine detail.
Due to this perlin noise combines several octaves to create variation on
Due to this fractal value noise combines several octaves to create variation on
multiple scales. Each additional octave has a smaller 'wavelength' than the
previous.
@ -4465,7 +4546,7 @@ This combination results in noise varying very roughly between -2.0 and 2.0 and
with an average value of 0.0, so `scale` and `offset` are then used to multiply
and offset the noise variation.
The final perlin noise variation is created as follows:
The final fractal value noise variation is created as follows:
noise = offset + scale * (octave1 +
octave2 * persistence +
@ -4583,7 +4664,7 @@ with restraint.
#### `absvalue`
The absolute value of each octave's noise variation is used when combining the
octaves. The final perlin noise variation is created as follows:
octaves. The final value noise variation is created as follows:
noise = offset + scale * (abs(octave1) +
abs(octave2) * persistence +
@ -4593,7 +4674,7 @@ noise = offset + scale * (abs(octave1) +
### Format example
For 2D or 3D perlin noise or perlin noise maps:
For 2D or 3D value noise or value noise maps:
```lua
np_terrain = {
@ -4628,13 +4709,13 @@ All default ores are of the uniformly-distributed scatter type.
Randomly chooses a location and generates a cluster of ore.
If `noise_params` is specified, the ore will be placed if the 3D perlin noise
If `noise_params` is specified, the ore will be placed if the 3D value noise
at that point is greater than the `noise_threshold`, giving the ability to
create a non-equal distribution of ore.
### `sheet`
Creates a sheet of ore in a blob shape according to the 2D perlin noise
Creates a sheet of ore in a blob shape according to the 2D value noise
described by `noise_params` and `noise_threshold`. This is essentially an
improved version of the so-called "stratus" ore seen in some unofficial mods.
@ -4667,14 +4748,14 @@ noise parameters `np_puff_top` and `np_puff_bottom`, respectively.
### `blob`
Creates a deformed sphere of ore according to 3d perlin noise described by
Creates a deformed sphere of ore according to 3d value noise described by
`noise_params`. The maximum size of the blob is `clust_size`, and
`clust_scarcity` has the same meaning as with the `scatter` type.
### `vein`
Creates veins of ore varying in density by according to the intersection of two
instances of 3d perlin noise with different seeds, both described by
instances of 3d value noise with different seeds, both described by
`noise_params`.
`random_factor` varies the influence random chance has on placement of an ore
@ -4709,8 +4790,8 @@ computationally expensive than any other ore.
Creates a single undulating ore stratum that is continuous across mapchunk
borders and horizontally spans the world.
The 2D perlin noise described by `noise_params` defines the Y coordinate of
the stratum midpoint. The 2D perlin noise described by `np_stratum_thickness`
The 2D value noise described by `noise_params` defines the Y coordinate of
the stratum midpoint. The 2D value noise described by `np_stratum_thickness`
defines the stratum's vertical thickness (in units of nodes). Due to being
continuous across mapchunk borders the stratum's vertical thickness is
unlimited.
@ -4958,7 +5039,7 @@ and the array index for a position p contained completely in p1..p2 is:
`(p.Z - p1.Z) * Ny * Nx + (p.Y - p1.Y) * Nx + (p.X - p1.X) + 1`
Note that this is the same "flat 3D array" format as
`PerlinNoiseMap:get3dMap_flat()`.
`ValueNoiseMap:get3dMap_flat()`.
VoxelArea objects (see section [`VoxelArea`]) can be used to simplify calculation
of the index for a single point in a flat VoxelManip array.
@ -5149,7 +5230,7 @@ The coordinates are *inclusive*, like most other things in Luanti.
* The position (x, y, z) is not checked for being inside the area volume,
being outside can cause an incorrect index result.
* Useful for things like `VoxelManip`, raw Schematic specifiers,
`PerlinNoiseMap:get2d`/`3dMap`, and so on.
`ValueNoiseMap:get2d`/`3dMap`, and so on.
* `indexp(p)`: same functionality as `index(x, y, z)` but takes a vector.
* As with `index(x, y, z)`, the components of `p` must be integers, and `p`
is not checked for being inside the area volume.
@ -5556,7 +5637,7 @@ Utilities
* It's possible that multiple Luanti instances are running at the same
time, which may lead to corruption if you are not careful.
* `core.is_singleplayer()`
* `core.features`: Table containing API feature flags
* `core.features`: Table containing *server-side* API feature flags
```lua
{
@ -5667,10 +5748,13 @@ Utilities
biome_weights = true,
-- Particles can specify a "clip" blend mode (5.11.0)
particle_blend_clip = true,
-- The `match_meta` optional parameter is available for `InvRef:remove_item()` (5.12.0)
remove_item_match_meta = true,
}
```
* `core.has_feature(arg)`: returns `boolean, missing_features`
* checks for *server-side* feature availability
* `arg`: string or table in format `{foo=true, bar=true}`
* `missing_features`: `{foo=true, bar=true}`
* `core.get_player_information(player_name)`: Table containing information
@ -5692,17 +5776,40 @@ Utilities
min_jitter = 0.01, -- minimum packet time jitter
max_jitter = 0.5, -- maximum packet time jitter
avg_jitter = 0.03, -- average packet time jitter
-- The version information is provided by the client and may be spoofed
-- or inconsistent in engine forks. You must not use this for checking
-- feature availability of clients. Instead, do use the fields
-- `protocol_version` and `formspec_version` where it matters.
-- Use `core.protocol_versions` to map Luanti versions to protocol versions.
-- This version string is only suitable for analysis purposes.
version_string = "0.4.9-git", -- full version string
-- the following information is available in a debug build only!!!
-- DO NOT USE IN MODS
--ser_vers = 26, -- serialization version used by client
--serialization_version = 26, -- serialization version used by client
--major = 0, -- major version number
--minor = 4, -- minor version number
--patch = 10, -- patch version number
--vers_string = "0.4.9-git", -- full version string
--state = "Active" -- current client state
}
```
* `core.protocol_versions`:
* Table mapping Luanti versions to corresponding protocol versions for modder convenience.
* For example, to check whether a client has at least the feature set
of Luanti 5.8.0 or newer, you could do:
`core.get_player_information(player_name).protocol_version >= core.protocol_versions["5.8.0"]`
* (available since 5.11)
```lua
{
[version string] = protocol version at time of release
-- every major and minor version has an entry
-- patch versions only for the first release whose protocol version is not already present in the table
}
```
* `core.get_player_window_information(player_name)`:
```lua
@ -5840,16 +5947,23 @@ Call these functions only at load time!
### Environment
* `core.register_node(name, node definition)`
* `core.register_craftitem(name, item definition)`
* `core.register_tool(name, item definition)`
* `core.register_node(name, nodedef)`
* register a node with its definition
* Note: you must pass a clean table that hasn't already been used for
another registration to this function, as it will be modified.
* `core.register_craftitem(name, itemdef)`
* register an item with its definition
* Note: (as above)
* `core.register_tool(name, tooldef)`
* register a tool item with its definition
* Note: (as above)
* `core.override_item(name, redefinition, del_fields)`
* `redefinition` is a table of fields `[name] = new_value`,
overwriting fields of or adding fields to the existing definition.
* `del_fields` is a list of field names to be set
to `nil` ("deleted from") the original definition.
* Overrides fields of an item registered with register_node/tool/craftitem.
* Note: Item must already be defined, (opt)depend on the mod defining it.
* Note: Item must already be defined.
* Example: `core.override_item("default:mese",
{light_source=core.LIGHT_MAX}, {"sounds"})`:
Overwrites the `light_source` field,
@ -5857,7 +5971,7 @@ Call these functions only at load time!
* `core.unregister_item(name)`
* Unregisters the item from the engine, and deletes the entry with key
`name` from `core.registered_items` and from the associated item table
according to its nature: `core.registered_nodes`, etc.
according to its nature (e.g. `core.registered_nodes`)
* `core.register_entity(name, entity definition)`
* `core.register_abm(abm definition)`
* `core.register_lbm(lbm definition)`
@ -6401,18 +6515,24 @@ Environment access
first value: Table with all node positions
second value: Table with the count of each node with the node name
as index
* Area volume is limited to 4,096,000 nodes
* Area volume is limited to 150,000,000 nodes
* `core.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a
list of positions.
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* Return value: Table with all node positions with a node air above
* Area volume is limited to 4,096,000 nodes
* `core.get_perlin(noiseparams)`
* Return world-specific perlin noise.
* Area volume is limited to 150,000,000 nodes
* `core.get_value_noise(noiseparams)`
* Return world-specific value noise.
* The actual seed used is the noiseparams seed plus the world seed.
* `core.get_value_noise(seeddiff, octaves, persistence, spread)`
* Deprecated: use `core.get_value_noise(noiseparams)` instead.
* Return world-specific value noise
* `core.get_perlin(noiseparams)`
* Deprecated: use `core.get_value_noise(noiseparams)` instead.
* Return world-specific value noise (was not Perlin noise)
* `core.get_perlin(seeddiff, octaves, persistence, spread)`
* Deprecated: use `core.get_perlin(noiseparams)` instead.
* Return world-specific perlin noise.
* Deprecated: use `core.get_value_noise(noiseparams)` instead.
* Return world-specific value noise (was not Perlin noise)
* `core.get_voxel_manip([pos1, pos2])`
* Return voxel manipulator object.
* Loads the manipulator from the map if positions are passed.
@ -6889,8 +7009,13 @@ Defaults for the `on_place` and `on_drop` item definition functions
* Parameters are the same as in `on_pickup`.
* Returns the leftover itemstack.
* `core.item_drop(itemstack, dropper, pos)`
* Drop the item
* returns the leftover itemstack
* Converts `itemstack` to an in-world Lua entity.
* `itemstack` (`ItemStack`) is modified (cleared) on success.
* In versions < 5.12.0, `itemstack` was cleared in all cases.
* `dropper` (`ObjectRef`) is optional.
* Returned values on success:
1. leftover itemstack
2. `ObjectRef` of the spawned object (provided since 5.12.0)
* `core.item_eat(hp_change[, replace_with_item])`
* Returns `function(itemstack, user, pointed_thing)` as a
function wrapper for `core.do_item_eat`.
@ -6982,8 +7107,8 @@ Classes:
* `AreaStore`
* `ItemStack`
* `PerlinNoise`
* `PerlinNoiseMap`
* `ValueNoise`
* `ValueNoiseMap`
* `PseudoRandom`
* `PcgRandom`
* `SecureRandom`
@ -6995,8 +7120,8 @@ Classes:
Class instances that can be transferred between environments:
* `ItemStack`
* `PerlinNoise`
* `PerlinNoiseMap`
* `ValueNoise`
* `ValueNoiseMap`
* `VoxelManip`
Functions:
@ -7062,8 +7187,8 @@ Classes:
* `AreaStore`
* `ItemStack`
* `PerlinNoise`
* `PerlinNoiseMap`
* `ValueNoise`
* `ValueNoiseMap`
* `PseudoRandom`
* `PcgRandom`
* `SecureRandom`
@ -7408,7 +7533,8 @@ Misc.
* This function can be overridden by mods to change the leave message.
* `core.hash_node_position(pos)`: returns a 48-bit integer
* `pos`: table {x=number, y=number, z=number},
* Gives a unique hash number for a node position (16+16+16=48bit)
* Gives a unique numeric encoding for a node position (16+16+16=48bit)
* Despite the name, this is not a hash function (so it doesn't mix or produce collisions).
* `core.get_position_from_hash(hash)`: returns a position
* Inverse transform of `core.hash_node_position`
* `core.get_item_group(name, group)`: returns a rating
@ -7568,6 +7694,8 @@ Misc.
* `core.forceload_block(pos[, transient[, limit]])`
* forceloads the position `pos`.
* this means that the mapblock containing `pos` will always be kept in the
`"active"` state, regardless of nearby players or server settings.
* returns `true` if area could be forceloaded
* If `transient` is `false` or absent, the forceload will be persistent
(saved between server runs). If `true`, the forceload will be transient
@ -7826,13 +7954,15 @@ An `InvRef` is a reference to an inventory.
can be fully added to the list
* `contains_item(listname, stack, [match_meta])`: returns `true` if
the stack of items can be fully taken from the list.
If `match_meta` is false, only the items' names are compared
(default: `false`).
* `remove_item(listname, stack)`: take as many items as specified from the
list, returns the items that were actually removed (as an `ItemStack`)
-- note that any item metadata is ignored, so attempting to remove a specific
unique item this way will likely remove the wrong one -- to do that use
`set_stack` with an empty `ItemStack`.
* If `match_meta` is `true`, item metadata is also considered when comparing
items. Otherwise, only the items names are compared. Default: `false`
* The method ignores wear.
* `remove_item(listname, stack, [match_meta])`: take as many items as specified from the
list, returns the items that were actually removed (as an `ItemStack`).
* If `match_meta` is `true` (available since feature `remove_item_match_meta`),
item metadata is also considered when comparing items. Otherwise, only the
items names are compared. Default: `false`
* The method ignores wear.
* `get_location()`: returns a location compatible to
`core.get_inventory(location)`.
* returns `{type="undefined"}` in case location is not known
@ -8781,6 +8911,14 @@ child will follow movement and rotation of that bone.
Same limits as for `thirdperson_back` apply.
Defaults to `thirdperson_back` if unspecified.
* `get_eye_offset()`: Returns camera offset vectors as set via `set_eye_offset`.
* `set_camera(params)`: Sets camera parameters.
* `mode`: Defines the camera mode used
- `any`: free choice between all modes (default)
- `first`: first-person camera
- `third`: third-person camera
- `third_front`: third-person camera, looking opposite of movement direction
* Supported by client since 5.12.0.
* `get_camera()`: Returns the camera parameters as a table as above.
* `send_mapblock(blockpos)`:
* Sends an already loaded mapblock to the player.
* Returns `false` if nothing was sent (note that this can also mean that
@ -8895,33 +9033,41 @@ offering very strong randomness.
* `get_state()`: return generator state encoded in string
* `set_state(state_string)`: restore generator state from encoded string
`PerlinNoise`
`ValueNoise`
-------------
A perlin noise generator.
It can be created via `PerlinNoise()` or `core.get_perlin()`.
For `core.get_perlin()`, the actual seed used is the noiseparams seed
A value noise generator.
It can be created via `ValueNoise()` or `core.get_value_noise()`.
For legacy reasons, it can also be created via `PerlinNoise()` or `core.get_perlin()`,
but the implemented noise is not Perlin noise.
For `core.get_value_noise()`, the actual seed used is the noiseparams seed
plus the world seed, to create world-specific noise.
`PerlinNoise(noiseparams)`
`PerlinNoise(seed, octaves, persistence, spread)` (Deprecated).
* `ValueNoise(noiseparams)
* `ValueNoise(seed, octaves, persistence, spread)` (Deprecated)
* `PerlinNoise(noiseparams)` (Deprecated)
* `PerlinNoise(seed, octaves, persistence, spread)` (Deprecated)
`core.get_perlin(noiseparams)`
`core.get_perlin(seeddiff, octaves, persistence, spread)` (Deprecated).
* `core.get_value_noise(noiseparams)`
* `core.get_value_noise(seeddiff, octaves, persistence, spread)` (Deprecated)
* `core.get_perlin(noiseparams)` (Deprecated)
* `core.get_perlin(seeddiff, octaves, persistence, spread)` (Deprecated)
### Methods
* `get_2d(pos)`: returns 2D noise value at `pos={x=,y=}`
* `get_3d(pos)`: returns 3D noise value at `pos={x=,y=,z=}`
`PerlinNoiseMap`
`ValueNoiseMap`
----------------
A fast, bulk perlin noise generator.
A fast, bulk noise generator.
It can be created via `PerlinNoiseMap(noiseparams, size)` or
`core.get_perlin_map(noiseparams, size)`.
For `core.get_perlin_map()`, the actual seed used is the noiseparams seed
It can be created via `ValueNoiseMap(noiseparams, size)` or
`core.get_value_noise_map(noiseparams, size)`.
For legacy reasons, it can also be created via `PerlinNoiseMap(noiseparams, size)`
or `core.get_perlin_map(noiseparams, size)`, but it is not Perlin noise.
For `core.get_value_noise_map()`, the actual seed used is the noiseparams seed
plus the world seed, to create world-specific noise.
Format of `size` is `{x=dimx, y=dimy, z=dimz}`. The `z` component is omitted
@ -8948,7 +9094,7 @@ table.
* `get_map_slice(slice_offset, slice_size, buffer)`: In the form of an array,
returns a slice of the most recently computed noise results. The result slice
begins at coordinates `slice_offset` and takes a chunk of `slice_size`.
E.g. to grab a 2-slice high horizontal 2d plane of noise starting at buffer
E.g., to grab a 2-slice high horizontal 2d plane of noise starting at buffer
offset y = 20:
`noisevals = noise:get_map_slice({y=20}, {y=2})`
It is important to note that `slice_offset` offset coordinates begin at 1,
@ -9194,7 +9340,7 @@ Player properties need to be saved manually.
-- Clients older than 5.9.0 interpret `pointable = "blocking"` as `pointable = true`.
-- Can be overridden by the `pointabilities` of the held item.
visual = "cube" / "sprite" / "upright_sprite" / "mesh" / "wielditem" / "item",
visual = "",
-- "cube" is a node-sized cube.
-- "sprite" is a flat texture always facing the player.
-- "upright_sprite" is a vertical flat texture.
@ -9216,6 +9362,8 @@ Player properties need to be saved manually.
-- Wielditems are scaled a bit. If you want a wielditem to appear
-- to be as large as a node, use `0.667` in `visual_size`
-- "item" is similar to "wielditem" but ignores the 'wield_image' parameter.
-- "node" looks exactly like a node in-world (supported since 5.12.0)
-- Note that visual effects like waving or liquid reflections will not work.
visual_size = {x = 1, y = 1, z = 1},
-- Multipliers for the visual size. If `z` is not specified, `x` will be used
@ -9225,7 +9373,7 @@ Player properties need to be saved manually.
-- File name of mesh when using "mesh" visual
textures = {},
-- Number of required textures depends on visual.
-- Number of required textures depends on visual:
-- "cube" uses 6 textures just like a node, but all 6 must be defined.
-- "sprite" uses 1 texture.
-- "upright_sprite" uses 2 textures: {front, back}.
@ -9233,13 +9381,16 @@ Player properties need to be saved manually.
-- Deprecated usage of "wielditem" expects 'textures = {itemname}' (see 'visual' above).
colors = {},
-- Number of required colors depends on visual
-- Currently unused.
node = {name = "ignore", param1=0, param2=0},
-- Node to show when using the "node" visual
use_texture_alpha = false,
-- Use texture's alpha channel.
-- Excludes "upright_sprite" and "wielditem".
-- Use texture's alpha channel for transparency blending.
-- Note: currently causes visual issues when viewed through other
-- semi-transparent materials such as water.
-- Note: ignored for "item", "wielditem" and "node" visual.
spritediv = {x = 1, y = 1},
-- Used with spritesheet textures for animation and/or frame selection
@ -9256,7 +9407,7 @@ Player properties need to be saved manually.
-- If false, object is invisible and can't be pointed.
makes_footstep_sound = false,
-- If true, is able to make footstep sounds of nodes
-- If true, object is able to make footstep sounds of nodes
-- (see node sound definition for details).
automatic_rotate = 0,
@ -9279,6 +9430,7 @@ Player properties need to be saved manually.
backface_culling = true,
-- Set to false to disable backface_culling for model
-- Note: only used by "mesh" and "cube" visual
glow = 0,
-- Add this much extra lighting when calculating texture color.
@ -9314,6 +9466,7 @@ Player properties need to be saved manually.
shaded = true,
-- Setting this to 'false' disables diffuse lighting of entity
-- Note: ignored for "item", "wielditem" and "node" visual
show_on_minimap = false,
-- Defaults to true for players, false for other entities.
@ -9364,6 +9517,10 @@ ABM (ActiveBlockModifier) definition
Used by `core.register_abm`.
An active block modifier (ABM) is used to define a function that is continously
and randomly called for specific nodes (defined by `nodenames` and other conditions)
in active mapblocks.
```lua
{
label = "Lava cooling",
@ -9420,12 +9577,29 @@ Used by `core.register_lbm`.
A loading block modifier (LBM) is used to define a function that is called for
specific nodes (defined by `nodenames`) when a mapblock which contains such nodes
gets activated (not loaded!).
gets **activated** (**not loaded!**).
Note: LBMs operate on a "snapshot" of node positions taken once before they are triggered.
*Note*: LBMs operate on a "snapshot" of node positions taken once before they are triggered.
That means if an LBM callback adds a node, it won't be taken into account.
However the engine guarantees that when the callback is called that all given position(s)
contain a matching node.
However the engine guarantees that at the point in time when the callback is called
that all given positions contain a matching node.
For `run_at_every_load = false` to work, both mapblocks and LBMs have timestamps
associated with them:
* Each mapblock has a "last active" timestamp. It is also updated when the
mapblock is generated.
* For each LBM, an introduction timestamp is stored in the world data, identified
by the LBM's `name` field. If an LBM disappears, the corresponding timestamp
is cleared.
When a mapblock is activated, only LBMs whose introduction timestamp is newer
than the mapblock's timestamp are run.
*Note*: For maps generated in 5.11.0 or older, many newly generated mapblocks
did not get a timestamp set. This means LBMs introduced between generation time
and time of first activation will never run.
Currently the only workaround is to use `run_at_every_load = true`.
```lua
{
@ -9442,14 +9616,17 @@ contain a matching node.
-- will work as well.
run_at_every_load = false,
-- Whether to run the LBM's action every time a block gets activated,
-- and not only the first time the block gets activated after the LBM
-- was introduced.
-- If `false`: The LBM only runs on mapblocks the first time they are
-- activated after the LBM was introduced.
-- It never runs on mapblocks generated after the LBM's introduction.
-- See above for details.
--
-- If `true`: The LBM runs every time a mapblock is activated.
action = function(pos, node, dtime_s) end,
-- Function triggered for each qualifying node.
-- `dtime_s` is the in-game time (in seconds) elapsed since the block
-- was last active.
-- `dtime_s` is the in-game time (in seconds) elapsed since the mapblock
-- was last active (available since 5.7.0).
bulk_action = function(pos_list, dtime_s) end,
-- Function triggered with a list of all applicable node positions at once.
@ -10605,7 +10782,7 @@ See [Ores] section above for essential information.
octaves = 3,
persistence = 0.7
},
-- NoiseParams structure describing one of the perlin noises used for
-- NoiseParams structure describing one of the noises used for
-- ore distribution.
-- Needed by "sheet", "puff", "blob" and "vein" ores.
-- Omit from "scatter" ore for a uniform ore distribution.
@ -10792,7 +10969,7 @@ See [Decoration types]. Used by `core.register_decoration`.
lacunarity = 2.0,
flags = "absvalue"
},
-- NoiseParams structure describing the perlin noise used for decoration
-- NoiseParams structure describing the noise used for decoration
-- distribution.
-- A noise value is calculated for each square division and determines
-- 'decorations per surface node' within each division.

View file

@ -1,3 +1,3 @@
Moved to lua_api.md
URL: https://github.com/minetest/minetest/blob/master/doc/lua_api.md
URL: https://github.com/luanti-org/luanti/blob/master/doc/lua_api.md

View file

@ -124,7 +124,7 @@ Colon delimited list of directories to search for mods.
Path to Luanti user data directory.
.SH BUGS
Please report all bugs at https://github.com/minetest/minetest/issues.
Please report all bugs at https://github.com/luanti-org/luanti/issues.
.SH AUTHOR
.PP
@ -134,4 +134,4 @@ This man page was originally written by
Juhani Numminen <juhaninumminen0@gmail.com>.
.SH WWW
http://www.minetest.net/
http://www.luanti.org/

View file

@ -1,7 +1,7 @@
/** @mainpage The Minetest engine internal documentation
/** @mainpage The Luanti engine internal documentation
Welcome to the Minetest engine Doxygen documentation site!\n
This page documents the internal structure of the Minetest engine's C++ code.\n
Welcome to the Luanti engine Doxygen documentation site!\n
This page documents the internal structure of the Luanti engine's C++ code.\n
Use the tree view at the left to navigate.
*/

View file

@ -1,4 +1,4 @@
Luanti Lua Mainmenu API Reference 5.11.0
Luanti Lua Mainmenu API Reference 5.12.0
========================================
Introduction
@ -105,11 +105,6 @@ of manually putting one, as different OSs use different delimiters. E.g.
* `spec` = `SimpleSoundSpec` (see `lua_api.md`)
* `looped` = bool
* `handle:stop()` or `core.sound_stop(handle)`
* `core.get_video_drivers()`
* get list of video drivers supported by engine (not all modes are guaranteed to work)
* returns list of available video drivers' settings name and 'friendly' display name
e.g. `{ {name="opengl", friendly_name="OpenGL"}, {name="software", friendly_name="Software Renderer"} }`
* first element of returned list is guaranteed to be the NULL driver
* `core.get_mapgen_names([include_hidden=false])` -> table of map generator algorithms
registered in the core (possible in async calls)
* `core.get_cache_path()` -> path of cache
@ -336,7 +331,7 @@ Package - content which is downloadable from the content db, may or may not be i
```lua
{
name = "technical_id",
type = "mod" or "modpack" or "game" or "txp",
type = "mod" or "modpack" or "game" or "txp" or "unknown",
title = "Human readable title",
description = "description",
author = "author",
@ -387,7 +382,7 @@ Settings
* `core.settings:save()` -> nil, save all settings to config file
For a complete list of methods of the `Settings` object see
[lua_api.md](https://github.com/minetest/minetest/blob/master/doc/lua_api.md)
[lua_api.md](./lua_api.md)
Worlds

View file

@ -22,6 +22,11 @@ This is a directory containing the entire contents of a single texture pack.
It can be chosen more or less freely and will also become the name of the
texture pack. The name must not be “base”.
### The "server" texture pack
If a texture pack named `server` exists, the textures in it will replace textures
sent to clients.
It's independent of the client's texture pack, which will take precedence as usual.
### `texture_pack.conf`
A key-value config file with the following keys:
@ -139,21 +144,34 @@ are placeholders intended to be overwritten by the game.
### Android textures
* `drop_btn.png`
* `fast_btn.png`
* `fly_btn.png`
* `jump_btn.png`
* `noclip_btn.png`
* `dig_btn.png`
* `place_btn.png`
* `jump_btn.png`
* `down.png`
* `zoom.png`
* `aux1_btn.png`
* `overflow_btn.png`
* `camera_btn.png`
* `chat_btn.png`
* `inventory_btn.png`
* `rangeview_btn.png`
* `debug_btn.png`
* `overflow_btn.png`
* `drop_btn.png`
* `exit_btn.png`
* `fly_btn.png`
* `fast_btn.png`
* `noclip_btn.png`
* `debug_btn.png`
* `camera_btn.png`
* `rangeview_btn.png`
* `minimap_btn.png`
* `chat_hide_btn.png`
* `chat_show_btn.png`
* `joystick_off.png`
* `joystick_bg.png`
* `joystick_center.png`
Texture Overrides
-----------------

View file

@ -281,46 +281,61 @@ storing coordinates separately), but the format has been kept unchanged for
that part.
## `map.sqlite`
`map.sqlite` is a `SQLite3` database, containing a single table, called
`map.sqlite` is an `SQLite3` database, containing a single table, called
`blocks`. It looks like this:
```sql
CREATE TABLE `blocks` (
`x` INTEGER, `y` INTEGER, `z` INTEGER,
`data` BLOB NOT NULL,
PRIMARY KEY (`x`, `z`, `y`)
);
```
Before 5.12.0 it looked like this:
```sql
CREATE TABLE `blocks` (`pos` INT NOT NULL PRIMARY KEY, `data` BLOB);
```
## Position Hashing
## Position Encoding
`pos` (a node position hash) is created from the three coordinates of a
`MapBlock` using this algorithm, defined here in Python:
Applies to the pre-5.12.0 schema:
```python
def getBlockAsInteger(p):
return int64(p[2]*16777216 + p[1]*4096 + p[0])
`pos` (a node position encoding) is created from the three coordinates of a
`MapBlock` using the following simple equation:
def int64(u):
while u >= 2**63:
u -= 2**64
while u <= -2**63:
u += 2**64
return u
```C
pos = (z << 24) + (y << 12) + x;
```
or, equivalently, `pos = (z * 0x1000000) + (y * 0x1000) + x`.
A position can be decoded using:
```C
pos = pos + 0x800800800;
x = (pos & 0xFFF) - 0x800;
y = ((pos >> 12) & 0xFFF) - 0x800;
z = ((pos >> 24) & 0xFFF) - 0x800;
```
It can be converted the other way by using this code:
Positions are sequential along the x axis (as easily seen from the position equation above).
It is possible to retrieve all blocks from an interval using the following SQL statement:
```python
def getIntegerAsBlock(i):
x = unsignedToSigned(i % 4096, 2048)
i = int((i - x) / 4096)
y = unsignedToSigned(i % 4096, 2048)
i = int((i - y) / 4096)
z = unsignedToSigned(i % 4096, 2048)
return x,y,z
def unsignedToSigned(i, max_positive):
if i < max_positive:
return i
else:
return i - 2*max_positive
```sql
SELECT
`pos`,
`data`,
( (`pos` + 0x800800800) & 0xFFF) - 0x800 as x,
(((`pos` + 0x800800800) >> 12) & 0xFFF) - 0x800 as y,
(((`pos` + 0x800800800) >> 24) & 0xFFF) - 0x800 as z
FROM `blocks` WHERE
( (`pos` + 0x800800800) & 0xFFF) - 0x800 >= ? AND -- minx
( (`pos` + 0x800800800) & 0xFFF) - 0x800 <= ? AND -- maxx
(((`pos` + 0x800800800) >> 12) & 0xFFF) - 0x800 >= ? AND -- miny
(((`pos` + 0x800800800) >> 12) & 0xFFF) - 0x800 <= ? AND -- maxy
`pos` >= (? << 24) - 0x800800 AND -- minz
`pos` <= (? << 24) + 0x7FF7FF; -- maxz
```
## Blob
@ -335,8 +350,8 @@ See below for description.
> * NOTE: Byte order is MSB first (big-endian).
> * NOTE: Zlib data is in such a format that Python's `zlib` at least can
> directly decompress.
> * NOTE: Since version 29 zstd is used instead of zlib. In addition, the entire
> block is first serialized and then compressed (except the version byte).
> * NOTE: Since version 29 zstd is used instead of zlib. In addition, the
> **entire block** is first serialized and then compressed (except version byte).
`u8` version
* map format version number, see serialization.h for the latest number

View file

@ -31,7 +31,7 @@ read_globals = {
"PcgRandom",
string = {fields = {"split", "trim"}},
table = {fields = {"copy", "getn", "indexof", "insert_all"}},
table = {fields = {"copy", "getn", "indexof", "insert_all", "key_value_swap"}},
math = {fields = {"hypot", "round"}},
}

View file

@ -22,11 +22,15 @@ core.register_node("basenodes:desert_stone", {
core.register_node("basenodes:dirt_with_grass", {
description = "Dirt with Grass",
tiles ={"default_grass.png",
-- Using overlays here has no real merit here but we do it anyway so
-- overlay-related bugs become more apparent in devtest.
tiles = {"default_dirt.png"},
overlay_tiles = {
"default_grass.png",
-- a little dot on the bottom to distinguish it from dirt
"default_dirt.png^basenodes_dirt_with_grass_bottom.png",
{name = "default_dirt.png^default_grass_side.png",
tileable_vertical = false}},
"basenodes_dirt_with_grass_bottom.png",
{name = "default_grass_side.png", tileable_vertical = false},
},
groups = {crumbly=3, soil=1},
})

View file

@ -66,6 +66,15 @@ core.register_entity("testentities:mesh_unshaded", {
},
})
core.register_entity("testentities:node", {
initial_properties = {
visual = "node",
node = { name = "stairs:stair_stone" },
},
})
-- More complex meshes
core.register_entity("testentities:sam", {
initial_properties = {
visual = "mesh",

View file

@ -12,7 +12,8 @@ core.register_node("tiled:tiled", {
})
core.register_node("tiled:tiled_rooted", {
description = "Tiled 'plantlike_rooted' Node (world-aligned)".."\n"..
description =
"Tiled 'plantlike_rooted' Node (world-aligned)".."\n"..
"Base node texture spans over a space of 8×8 nodes".."\n"..
"A plantlike thing grows on top",
paramtype = "light",

View file

@ -234,3 +234,24 @@ local function test_get_bone_rot(_, pos)
end
end
unittests.register("test_get_bone_rot", test_get_bone_rot, {map=true})
---------
-- Spawn an entity from an ItemStack
local function test_item_drop(_, pos)
local itemstack_src, itemstack_ret, obj
-- Try to place something that does not exist (placement fails)
itemstack_src = ItemStack("n_np_solution 1")
itemstack_ret, obj = core.item_drop(itemstack_src, nil, pos)
assert(obj == nil)
assert(itemstack_ret == nil)
-- Test known item (placement successful)
itemstack_src = ItemStack("testnodes:normal 69")
itemstack_ret, obj = core.item_drop(itemstack_src, nil, pos)
assert(obj:get_hp() ~= nil)
assert(itemstack_ret and itemstack_ret:is_empty())
assert(itemstack_ret:equals(itemstack_src))
end
unittests.register("test_item_drop", test_item_drop, {map=true})

View file

@ -1,16 +0,0 @@
unittests.register("test_get_version", function()
local version = core.get_version()
assert(type(version) == "table")
assert(type(version.project) == "string")
assert(type(version.string) == "string")
assert(type(version.proto_min) == "number")
assert(type(version.proto_max) == "number")
assert(version.proto_max >= version.proto_min)
assert(type(version.is_dev) == "boolean")
if version.is_dev then
assert(type(version.hash) == "string")
else
assert(version.hash == nil)
end
end)

View file

@ -192,7 +192,7 @@ dofile(modpath .. "/crafting.lua")
dofile(modpath .. "/itemdescription.lua")
dofile(modpath .. "/async_env.lua")
dofile(modpath .. "/entity.lua")
dofile(modpath .. "/get_version.lua")
dofile(modpath .. "/version.lua")
dofile(modpath .. "/itemstack_equals.lua")
dofile(modpath .. "/content_ids.lua")
dofile(modpath .. "/metadata.lua")

View file

@ -1,10 +1,11 @@
local item_with_meta = ItemStack({name = "air", meta = {test = "abc"}})
local function get_stack_with_meta(count)
return ItemStack({name = "air", count = count, meta = {test = "abc"}})
end
local test_list = {
ItemStack("air"),
ItemStack(""),
ItemStack(item_with_meta),
ItemStack(get_stack_with_meta(1)),
}
local function compare_lists(a, b)
@ -34,12 +35,12 @@ local function test_inventory()
assert(not inv:set_width("test", -1))
inv:set_stack("test", 1, "air")
inv:set_stack("test", 3, item_with_meta)
inv:set_stack("test", 3, get_stack_with_meta(1))
assert(not inv:is_empty("test"))
assert(compare_lists(inv:get_list("test"), test_list))
assert(inv:add_item("test", "air") == ItemStack())
assert(inv:add_item("test", item_with_meta) == ItemStack())
assert(inv:add_item("test", get_stack_with_meta(1)) == ItemStack())
assert(inv:get_stack("test", 1) == ItemStack("air 2"))
assert(inv:room_for_item("test", "air 99"))
@ -48,16 +49,28 @@ local function test_inventory()
inv:set_stack("test", 2, "")
assert(inv:contains_item("test", "air"))
assert(inv:contains_item("test", "air 4"))
assert(not inv:contains_item("test", "air 5"))
assert(not inv:contains_item("test", "air 99"))
assert(inv:contains_item("test", item_with_meta, true))
assert(inv:contains_item("test", "air 2", true))
assert(not inv:contains_item("test", "air 3", true))
assert(inv:contains_item("test", get_stack_with_meta(2), true))
assert(not inv:contains_item("test", get_stack_with_meta(3), true))
-- Items should be removed in reverse and combine with first stack removed
assert(inv:remove_item("test", "air") == item_with_meta)
item_with_meta:set_count(2)
assert(inv:remove_item("test", "air 2") == item_with_meta)
assert(inv:remove_item("test", "air") == get_stack_with_meta(1))
assert(inv:remove_item("test", "air 2") == get_stack_with_meta(2))
assert(inv:remove_item("test", "air") == ItemStack("air"))
assert(inv:is_empty("test"))
inv:set_stack("test", 1, "air 3")
inv:set_stack("test", 3, get_stack_with_meta(2))
assert(inv:remove_item("test", "air 4", true) == ItemStack("air 3"))
inv:set_stack("test", 1, "air 3")
assert(inv:remove_item("test", get_stack_with_meta(3), true) == get_stack_with_meta(2))
assert(inv:remove_item("test", "air 3", true) == ItemStack("air 3"))
assert(inv:is_empty("test"))
-- Failure of set_list(s) should not change inventory
local before = inv:get_list("test")
pcall(inv.set_lists, inv, {test = true})

View file

@ -189,6 +189,29 @@ local function test_write_json()
end
unittests.register("test_write_json", test_write_json)
local function lint_json_files()
-- Check that files we ship with Luanti are valid JSON
local stack = {core.get_builtin_path()}
local checked = 0
while #stack > 0 do
local path = table.remove(stack)
for _, name in ipairs(core.get_dir_list(path, true)) do
stack[#stack+1] = path .. "/" .. name
end
for _, name in ipairs(core.get_dir_list(path, false)) do
if name:match("%.json$") then
local f = io.open(path .. "/" .. name, "rb")
print(path .. "/" .. name)
assert(core.parse_json(f:read("*all"), -1) ~= nil)
f:close()
checked = checked + 1
end
end
end
assert(checked > 0, "no files found?!")
end
unittests.register("lint_json_files", lint_json_files)
local function test_game_info()
local info = core.get_game_info()
local game_conf = Settings(info.path .. "/game.conf")

View file

@ -0,0 +1,40 @@
unittests.register("test_get_version", function()
local version = core.get_version()
assert(type(version) == "table")
assert(type(version.project) == "string")
assert(type(version.string) == "string")
assert(type(version.proto_min) == "number")
assert(type(version.proto_max) == "number")
assert(version.proto_max >= version.proto_min)
assert(type(version.is_dev) == "boolean")
if version.is_dev then
assert(type(version.hash) == "string")
else
assert(version.hash == nil)
end
end)
unittests.register("test_protocol_version", function(player)
local info = core.get_player_information(player:get_player_name())
local maxver = 0
for _, v in pairs(core.protocol_versions) do
maxver = math.max(maxver, v)
end
assert(maxver > 0) -- table must contain something valid
-- If the client is older than a known version then it's pointless.
if info.protocol_version < maxver then
core.log("warning", "test_protocol_version: client is outdated, skipping test!")
return
end
local info_server = core.get_version()
if info.version_string ~= (info_server.hash or info_server.string) then
core.log("warning", "test_protocol_version: client is not the same version. False-positive possible.")
end
-- The protocol version the client and server agreed on must exist in the table.
local match = table.key_value_swap(core.protocol_versions)[info.protocol_version]
print(string.format("client proto matched: %s sent: %s", match, info.version_string))
assert(match ~= nil)
end, {player = true})

View file

@ -132,6 +132,9 @@ enum E_VIDEO_DRIVER_FEATURE
//! Support for multisample textures.
EVDF_TEXTURE_MULTISAMPLE,
//! Support for 2D array textures.
EVDF_TEXTURE_2D_ARRAY,
//! Only used for counting the elements of this enum
EVDF_COUNT
};

View file

@ -26,12 +26,6 @@ enum E_FILE_ARCHIVE_TYPE
//! A PKZIP archive
EFAT_ZIP = MAKE_IRR_ID('Z', 'I', 'P', 0),
//! A gzip archive
EFAT_GZIP = MAKE_IRR_ID('g', 'z', 'i', 'p'),
//! An Android asset file archive
EFAT_ANDROID_ASSET = MAKE_IRR_ID('A', 'S', 'S', 'E'),
//! The type of this archive is unknown
EFAT_UNKNOWN = MAKE_IRR_ID('u', 'n', 'k', 'n')
};
@ -73,13 +67,6 @@ public:
but checks if file exists will fail.
*/
virtual void addDirectoryToFileList(const io::path &filename) {}
//! An optionally used password string
/** This variable is publicly accessible from the interface in order to
avoid single access patterns to this place, and hence allow some more
obscurity.
*/
core::stringc Password;
};
//! Class which is able to create an archive from a file.

View file

@ -25,7 +25,7 @@ class IImage : public virtual IReferenceCounted
public:
//! constructor
IImage(ECOLOR_FORMAT format, const core::dimension2d<u32> &size, bool deleteMemory) :
Format(format), Size(size), Data(0), MipMapsData(0), BytesPerPixel(0), Pitch(0), DeleteMemory(deleteMemory), DeleteMipMapsMemory(false)
Format(format), Size(size), Data(0), BytesPerPixel(0), Pitch(0), DeleteMemory(deleteMemory)
{
BytesPerPixel = getBitsPerPixelFromFormat(Format) / 8;
Pitch = BytesPerPixel * Size.Width;
@ -36,9 +36,6 @@ public:
{
if (DeleteMemory)
delete[] Data;
if (DeleteMipMapsMemory)
delete[] MipMapsData;
}
//! Returns the color format
@ -56,7 +53,6 @@ public:
//! Returns bits per pixel.
u32 getBitsPerPixel() const
{
return getBitsPerPixelFromFormat(Format);
}
@ -189,87 +185,6 @@ public:
return result;
}
//! Get mipmaps data.
/** Note that different mip levels are just behind each other in memory block.
So if you just get level 1 you also have the data for all other levels.
There is no level 0 - use getData to get the original image data.
*/
void *getMipMapsData(irr::u32 mipLevel = 1) const
{
if (MipMapsData && mipLevel > 0) {
size_t dataSize = 0;
core::dimension2du mipSize(Size);
u32 i = 1; // We want the start of data for this level, not end.
while (i != mipLevel) {
if (mipSize.Width > 1)
mipSize.Width >>= 1;
if (mipSize.Height > 1)
mipSize.Height >>= 1;
dataSize += getDataSizeFromFormat(Format, mipSize.Width, mipSize.Height);
++i;
if (mipSize.Width == 1 && mipSize.Height == 1 && i < mipLevel)
return 0;
}
return MipMapsData + dataSize;
}
return 0;
}
//! Set mipmaps data.
/** This method allows you to put custom mipmaps data for
image.
\param data A byte array with pixel color information
\param ownForeignMemory If true, the image will use the data
pointer directly and own it afterward. If false, the memory
will by copied internally.
\param deleteMemory Whether the memory is deallocated upon
destruction. */
void setMipMapsData(void *data, bool ownForeignMemory)
{
if (data != MipMapsData) {
if (DeleteMipMapsMemory) {
delete[] MipMapsData;
DeleteMipMapsMemory = false;
}
if (data) {
if (ownForeignMemory) {
MipMapsData = static_cast<u8 *>(data);
DeleteMipMapsMemory = false;
} else {
u32 dataSize = 0;
u32 width = Size.Width;
u32 height = Size.Height;
do {
if (width > 1)
width >>= 1;
if (height > 1)
height >>= 1;
dataSize += getDataSizeFromFormat(Format, width, height);
} while (width != 1 || height != 1);
MipMapsData = new u8[dataSize];
memcpy(MipMapsData, data, dataSize);
DeleteMipMapsMemory = true;
}
} else {
MipMapsData = 0;
}
}
}
//! Returns a pixel
virtual SColor getPixel(u32 x, u32 y) const = 0;
@ -277,30 +192,24 @@ public:
virtual void setPixel(u32 x, u32 y, const SColor &color, bool blend = false) = 0;
//! Copies this surface into another, if it has the exact same size and format.
/** NOTE: mipmaps are ignored
\return True if it was copied, false otherwise.
/** \return True if it was copied, false otherwise.
*/
virtual bool copyToNoScaling(void *target, u32 width, u32 height, ECOLOR_FORMAT format = ECF_A8R8G8B8, u32 pitch = 0) const = 0;
//! Copies the image into the target, scaling the image to fit
/** NOTE: mipmaps are ignored */
virtual void copyToScaling(void *target, u32 width, u32 height, ECOLOR_FORMAT format = ECF_A8R8G8B8, u32 pitch = 0) = 0;
//! Copies the image into the target, scaling the image to fit
/** NOTE: mipmaps are ignored */
virtual void copyToScaling(IImage *target) = 0;
//! copies this surface into another
/** NOTE: mipmaps are ignored */
virtual void copyTo(IImage *target, const core::position2d<s32> &pos = core::position2d<s32>(0, 0)) = 0;
//! copies this surface into another
/** NOTE: mipmaps are ignored */
virtual void copyTo(IImage *target, const core::position2d<s32> &pos, const core::rect<s32> &sourceRect, const core::rect<s32> *clipRect = 0) = 0;
//! copies this surface into another, using the alpha mask and cliprect and a color to add with
/** NOTE: mipmaps are ignored
\param combineAlpha - When true then combine alpha channels. When false replace target image alpha with source image alpha.
/** \param combineAlpha - When true then combine alpha channels. When false replace target image alpha with source image alpha.
*/
virtual void copyToWithAlpha(IImage *target, const core::position2d<s32> &pos,
const core::rect<s32> &sourceRect, const SColor &color,
@ -308,7 +217,6 @@ public:
bool combineAlpha = false) = 0;
//! copies this surface into another, scaling it to fit, applying a box filter
/** NOTE: mipmaps are ignored */
virtual void copyToScalingBoxFilter(IImage *target, s32 bias = 0, bool blend = false) = 0;
//! fills the surface with given color
@ -416,13 +324,11 @@ protected:
core::dimension2d<u32> Size;
u8 *Data;
u8 *MipMapsData;
u32 BytesPerPixel;
u32 Pitch;
bool DeleteMemory;
bool DeleteMipMapsMemory;
};
} // end namespace video

View file

@ -34,16 +34,16 @@ public:
/** \return Pointer to mesh which is displayed by this node. */
virtual IMesh *getMesh(void) = 0;
//! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.
//! Sets if the scene node should not copy the materials of the mesh but use them directly.
/** In this way it is possible to change the materials of a mesh
causing all mesh scene nodes referencing this mesh to change, too.
\param readonly Flag if the materials shall be read-only. */
virtual void setReadOnlyMaterials(bool readonly) = 0;
\param shared Flag if the materials shall be shared. */
virtual void setSharedMaterials(bool shared) = 0;
//! Check if the scene node should not copy the materials of the mesh but use them in a read only style
/** This flag can be set by setReadOnlyMaterials().
\return Whether the materials are read-only. */
virtual bool isReadOnlyMaterials() const = 0;
//! Check if the scene node does not copy the materials of the mesh but uses them directly.
/** This flag can be set by setSharedMaterials().
\return Whether the materials are shared. */
virtual bool isSharedMaterials() const = 0;
};
} // end namespace scene

View file

@ -310,7 +310,11 @@ public:
\return The material at that index. */
virtual video::SMaterial &getMaterial(u32 num)
{
return video::IdentityMaterial;
// We return a default material since a reference can't be null,
// but note that writing to this is a mistake either by a child class
// or the caller, because getMaterialCount() is zero.
// Doing so will helpfully cause a segfault.
return const_cast<video::SMaterial&>(video::IdentityMaterial);
}
//! Get amount of materials used by this scene node.

View file

@ -68,30 +68,12 @@ enum E_TEXTURE_CREATION_FLAG
not recommended to enable this flag. */
ETCF_NO_ALPHA_CHANNEL = 0x00000020,
//! Allow the Driver to use Non-Power-2-Textures
/** BurningVideo can handle Non-Power-2 Textures in 2D (GUI), but not in 3D. */
ETCF_ALLOW_NON_POWER_2 = 0x00000040,
//! Allow the driver to keep a copy of the texture in memory
/** Enabling this makes calls to ITexture::lock a lot faster, but costs main memory.
This is disabled by default.
*/
ETCF_ALLOW_MEMORY_COPY = 0x00000080,
//! Enable automatic updating mip maps when the base texture changes.
/** Default is true.
This flag is only used when ETCF_CREATE_MIP_MAPS is also enabled and if the driver supports it.
Please note:
- On D3D (and maybe older OGL?) you can no longer manually set mipmap data when enabled
(for example mips from image loading will be ignored).
- On D3D (and maybe older OGL?) texture locking for mipmap levels usually won't work anymore.
- On new OGL this flag is ignored.
- When disabled you do _not_ get hardware mipmaps on D3D, so mipmap generation can be slower.
- When disabled you can still update your mipmaps when the texture changed by manually calling regenerateMipMapLevels.
- You can still call regenerateMipMapLevels when this flag is enabled (it will be a hint on d3d to update mips immediately)
*/
ETCF_AUTO_GENERATE_MIP_MAPS = 0x00000100,
/** This flag is never used, it only forces the compiler to compile
these enumeration values to 32 bit. */
ETCF_FORCE_32_BIT_DO_NOT_USE = 0x7fffffff
@ -137,19 +119,6 @@ enum E_TEXTURE_LOCK_FLAGS
ETLF_FLIP_Y_UP_RTT = 1
};
//! Where did the last IVideoDriver::getTexture call find this texture
enum E_TEXTURE_SOURCE
{
//! IVideoDriver::getTexture was never called (texture created otherwise)
ETS_UNKNOWN,
//! Texture has been found in cache
ETS_FROM_CACHE,
//! Texture had to be loaded
ETS_FROM_FILE
};
//! Enumeration describing the type of ITexture.
enum E_TEXTURE_TYPE
{
@ -160,7 +129,10 @@ enum E_TEXTURE_TYPE
ETT_2D_MS,
//! Cubemap texture.
ETT_CUBEMAP
ETT_CUBEMAP,
//! 2D array texture
ETT_2D_ARRAY
};
//! Interface of a Video Driver dependent Texture.
@ -178,7 +150,7 @@ public:
//! constructor
ITexture(const io::path &name, E_TEXTURE_TYPE type) :
NamedPath(name), DriverType(EDT_NULL), OriginalColorFormat(ECF_UNKNOWN),
ColorFormat(ECF_UNKNOWN), Pitch(0), HasMipMaps(false), IsRenderTarget(false), Source(ETS_UNKNOWN), Type(type)
ColorFormat(ECF_UNKNOWN), Pitch(0), HasMipMaps(false), IsRenderTarget(false), Type(type)
{
}
@ -197,9 +169,7 @@ public:
only mode or read from in write only mode.
Support for this feature depends on the driver, so don't rely on the
texture being write-protected when locking with read-only, etc.
\param mipmapLevel NOTE: Currently broken, sorry, we try if we can repair it for 1.9 release.
Number of the mipmapLevel to lock. 0 is main texture.
Non-existing levels will silently fail and return 0.
\param mipmapLevel Number of the mipmapLevel to lock. 0 is main texture.
\param layer It determines which cubemap face or texture array layer should be locked.
\param lockFlags See E_TEXTURE_LOCK_FLAGS documentation.
\return Returns a pointer to the pixel data. The format of the pixel can
@ -215,14 +185,9 @@ public:
//! Regenerates the mip map levels of the texture.
/** Required after modifying the texture, usually after calling unlock().
\param data Optional parameter to pass in image data which will be
used instead of the previously stored or automatically generated mipmap
data. The data has to be a continuous pixel data for all mipmaps until
1x1 pixel. Each mipmap has to be half the width and height of the previous
level. At least one pixel will be always kept.
\param layer It informs a texture about which cubemap or texture array layer
needs mipmap regeneration. */
virtual void regenerateMipMapLevels(void *data = 0, u32 layer = 0) = 0;
virtual void regenerateMipMapLevels(u32 layer = 0) = 0;
//! Get original size of the texture.
/** The texture is usually scaled, if it was created with an unoptimal
@ -275,12 +240,6 @@ public:
//! Get name of texture (in most cases this is the filename)
const io::SNamedPath &getName() const { return NamedPath; }
//! Check where the last IVideoDriver::getTexture found this texture
E_TEXTURE_SOURCE getSource() const { return Source; }
//! Used internally by the engine to update Source status on IVideoDriver::getTexture calls.
void updateSource(E_TEXTURE_SOURCE source) { Source = source; }
//! Returns if the texture has an alpha channel
bool hasAlpha() const
{
@ -329,7 +288,6 @@ protected:
u32 Pitch;
bool HasMipMaps;
bool IsRenderTarget;
E_TEXTURE_SOURCE Source;
E_TEXTURE_TYPE Type;
};

View file

@ -232,6 +232,15 @@ public:
information. */
virtual ITexture *addTexture(const io::path &name, IImage *image) = 0;
/**
* Creates an array texture from IImages.
* @param name A name for the texture.
* @param images Pointer to array of images
* @param count Number of images (must be at least 1)
* @return Pointer to the newly created texture
*/
virtual ITexture *addArrayTexture(const io::path &name, IImage **images, u32 count) = 0;
//! Creates a cubemap texture from loaded IImages.
/** \param name A name for the texture. Later calls of getTexture() with this name will return this texture.
The name can _not_ be empty.

View file

@ -16,6 +16,7 @@
#include "IrrCompileConfig.h"
#include "position2d.h"
#include "SColor.h" // video::ECOLOR_FORMAT
#include <variant>
namespace irr
{
@ -342,6 +343,27 @@ public:
{
return video::isDriverSupported(driver);
}
//! Get the corresponding scancode for the keycode.
/**
\param key The keycode to convert.
\return The implementation-dependent scancode for the key (represented by the u32 component) or, if a scancode is not
available, the corresponding Irrlicht keycode (represented by the EKEY_CODE component).
*/
virtual std::variant<u32, EKEY_CODE> getScancodeFromKey(const Keycode &key) const {
if (auto pv = std::get_if<EKEY_CODE>(&key))
return *pv;
return (u32)std::get<wchar_t>(key);
}
//! Get the corresponding keycode for the scancode.
/**
\param scancode The implementation-dependent scancode for the key.
\return The corresponding keycode.
*/
virtual Keycode getKeyFromScancode(const u32 scancode) const {
return Keycode(KEY_UNKNOWN, (wchar_t)scancode);
}
};
} // end namespace irr

View file

@ -3,6 +3,7 @@
// For conditions of distribution and use, see copyright notice in irrlicht.h
#pragma once
#include <variant>
namespace irr
{
@ -182,4 +183,30 @@ enum EKEY_CODE
KEY_KEY_CODES_COUNT = 0x100 // this is not a key, but the amount of keycodes there are.
};
// A Keycode is either a character produced by the key or one of Irrlicht's codes (EKEY_CODE)
class Keycode : public std::variant<EKEY_CODE, wchar_t> {
using super = std::variant<EKEY_CODE, wchar_t>;
public:
Keycode() : Keycode(KEY_KEY_CODES_COUNT, L'\0') {}
Keycode(EKEY_CODE code, wchar_t ch)
{
emplace(code, ch);
}
using super::emplace;
void emplace(EKEY_CODE code, wchar_t ch)
{
if (isValid(code))
emplace<EKEY_CODE>(code);
else
emplace<wchar_t>(ch);
}
static bool isValid(EKEY_CODE code)
{
return code > 0 && code < KEY_KEY_CODES_COUNT;
}
};
} // end namespace irr

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