diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 0fb780464..14d63f4fb 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -34,28 +34,32 @@ env:
jobs:
# Older gcc version (should be close to our minimum supported version)
- gcc_7:
- runs-on: ubuntu-20.04
+ gcc_9:
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
- install_linux_deps g++-7
+ install_linux_deps g++-9
- name: Build
run: |
./util/ci/build.sh
env:
- CC: gcc-7
- CXX: g++-7
- # Test fallback SHA implementations
- CMAKE_FLAGS: '-DENABLE_OPENSSL=0'
+ CC: gcc-9
+ CXX: g++-9
+ CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
- - name: Test
+ - name: Unittest
run: |
./bin/luanti --run-unittests
+ # Do this here because we have ASan and error paths are sensitive to dangling pointers
+ - name: Test error cases
+ run: |
+ ./util/test_error_cases.sh
+
# Current gcc version
gcc_14:
runs-on: ubuntu-24.04
@@ -81,32 +85,28 @@ jobs:
../bin/luanti --run-unittests
# Older clang version (should be close to our minimum supported version)
- clang_7:
- runs-on: ubuntu-20.04
+ clang_11:
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
- install_linux_deps clang-7 llvm-7
+ install_linux_deps clang-11
- name: Build
run: |
./util/ci/build.sh
env:
- CC: clang-7
- CXX: clang++-7
- CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
+ CC: clang-11
+ CXX: clang++-11
+ # Test fallback SHA implementations
+ CMAKE_FLAGS: '-DENABLE_OPENSSL=0'
- - name: Unittest
+ - name: Test
run: |
./bin/luanti --run-unittests
- # Do this here because we have ASan and error paths are sensitive to dangling pointers
- - name: Test error cases
- run: |
- ./util/test_error_cases.sh
-
# Current clang version
clang_18:
runs-on: ubuntu-24.04
@@ -132,16 +132,16 @@ jobs:
run: |
./util/test_multiplayer.sh
- # Build with prometheus-cpp (server-only)
- clang_11_prometheus:
- name: "clang_11 (PROMETHEUS=1)"
- runs-on: ubuntu-22.04
+ # Build with prometheus-cpp (server-only), also runs on ARM64
+ clang_prometheus_arm:
+ name: "clang (with Prometheus, ARM64)"
+ runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Install deps
run: |
source ./util/ci/common.sh
- install_linux_deps clang-11
+ install_linux_deps --headless clang libluajit-5.1-dev
- name: Build prometheus-cpp
run: ./util/ci/build_prometheus_cpp.sh
@@ -150,8 +150,8 @@ jobs:
run: |
./util/ci/build.sh
env:
- CC: clang-11
- CXX: clang++-11
+ CC: clang
+ CXX: clang++
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0 -DENABLE_CURSES=0"
- name: Test
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index c073fe106..859cd3060 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -45,7 +45,7 @@ jobs:
mkdir build
cd build
cmake .. \
- -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
+ -DCMAKE_OSX_DEPLOYMENT_TARGET=13 \
-DCMAKE_FIND_FRAMEWORK=LAST \
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE \
diff --git a/.github/workflows/whitespace_checks.yml b/.github/workflows/whitespace_checks.yml
index b5fcc02e5..ea8d41e3b 100644
--- a/.github/workflows/whitespace_checks.yml
+++ b/.github/workflows/whitespace_checks.yml
@@ -92,7 +92,7 @@ jobs:
- name: Check indent spaces
run: |
if git ls-files |\
- grep -E '^src/.*\.cpp$|^src/.*\.[ch]$|\.lua' |\
+ grep -E '^src/.*\.cpp$|^src/.*\.[ch]$|\.lua$' |\
xargs grep -n -P '^\t*[ ]';\
then\
echo -e "\033[0;31mFound incorrect indent whitespaces";\
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index d82b9785d..979ab0144 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -71,9 +71,7 @@ jobs:
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
- VCPKG_VERSION: 01f602195983451bc83e72f4214af2cbc495aa94
- # 2024.05.24
- vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
+ VCPKG_DEFAULT_TRIPLET: ${{matrix.config.vcpkg_triplet}}
strategy:
fail-fast: false
matrix:
@@ -97,19 +95,17 @@ jobs:
- uses: actions/checkout@v4
- name: Restore from cache and run vcpkg
- uses: lukka/run-vcpkg@v7
+ uses: lukka/run-vcpkg@v11
with:
- vcpkgArguments: ${{env.vcpkg_packages}}
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
- appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
- vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
- vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
- name: CMake
+ # Note: See #15976 for why CMAKE_POLICY_VERSION_MINIMUM=3.5 is set.
run: |
cmake ${{matrix.config.generator}} `
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
-DCMAKE_BUILD_TYPE=Release `
+ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 `
-DENABLE_POSTGRESQL=OFF `
-DENABLE_LUAJIT=TRUE `
-DREQUIRE_LUAJIT=TRUE `
diff --git a/.luacheckrc b/.luacheckrc
index 82c10fcd3..670c84325 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -19,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"}},
}
@@ -33,6 +33,13 @@ globals = {
"_",
}
+stds.menu_common = {
+ globals = {
+ "mt_color_grey", "mt_color_blue", "mt_color_lightblue", "mt_color_green",
+ "mt_color_dark_green", "mt_color_orange", "mt_color_red",
+ },
+}
+
files["builtin/client/register.lua"] = {
globals = {
debug = {fields={"getinfo"}},
@@ -73,11 +80,16 @@ files["builtin/common/filterlist.lua"] = {
}
files["builtin/mainmenu"] = {
+ std = "+menu_common",
globals = {
"gamedata",
},
}
+files["builtin/common/settings"] = {
+ std = "+menu_common",
+}
+
files["builtin/common/tests"] = {
read_globals = {
"describe",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2c48196d2..70a027f57 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,7 @@
cmake_minimum_required(VERSION 3.12)
+if(POLICY CMP0177)
+ cmake_policy(SET CMP0177 NEW)
+endif()
# This can be read from ${PROJECT_NAME} after project() is called
project(luanti)
@@ -11,7 +14,7 @@ set(CLANG_MINIMUM_VERSION "7.0.1")
# You should not need to edit these manually, use util/bump_version.sh
set(VERSION_MAJOR 5)
-set(VERSION_MINOR 12)
+set(VERSION_MINOR 13)
set(VERSION_PATCH 0)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
@@ -262,8 +265,8 @@ install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
if(UNIX AND NOT APPLE)
install(FILES "doc/luanti.6" "doc/luantiserver.6" DESTINATION "${MANDIR}/man6")
- install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}")
- install(FILES "misc/net.minetest.minetest.metainfo.xml" DESTINATION "${METAINFODIR}")
+ install(FILES "misc/org.luanti.luanti.desktop" DESTINATION "${XDG_APPS_DIR}")
+ install(FILES "misc/org.luanti.luanti.metainfo.xml" DESTINATION "${METAINFODIR}")
install(FILES "misc/luanti.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
install(FILES "misc/luanti-xorg-icon-128.png"
DESTINATION "${ICONDIR}/hicolor/128x128/apps"
diff --git a/LICENSE.txt b/LICENSE.txt
index 01a335b83..10b9b8d3a 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -104,6 +104,14 @@ grorp:
using the font "undefined medium" (https://undefined-medium.com/),
which is licensed under the SIL Open Font License, Version 1.1
modified by DS
+ textures/base/pack/dig_btn.png
+ textures/base/pack/place_btn.png
+ derived by editing the text in aux1_btn.svg
+
+Material Design, Google (Apache license v2.0):
+ textures/base/pack/contentdb_thumb_up.png
+ textures/base/pack/contentdb_thumb_down.png
+ textures/base/pack/contentdb_neutral.png
License of Luanti source code
-------------------------------
diff --git a/README.md b/README.md
index 3ff436419..6abd6d22c 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,15 @@
-Luanti (formerly Minetest)
-==========================
-
-
-[](https://hosted.weblate.org/engage/minetest/?utm_source=widget)
-[](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)
+
+
+
Luanti (formerly Minetest)
+
+
+
+
+
Luanti is a free open-source voxel game engine with easy modding and game creation.
-Copyright (C) 2010-2024 Perttu Ahola
+Copyright (C) 2010-2025 Perttu Ahola
and contributors (see source file comments and the version control log)
Table of Contents
@@ -55,6 +57,7 @@ Some can be changed in the key config dialog in the settings tab.
| T | Chat |
| / | Command |
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
+| Shift + Esc | Exit directly to main menu from anywhere, bypassing pause menu |
| + | Increase view range |
| - | Decrease view range |
| K | Enable/disable fly mode (needs fly privilege) |
diff --git a/android/SDL_Java_RMB_fix.patch b/android/SDL_Java_RMB_fix.patch
index cc0eb4772..ef8840dca 100644
--- a/android/SDL_Java_RMB_fix.patch
+++ b/android/SDL_Java_RMB_fix.patch
@@ -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) {
diff --git a/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/android/app/src/main/java/net/minetest/minetest/GameActivity.java
index 22e861fb0..770c1c4f2 100644
--- a/android/app/src/main/java/net/minetest/minetest/GameActivity.java
+++ b/android/app/src/main/java/net/minetest/minetest/GameActivity.java
@@ -22,14 +22,19 @@ package net.minetest.minetest;
import org.libsdl.app.SDLActivity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.net.Uri;
-import android.os.Bundle;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
@@ -91,6 +96,9 @@ public class GameActivity extends SDLActivity {
saveSettings();
}
+ private NotificationManager mNotifyManager;
+ private boolean gameNotificationShown = false;
+
public void showTextInputDialog(String hint, String current, int editType) {
runOnUiThread(() -> showTextInputDialogUI(hint, current, editType));
}
@@ -263,4 +271,67 @@ public class GameActivity extends SDLActivity {
public boolean hasPhysicalKeyboard() {
return getContext().getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
}
+
+ // TODO: share code with UnzipService.createNotification
+ private void updateGameNotification() {
+ if (mNotifyManager == null) {
+ mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ if (!gameNotificationShown) {
+ mNotifyManager.cancel(MainActivity.NOTIFICATION_ID_GAME);
+ return;
+ }
+
+ Notification.Builder builder;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
+ } else {
+ builder = new Notification.Builder(this);
+ }
+
+ Intent notificationIntent = new Intent(this, GameActivity.class);
+ notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ int pendingIntentFlag = 0;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ pendingIntentFlag = PendingIntent.FLAG_MUTABLE;
+ }
+ PendingIntent intent = PendingIntent.getActivity(this, 0,
+ notificationIntent, pendingIntentFlag);
+
+ builder.setContentTitle(getString(R.string.game_notification_title))
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentIntent(intent)
+ .setOngoing(true);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ // This avoids a stuck notification if the app is killed while
+ // in-game: (1) if the user closes the app from the "Recents" screen
+ // or (2) if the system kills the app while it is in background.
+ // onStop is called too early to remove the notification and
+ // onDestroy is often not called at all, so there's this hack instead.
+ builder.setTimeoutAfter(16000);
+
+ // Replace the notification just before it expires as long as the app is
+ // running (and we're still in-game).
+ final Handler handler = new Handler(Looper.getMainLooper());
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (gameNotificationShown) {
+ updateGameNotification();
+ }
+ }
+ }, 15000);
+ }
+
+ mNotifyManager.notify(MainActivity.NOTIFICATION_ID_GAME, builder.build());
+ }
+
+
+ public void setPlayingNowNotification(boolean show) {
+ gameNotificationShown = show;
+ updateGameNotification();
+ }
}
diff --git a/android/app/src/main/java/net/minetest/minetest/MainActivity.java b/android/app/src/main/java/net/minetest/minetest/MainActivity.java
index 7735e8b3d..a2edb2d7a 100644
--- a/android/app/src/main/java/net/minetest/minetest/MainActivity.java
+++ b/android/app/src/main/java/net/minetest/minetest/MainActivity.java
@@ -43,6 +43,8 @@ import static net.minetest.minetest.UnzipService.*;
public class MainActivity extends AppCompatActivity {
public static final String NOTIFICATION_CHANNEL_ID = "Minetest channel";
+ public static final int NOTIFICATION_ID_UNZIP = 1;
+ public static final int NOTIFICATION_ID_GAME = 2;
private final static int versionCode = BuildConfig.VERSION_CODE;
private static final String SETTINGS = "MinetestSettings";
diff --git a/android/app/src/main/java/net/minetest/minetest/UnzipService.java b/android/app/src/main/java/net/minetest/minetest/UnzipService.java
index 542b2a159..895ad66fc 100644
--- a/android/app/src/main/java/net/minetest/minetest/UnzipService.java
+++ b/android/app/src/main/java/net/minetest/minetest/UnzipService.java
@@ -51,7 +51,6 @@ public class UnzipService extends IntentService {
public static final int SUCCESS = -1;
public static final int FAILURE = -2;
public static final int INDETERMINATE = -3;
- private final int id = 1;
private NotificationManager mNotifyManager;
private boolean isSuccess = true;
private String failureMessage;
@@ -100,11 +99,14 @@ public class UnzipService extends IntentService {
}
}
+ // TODO: share code with GameActivity.updateGameNotification
@NonNull
private Notification.Builder createNotification() {
- Notification.Builder builder;
- if (mNotifyManager == null)
+ if (mNotifyManager == null) {
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
} else {
@@ -128,7 +130,7 @@ public class UnzipService extends IntentService {
.setOngoing(true)
.setProgress(0, 0, true);
- mNotifyManager.notify(id, builder.build());
+ mNotifyManager.notify(MainActivity.NOTIFICATION_ID_UNZIP, builder.build());
return builder;
}
@@ -200,14 +202,14 @@ public class UnzipService extends IntentService {
} else {
notificationBuilder.setProgress(100, progress, false);
}
- mNotifyManager.notify(id, notificationBuilder.build());
+ mNotifyManager.notify(MainActivity.NOTIFICATION_ID_UNZIP, notificationBuilder.build());
}
}
@Override
public void onDestroy() {
super.onDestroy();
- mNotifyManager.cancel(id);
+ mNotifyManager.cancel(MainActivity.NOTIFICATION_ID_UNZIP);
publishProgress(null, R.string.loading, isSuccess ? SUCCESS : FAILURE);
}
}
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 393347d43..919640040 100644
--- a/android/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -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) {
diff --git a/android/app/src/main/res/values-br/strings.xml b/android/app/src/main/res/values-br/strings.xml
new file mode 100644
index 000000000..16ba0da21
--- /dev/null
+++ b/android/app/src/main/res/values-br/strings.xml
@@ -0,0 +1,11 @@
+
+
+ O kargañ Luanti
+ Luanti
+ O kargañ…
+ Evezhiadennoù gant Luanti
+ Nebeutoc\'h eget ur vunutenn…
+ Graet
+ Merdeer web ebet bet kavet
+ Evezhiadennoù hollek
+
diff --git a/android/app/src/main/res/values-de/strings.xml b/android/app/src/main/res/values-de/strings.xml
index 8585c95cc..80c025573 100644
--- a/android/app/src/main/res/values-de/strings.xml
+++ b/android/app/src/main/res/values-de/strings.xml
@@ -1,11 +1,12 @@
Luanti
- Lädt…
+ Laden …Luanti lädt
- Weniger als 1 Minute…
+ Weniger als 1 Minute …Fertig
- Kein Web-Browser gefunden
+ Keinen Web-Browser gefundenAllgemeine BenachrichtigungBenachrichtigungen von Luanti
-
\ No newline at end of file
+ Luanti läuft
+
diff --git a/android/app/src/main/res/values-fr/strings.xml b/android/app/src/main/res/values-fr/strings.xml
index 690965405..1b3ff29cc 100644
--- a/android/app/src/main/res/values-fr/strings.xml
+++ b/android/app/src/main/res/values-fr/strings.xml
@@ -8,4 +8,5 @@
Moins d\'une minute…TerminéAucun navigateur web trouvé
+ Luanti est en cours d\'exécution
diff --git a/android/app/src/main/res/values-in/strings.xml b/android/app/src/main/res/values-in/strings.xml
index cbd8acd9a..34ce22bbc 100644
--- a/android/app/src/main/res/values-in/strings.xml
+++ b/android/app/src/main/res/values-in/strings.xml
@@ -8,4 +8,5 @@
Kurang dari 1 menit…Pemberitahuan dari LuantiMemuat Luanti…
-
\ No newline at end of file
+ Luanti sedang berjalan
+
diff --git a/android/app/src/main/res/values-ro/strings.xml b/android/app/src/main/res/values-ro/strings.xml
new file mode 100644
index 000000000..debbd900a
--- /dev/null
+++ b/android/app/src/main/res/values-ro/strings.xml
@@ -0,0 +1,11 @@
+
+
+ Luanti
+ Se încarcă…
+ Notificare generală
+ Notificări de la Luanti
+ Luanti pornește
+ Sub 1 minut…
+ Gata
+ Niciun navigator web găsit
+
diff --git a/android/app/src/main/res/values-ru/strings.xml b/android/app/src/main/res/values-ru/strings.xml
index 77748a9f6..5f0a89c6b 100644
--- a/android/app/src/main/res/values-ru/strings.xml
+++ b/android/app/src/main/res/values-ru/strings.xml
@@ -1,11 +1,12 @@
Загрузка Luanti
- Меньше чам за 1 минуту…
+ Менее 1 минуты…ГотовоLuаntiУведомления от LuantiОсновные уведомленияЗагрузка…Не найдено веб-браузера
-
\ No newline at end of file
+ Luanti запущено
+
diff --git a/android/app/src/main/res/values-sl/strings.xml b/android/app/src/main/res/values-sl/strings.xml
new file mode 100644
index 000000000..97661d348
--- /dev/null
+++ b/android/app/src/main/res/values-sl/strings.xml
@@ -0,0 +1,11 @@
+
+
+ Ni bil najden spletni brskalnik
+ Glavno obvestilo
+ Nalaganje …
+ Manj kot 1 minuta …
+ Luanti
+ Obvestilo od Luantia
+ Končano!l
+ Nalaganje Luantia
+
diff --git a/android/app/src/main/res/values-sv/strings.xml b/android/app/src/main/res/values-sv/strings.xml
index 5dcf923eb..38badff14 100644
--- a/android/app/src/main/res/values-sv/strings.xml
+++ b/android/app/src/main/res/values-sv/strings.xml
@@ -4,8 +4,9 @@
Laddar…Mindre än 1 minut…Färdig
- Ingen webbläsare kunde hittas
- Generell notis
- Notiser från Luanti
+ Ingen webbläsare hittades
+ Allmän notifikation
+ Notifikationer från LuantiLaddar Luanti
-
\ No newline at end of file
+ Luanti är igång
+
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 98511e72c..8b8bc625b 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -6,6 +6,7 @@
Notifications from LuantiLoading LuantiLess than 1 minute…
+ Luanti is runningDoneNo web browser found
diff --git a/android/build.gradle b/android/build.gradle
index 61637c2ec..d2a3b3b2f 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -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", 12) // Version Minor
+project.ext.set("versionMinor", 13) // Version Minor
project.ext.set("versionPatch", 0) // Version Patch
// ^ keep in sync with cmake
diff --git a/android/icons/dig_btn.svg b/android/icons/dig_btn.svg
new file mode 100644
index 000000000..cbfa69e6f
--- /dev/null
+++ b/android/icons/dig_btn.svg
@@ -0,0 +1,148 @@
+
+
diff --git a/android/icons/place_btn.svg b/android/icons/place_btn.svg
new file mode 100644
index 000000000..b32dfc018
--- /dev/null
+++ b/android/icons/place_btn.svg
@@ -0,0 +1,148 @@
+
+
diff --git a/builtin/async/game.lua b/builtin/async/game.lua
index f7c9892c4..6bd369fb1 100644
--- a/builtin/async/game.lua
+++ b/builtin/async/game.lua
@@ -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)
diff --git a/builtin/async/mainmenu.lua b/builtin/async/mainmenu.lua
index 0e9c222d1..c1d8618b4 100644
--- a/builtin/async/mainmenu.lua
+++ b/builtin/async/mainmenu.lua
@@ -1,5 +1,6 @@
core.log("info", "Initializing asynchronous environment")
+
function core.job_processor(func, serialized_param)
local param = core.deserialize(serialized_param)
@@ -7,3 +8,15 @@ function core.job_processor(func, serialized_param)
return retval or core.serialize(nil)
end
+
+
+function core.get_http_accept_languages()
+ local languages
+ local current_language = core.get_language()
+ if current_language ~= "" then
+ languages = { current_language, "en;q=0.8" }
+ else
+ languages = { "en" }
+ end
+ return "Accept-Language: " .. table.concat(languages, ", ")
+end
diff --git a/builtin/common/filterlist.lua b/builtin/common/filterlist.lua
index c323a0d55..058a573c6 100644
--- a/builtin/common/filterlist.lua
+++ b/builtin/common/filterlist.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2013 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2013 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
-- TODO improve doc --
diff --git a/builtin/common/menu.lua b/builtin/common/menu.lua
new file mode 100644
index 000000000..4eb3dfe44
--- /dev/null
+++ b/builtin/common/menu.lua
@@ -0,0 +1,15 @@
+-- Luanti
+-- SPDX-License-Identifier: LGPL-2.1-or-later
+
+-- These colors are used by the main menu and the settings menu
+mt_color_grey = "#AAAAAA"
+mt_color_blue = "#6389FF"
+mt_color_lightblue = "#99CCFF"
+mt_color_green = "#72FF63"
+mt_color_dark_green = "#25C191"
+mt_color_orange = "#FF8800"
+mt_color_red = "#FF3300"
+
+function core.are_keycodes_equal(k1, k2)
+ return core.normalize_keycode(k1) == core.normalize_keycode(k2)
+end
diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
index ce4179f54..29aa3e5c2 100644
--- a/builtin/common/misc_helpers.lua
+++ b/builtin/common/misc_helpers.lua
@@ -7,18 +7,21 @@ local math = math
local function basic_dump(o)
local tp = type(o)
if tp == "number" then
- return tostring(o)
+ local s = tostring(o)
+ if tonumber(s) == o then
+ return s
+ end
+ -- Prefer an exact representation over a compact representation.
+ -- e.g. basic_dump(0.3) == "0.3",
+ -- but basic_dump(0.1 + 0.2) == "0.30000000000000004"
+ -- so the user can see that 0.1 + 0.2 ~= 0.3
+ return string.format("%.17g", o)
elseif tp == "string" then
return string.format("%q", o)
elseif tp == "boolean" then
return tostring(o)
elseif tp == "nil" then
return "nil"
- -- Uncomment for full function dumping support.
- -- Not currently enabled because bytecode isn't very human-readable and
- -- dump's output is intended for humans.
- --elseif tp == "function" then
- -- return string.format("loadstring(%q)", string.dump(o))
elseif tp == "userdata" then
return tostring(o)
else
@@ -105,65 +108,141 @@ function dump2(o, name, dumped)
return string.format("%s = {}\n%s", name, table.concat(t))
end
---------------------------------------------------------------------------------
--- This dumps values in a one-statement format.
+
+-- This dumps values in a human-readable expression format.
+-- If possible, the resulting string should evaluate to an equivalent value if loaded and executed.
-- For example, {test = {"Testing..."}} becomes:
-- [[{
-- test = {
-- "Testing..."
-- }
-- }]]
--- This supports tables as keys, but not circular references.
--- It performs poorly with multiple references as it writes out the full
--- table each time.
--- The indent field specifies a indentation string, it defaults to a tab.
--- Use the empty string to disable indentation.
--- The dumped and level arguments are internal-only.
-
-function dump(o, indent, nested, level)
- local t = type(o)
- if not level and t == "userdata" then
- -- when userdata (e.g. player) is passed directly, print its metatable:
- return "userdata metatable: " .. dump(getmetatable(o))
- end
- if t ~= "table" then
- return basic_dump(o)
- end
-
- -- Contains table -> true/nil of currently nested tables
- nested = nested or {}
- if nested[o] then
- return ""
- end
- nested[o] = true
+function dump(value, indent)
indent = indent or "\t"
- level = level or 1
+ local newline = indent == "" and "" or "\n"
- local ret = {}
- local dumped_indexes = {}
- for i, v in ipairs(o) do
- ret[#ret + 1] = dump(v, indent, nested, level + 1)
- dumped_indexes[i] = true
- end
- for k, v in pairs(o) do
- if not dumped_indexes[k] then
- if type(k) ~= "string" or not is_valid_identifier(k) then
- k = "["..dump(k, indent, nested, level + 1).."]"
- end
- v = dump(v, indent, nested, level + 1)
- ret[#ret + 1] = k.." = "..v
+ local rope = {}
+ local write
+ do
+ -- Keeping the length of the table as a local variable is *much*
+ -- faster than invoking the length operator.
+ -- See https://gitspartv.github.io/LuaJIT-Benchmarks/#test12.
+ local i = 0
+ function write(str)
+ i = i + 1
+ rope[i] = str
end
end
- nested[o] = nil
- if indent ~= "" then
- local indent_str = "\n"..string.rep(indent, level)
- local end_indent_str = "\n"..string.rep(indent, level - 1)
- return string.format("{%s%s%s}",
- indent_str,
- table.concat(ret, ","..indent_str),
- end_indent_str)
+
+ local n_refs = {}
+ local function count_refs(val)
+ if type(val) ~= "table" then
+ return
+ end
+ local tbl = val
+ if n_refs[tbl] then
+ n_refs[tbl] = n_refs[tbl] + 1
+ return
+ end
+ n_refs[tbl] = 1
+ for k, v in pairs(tbl) do
+ count_refs(k)
+ count_refs(v)
+ end
end
- return "{"..table.concat(ret, ", ").."}"
+ count_refs(value)
+
+ local refs = {}
+ local cur_ref = 1
+ local function write_value(val, level)
+ if type(val) ~= "table" then
+ write(basic_dump(val))
+ return
+ end
+
+ local tbl = val
+ if refs[tbl] then
+ write(refs[tbl])
+ return
+ end
+
+ if n_refs[val] > 1 then
+ refs[val] = ("getref(%d)"):format(cur_ref)
+ write(("setref(%d)"):format(cur_ref))
+ cur_ref = cur_ref + 1
+ end
+ write("{")
+ if next(tbl) == nil then
+ write("}")
+ return
+ end
+ write(newline)
+
+ local function write_entry(k, v)
+ write(indent:rep(level))
+ write("[")
+ write_value(k, level + 1)
+ write("] = ")
+ write_value(v, level + 1)
+ write(",")
+ write(newline)
+ end
+
+ local keys = {string = {}, number = {}}
+ for k in pairs(tbl) do
+ local t = type(k)
+ if keys[t] then
+ table.insert(keys[t], k)
+ end
+ end
+
+ -- Write string-keyed entries
+ table.sort(keys.string)
+ for _, k in ipairs(keys.string) do
+ local v = val[k]
+ if is_valid_identifier(k) then
+ write(indent:rep(level))
+ write(k)
+ write(" = ")
+ write_value(v, level + 1)
+ write(",")
+ write(newline)
+ else
+ write_entry(k, v)
+ end
+ end
+
+ -- Write number-keyed entries
+ local len = 0
+ for i in ipairs(tbl) do
+ len = i
+ end
+ if #keys.number == len then -- table is a list
+ for _, v in ipairs(tbl) do
+ write(indent:rep(level))
+ write_value(v, level + 1)
+ write(",")
+ write(newline)
+ end
+ else -- table harbors arbitrary number keys
+ table.sort(keys.number)
+ for _, k in ipairs(keys.number) do
+ write_entry(k, tbl[k])
+ end
+ end
+
+ -- Write all remaining entries
+ for k, v in pairs(val) do
+ if not keys[type(k)] then
+ write_entry(k, v)
+ end
+ end
+
+ write(indent:rep(level - 1))
+ write("}")
+ end
+ write_value(value, 1)
+ return table.concat(rope)
end
--------------------------------------------------------------------------------
@@ -457,18 +536,37 @@ do
end
end
---------------------------------------------------------------------------------
-function table.copy(t, seen)
- local n = {}
- seen = seen or {}
- seen[t] = n
- for k, v in pairs(t) do
- n[(type(k) == "table" and (seen[k] or table.copy(k, seen))) or k] =
- (type(v) == "table" and (seen[v] or table.copy(v, seen))) or v
+
+local function table_copy(value, preserve_metatables)
+ local seen = {}
+ local function copy(val)
+ if type(val) ~= "table" then
+ return val
+ end
+ 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 n
+ return copy(value)
end
+function table.copy(value)
+ return table_copy(value, false)
+end
+
+function table.copy_with_metatables(value)
+ return table_copy(value, true)
+end
function table.insert_all(t, other)
if table.move then -- LuaJIT
@@ -536,6 +634,10 @@ if core.gettext then -- for client and mainmenu
function fgettext(text, ...)
return core.formspec_escape(fgettext_ne(text, ...))
end
+
+ function hgettext(text, ...)
+ return core.hypertext_escape(fgettext_ne(text, ...))
+ end
end
local ESCAPE_CHAR = string.char(0x1b)
diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua
index 146128e0c..9ebece6d0 100644
--- a/builtin/common/serialize.lua
+++ b/builtin/common/serialize.lua
@@ -190,11 +190,41 @@ local function serialize(value, write)
dump(value)
end
+-- Whether `value` recursively contains a function
+local function contains_function(value)
+ local seen = {}
+ local function check(val)
+ if type(val) == "function" then
+ return true
+ end
+ if type(val) == "table" then
+ if seen[val] then
+ return false
+ end
+ seen[val] = true
+ for k, v in pairs(val) do
+ if check(k) or check(v) then
+ return true
+ end
+ end
+ end
+ return false
+ end
+ return check(value)
+end
+
function core.serialize(value)
+ if contains_function(value) then
+ core.log("deprecated", "Support for dumping functions in `core.serialize` is deprecated.")
+ end
local rope = {}
+ -- Keeping the length of the table as a local variable is *much*
+ -- faster than invoking the length operator.
+ -- See https://gitspartv.github.io/LuaJIT-Benchmarks/#test12.
+ local i = 0
serialize(value, function(text)
- -- Faster than table.insert(rope, text) on PUC Lua 5.1
- rope[#rope + 1] = text
+ i = i + 1
+ rope[i] = text
end)
return table_concat(rope)
end
diff --git a/builtin/common/settings/components.lua b/builtin/common/settings/components.lua
index aec2b8898..99fb0cd76 100644
--- a/builtin/common/settings/components.lua
+++ b/builtin/common/settings/components.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2022 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2022 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local make = {}
@@ -37,6 +24,7 @@ local make = {}
-- * `fs` is a string for the formspec.
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
-- * `used_height` is the space used by components in `fs`.
+-- * `spacing`: (Optional) the vertical margin to be added before the component (default 0.25)
-- * `on_submit = function(self, fields, parent)`:
-- * `fields`: submitted formspec fields
-- * `parent`: the fstk element for the settings UI, use to show dialogs
@@ -442,13 +430,66 @@ local function make_noise_params(setting)
}
end
+function make.key(setting)
+ local btn_bind = "bind_" .. setting.name
+ local btn_clear = "unbind_" .. setting.name
+ local function add_conflict_warnings(fs, height)
+ local value = core.settings:get(setting.name)
+ if value == "" then
+ return height
+ end
+ for _, o in ipairs(core.full_settingtypes) do
+ if o.type == "key" and o.name ~= setting.name and core.are_keycodes_equal(core.settings:get(o.name), value) then
+ table.insert(fs, ("label[0,%f;%s]"):format(height + 0.3,
+ core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext(o.readable_name)))))
+ height = height + 0.6
+ end
+ end
+ return height
+ end
+ return {
+ info_text = setting.comment,
+ setting = setting,
+ spacing = 0.1,
+
+ get_formspec = function(self, avail_w)
+ self.resettable = core.settings:has(setting.name)
+ local btn_bind_width = math.max(2.5, avail_w/2)
+ local value = core.settings:get(setting.name)
+ local fs = {
+ ("label[0,0.4;%s]"):format(get_label(setting)),
+ ("button_key[%f,0;%f,0.8;%s;%s]"):format(
+ btn_bind_width, btn_bind_width-0.8,
+ btn_bind, core.formspec_escape(value)),
+ ("image_button[%f,0;0.8,0.8;%s;%s;]"):format(avail_w - 0.8,
+ core.formspec_escape(defaulttexturedir .. "clear.png"),
+ btn_clear),
+ ("tooltip[%s;%s]"):format(btn_clear, fgettext("Remove keybinding")),
+ }
+ local height = 0.8
+ height = add_conflict_warnings(fs, height)
+ return table.concat(fs), height
+ end,
+
+ on_submit = function(self, fields)
+ if fields[btn_bind] then
+ core.settings:set(setting.name, fields[btn_bind])
+ return true
+ elseif fields[btn_clear] then
+ core.settings:set(setting.name, "")
+ return true
+ end
+ end,
+ }
+end
+
if INIT == "pause_menu" then
-- Making the noise parameter dialog work in the pause menu settings would
-- require porting "FSTK" (at least the dialog API) from the mainmenu formspec
-- API to the in-game formspec API.
-- There's no reason you'd want to adjust mapgen noise parameter settings
- -- in-game (they only apply to new worlds), so there's no reason to implement
- -- this.
+ -- 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
diff --git a/builtin/common/settings/dlg_change_mapgen_flags.lua b/builtin/common/settings/dlg_change_mapgen_flags.lua
index 31dd40bd8..de96c75e3 100644
--- a/builtin/common/settings/dlg_change_mapgen_flags.lua
+++ b/builtin/common/settings/dlg_change_mapgen_flags.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2015 PilzAdam
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2015 PilzAdam
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local checkboxes = {}
diff --git a/builtin/common/settings/dlg_settings.lua b/builtin/common/settings/dlg_settings.lua
index 894eaa596..77fc8be3f 100644
--- a/builtin/common/settings/dlg_settings.lua
+++ b/builtin/common/settings/dlg_settings.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2022 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2022 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM
@@ -22,7 +9,6 @@ local component_funcs = dofile(path .. "components.lua")
local shadows_component = dofile(path .. "shadows_component.lua")
local loaded = false
-local full_settings
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
local all_pages = {}
@@ -32,7 +18,7 @@ local filtered_page_by_id = page_by_id
local function get_setting_info(name)
- for _, entry in ipairs(full_settings) do
+ for _, entry in ipairs(core.full_settingtypes) do
if entry.type ~= "category" and entry.name == name then
return entry
end
@@ -70,7 +56,7 @@ local function load_settingtypes()
end
end
- for _, entry in ipairs(full_settings) do
+ for _, entry in ipairs(core.full_settingtypes) do
if entry.type == "category" then
if entry.level == 0 then
section = entry.name
@@ -104,29 +90,14 @@ local function load()
end
loaded = true
- full_settings = settingtypes.parse_config_file(false, true)
-
- local change_keys = {
- query_text = "Controls",
- requires = {
- keyboard_mouse = true,
- },
- get_formspec = function(self, avail_w)
- local btn_w = math.min(avail_w, 3)
- return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
- end,
- on_submit = function(self, fields)
- if fields.btn_change_keys then
- core.show_keys_menu()
- end
- end,
- }
+ core.full_settingtypes = settingtypes.parse_config_file(false, true)
local touchscreen_layout = {
query_text = "Touchscreen layout",
requires = {
touchscreen = true,
},
+ context = "client",
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 6)
return ("button[0,0;%f,0.8;btn_touch_layout;%s]"):format(btn_w, fgettext("Touchscreen layout")), 0.8
@@ -159,13 +130,11 @@ local function load()
{ heading = fgettext_ne("Movement") },
"arm_inertia",
"view_bobbing_amount",
- "fall_bobbing_amount",
},
})
load_settingtypes()
- table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
-- insert after "touch_controls"
table.insert(page_by_id.controls_touchscreen.content, 2, touchscreen_layout)
do
@@ -174,18 +143,24 @@ local function load()
table.insert(content, idx, shadows_component)
idx = table.indexof(content, "enable_auto_exposure") + 1
+ local setting_info = get_setting_info("enable_auto_exposure")
local note = component_funcs.note(fgettext_ne("(The game will need to enable automatic exposure as well)"))
- note.requires = get_setting_info("enable_auto_exposure").requires
+ note.requires = setting_info.requires
+ note.context = setting_info.context
table.insert(content, idx, note)
idx = table.indexof(content, "enable_bloom") + 1
+ setting_info = get_setting_info("enable_bloom")
note = component_funcs.note(fgettext_ne("(The game will need to enable bloom as well)"))
- note.requires = get_setting_info("enable_bloom").requires
+ note.requires = setting_info.requires
+ note.context = setting_info.context
table.insert(content, idx, note)
idx = table.indexof(content, "enable_volumetric_lighting") + 1
+ setting_info = get_setting_info("enable_volumetric_lighting")
note = component_funcs.note(fgettext_ne("(The game will need to enable volumetric lighting as well)"))
- note.requires = get_setting_info("enable_volumetric_lighting").requires
+ note.requires = setting_info.requires
+ note.context = setting_info.context
table.insert(content, idx, note)
end
@@ -260,6 +235,17 @@ local function load()
["true"] = fgettext_ne("Enabled"),
["false"] = fgettext_ne("Disabled"),
}
+
+ get_setting_info("touch_interaction_style").option_labels = {
+ ["tap"] = fgettext_ne("Tap"),
+ ["tap_crosshair"] = fgettext_ne("Tap with crosshair"),
+ ["buttons_crosshair"] = fgettext("Buttons with crosshair"),
+ }
+
+ get_setting_info("touch_punch_gesture").option_labels = {
+ ["short_tap"] = fgettext_ne("Short tap"),
+ ["long_tap"] = fgettext_ne("Long tap"),
+ }
end
@@ -352,7 +338,18 @@ local function update_filtered_pages(query)
end
-local function check_requirements(name, requires)
+local shown_contexts = {
+ common = true,
+ client = true,
+ server = INIT ~= "pause_menu" or core.is_internal_server(),
+ world_creation = INIT ~= "pause_menu",
+}
+
+local function check_requirements(name, requires, context)
+ if context and not shown_contexts[context] then
+ return false
+ end
+
if requires == nil then
return true
end
@@ -360,6 +357,7 @@ local function check_requirements(name, requires)
local video_driver = core.get_active_driver()
local touch_support = core.irrlicht_device_supports_touch()
local touch_controls = core.settings:get("touch_controls")
+ local touch_interaction_style = core.settings:get("touch_interaction_style")
local special = {
android = PLATFORM == "Android",
desktop = PLATFORM ~= "Android",
@@ -370,6 +368,7 @@ local function check_requirements(name, requires)
keyboard_mouse = not touch_support or (touch_controls == "auto" or not core.is_yes(touch_controls)),
opengl = (video_driver == "opengl" or video_driver == "opengl3"),
gles = video_driver:sub(1, 5) == "ogles",
+ touch_interaction_style_tap = touch_interaction_style ~= "buttons_crosshair",
}
for req_key, req_value in pairs(requires) do
@@ -411,11 +410,11 @@ function page_has_contents(page, actual_content)
elseif type(item) == "string" then
local setting = get_setting_info(item)
assert(setting, "Unknown setting: " .. item)
- if check_requirements(setting.name, setting.requires) then
+ if check_requirements(setting.name, setting.requires, setting.context) then
return true
end
elseif item.get_formspec then
- if check_requirements(item.id, item.requires) then
+ if check_requirements(item.id, item.requires, item.context) then
return true
end
else
@@ -437,20 +436,22 @@ local function build_page_components(page)
elseif item.heading then
last_heading = item
else
- local name, requires
+ local name, requires, context
if type(item) == "string" then
local setting = get_setting_info(item)
assert(setting, "Unknown setting: " .. item)
name = setting.name
requires = setting.requires
+ context = setting.context
elseif item.get_formspec then
name = item.id
requires = item.requires
+ context = item.context
else
error("Unknown content in page: " .. dump(item))
end
- if check_requirements(name, requires) then
+ if check_requirements(name, requires, context) then
if last_heading then
content[#content + 1] = last_heading
last_heading = nil
@@ -517,7 +518,7 @@ local function get_formspec(dialogdata)
("button[0,%f;%f,0.8;back;%s]"):format(
tabsize.height + 0.2, back_w,
- INIT == "pause_menu" and fgettext("Exit") or fgettext("Back")),
+ fgettext("Back")),
("box[%f,%f;%f,0.8;#0000008C]"):format(
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
@@ -632,7 +633,13 @@ local function get_formspec(dialogdata)
fs[#fs + 1] = "container_end[]"
if used_h > 0 then
- y = y + used_h + 0.25
+ local spacing = 0.25
+ local next_comp = dialogdata.components[i + 1]
+ if next_comp and next_comp.spacing then
+ spacing = next_comp.spacing
+ end
+
+ y = y + used_h + spacing
end
end
@@ -771,11 +778,11 @@ end
if INIT == "mainmenu" then
- function create_settings_dlg()
+ function create_settings_dlg(page_id)
load()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
- dlg.data.page_id = update_filtered_pages("")
+ dlg.data.page_id = page_id or update_filtered_pages("")
return dlg
end
diff --git a/builtin/common/settings/generate_from_settingtypes.lua b/builtin/common/settings/generate_from_settingtypes.lua
index 58f2c3301..4c33a8fc1 100644
--- a/builtin/common/settings/generate_from_settingtypes.lua
+++ b/builtin/common/settings/generate_from_settingtypes.lua
@@ -61,7 +61,7 @@ local function create_minetest_conf_example(settings)
end
end
if entry.type == "key" then
- local line = "See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h"
+ local line = "See https://docs.luanti.org/for-players/controls/"
insert(result, "# " .. line .. "\n")
end
insert(result, "# type: " .. entry.type)
diff --git a/builtin/common/settings/init.lua b/builtin/common/settings/init.lua
index d475e0c96..71a95424b 100644
--- a/builtin/common/settings/init.lua
+++ b/builtin/common/settings/init.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2022 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2022 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM
diff --git a/builtin/common/settings/settingtypes.lua b/builtin/common/settings/settingtypes.lua
index a4dd28483..fa54ec33b 100644
--- a/builtin/common/settings/settingtypes.lua
+++ b/builtin/common/settings/settingtypes.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2015 PilzAdam
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2015 PilzAdam
+-- SPDX-License-Identifier: LGPL-2.1-or-later
settingtypes = {}
@@ -40,12 +27,24 @@ local CHAR_CLASSES = {
FLAGS = "[%w_%-%.,]",
}
+local valid_contexts = {common = true, client = true, server = true, world_creation = true}
+
+local function check_context_annotation(context, force_context)
+ if force_context then
+ return "Context annotations are not allowed, context is always " .. force_context
+ end
+ if not valid_contexts[context] then
+ return "Unknown context"
+ end
+ return nil
+end
+
local function flags_to_table(flags)
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
end
-- returns error message, or nil
-local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
+local function parse_setting_line(settings, line, read_all, base_level, allow_secure, force_context)
-- strip carriage returns (CR, /r)
line = line:gsub("\r", "")
@@ -69,9 +68,32 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
-- category
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
+ local category_context
+ if not category then
+ stars, category, category_context = line:match("^%[([%*]*)([^%]]+)%] %[([^%]]+)%]$")
+ end
if category then
local category_level = stars:len() + base_level
+ if settings.current_context_level and
+ category_level <= settings.current_context_level then
+ -- The start of this category marks the end of the context annotation's scope.
+ settings.current_context_level = nil
+ settings.current_context = nil
+ end
+
+ if category_context then
+ local err = check_context_annotation(category_context, force_context)
+ if err then
+ return err
+ end
+ if settings.current_context_level then
+ return "Category context annotations cannot be nested"
+ end
+ settings.current_context_level = category_level
+ settings.current_context = category_context
+ end
+
if settings.current_hide_level then
if settings.current_hide_level < category_level then
-- Skip this category, it's inside a hidden category.
@@ -102,7 +124,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
end
-- settings
- local first_part, name, readable_name, setting_type = line:match("^"
+ local function make_pattern(include_context)
+ return "^"
-- this first capture group matches the whole first part,
-- so we can later strip it from the rest of the line
.. "("
@@ -110,9 +133,19 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
.. CHAR_CLASSES.SPACE .. "*"
.. "%(([^%)]*)%)" -- readable name
.. CHAR_CLASSES.SPACE .. "*"
+ .. (include_context and (
+ "%[([^%]]+)%]" -- context annotation
+ .. CHAR_CLASSES.SPACE .. "*"
+ ) or "")
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
.. CHAR_CLASSES.SPACE .. "*"
- .. ")")
+ .. ")"
+ end
+ local first_part, name, readable_name, setting_type = line:match(make_pattern(false))
+ local setting_context
+ if not first_part then
+ first_part, name, readable_name, setting_context, setting_type = line:match(make_pattern(true))
+ end
if not first_part then
return "Invalid line"
@@ -122,6 +155,26 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
return "Tried to add \"secure.\" setting"
end
+ if setting_context then
+ local err = check_context_annotation(setting_context, force_context)
+ if err then
+ return err
+ end
+ end
+
+ local context
+ if force_context then
+ context = force_context
+ else
+ if setting_context then
+ context = setting_context
+ elseif settings.current_context_level then
+ context = settings.current_context
+ else
+ return "Missing context annotation"
+ end
+ end
+
local requires = {}
local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
if last_line and last_line:lower():sub(1, 9) == "requires:" then
@@ -170,6 +223,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
min = min,
max = max,
requires = requires,
+ context = context,
comment = comment,
})
return
@@ -182,9 +236,9 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
if not default then
return "Invalid string setting"
end
- if setting_type == "key" and not read_all then
- -- ignore key type if read_all is false
- return
+
+ if setting_type == "key" then
+ requires.keyboard_mouse = true
end
table.insert(settings, {
@@ -193,6 +247,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
type = setting_type,
default = default,
requires = requires,
+ context = context,
comment = comment,
})
return
@@ -245,6 +300,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
},
values = values,
requires = requires,
+ context = context,
comment = comment,
noise_params = true,
flags = flags_to_table("defaults,eased,absvalue")
@@ -263,6 +319,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
type = "bool",
default = remaining_line,
requires = requires,
+ context = context,
comment = comment,
})
return
@@ -290,6 +347,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
min = min,
max = max,
requires = requires,
+ context = context,
comment = comment,
})
return
@@ -313,6 +371,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
default = default,
values = values:split(",", true),
requires = requires,
+ context = context,
comment = comment,
})
return
@@ -331,6 +390,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
type = setting_type,
default = default,
requires = requires,
+ context = context,
comment = comment,
})
return
@@ -361,6 +421,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
default = default,
possible = flags_to_table(possible),
requires = requires,
+ context = context,
comment = comment,
})
return
@@ -369,14 +430,14 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
return "Invalid setting type \"" .. setting_type .. "\""
end
-local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
+local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure, force_context)
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
result.current_comment = {}
result.current_hide_level = nil
local line = file:read("*line")
while line do
- local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
+ local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure, force_context)
if error_msg then
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
end
@@ -411,7 +472,6 @@ function settingtypes.parse_config_file(read_all, parse_mods)
-- 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
-- * 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)
@@ -441,7 +501,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
type = "category",
})
- parse_single_file(file, path, read_all, settings, 2, false)
+ parse_single_file(file, path, read_all, settings, 2, false, "server")
file:close()
end
@@ -474,7 +534,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
type = "category",
})
- parse_single_file(file, path, read_all, settings, 2, false)
+ parse_single_file(file, path, read_all, settings, 2, false, "server")
file:close()
end
@@ -505,7 +565,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
type = "category",
})
- parse_single_file(file, path, read_all, settings, 2, false)
+ parse_single_file(file, path, read_all, settings, 2, false, "client")
file:close()
end
diff --git a/builtin/common/settings/shadows_component.lua b/builtin/common/settings/shadows_component.lua
index 2d68f9d3d..59b152ab8 100644
--- a/builtin/common/settings/shadows_component.lua
+++ b/builtin/common/settings/shadows_component.lua
@@ -1,20 +1,7 @@
---Luanti
---Copyright (C) 2021-2 x2048
---Copyright (C) 2022-3 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2021-2 x2048
+-- Copyright (C) 2022-3 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local shadow_levels_labels = {
@@ -84,6 +71,7 @@ return {
requires = {
opengl = true,
},
+ context = "client",
get_formspec = function(self, avail_w)
local labels = table.copy(shadow_levels_labels)
local idx = detect_mapping_idx()
diff --git a/builtin/common/strict.lua b/builtin/common/strict.lua
index 9bfa8d7a2..b3c4ccce4 100644
--- a/builtin/common/strict.lua
+++ b/builtin/common/strict.lua
@@ -19,12 +19,14 @@ function meta:__newindex(name, value)
return
end
local info = getinfo(2, "Sl")
- local desc = ("%s:%d"):format(info.short_src, info.currentline)
- local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name)
- if not warned[warn_key] and info.what ~= "main" and info.what ~= "C" then
- core.log("warning", ("Assignment to undeclared global %q inside a function at %s.")
- :format(name, desc))
- warned[warn_key] = true
+ if info ~= nil then
+ local desc = ("%s:%d"):format(info.short_src, info.currentline)
+ local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name)
+ if not warned[warn_key] and info.what ~= "main" and info.what ~= "C" then
+ core.log("warning", ("Assignment to undeclared global %q inside a function at %s.")
+ :format(name, desc))
+ warned[warn_key] = true
+ end
end
declared[name] = true
end
@@ -35,6 +37,9 @@ function meta:__index(name)
return
end
local info = getinfo(2, "Sl")
+ if info == nil then
+ return
+ end
local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name)
if not warned[warn_key] and info.what ~= "C" then
core.log("warning", ("Undeclared global variable %q accessed at %s:%s")
diff --git a/builtin/common/tests/misc_helpers_spec.lua b/builtin/common/tests/misc_helpers_spec.lua
index 10e2bf277..59eb4ec5f 100644
--- a/builtin/common/tests/misc_helpers_spec.lua
+++ b/builtin/common/tests/misc_helpers_spec.lua
@@ -178,6 +178,35 @@ describe("table", function()
assert.equal(2, table.keyof({[2] = "foo", [3] = "bar"}, "foo"))
assert.equal(3, table.keyof({[1] = "foo", [3] = "bar"}, "bar"))
end)
+
+ describe("copy()", function()
+ it("strips metatables", function()
+ local v = vector.new(1, 2, 3)
+ local w = table.copy(v)
+ assert.are_not.equal(v, w)
+ assert.same(v, w)
+ assert.equal(nil, getmetatable(w))
+ end)
+ it("preserves referential structure", function()
+ local t = {{}, {}}
+ t[1][1] = t[2]
+ t[2][1] = t[1]
+ local copy = table.copy(t)
+ assert.same(t, copy)
+ assert.equal(copy[1][1], copy[2])
+ assert.equal(copy[2][1], copy[1])
+ end)
+ end)
+
+ describe("copy_with_metatables()", function()
+ it("preserves metatables", function()
+ local v = vector.new(1, 2, 3)
+ local w = table.copy_with_metatables(v)
+ assert.equal(getmetatable(v), getmetatable(w))
+ assert(vector.check(w))
+ assert.equal(v, w) -- vector overrides ==
+ end)
+ end)
end)
describe("formspec_escape", function()
@@ -201,3 +230,124 @@ describe("math", function()
assert.equal(0, math.round(-0.49999999999999994))
end)
end)
+
+describe("dump", function()
+ local function test_expression(expr)
+ local chunk = assert(loadstring("return " .. expr))
+ local refs = {}
+ setfenv(chunk, {
+ setref = function(id)
+ refs[id] = {}
+ return function(fields)
+ for k, v in pairs(fields) do
+ refs[id][k] = v
+ end
+ return refs[id]
+ end
+ end,
+ getref = function(id)
+ return assert(refs[id])
+ end,
+ })
+ assert.equal(expr, dump(chunk()))
+ end
+
+ it("nil", function()
+ test_expression("nil")
+ end)
+
+ it("booleans", function()
+ test_expression("false")
+ test_expression("true")
+ end)
+
+ describe("numbers", function()
+ it("formats integers nicely", function()
+ test_expression("42")
+ end)
+ it("avoids misleading rounding", function()
+ test_expression("0.3")
+ assert.equal("0.30000000000000004", dump(0.1 + 0.2))
+ end)
+ end)
+
+ it("strings", function()
+ test_expression('"hello world"')
+ test_expression([["hello \"world\""]])
+ end)
+
+ describe("tables", function()
+ it("empty", function()
+ test_expression("{}")
+ end)
+
+ it("lists", function()
+ test_expression([[
+{
+ false,
+ true,
+ "foo",
+ 1,
+ 2,
+}]])
+ end)
+
+ it("number keys", function()
+test_expression([[
+{
+ [0.5] = false,
+ [1.5] = true,
+ [2.5] = "foo",
+}]])
+ end)
+
+ it("dicts", function()
+ test_expression([[{
+ a = 1,
+ b = 2,
+ c = 3,
+}]])
+ end)
+
+ it("mixed", function()
+ test_expression([[{
+ a = 1,
+ b = 2,
+ c = 3,
+ ["d e"] = true,
+ "foo",
+ "bar",
+}]])
+ end)
+
+ it("nested", function()
+test_expression([[{
+ a = {
+ 1,
+ {},
+ },
+ b = "foo",
+ c = {
+ [0.5] = 0.1,
+ [1.5] = 0.2,
+ },
+}]])
+ end)
+
+ it("circular references", function()
+test_expression([[setref(1){
+ child = {
+ parent = getref(1),
+ },
+ other_child = {
+ parent = getref(1),
+ },
+}]])
+ end)
+
+ it("supports variable indent", function()
+ assert.equal('{1,2,3,{foo = "bar",},}', dump({1, 2, 3, {foo = "bar"}}, ""))
+ assert.equal('{\n "x",\n "y",\n}', dump({"x", "y"}, " "))
+ end)
+ end)
+end)
diff --git a/builtin/common/tests/serialize_spec.lua b/builtin/common/tests/serialize_spec.lua
index d4e501468..2a7a0f3ce 100644
--- a/builtin/common/tests/serialize_spec.lua
+++ b/builtin/common/tests/serialize_spec.lua
@@ -93,21 +93,49 @@ describe("serialize", function()
assert_preserves(test_in)
end)
- it("strips functions in safe mode", function()
- local test_in = {
- func = function(a, b)
- error("test")
- end,
- foo = "bar"
- }
- setfenv(test_in.func, _G)
+ describe("safe mode", function()
+ setup(function()
+ assert(not core.log)
+ -- logging a deprecation warning will be attempted
+ function core.log() end
+ end)
+ teardown(function()
+ core.log = nil
+ end)
+ it("functions are stripped", function()
+ local test_in = {
+ func = function(a, b)
+ error("test")
+ end,
+ foo = "bar"
+ }
+ setfenv(test_in.func, _G)
- local str = core.serialize(test_in)
- assert.not_nil(str:find("loadstring"))
+ local str = core.serialize(test_in)
+ assert.not_nil(str:find("loadstring"))
- local test_out = core.deserialize(str, true)
- assert.is_nil(test_out.func)
- assert.equals(test_out.foo, "bar")
+ local test_out = core.deserialize(str, true)
+ assert.is_nil(test_out.func)
+ assert.equals(test_out.foo, "bar")
+ end)
+ end)
+
+ describe("deprecation warnings", function()
+ before_each(function()
+ assert(not core.log)
+ core.log = spy.new(function(level)
+ assert(level == "deprecated")
+ end)
+ end)
+ after_each(function()
+ core.log = nil
+ end)
+ it("dumping functions", function()
+ local t = {f = function() end, g = function() end}
+ t.t = t
+ core.serialize(t)
+ assert.spy(core.log).was.called(1) -- should have been called exactly *once*
+ end)
end)
it("vectors work", function()
diff --git a/builtin/emerge/env.lua b/builtin/emerge/env.lua
index 5beb6285c..6d3fb0a3b 100644
--- a/builtin/emerge/env.lua
+++ b/builtin/emerge/env.lua
@@ -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
diff --git a/builtin/emerge/register.lua b/builtin/emerge/register.lua
index 308fe4d7e..a07849c7b 100644
--- a/builtin/emerge/register.lua
+++ b/builtin/emerge/register.lua
@@ -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)
diff --git a/builtin/fstk/buttonbar.lua b/builtin/fstk/buttonbar.lua
index 577eb7c6d..461babc1f 100644
--- a/builtin/fstk/buttonbar.lua
+++ b/builtin/fstk/buttonbar.lua
@@ -1,20 +1,7 @@
---Luanti
---Copyright (C) 2014 sapier
---Copyright (C) 2023 Gregor Parzefall
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- Copyright (C) 2023 Gregor Parzefall
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local BASE_SPACING = 0.1
diff --git a/builtin/fstk/dialog.lua b/builtin/fstk/dialog.lua
index 7043646a4..f0b7cf14b 100644
--- a/builtin/fstk/dialog.lua
+++ b/builtin/fstk/dialog.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---this program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local function dialog_event_handler(self,event)
if self.user_eventhandler == nil or
diff --git a/builtin/fstk/tabview.lua b/builtin/fstk/tabview.lua
index c88c4917a..13a96abff 100644
--- a/builtin/fstk/tabview.lua
+++ b/builtin/fstk/tabview.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua
index b5b95d0e4..44d2ab3d8 100644
--- a/builtin/fstk/ui.lua
+++ b/builtin/fstk/ui.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
ui = {}
ui.childlist = {}
@@ -179,6 +166,10 @@ end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
core.button_handler = function(fields)
+ if fields["try_quit"] and not fields["key_enter"] then
+ core.event_handler("MenuQuit")
+ return
+ end
if fields["btn_reconnect_yes"] then
gamedata.reconnect_requested = false
gamedata.errormessage = nil
diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua
index 69c657194..80c9a6e81 100644
--- a/builtin/game/chat.lua
+++ b/builtin/game/chat.lua
@@ -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,
})
diff --git a/builtin/game/deprecated.lua b/builtin/game/deprecated.lua
index 87b785995..48e825ea2 100644
--- a/builtin/game/deprecated.lua
+++ b/builtin/game/deprecated.lua
@@ -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
diff --git a/builtin/game/features.lua b/builtin/game/features.lua
index 4d5e919b4..1b329c7a3 100644
--- a/builtin/game/features.lua
+++ b/builtin/game/features.lua
@@ -46,6 +46,7 @@ core.features = {
biome_weights = true,
particle_blend_clip = true,
remove_item_match_meta = true,
+ httpfetch_additional_methods = true,
}
function core.has_feature(arg)
diff --git a/builtin/game/item.lua b/builtin/game/item.lua
index cc9be44af..4d68f1136 100644
--- a/builtin/game/item.lua
+++ b/builtin/game/item.lua
@@ -360,13 +360,12 @@ end
function core.item_drop(itemstack, dropper, pos)
local dropper_is_player = dropper and dropper:is_player()
local p = table.copy(pos)
- local cnt = itemstack:get_count()
if dropper_is_player then
p.y = p.y + 1.2
end
- local item = itemstack:take_item(cnt)
- local obj = core.add_item(p, item)
+ local obj = core.add_item(p, ItemStack(itemstack))
if obj then
+ itemstack:clear()
if dropper_is_player then
local dir = dropper:get_look_dir()
dir.x = dir.x * 2.9
@@ -375,7 +374,7 @@ function core.item_drop(itemstack, dropper, pos)
obj:set_velocity(dir)
obj:get_luaentity().dropped_by = dropper:get_player_name()
end
- return itemstack
+ return itemstack, obj
end
-- If we reach this, adding the object to the
-- environment failed
@@ -514,7 +513,8 @@ function core.node_dig(pos, node, digger)
.. node.name .. " at " .. core.pos_to_string(pos))
local wielded = digger and digger:get_wielded_item()
- local drops = core.get_node_drops(node, wielded and wielded:get_name())
+ local drops = core.get_node_drops(node, wielded and wielded:get_name(),
+ wielded and ItemStack(wielded), digger, vector.copy(pos))
if wielded then
local wdef = wielded:get_definition()
diff --git a/builtin/game/misc_s.lua b/builtin/game/misc_s.lua
index e64134e15..9433f74bb 100644
--- a/builtin/game/misc_s.lua
+++ b/builtin/game/misc_s.lua
@@ -129,6 +129,7 @@ core.protocol_versions = {
["5.9.1"] = 45,
["5.10.0"] = 46,
["5.11.0"] = 47,
+ ["5.12.0"] = 48,
}
setmetatable(core.protocol_versions, {__newindex = function()
diff --git a/builtin/game/register.lua b/builtin/game/register.lua
index 3e4b9be96..d6ada6920 100644
--- a/builtin/game/register.lua
+++ b/builtin/game/register.lua
@@ -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")
+local function preprocess_node(nodedef)
+ -- Use the nodebox as selection box if it's not set manually
+ 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
- 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
- -- 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 = {
- 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
- 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))
+ 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 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)
diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua
index d9edd745b..240e0f083 100644
--- a/builtin/mainmenu/common.lua
+++ b/builtin/mainmenu/common.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Global menu data
menudata = {}
@@ -34,7 +21,6 @@ function check_cache_age(key, max_age)
end
function core.on_before_close()
- -- called before the menu is closed, either exit or to join a game
cache_settings:write()
end
diff --git a/builtin/mainmenu/content/contentdb.lua b/builtin/mainmenu/content/contentdb.lua
index 963400a12..fbd94376d 100644
--- a/builtin/mainmenu/content/contentdb.lua
+++ b/builtin/mainmenu/content/contentdb.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2018-24 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2018-24 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
if not core.get_http_api then
return
@@ -41,6 +28,7 @@ contentdb = {
REASON_DEPENDENCY = "dependency",
}
+-- API documentation: https://content.luanti.org/help/api/
local function get_download_url(package, reason)
local base_url = core.settings:get("contentdb_url")
@@ -182,14 +170,16 @@ function contentdb.get_package_by_id(id)
end
-function contentdb.calculate_package_id(type, author, name)
- local id = author:lower() .. "/"
+local function strip_game_suffix(type, name)
if (type == nil or type == "game") and #name > 5 and name:sub(#name - 4) == "_game" then
- id = id .. name:sub(1, #name - 5)
+ return name:sub(1, #name - 5)
else
- id = id .. name
+ return name
end
- return id
+end
+
+function contentdb.calculate_package_id(type, author, name)
+ return author:lower() .. "/" .. strip_game_suffix(type, name)
end
@@ -398,7 +388,6 @@ local function fetch_pkgs()
local url = base_url ..
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
-
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
item = item:trim()
if item ~= "" then
@@ -406,19 +395,11 @@ local function fetch_pkgs()
end
end
- local languages
- local current_language = core.get_language()
- if current_language ~= "" then
- languages = { current_language, "en;q=0.8" }
- else
- languages = { "en" }
- end
-
local http = core.get_http_api()
local response = http.fetch_sync({
url = url,
extra_headers = {
- "Accept-Language: " .. table.concat(languages, ", ")
+ core.get_http_accept_languages()
},
})
if not response.succeeded then
@@ -448,7 +429,7 @@ function contentdb.set_packages_from_api(packages)
-- We currently don't support name changing
local suffix = "/" .. package.name
if alias:sub(-#suffix) == suffix then
- contentdb.aliases[alias:lower()] = package.id
+ contentdb.aliases[strip_game_suffix(packages.type, alias:lower())] = package.id
end
end
end
@@ -596,57 +577,54 @@ function contentdb.filter_packages(query, by_type)
end
-function contentdb.get_full_package_info(package, callback)
- assert(package)
- if package.full_info then
- callback(package.full_info)
- return
- end
-
- local function fetch(params)
- local version = core.get_version()
- local base_url = core.settings:get("contentdb_url")
-
- local languages
- local current_language = core.get_language()
- if current_language ~= "" then
- languages = { current_language, "en;q=0.8" }
- else
- languages = { "en" }
+local function get_package_info(key, path)
+ return function(package, callback)
+ assert(package)
+ if package[key] then
+ callback(package[key])
+ return
end
- local url = base_url ..
- "/api/packages/" .. params.package.url_part .. "/for-client/?" ..
- "protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
- "&engine_version=" .. core.urlencode(version.string) ..
- "&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
- "&include_images=false"
- local http = core.get_http_api()
- local response = http.fetch_sync({
- url = url,
- extra_headers = {
- "Accept-Language: " .. table.concat(languages, ", ")
- },
- })
- if not response.succeeded then
- return nil
+ local function fetch(params)
+ local version = core.get_version()
+ local base_url = core.settings:get("contentdb_url")
+ local url = base_url ..
+ "/api/packages/" .. params.package.url_part .. params.path .. "?" ..
+ "protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
+ "&engine_version=" .. core.urlencode(version.string) ..
+ "&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
+ "&include_images=false"
+ local http = core.get_http_api()
+ local response = http.fetch_sync({
+ url = url,
+ extra_headers = {
+ core.get_http_accept_languages()
+ },
+ })
+ if not response.succeeded then
+ return nil
+ end
+
+ return core.parse_json(response.data)
end
- return core.parse_json(response.data)
- end
+ local function my_callback(value)
+ package[key] = value
+ callback(value)
+ end
- local function my_callback(value)
- package.full_info = value
- callback(value)
- end
-
- if not core.handle_async(fetch, { package = package }, my_callback) then
- core.log("error", "ERROR: async event failed")
- callback(nil)
+ if not core.handle_async(fetch, { package = package, path = path }, my_callback) then
+ core.log("error", "ERROR: async event failed")
+ callback(nil)
+ end
end
end
+contentdb.get_full_package_info = get_package_info("full_info", "/for-client/")
+contentdb.get_package_reviews = get_package_info("reviews", "/for-client/reviews/")
+
+
function contentdb.get_formspec_padding()
-- Padding is increased on Android to account for notches
-- TODO: use Android API to determine size of cut outs
diff --git a/builtin/mainmenu/content/dlg_contentdb.lua b/builtin/mainmenu/content/dlg_contentdb.lua
index 3c3f7987d..7f389135b 100644
--- a/builtin/mainmenu/content/dlg_contentdb.lua
+++ b/builtin/mainmenu/content/dlg_contentdb.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2018-20 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2018-20 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
if not core.get_http_api then
function create_contentdb_dlg()
@@ -323,9 +310,17 @@ local function get_formspec(dlgdata)
})
local img_w = cell_h * 3 / 2
+ -- Use as much of the available space as possible (so no padding on the
+ -- right/bottom), but don't quite allow the text to touch the border.
+ local text_w = cell_w - img_w - 0.25 - 0.025
+ local text_h = cell_h - 0.25 - 0.025
+
local start_idx = (cur_page - 1) * num_per_page + 1
for i=start_idx, math.min(#contentdb.packages, start_idx+num_per_page-1) do
local package = contentdb.packages[i]
+ local text = core.colorize(mt_color_green, package.title) ..
+ core.colorize("#BFBFBF", " by " .. package.author) .. "\n" ..
+ package.short_description
table.insert_all(formspec, {
"container[",
@@ -340,13 +335,14 @@ local function get_formspec(dlgdata)
"image[0,0;", img_w, ",", cell_h, ";",
core.formspec_escape(get_screenshot(package, package.thumbnail, 2)), "]",
- "label[", img_w + 0.25 + 0.05, ",0.5;",
- core.formspec_escape(
- core.colorize(mt_color_green, package.title) ..
- core.colorize("#BFBFBF", " by " .. package.author)), "]",
+ "label[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
+ core.formspec_escape(text), "]",
- "textarea[", img_w + 0.25, ",0.75;", cell_w - img_w - 0.25, ",", cell_h - 0.75, ";;;",
- core.formspec_escape(package.short_description), "]",
+ -- Add a tooltip in case the label overflows and the short description is cut off.
+ "tooltip[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
+ -- Text in tooltips doesn't wrap automatically, so we do it manually to
+ -- avoid everything being one long line.
+ core.formspec_escape(core.wrap_text(package.short_description, 80)), "]",
"style[view_", i, ";border=false]",
"style[view_", i, ":hovered;bgimg=", core.formspec_escape(defaulttexturedir .. "button_hover_semitrans.png"), "]",
@@ -362,7 +358,7 @@ local function get_formspec(dlgdata)
end
table.insert_all(formspec, {
- "container[", cell_w - 0.625,",", 0.25, "]",
+ "container[", cell_w - 0.625,",", 0.125, "]",
})
if package.downloading then
diff --git a/builtin/mainmenu/content/dlg_install.lua b/builtin/mainmenu/content/dlg_install.lua
index ef201f56a..3d9e45760 100644
--- a/builtin/mainmenu/content/dlg_install.lua
+++ b/builtin/mainmenu/content/dlg_install.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2018-24 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2018-24 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local function is_still_visible(dlg)
local this = ui.find_by_name("install_dialog")
diff --git a/builtin/mainmenu/content/dlg_overwrite.lua b/builtin/mainmenu/content/dlg_overwrite.lua
index 5baaa5cd2..08d49bce3 100644
--- a/builtin/mainmenu/content/dlg_overwrite.lua
+++ b/builtin/mainmenu/content/dlg_overwrite.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2018-24 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2018-24 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
function get_formspec(data)
local package = data.package
diff --git a/builtin/mainmenu/content/dlg_package.lua b/builtin/mainmenu/content/dlg_package.lua
index 7edbf678f..500fb3f6c 100644
--- a/builtin/mainmenu/content/dlg_package.lua
+++ b/builtin/mainmenu/content/dlg_package.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2018-24 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2018-24 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_info_formspec(size, padding, text)
@@ -32,6 +19,7 @@ end
local function get_formspec(data)
+ local package = data.package
local window_padding = contentdb.get_formspec_padding()
local size = contentdb.get_formspec_size()
size.x = math.min(size.x, 20)
@@ -42,7 +30,7 @@ local function get_formspec(data)
if not data.loading and not data.loading_error then
data.loading = true
- contentdb.get_full_package_info(data.package, function(info)
+ contentdb.get_full_package_info(package, function(info)
data.loading = false
if info == nil then
@@ -61,7 +49,7 @@ local function get_formspec(data)
-- check to see if that happened
if not data.info then
if data.loading_error then
- return get_info_formspec(size, window_padding, fgettext("No packages could be retrieved"))
+ return get_info_formspec(size, window_padding, fgettext("Error loading package information"))
end
return get_info_formspec(size, window_padding, fgettext("Loading..."))
end
@@ -103,15 +91,15 @@ local function get_formspec(data)
local left_button_rect = "0,0;2.875,1"
local right_button_rect = "3.125,0;2.875,1"
- if data.package.downloading then
+ if package.downloading then
formspec[#formspec + 1] = "animated_image[5,0;1,1;downloading;"
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
- elseif data.package.queued then
+ elseif package.queued then
formspec[#formspec + 1] = "style[queued;border=false]"
formspec[#formspec + 1] = "image_button[5,0;1,1;" .. core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
- elseif not data.package.path then
+ elseif not package.path then
formspec[#formspec + 1] = "style[install;bgcolor=green]"
formspec[#formspec + 1] = "button["
formspec[#formspec + 1] = right_button_rect
@@ -119,7 +107,7 @@ local function get_formspec(data)
formspec[#formspec + 1] = fgettext("Install [$1]", info.download_size)
formspec[#formspec + 1] = "]"
else
- if data.package.installed_release < data.package.release then
+ if package.installed_release < package.release then
-- The install_ action also handles updating
formspec[#formspec + 1] = "style[install;bgcolor=#28ccdf]"
formspec[#formspec + 1] = "button["
@@ -137,10 +125,12 @@ local function get_formspec(data)
formspec[#formspec + 1] = "]"
end
+ local review_count = info.reviews.positive + info.reviews.neutral + info.reviews.negative
local current_tab = data.current_tab or 1
local tab_titles = {
fgettext("Description"),
fgettext("Information"),
+ fgettext("Reviews") .. core.formspec_escape(" [" .. review_count .. "]"),
}
local tab_body_height = bottom_buttons_y - 2.8
@@ -162,8 +152,8 @@ local function get_formspec(data)
local winfo = core.get_window_info()
local fs_to_px = winfo.size.x / winfo.max_formspec_size.x
for i, ss in ipairs(info.screenshots) do
- local path = get_screenshot(data.package, ss.url, 2)
- hypertext = hypertext .. ""
if i ~= #info.screenshots then
@@ -194,22 +184,54 @@ local function get_formspec(data)
hypertext = hypertext .. "\n\n" .. info.long_description.body
+ -- Fix the path to blank.png. This is needed for bullet indentation.
hypertext = hypertext:gsub("",
+ "")
+ hypertext = hypertext:gsub("",
+ "")
+ hypertext = hypertext:gsub("",
+ "")
+ table.insert_all(formspec, {
+ "hypertext[0,0;", W, ",", tab_body_height - 0.375,
+ ";reviews;", core.formspec_escape(hypertext), "]",
+ })
+ elseif data.reviews_error then
+ table.insert_all(formspec, {"label[2,2;", fgettext("Error loading reviews"), "]"} )
+ else
+ table.insert_all(formspec, {"label[2,2;", fgettext("Loading..."), "]"} )
+ end
else
error("Unknown tab " .. current_tab)
end
@@ -269,9 +291,10 @@ local function handle_submit(this, fields)
end
if fields.open_contentdb then
- local url = ("%s/packages/%s/?protocol_version=%d"):format(
- core.settings:get("contentdb_url"), package.url_part,
- core.get_max_supp_proto())
+ local version = core.get_version()
+ local url = core.settings:get("contentdb_url") .. "/packages/" .. package.url_part ..
+ "/?protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
+ "&engine_version=" .. core.urlencode(version.string)
core.open_url(url)
return true
end
@@ -295,7 +318,8 @@ local function handle_submit(this, fields)
end
if handle_hypertext_event(this, fields.desc, info.long_description) or
- handle_hypertext_event(this, fields.info, info.info_hypertext) then
+ handle_hypertext_event(this, fields.info, info.info_hypertext) or
+ (package.reviews and handle_hypertext_event(this, fields.reviews, package.reviews)) then
return true
end
end
diff --git a/builtin/mainmenu/content/init.lua b/builtin/mainmenu/content/init.lua
index dbf4cc888..e35ca1734 100644
--- a/builtin/mainmenu/content/init.lua
+++ b/builtin/mainmenu/content/init.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2023 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2023 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"
diff --git a/builtin/mainmenu/content/pkgmgr.lua b/builtin/mainmenu/content/pkgmgr.lua
index 986d80398..2863e2a5f 100644
--- a/builtin/mainmenu/content/pkgmgr.lua
+++ b/builtin/mainmenu/content/pkgmgr.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2013 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2013 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
local function get_last_folder(text,count)
diff --git a/builtin/mainmenu/content/screenshots.lua b/builtin/mainmenu/content/screenshots.lua
index 718666085..e53f03ed0 100644
--- a/builtin/mainmenu/content/screenshots.lua
+++ b/builtin/mainmenu/content/screenshots.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2023-24 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2023-24 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Screenshot
diff --git a/builtin/mainmenu/content/tests/pkgmgr_spec.lua b/builtin/mainmenu/content/tests/pkgmgr_spec.lua
index 8870bb68f..5532764d9 100644
--- a/builtin/mainmenu/content/tests/pkgmgr_spec.lua
+++ b/builtin/mainmenu/content/tests/pkgmgr_spec.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2022 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2022 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local mods_dir = "/tmp/.minetest/mods"
local games_dir = "/tmp/.minetest/games"
diff --git a/builtin/mainmenu/content/update_detector.lua b/builtin/mainmenu/content/update_detector.lua
index 4d0c1196b..1479328f7 100644
--- a/builtin/mainmenu/content/update_detector.lua
+++ b/builtin/mainmenu/content/update_detector.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2023 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2023 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
update_detector = {}
diff --git a/builtin/mainmenu/credits.json b/builtin/mainmenu/credits.json
index cd2b15d78..61e53d3ba 100644
--- a/builtin/mainmenu/credits.json
+++ b/builtin/mainmenu/credits.json
@@ -47,22 +47,20 @@
],
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
"contributors": [
- "JosiahWI",
"Erich Schubert",
"wrrrzr",
- "1F616EMO",
- "red-001 ",
- "veprogames",
- "paradust7",
- "AFCMS",
"siliconsniffer",
- "Wuzzy",
- "Zemtzov7",
+ "JosiahWI",
+ "veprogames",
+ "Miguel P.L",
+ "AFCMS"
],
"previous_contributors": [
"Ælla Chiana Moskopp (erle) [Logo]",
"numzero",
+ "red-001 ",
"Giuseppe Bilotta",
+ "HybridDog",
"ClobberXD",
"Dániel Juhász (juhdanad) ",
"MirceaKitsune ",
@@ -75,6 +73,7 @@
"stujones11",
"Rogier ",
"Gregory Currie (gregorycu)",
+ "paradust7",
"JacobF",
"Jeija "
]
diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua
index 416a4a6f3..36cc5580c 100644
--- a/builtin/mainmenu/dlg_config_world.lua
+++ b/builtin/mainmenu/dlg_config_world.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2013 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2013 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua
index 4844c85fe..27fe68050 100644
--- a/builtin/mainmenu/dlg_create_world.lua
+++ b/builtin/mainmenu/dlg_create_world.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local function table_to_flags(ftable)
-- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"
diff --git a/builtin/mainmenu/dlg_delete_content.lua b/builtin/mainmenu/dlg_delete_content.lua
index a36bcb2d7..799050d0b 100644
--- a/builtin/mainmenu/dlg_delete_content.lua
+++ b/builtin/mainmenu/dlg_delete_content.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
diff --git a/builtin/mainmenu/dlg_delete_world.lua b/builtin/mainmenu/dlg_delete_world.lua
index 531b4927d..25e9dd0fc 100644
--- a/builtin/mainmenu/dlg_delete_world.lua
+++ b/builtin/mainmenu/dlg_delete_world.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local function delete_world_formspec(dialogdata)
diff --git a/builtin/mainmenu/dlg_rebind_keys.lua b/builtin/mainmenu/dlg_rebind_keys.lua
new file mode 100644
index 000000000..d1b442004
--- /dev/null
+++ b/builtin/mainmenu/dlg_rebind_keys.lua
@@ -0,0 +1,108 @@
+-- Luanti
+-- SPDX-License-Identifier: LGPL-2.1-or-later
+-- Modified based on dlg_reinstall_mtg.lua
+-- Note that this is only needed for migrating from <5.11 to 5.12.
+
+local doc_url = "https://docs.luanti.org/for-players/controls/"
+local SETTING_NAME = "no_keycode_migration_warning"
+
+local function get_formspec(dialogdata)
+ local markup = table.concat({
+ "" .. hgettext("Keybindings changed") .. "",
+ hgettext("The input handling system was reworked in Luanti 5.12.0."),
+ hgettext("As a result, your keybindings may have been changed."),
+ hgettext("Check out the key settings or refer to the documentation:"),
+ (""):format(doc_url),
+ }, "\n")
+
+ return table.concat({
+ "formspec_version[6]",
+ "size[12,7]",
+ "hypertext[0.5,0.5;11,4.7;text;", core.formspec_escape(markup), "]",
+ "container[0.5,5.7]",
+ "button[0,0;4,0.8;dismiss;", fgettext("Close"), "]",
+ "button[4.5,0;6.5,0.8;reconfigure;", fgettext("Open settings"), "]",
+ "container_end[]",
+ })
+end
+
+local function close_dialog(this)
+ cache_settings:set_bool(SETTING_NAME, true)
+ this:delete()
+end
+
+local function buttonhandler(this, fields)
+ if fields.reconfigure then
+ close_dialog(this)
+
+ local maintab = ui.find_by_name("maintab")
+
+ local dlg = create_settings_dlg("controls_keyboard_and_mouse")
+ dlg:set_parent(maintab)
+ maintab:hide()
+ dlg:show()
+
+ return true
+ end
+
+ if fields.dismiss then
+ close_dialog(this)
+ return true
+ end
+
+ if fields.text == "action:doc_url" then
+ core.open_url(doc_url)
+ end
+end
+
+local function eventhandler(event)
+ if event == "DialogShow" then
+ mm_game_theme.set_engine()
+ return true
+ elseif event == "MenuQuit" then
+ -- Don't allow closing the dialog with ESC, but still allow exiting
+ -- Luanti
+ core.close()
+ return true
+ end
+ return false
+end
+
+local function create_rebind_keys_dlg()
+ local dlg = dialog_create("dlg_rebind_keys", get_formspec,
+ buttonhandler, eventhandler)
+ return dlg
+end
+
+function migrate_keybindings()
+ -- Show migration dialog if the user upgraded from an earlier version
+ -- and this has not yet been shown before, *or* if keys settings had to be changed
+ if core.is_first_run then
+ cache_settings:set_bool(SETTING_NAME, true)
+ end
+ local has_migration = not cache_settings:get_bool(SETTING_NAME)
+
+ -- normalize all existing key settings, this converts them from KEY_KEY_C to SYSTEM_SCANCODE_6
+ local settings = core.settings:to_table()
+ for name, value in pairs(settings) do
+ if name:match("^keymap_") then
+ local normalized = core.normalize_keycode(value)
+ if value ~= normalized then
+ has_migration = true
+ core.settings:set(name, normalized)
+ end
+ end
+ end
+
+ if not has_migration then
+ return
+ end
+
+ local maintab = ui.find_by_name("maintab")
+
+ local dlg = create_rebind_keys_dlg()
+ dlg:set_parent(maintab)
+ maintab:hide()
+ dlg:show()
+ ui.update()
+end
diff --git a/builtin/mainmenu/dlg_register.lua b/builtin/mainmenu/dlg_register.lua
index 88a449ae3..5047eda3c 100644
--- a/builtin/mainmenu/dlg_register.lua
+++ b/builtin/mainmenu/dlg_register.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2022 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2022 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
diff --git a/builtin/mainmenu/dlg_reinstall_mtg.lua b/builtin/mainmenu/dlg_reinstall_mtg.lua
index 11b20f637..c167b2656 100644
--- a/builtin/mainmenu/dlg_reinstall_mtg.lua
+++ b/builtin/mainmenu/dlg_reinstall_mtg.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2023 Gregor Parzefall
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2023 Gregor Parzefall
+-- SPDX-License-Identifier: LGPL-2.1-or-later
---- IMPORTANT ----
-- This whole file can be removed after a while.
@@ -67,10 +54,10 @@ end
local function get_formspec(dialogdata)
local markup = table.concat({
- "", fgettext("Minetest Game is no longer installed by default"), "\n",
- fgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
+ "", hgettext("Minetest Game is no longer installed by default"), "\n",
+ hgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
"Since version 5.8.0, Luanti ships without a default game."), "\n",
- fgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
+ hgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
})
return table.concat({
diff --git a/builtin/mainmenu/dlg_rename_modpack.lua b/builtin/mainmenu/dlg_rename_modpack.lua
index 09df92d25..830c3cef3 100644
--- a/builtin/mainmenu/dlg_rename_modpack.lua
+++ b/builtin/mainmenu/dlg_rename_modpack.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
diff --git a/builtin/mainmenu/game_theme.lua b/builtin/mainmenu/game_theme.lua
index 7c6408157..729110025 100644
--- a/builtin/mainmenu/game_theme.lua
+++ b/builtin/mainmenu/game_theme.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2013 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2013 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
mm_game_theme = {}
diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua
index ec33f33b3..14185a484 100644
--- a/builtin/mainmenu/init.lua
+++ b/builtin/mainmenu/init.lua
@@ -1,27 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-mt_color_grey = "#AAAAAA"
-mt_color_blue = "#6389FF"
-mt_color_lightblue = "#99CCFF"
-mt_color_green = "#72FF63"
-mt_color_dark_green = "#25C191"
-mt_color_orange = "#FF8800"
-mt_color_red = "#FF3300"
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
MAIN_TAB_W = 15.5
MAIN_TAB_H = 7.1
@@ -35,6 +14,7 @@ local basepath = core.get_builtin_path()
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
DIR_DELIM .. "pack" .. DIR_DELIM
+dofile(basepath .. "common" .. DIR_DELIM .. "menu.lua")
dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua")
@@ -55,6 +35,7 @@ dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
+dofile(menupath .. DIR_DELIM .. "dlg_rebind_keys.lua")
dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua")
dofile(menupath .. DIR_DELIM .. "dlg_server_list_mods.lua")
@@ -132,6 +113,7 @@ local function init_globals()
ui.update()
check_reinstall_mtg()
+ migrate_keybindings()
check_new_version()
end
diff --git a/builtin/mainmenu/serverlistmgr.lua b/builtin/mainmenu/serverlistmgr.lua
index 1b69d7a55..34eca287a 100644
--- a/builtin/mainmenu/serverlistmgr.lua
+++ b/builtin/mainmenu/serverlistmgr.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2020 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2020 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
serverlistmgr = {
-- continent code we detected for ourselves
diff --git a/builtin/mainmenu/tab_about.lua b/builtin/mainmenu/tab_about.lua
index 86c811457..5d2e606df 100644
--- a/builtin/mainmenu/tab_about.lua
+++ b/builtin/mainmenu/tab_about.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2013 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2013 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local function prepare_credits(dest, source)
diff --git a/builtin/mainmenu/tab_content.lua b/builtin/mainmenu/tab_content.lua
index cd384c905..8a6fc5b01 100644
--- a/builtin/mainmenu/tab_content.lua
+++ b/builtin/mainmenu/tab_content.lua
@@ -1,20 +1,7 @@
---Luanti
---Copyright (C) 2014 sapier
---Copyright (C) 2018 rubenwardy
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- Copyright (C) 2018 rubenwardy
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_content_icons(packages_with_updates)
diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua
index 083f9a50a..0d0b20ff1 100644
--- a/builtin/mainmenu/tab_local.lua
+++ b/builtin/mainmenu/tab_local.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local current_game, singleplayer_refresh_gamebar
diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua
index f3f19c50f..ab5ddfe11 100644
--- a/builtin/mainmenu/tab_online.lua
+++ b/builtin/mainmenu/tab_online.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2014 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2014 sapier
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_sorted_servers()
local servers = {
diff --git a/builtin/pause_menu/init.lua b/builtin/pause_menu/init.lua
index 035d2ba99..01c5dc856 100644
--- a/builtin/pause_menu/init.lua
+++ b/builtin/pause_menu/init.lua
@@ -8,5 +8,6 @@ defaulttexturedir = ""
local builtin_shared = {}
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
+assert(loadfile(commonpath .. "menu.lua"))(builtin_shared)
assert(loadfile(pausepath .. "register.lua"))(builtin_shared)
dofile(commonpath .. "settings" .. DIR_DELIM .. "init.lua")
diff --git a/builtin/profiler/init.lua b/builtin/profiler/init.lua
index f5b4b7c7e..ede9e0e7e 100644
--- a/builtin/profiler/init.lua
+++ b/builtin/profiler/init.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2016 T4im
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2016 T4im
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local S = core.get_translator("__builtin")
diff --git a/builtin/profiler/instrumentation.lua b/builtin/profiler/instrumentation.lua
index e012f07a0..613d3d692 100644
--- a/builtin/profiler/instrumentation.lua
+++ b/builtin/profiler/instrumentation.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2016 T4im
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2016 T4im
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local format, pairs, type = string.format, pairs, type
local core, get_current_modname = core, core.get_current_modname
diff --git a/builtin/profiler/reporter.lua b/builtin/profiler/reporter.lua
index 7bc1b235d..b2bad7560 100644
--- a/builtin/profiler/reporter.lua
+++ b/builtin/profiler/reporter.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2016 T4im
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2016 T4im
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local S = core.get_translator("__builtin")
-- Note: In this file, only messages are translated
diff --git a/builtin/profiler/sampling.lua b/builtin/profiler/sampling.lua
index 16d6c2012..5f1f5414c 100644
--- a/builtin/profiler/sampling.lua
+++ b/builtin/profiler/sampling.lua
@@ -1,19 +1,6 @@
---Luanti
---Copyright (C) 2016 T4im
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-- Luanti
+-- Copyright (C) 2016 T4im
+-- SPDX-License-Identifier: LGPL-2.1-or-later
local setmetatable = setmetatable
local pairs, format = pairs, string.format
local min, max, huge = math.min, math.max, math.huge
diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt
index 11aac1d66..17ecf5b34 100644
--- a/builtin/settingtypes.txt
+++ b/builtin/settingtypes.txt
@@ -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
@@ -13,7 +31,7 @@
# - enum
# - path
# - filepath
-# - key (will be ignored in GUI, since a special key change dialog exists)
+# - key
# - flags
# - noise_params_2d
# - noise_params_3d
@@ -73,10 +91,14 @@
# * touchscreen / keyboard_mouse
# * opengl / gles
# * You can negate any requirement by prepending with !
+# * The "keyboard_mouse" requirement is automatically added to settings with the
+# "key" type.
#
# 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 +106,7 @@
# They contain settings not intended for the "average user".
-[Controls]
+[Controls] [client]
[*General]
@@ -111,6 +133,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.
#
@@ -151,6 +180,228 @@ enable_hotbar_mouse_wheel (Hotbar: Enable mouse wheel for selection) bool true
# Requires: keyboard_mouse
invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
+[**Keybindings]
+
+# Key for moving the player forward.
+keymap_forward (Move forward) key SYSTEM_SCANCODE_26
+
+# Key for moving the player backward.
+# Will also disable autoforward, when active.
+keymap_backward (Move backward) key SYSTEM_SCANCODE_22
+
+# Key for moving the player left.
+keymap_left (Move left) key SYSTEM_SCANCODE_4
+
+# Key for moving the player right.
+keymap_right (Move right) key SYSTEM_SCANCODE_7
+
+# Key for jumping.
+keymap_jump (Jump) key SYSTEM_SCANCODE_44
+
+# Key for sneaking.
+# Also used for climbing down and descending in water if aux1_descends is disabled.
+keymap_sneak (Sneak) key SYSTEM_SCANCODE_225
+
+# Key for digging, punching or using something.
+# (Note: The actual meaning might vary on a per-game basis.)
+keymap_dig (Dig/punch/use) key KEY_LBUTTON
+
+# Key for placing an item/block or for using something.
+# (Note: The actual meaning might vary on a per-game basis.)
+keymap_place (Place/use) key KEY_RBUTTON
+
+# Key for opening the inventory.
+keymap_inventory (Open inventory) key SYSTEM_SCANCODE_12
+
+# Key for moving fast in fast mode.
+keymap_aux1 (Aux1) key SYSTEM_SCANCODE_8
+
+# Key for opening the chat window.
+keymap_chat (Open chat) key SYSTEM_SCANCODE_23
+
+# Key for opening the chat window to type commands.
+keymap_cmd (Command) key SYSTEM_SCANCODE_56
+
+# Key for opening the chat window to type local commands.
+keymap_cmd_local (Local command) key SYSTEM_SCANCODE_55
+
+# Key for toggling unlimited view range.
+keymap_rangeselect (Range select) key
+
+# Key for toggling flying.
+keymap_freemove (Toggle fly) key SYSTEM_SCANCODE_14
+
+# Key for toggling pitch move mode.
+keymap_pitchmove (Toggle pitchmove) key
+
+# Key for toggling fast mode.
+keymap_fastmove (Toggle fast) key SYSTEM_SCANCODE_13
+
+# Key for toggling noclip mode.
+keymap_noclip (Toggle noclip) key SYSTEM_SCANCODE_11
+
+# Key for selecting the next item in the hotbar.
+keymap_hotbar_next (Hotbar: select next item) key SYSTEM_SCANCODE_17
+
+# Key for selecting the previous item in the hotbar.
+keymap_hotbar_previous (Hotbar: select previous item) key SYSTEM_SCANCODE_5
+
+# Key for muting the game.
+keymap_mute (Mute) key SYSTEM_SCANCODE_16
+
+# Key for increasing the volume.
+keymap_increase_volume (Increase volume) key
+
+# Key for decreasing the volume.
+keymap_decrease_volume (Decrease volume) key
+
+# Key for toggling autoforward.
+keymap_autoforward (Toggle automatic forward) key
+
+# Key for toggling cinematic mode.
+keymap_cinematic (Toggle cinematic mode) key
+
+# Key for toggling display of minimap.
+keymap_minimap (Toggle minimap) key SYSTEM_SCANCODE_25
+
+# Key for taking screenshots.
+keymap_screenshot (Screenshot) key SYSTEM_SCANCODE_69
+
+# Key for toggling fullscreen mode.
+keymap_fullscreen (Toggle fullscreen) key SYSTEM_SCANCODE_68
+
+# Key for dropping the currently selected item.
+keymap_drop (Drop item) key SYSTEM_SCANCODE_20
+
+# Key to use view zoom when possible.
+keymap_zoom (Zoom) key SYSTEM_SCANCODE_29
+
+# Key for toggling the display of the HUD.
+keymap_toggle_hud (Toggle HUD) key SYSTEM_SCANCODE_58
+
+# Key for toggling the display of chat.
+keymap_toggle_chat (Toggle chat log) key SYSTEM_SCANCODE_59
+
+# Key for toggling the display of the large chat console.
+keymap_console (Large chat console) key SYSTEM_SCANCODE_67
+
+# Key for toggling the display of fog.
+keymap_toggle_fog (Toggle fog) key SYSTEM_SCANCODE_60
+
+# Key for toggling the display of debug info.
+keymap_toggle_debug (Toggle debug info) key SYSTEM_SCANCODE_62
+
+# Key for toggling the display of the profiler. Used for development.
+keymap_toggle_profiler (Toggle profiler) key SYSTEM_SCANCODE_63
+
+# Key for toggling the display of mapblock boundaries.
+keymap_toggle_block_bounds (Toggle block bounds) key
+
+# Key for switching between first- and third-person camera.
+keymap_camera_mode (Toggle camera mode) key SYSTEM_SCANCODE_6
+
+# Key for increasing the viewing range.
+keymap_increase_viewing_range_min (Increase view range) key SYSTEM_SCANCODE_46
+
+# Key for decreasing the viewing range.
+keymap_decrease_viewing_range_min (Decrease view range) key SYSTEM_SCANCODE_45
+
+# Key for selecting the first hotbar slot.
+keymap_slot1 (Hotbar slot 1) key SYSTEM_SCANCODE_30
+
+# Key for selecting the second hotbar slot.
+keymap_slot2 (Hotbar slot 2) key SYSTEM_SCANCODE_31
+
+# Key for selecting the third hotbar slot.
+keymap_slot3 (Hotbar slot 3) key SYSTEM_SCANCODE_32
+
+# Key for selecting the fourth hotbar slot.
+keymap_slot4 (Hotbar slot 4) key SYSTEM_SCANCODE_33
+
+# Key for selecting the fifth hotbar slot.
+keymap_slot5 (Hotbar slot 5) key SYSTEM_SCANCODE_34
+
+# Key for selecting the sixth hotbar slot.
+keymap_slot6 (Hotbar slot 6) key SYSTEM_SCANCODE_35
+
+# Key for selecting the seventh hotbar slot.
+keymap_slot7 (Hotbar slot 7) key SYSTEM_SCANCODE_36
+
+# Key for selecting the eighth hotbar slot.
+keymap_slot8 (Hotbar slot 8) key SYSTEM_SCANCODE_37
+
+# Key for selecting the ninth hotbar slot.
+keymap_slot9 (Hotbar slot 9) key SYSTEM_SCANCODE_38
+
+# Key for selecting the tenth hotbar slot.
+keymap_slot10 (Hotbar slot 10) key SYSTEM_SCANCODE_39
+
+# Key for selecting the 11th hotbar slot.
+keymap_slot11 (Hotbar slot 11) key
+
+# Key for selecting the 12th hotbar slot.
+keymap_slot12 (Hotbar slot 12) key
+
+# Key for selecting the 13th hotbar slot.
+keymap_slot13 (Hotbar slot 13) key
+
+# Key for selecting the 14th hotbar slot.
+keymap_slot14 (Hotbar slot 14) key
+
+# Key for selecting the 15th hotbar slot.
+keymap_slot15 (Hotbar slot 15) key
+
+# Key for selecting the 16th hotbar slot.
+keymap_slot16 (Hotbar slot 16) key
+
+# Key for selecting the 17th hotbar slot.
+keymap_slot17 (Hotbar slot 17) key
+
+# Key for selecting the 18th hotbar slot.
+keymap_slot18 (Hotbar slot 18) key
+
+# Key for selecting the 19th hotbar slot.
+keymap_slot19 (Hotbar slot 19) key
+
+# Key for selecting the 20th hotbar slot.
+keymap_slot20 (Hotbar slot 20) key
+
+# Key for selecting the 21st hotbar slot.
+keymap_slot21 (Hotbar slot 21) key
+
+# Key for selecting the 22nd hotbar slot.
+keymap_slot22 (Hotbar slot 22) key
+
+# Key for selecting the 23rd hotbar slot.
+keymap_slot23 (Hotbar slot 23) key
+
+# Key for selecting the 24th hotbar slot.
+keymap_slot24 (Hotbar slot 24) key
+
+# Key for selecting the 25th hotbar slot.
+keymap_slot25 (Hotbar slot 25) key
+
+# Key for selecting the 26th hotbar slot.
+keymap_slot26 (Hotbar slot 26) key
+
+# Key for selecting the 27th hotbar slot.
+keymap_slot27 (Hotbar slot 27) key
+
+# Key for selecting the 28th hotbar slot.
+keymap_slot28 (Hotbar slot 28) key
+
+# Key for selecting the 29th hotbar slot.
+keymap_slot29 (Hotbar slot 29) key
+
+# Key for selecting the 30th hotbar slot.
+keymap_slot30 (Hotbar slot 30) key
+
+# Key for selecting the 31st hotbar slot.
+keymap_slot31 (Hotbar slot 31) key
+
+# Key for selecting the 32nd hotbar slot.
+keymap_slot32 (Hotbar slot 32) key
+
[*Touchscreen]
# Enables the touchscreen controls, allowing you to play the game with a touchscreen.
@@ -160,6 +411,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 +456,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 +468,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]
@@ -295,10 +556,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.
@@ -615,11 +872,6 @@ enable_volumetric_lighting (Volumetric lighting) bool false
# Requires: enable_dynamic_shadows
enable_translucent_foliage (Translucent foliage) bool false
-# Apply specular shading to nodes.
-#
-# Requires: enable_dynamic_shadows
-enable_node_specular (Node specular) bool false
-
# When enabled, liquid reflections are simulated.
#
# Requires: enable_waving_water, enable_dynamic_shadows
@@ -749,13 +1001,13 @@ 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, server account registration is separate from login in the UI.
# If disabled, connecting to a server will automatically register a new account.
@@ -765,7 +1017,7 @@ enable_split_login_register (Enable split login/register) bool true
# If this is empty the engine will never check for updates.
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, a client connecting with this name is admin.
@@ -793,7 +1045,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
@@ -839,7 +1091,7 @@ remote_media (Remote media) string
# 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
@@ -899,7 +1151,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:
@@ -907,7 +1159,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.
@@ -962,7 +1214,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.
@@ -978,7 +1230,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.
@@ -1691,12 +1943,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
@@ -1720,33 +1972,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.
@@ -1786,16 +2038,15 @@ 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.
profiler_print_interval (Engine profiling data print interval) int 0 0
-
[*Advanced]
-[**Graphics]
+[**Graphics] [client]
# Enables debug and error-checking in the OpenGL driver.
opengl_debug (OpenGL debug) bool false
@@ -1850,7 +2101,7 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc
# World-aligned textures may be scaled to span several nodes. However,
# the server may not send the scale you want, especially if you use
# a specially-designed texture pack; with this option, the client tries
-# to determine the scale automatically basing on the texture size.
+# to determine the scale automatically based on the texture size.
# See also texture_min_size.
# Warning: This option is EXPERIMENTAL!
autoscale_mode (Autoscaling mode) enum disable disable,enable,force
@@ -1862,7 +2113,7 @@ autoscale_mode (Autoscaling mode) enum disable disable,enable,force
# This setting is ONLY applied if any of the mentioned filters are enabled.
# This is also used as the base node texture size for world-aligned
# texture autoscaling.
-texture_min_size (Base texture size) int 64 1 32768
+texture_min_size (Base texture size) int 192 192 16384
# Side length of a cube of map blocks that the client will consider together
# when generating meshes.
@@ -1899,12 +2150,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
@@ -1954,7 +2205,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.
@@ -1982,45 +2233,48 @@ lighting_boost_spread (Light curve boost spread) float 0.2 0.0 0.4
# Enable IPv6 support (for both client and server).
# Required for IPv6 connections to work at all.
-enable_ipv6 (IPv6) bool true
+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
+# If Luanti is compiled with Prometheus support, this setting
+# enables the metrics listener for Prometheus on that address.
+# By default you can fetch metrics from http://127.0.0.1:30000/metrics.
+# An empty value disables the metrics listener.
+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)
@@ -2040,7 +2294,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.
@@ -2091,14 +2345,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
@@ -2133,7 +2387,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
@@ -2141,7 +2395,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
@@ -2169,7 +2423,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
@@ -2184,51 +2438,68 @@ curl_parallel_limit (cURL parallel limit) int 8 1 2147483647
# Maximum time a file download (e.g. a mod download) may take, stated in milliseconds.
curl_file_download_timeout (cURL file download timeout) int 300000 5000 2147483647
+[**Client Debugging] [client]
+
+# Key for toggling the camera update. Only usable with 'debug' privilege.
+keymap_toggle_update_camera (Toggle camera update) key
+
+# Key for switching to the previous entry in Quicktune.
+keymap_quicktune_prev (Quicktune: select previous entry) key
+
+# Key for switching to the next entry in Quicktune.
+keymap_quicktune_next (Quicktune: select next entry) key
+
+# Key for decrementing the selected value in Quicktune.
+keymap_quicktune_dec (Quicktune: decrement value) key
+
+# Key for incrementing the selected value in Quicktune.
+keymap_quicktune_inc (Quicktune: increment value) key
+
[**Miscellaneous]
# Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output.
-clickable_chat_weblinks (Chat weblinks) bool true
+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) bool false
+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
@@ -2251,7 +2522,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
@@ -2310,226 +2581,3 @@ show_technical_names (Show technical names) bool false
# Controlled by a checkbox in the settings menu.
show_advanced (Show advanced settings) bool false
-
-# Key for moving the player forward.
-keymap_forward (Forward key) key KEY_KEY_W
-
-# Key for moving the player backward.
-# Will also disable autoforward, when active.
-keymap_backward (Backward key) key KEY_KEY_S
-
-# Key for moving the player left.
-keymap_left (Left key) key KEY_KEY_A
-
-# Key for moving the player right.
-keymap_right (Right key) key KEY_KEY_D
-
-# Key for jumping.
-keymap_jump (Jump key) key KEY_SPACE
-
-# Key for sneaking.
-# Also used for climbing down and descending in water if aux1_descends is disabled.
-keymap_sneak (Sneak key) key KEY_LSHIFT
-
-# Key for digging, punching or using something.
-# (Note: The actual meaning might vary on a per-game basis.)
-keymap_dig (Dig/punch/use key) key KEY_LBUTTON
-
-# Key for placing an item/block or for using something.
-# (Note: The actual meaning might vary on a per-game basis.)
-keymap_place (Place/use key) key KEY_RBUTTON
-
-# Key for opening the inventory.
-keymap_inventory (Inventory key) key KEY_KEY_I
-
-# Key for moving fast in fast mode.
-keymap_aux1 (Aux1 key) key KEY_KEY_E
-
-# Key for opening the chat window.
-keymap_chat (Chat key) key KEY_KEY_T
-
-# Key for opening the chat window to type commands.
-keymap_cmd (Command key) key /
-
-# Key for opening the chat window to type local commands.
-keymap_cmd_local (Command key) key .
-
-# Key for toggling unlimited view range.
-keymap_rangeselect (Range select key) key
-
-# Key for toggling flying.
-keymap_freemove (Fly key) key KEY_KEY_K
-
-# Key for toggling pitch move mode.
-keymap_pitchmove (Pitch move key) key
-
-# Key for toggling fast mode.
-keymap_fastmove (Fast key) key KEY_KEY_J
-
-# Key for toggling noclip mode.
-keymap_noclip (Noclip key) key KEY_KEY_H
-
-# Key for selecting the next item in the hotbar.
-keymap_hotbar_next (Hotbar next key) key KEY_KEY_N
-
-# Key for selecting the previous item in the hotbar.
-keymap_hotbar_previous (Hotbar previous key) key KEY_KEY_B
-
-# Key for muting the game.
-keymap_mute (Mute key) key KEY_KEY_M
-
-# Key for increasing the volume.
-keymap_increase_volume (Inc. volume key) key
-
-# Key for decreasing the volume.
-keymap_decrease_volume (Dec. volume key) key
-
-# Key for toggling autoforward.
-keymap_autoforward (Automatic forward key) key
-
-# Key for toggling cinematic mode.
-keymap_cinematic (Cinematic mode key) key
-
-# Key for toggling display of minimap.
-keymap_minimap (Minimap key) key KEY_KEY_V
-
-# Key for taking screenshots.
-keymap_screenshot (Screenshot) key KEY_F12
-
-# Key for toggling fullscreen mode.
-keymap_fullscreen (Fullscreen key) key KEY_F11
-
-# Key for dropping the currently selected item.
-keymap_drop (Drop item key) key KEY_KEY_Q
-
-# Key to use view zoom when possible.
-keymap_zoom (View zoom key) key KEY_KEY_Z
-
-# Key for selecting the first hotbar slot.
-keymap_slot1 (Hotbar slot 1 key) key KEY_KEY_1
-
-# Key for selecting the second hotbar slot.
-keymap_slot2 (Hotbar slot 2 key) key KEY_KEY_2
-
-# Key for selecting the third hotbar slot.
-keymap_slot3 (Hotbar slot 3 key) key KEY_KEY_3
-
-# Key for selecting the fourth hotbar slot.
-keymap_slot4 (Hotbar slot 4 key) key KEY_KEY_4
-
-# Key for selecting the fifth hotbar slot.
-keymap_slot5 (Hotbar slot 5 key) key KEY_KEY_5
-
-# Key for selecting the sixth hotbar slot.
-keymap_slot6 (Hotbar slot 6 key) key KEY_KEY_6
-
-# Key for selecting the seventh hotbar slot.
-keymap_slot7 (Hotbar slot 7 key) key KEY_KEY_7
-
-# Key for selecting the eighth hotbar slot.
-keymap_slot8 (Hotbar slot 8 key) key KEY_KEY_8
-
-# Key for selecting the ninth hotbar slot.
-keymap_slot9 (Hotbar slot 9 key) key KEY_KEY_9
-
-# Key for selecting the tenth hotbar slot.
-keymap_slot10 (Hotbar slot 10 key) key KEY_KEY_0
-
-# Key for selecting the 11th hotbar slot.
-keymap_slot11 (Hotbar slot 11 key) key
-
-# Key for selecting the 12th hotbar slot.
-keymap_slot12 (Hotbar slot 12 key) key
-
-# Key for selecting the 13th hotbar slot.
-keymap_slot13 (Hotbar slot 13 key) key
-
-# Key for selecting the 14th hotbar slot.
-keymap_slot14 (Hotbar slot 14 key) key
-
-# Key for selecting the 15th hotbar slot.
-keymap_slot15 (Hotbar slot 15 key) key
-
-# Key for selecting the 16th hotbar slot.
-keymap_slot16 (Hotbar slot 16 key) key
-
-# Key for selecting the 17th hotbar slot.
-keymap_slot17 (Hotbar slot 17 key) key
-
-# Key for selecting the 18th hotbar slot.
-keymap_slot18 (Hotbar slot 18 key) key
-
-# Key for selecting the 19th hotbar slot.
-keymap_slot19 (Hotbar slot 19 key) key
-
-# Key for selecting the 20th hotbar slot.
-keymap_slot20 (Hotbar slot 20 key) key
-
-# Key for selecting the 21st hotbar slot.
-keymap_slot21 (Hotbar slot 21 key) key
-
-# Key for selecting the 22nd hotbar slot.
-keymap_slot22 (Hotbar slot 22 key) key
-
-# Key for selecting the 23rd hotbar slot.
-keymap_slot23 (Hotbar slot 23 key) key
-
-# Key for selecting the 24th hotbar slot.
-keymap_slot24 (Hotbar slot 24 key) key
-
-# Key for selecting the 25th hotbar slot.
-keymap_slot25 (Hotbar slot 25 key) key
-
-# Key for selecting the 26th hotbar slot.
-keymap_slot26 (Hotbar slot 26 key) key
-
-# Key for selecting the 27th hotbar slot.
-keymap_slot27 (Hotbar slot 27 key) key
-
-# Key for selecting the 28th hotbar slot.
-keymap_slot28 (Hotbar slot 28 key) key
-
-# Key for selecting the 29th hotbar slot.
-keymap_slot29 (Hotbar slot 29 key) key
-
-# Key for selecting the 30th hotbar slot.
-keymap_slot30 (Hotbar slot 30 key) key
-
-# Key for selecting the 31st hotbar slot.
-keymap_slot31 (Hotbar slot 31 key) key
-
-# Key for selecting the 32nd hotbar slot.
-keymap_slot32 (Hotbar slot 32 key) key
-
-# Key for toggling the display of the HUD.
-keymap_toggle_hud (HUD toggle key) key KEY_F1
-
-# Key for toggling the display of chat.
-keymap_toggle_chat (Chat toggle key) key KEY_F2
-
-# Key for toggling the display of the large chat console.
-keymap_console (Large chat console key) key KEY_F10
-
-# Key for toggling the display of fog.
-keymap_toggle_fog (Fog toggle key) key KEY_F3
-
-# Key for toggling the camera update. Only usable with 'debug' privilege.
-keymap_toggle_update_camera (Camera update toggle key) key
-
-# Key for toggling the display of debug info.
-keymap_toggle_debug (Debug info toggle key) key KEY_F5
-
-# Key for toggling the display of the profiler. Used for development.
-keymap_toggle_profiler (Profiler toggle key) key KEY_F6
-
-# Key for toggling the display of mapblock boundaries.
-keymap_toggle_block_bounds (Block bounds toggle key) key
-
-# Key for switching between first- and third-person camera.
-keymap_camera_mode (Toggle camera mode key) key KEY_KEY_C
-
-# Key for increasing the viewing range.
-keymap_increase_viewing_range_min (View range increase key) key +
-
-# Key for decreasing the viewing range.
-keymap_decrease_viewing_range_min (View range decrease key) key -
diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl
index 0f508dc6a..6fe7acd85 100644
--- a/client/shaders/nodes_shader/opengl_vertex.glsl
+++ b/client/shaders/nodes_shader/opengl_vertex.glsl
@@ -44,8 +44,6 @@ centroid varying float nightRatio;
varying float perspective_factor;
#endif
-varying float area_enable_parallax;
-
varying highp vec3 eyeVec;
// Color of the light emitted by the light sources.
const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
diff --git a/doc/breakages.md b/doc/breakages.md
index 51387e279..6c9acbd95 100644
--- a/doc/breakages.md
+++ b/doc/breakages.md
@@ -23,3 +23,6 @@ This list is largely advisory and items may be reevaluated once the time comes.
* stop reading initial properties from bare entity def
* change particle default blend mode to `clip`
* remove built-in knockback and related functions entirely
+* remove `safe` parameter from `core.serialize`, always enforce `safe = true`.
+ possibly error when `loadstring` calls are encountered in `core.deserialize`.
+* introduce strict type checking for all instances of `v3s16` / `v3f` read from Lua
diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md
index aedf0a4ff..ff4af5ccc 100644
--- a/doc/client_lua_api.md
+++ b/doc/client_lua_api.md
@@ -1,12 +1,15 @@
-Luanti Lua Client Modding API Reference 5.12.0
+Luanti Lua Client Modding API Reference 5.13.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.
+Note that `core` has already existed since version 0.4.10, so you can use it
+safely without breaking backwards compatibility.
+
* More information at
-* Developer Wiki:
+* Additional documentation:
Introduction
------------
diff --git a/doc/compiling/README.md b/doc/compiling/README.md
index 16167977b..9ce8a800e 100644
--- a/doc/compiling/README.md
+++ b/doc/compiling/README.md
@@ -23,6 +23,7 @@ General options and their default values:
PRECOMPILE_HEADERS=FALSE - Precompile some headers (experimental; requires CMake 3.16 or later)
PRECOMPILED_HEADERS_PATH= - Path to a file listing all headers to precompile (default points to src/precompiled_headers.txt)
USE_SDL2=TRUE - Build with SDL2; Enables IrrlichtMt device SDL2
+ USE_SDL2_STATIC=TRUE - Links with SDL2::SDL2-static instead of SDL2::SDL2
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
diff --git a/doc/compiling/linux.md b/doc/compiling/linux.md
index 946d88dac..7984d34d7 100644
--- a/doc/compiling/linux.md
+++ b/doc/compiling/linux.md
@@ -90,6 +90,10 @@ Run it:
./bin/luanti
+Run unit tests:
+
+ ./bin/luanti --run-unittests
+
- 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),
you will want to use `-DRUN_IN_PLACE=FALSE`.
diff --git a/doc/direction.md b/doc/direction.md
index 574572516..bd70003d1 100644
--- a/doc/direction.md
+++ b/doc/direction.md
@@ -5,7 +5,7 @@
The long-term roadmaps, aims, and guiding philosophies are set out using the
following documents:
-* [What is Minetest?](http://c55.me/blog/?p=1491)
+* [What is Minetest? (archived)](https://web.archive.org/web/20160328054721/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/luanti-org/luanti/issues/3476#issuecomment-167399287)
* [Core developer to-do/wish lists](https://forum.luanti.org/viewforum.php?f=7)
diff --git a/doc/lua_api.md b/doc/lua_api.md
index 7b2a8945e..747c523a4 100644
--- a/doc/lua_api.md
+++ b/doc/lua_api.md
@@ -5,8 +5,11 @@ 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.
+Note that `core` has already existed since version 0.4.10, so you can use it
+safely without breaking backwards compatibility.
+
* More information at
-* Developer Wiki:
+* Additional documentation:
* (Unofficial) Minetest Modding Book by rubenwardy:
* Modding tools:
@@ -518,6 +521,15 @@ stripping out the file extension:
Supported texture formats are PNG (`.png`), JPEG (`.jpg`) and Targa (`.tga`).
+Luanti generally uses nearest-neighbor upscaling for textures to preserve the crisp
+look of pixel art (low-res textures).
+Users can optionally enable bilinear and/or trilinear filtering. However, to avoid
+everything becoming blurry, textures smaller than 192px will either not be filtered,
+or will be upscaled to that minimum resolution first without filtering.
+
+This is subject to change to move more control to the Lua API, but you can rely on
+low-res textures not suddenly becoming filtered.
+
Texture modifiers
-----------------
@@ -1596,7 +1608,8 @@ There are a bunch of different looking node types.
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:
@@ -1681,7 +1694,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.
@@ -1690,6 +1705,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
-----------
@@ -1713,7 +1760,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
@@ -1721,9 +1768,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
@@ -2024,6 +2071,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
---------------
@@ -2783,6 +2845,9 @@ Version History
* Add field_enter_after_edit[] (experimental)
* Formspec version 8 (5.10.0)
* scroll_container[]: content padding parameter
+* Formspec version 9 (5.12.0)
+ * Add allow_close[]
+ * label[]: Add "area label" variant
Elements
--------
@@ -2853,6 +2918,13 @@ Elements
* For information on converting forms to the new coordinate system, see `Migrating
to Real Coordinates`.
+### `allow_close[]`
+
+* When set to false, the formspec will not close when the user tries to close
+ it with the Escape key or similar. Default true.
+* The formspec can still be closed with `*_exit[]` elements and
+ `core.close_formspec()`, regardless of this setting.
+
### `container[,]`
* Start of a container block, moves all physical elements in the container by
@@ -3088,9 +3160,11 @@ Elements
### `textarea[,;,;;