diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 6b7b95369..51a7eae63 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -36,6 +36,9 @@ Contributions are welcome! Here's how you can help:
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and
[Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
- Check your code works as expected and document any changes to the Lua API.
+ - To avoid conflicting changes between contributions, do not do the following manually. They will be done before each release.
+ - Run `updatepo.sh` or update `minetest.po{,t}` even if your code adds new translatable strings.
+ - Update `minetest.conf.example` and `settings_translation_file.cpp` even if your code adds new core settings.
4. Commit & [push](https://help.github.com/articles/pushing-to-a-remote/) your changes to a new branch (not `master`, one change per branch)
- Commit messages should:
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 04c650c27..1ae4ac663 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -20,6 +20,8 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
+ - 'irr/**.[ch]'
+ - 'irr/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'android/**'
@@ -34,8 +36,17 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends gettext openjdk-11-jdk-headless
- - name: Build with Gradle
+ - name: Build AAB with Gradle
+ # We build an AAB as well for uploading to the the Play Store.
+ run: cd android; ./gradlew bundlerelease
+ - name: Build APKs with Gradle
+ # "assemblerelease" is very fast after "bundlerelease".
run: cd android; ./gradlew assemblerelease
+ - name: Save AAB artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: Minetest-release.aab
+ path: android/app/build/outputs/bundle/release/app-release.aab
- name: Save armeabi artifact
uses: actions/upload-artifact@v4
with:
diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml
index 79b8ffc4e..0ec8ebf1d 100644
--- a/.github/workflows/cpp_lint.yml
+++ b/.github/workflows/cpp_lint.yml
@@ -20,6 +20,8 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
+ - 'irr/**.[ch]'
+ - 'irr/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/ci/**'
diff --git a/.github/workflows/docker_image.yml b/.github/workflows/docker_image.yml
index eaac7bf1b..1a2805e22 100644
--- a/.github/workflows/docker_image.yml
+++ b/.github/workflows/docker_image.yml
@@ -9,7 +9,7 @@ on:
push:
branches: [ "master" ]
# Publish semver tags as releases.
- tags: [ "*.*.*" ]
+ tags: [ "*" ]
pull_request:
# Build docker image on pull requests. (but do not publish)
paths:
@@ -17,6 +17,8 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
+ - 'irr/**.[ch]'
+ - 'irr/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/ci/**'
@@ -25,6 +27,12 @@ on:
- '.dockerignore'
- '.github/workflows/docker_image.yml'
workflow_dispatch:
+ inputs:
+ use_cache:
+ description: "Use build cache"
+ required: true
+ type: boolean
+ default: true
env:
REGISTRY: ghcr.io
@@ -82,6 +90,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
+ no-cache: ${{ (github.event_name == 'workflow_dispatch' && !inputs.use_cache) || startsWith(github.ref, 'refs/tags/') }}
- name: Test Docker Image
run: |
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 08fc34ad6..2ecc42052 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -22,6 +22,8 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
+ - 'irr/**.[ch]'
+ - 'irr/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/ci/**'
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 24c2b9f51..34556ce8c 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -10,6 +10,7 @@ on:
- 'src/**.cpp'
- 'irr/**.[ch]'
- 'irr/**.cpp'
+ - 'irr/**.mm' # Objective-C(++)
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- '.github/workflows/macos.yml'
@@ -19,13 +20,17 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
+ - 'irr/**.[ch]'
+ - 'irr/**.cpp'
+ - 'irr/**.mm' # Objective-C(++)
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- '.github/workflows/macos.yml'
jobs:
build:
- runs-on: macos-latest
+ # use macOS 13 since it's the last one that still runs on x86
+ runs-on: macos-13
steps:
- uses: actions/checkout@v4
- name: Install deps
@@ -55,6 +60,8 @@ jobs:
- name: CPack
run: |
cd build
+ rm -rf macos
+ cmake .. -DINSTALL_DEVTEST=FALSE
cpack -G ZIP -B macos
- uses: actions/upload-artifact@v4
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 709b98437..874788c41 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -21,6 +21,8 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
+ - 'irr/**.[ch]'
+ - 'irr/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'util/buildbot/**'
@@ -67,8 +69,8 @@ jobs:
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
- VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50
- # 2023.10.19
+ VCPKG_VERSION: 01f602195983451bc83e72f4214af2cbc495aa94
+ # 2024.05.24
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
strategy:
fail-fast: false
@@ -92,12 +94,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- # Workaround for regression, see https://github.com/minetest/minetest/pull/14536
- - name: Pin CMake to 3.28
- uses: lukka/get-cmake@latest
- with:
- cmakeVersion: "~3.28.0"
-
- name: Restore from cache and run vcpkg
uses: lukka/run-vcpkg@v7
with:
diff --git a/.gitignore b/.gitignore
index a018237d9..fc2558a02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,8 @@
## Editors and development environments
*~
+.cmake
+CMakeUserPresets.json
+Testing/*
*.swp
*.bak*
*.orig
@@ -26,7 +29,8 @@ gtags.files
# Codelite
*.project
# Visual Studio Code & plugins
-.vscode/
+.vscode/*
+!.vscode/extensions.json
build/.cmake/
# Fleet
.fleet
@@ -108,7 +112,10 @@ src/cmake_config_githash.h
*.iml
test_config.h
cmake-build-debug/
+cmake-build-minsizerel/
cmake-build-release/
+cmake-build-relwithdebinfo/
+cmake-build-default/
cmake_config.h
cmake_config_githash.h
CMakeDoxy*
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 000000000..fd9b2c09b
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "ms-vscode.cpptools-extension-pack"
+ ]
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8254db7f..fd996b8e6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,7 +16,7 @@ set(VERSION_PATCH 0)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Change to false for releases
-set(DEVELOPMENT_BUILD TRUE)
+set(DEVELOPMENT_BUILD FALSE)
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
if(VERSION_EXTRA)
@@ -156,7 +156,7 @@ elseif(UNIX) # Linux, BSD etc
set(EXAMPLE_CONF_DIR ".")
set(MANDIR "unix/man")
set(XDG_APPS_DIR "unix/applications")
- set(APPDATADIR "unix/metainfo")
+ set(METAINFODIR "unix/metainfo")
set(ICONDIR "unix/icons")
set(LOCALEDIR "locale")
else()
@@ -167,7 +167,7 @@ elseif(UNIX) # Linux, BSD etc
set(MANDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_MANDIR}")
set(EXAMPLE_CONF_DIR ${DOCDIR})
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications")
- set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/metainfo")
+ set(METAINFODIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/metainfo")
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons")
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}")
endif()
@@ -258,7 +258,7 @@ install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
if(UNIX AND NOT APPLE)
install(FILES "doc/minetest.6" "doc/minetestserver.6" DESTINATION "${MANDIR}/man6")
install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}")
- install(FILES "misc/net.minetest.minetest.appdata.xml" DESTINATION "${APPDATADIR}")
+ install(FILES "misc/net.minetest.minetest.metainfo.xml" DESTINATION "${METAINFODIR}")
install(FILES "misc/minetest.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
install(FILES "misc/minetest-xorg-icon-128.png"
DESTINATION "${ICONDIR}/hicolor/128x128/apps"
@@ -277,8 +277,9 @@ find_package(Lua REQUIRED)
if(NOT USE_LUAJIT)
add_subdirectory(lib/bitop)
endif()
+add_subdirectory(lib/sha256)
-if(BUILD_BENCHMARKS)
+if(BUILD_UNITTESTS OR BUILD_BENCHMARKS)
add_subdirectory(lib/catch2)
endif()
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 000000000..d1e6a4037
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,41 @@
+{
+ "version": 3,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 12
+ },
+ "configurePresets": [
+ {
+ "name": "Debug",
+ "displayName": "Debug",
+ "description": "Debug preset with debug symbols and no optimizations",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug"
+ }
+ },
+ {
+ "name": "Release",
+ "displayName": "Release",
+ "description": "Release preset with optimizations and no debug symbols",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ },
+ {
+ "name": "RelWithDebInfo",
+ "displayName": "RelWithDebInfo",
+ "description": "Release with debug symbols",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "RelWithDebInfo"
+ }
+ },
+ {
+ "name": "MinSizeRel",
+ "displayName": "MinSizeRel",
+ "description": "Release with minimal code size",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "MinSizeRel"
+ }
+ }
+ ]
+}
diff --git a/Dockerfile b/Dockerfile
index 701c0492a..39974a1c3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,6 @@
ARG DOCKER_IMAGE=alpine:3.19
FROM $DOCKER_IMAGE AS dev
-ENV SPATIALINDEX_VERSION master
ENV LUAJIT_VERSION v2.1
RUN apk add --no-cache git build-base cmake curl-dev zlib-dev zstd-dev \
@@ -19,7 +18,7 @@ RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp && \
cmake --build build && \
cmake --install build && \
cd /usr/src/ && \
- git clone --recursive https://github.com/libspatialindex/libspatialindex -b ${SPATIALINDEX_VERSION} && \
+ git clone --recursive https://github.com/libspatialindex/libspatialindex && \
cd libspatialindex && \
cmake -B build \
-DCMAKE_INSTALL_PREFIX=/usr/local && \
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 2646721d1..b0e301c53 100644
--- a/android/app/src/main/java/net/minetest/minetest/GameActivity.java
+++ b/android/app/src/main/java/net/minetest/minetest/GameActivity.java
@@ -23,6 +23,7 @@
import org.libsdl.app.SDLActivity;
import android.content.Intent;
+import android.content.ActivityNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.text.InputType;
@@ -33,6 +34,7 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.LinearLayout;
+import android.widget.Toast;
import android.content.res.Configuration;
import androidx.annotation.Keep;
@@ -201,7 +203,11 @@ public int getDisplayWidth() {
public void openURI(String uri) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
- startActivity(browserIntent);
+ try {
+ startActivity(browserIntent);
+ } catch (ActivityNotFoundException e) {
+ runOnUiThread(() -> Toast.makeText(this, R.string.no_web_browser, Toast.LENGTH_SHORT).show());
+ }
}
public String getUserDataPath() {
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 9eb1d3ca1..5885a7b7a 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -7,4 +7,5 @@
Loading Minetest
Less than 1 minute…
Done
+ No web browser found
diff --git a/android/gradle.properties b/android/gradle.properties
index 8d5c2efb6..333d3ad95 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,7 +1,7 @@
<#if isLowMemory>
-org.gradle.jvmargs=-Xmx4G -XX:MaxPermSize=2G -XX:+HeapDumpOnOutOfMemoryError
+org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
<#else>
-org.gradle.jvmargs=-Xmx16G -XX:MaxPermSize=8G -XX:+HeapDumpOnOutOfMemoryError
+org.gradle.jvmargs=-Xmx16G -XX:+HeapDumpOnOutOfMemoryError
#if>
org.gradle.daemon=true
org.gradle.parallel=true
diff --git a/android/native/build.gradle b/android/native/build.gradle
index 705d14f82..96a7739c0 100644
--- a/android/native/build.gradle
+++ b/android/native/build.gradle
@@ -36,7 +36,7 @@ android {
buildTypes {
release {
ndk {
- debugSymbolLevel 'SYMBOL_TABLE'
+ debugSymbolLevel 'FULL'
}
}
}
diff --git a/builtin/common/metatable.lua b/builtin/common/metatable.lua
index b402dd63a..40003d293 100644
--- a/builtin/common/metatable.lua
+++ b/builtin/common/metatable.lua
@@ -1,6 +1,6 @@
-- Registered metatables, used by the C++ packer
local known_metatables = {}
-function core.register_async_metatable(name, mt)
+function core.register_portable_metatable(name, mt)
assert(type(name) == "string", ("attempt to use %s value as metatable name"):format(type(name)))
assert(type(mt) == "table", ("attempt to register a %s value as metatable"):format(type(mt)))
assert(known_metatables[name] == nil or known_metatables[name] == mt,
@@ -10,4 +10,10 @@ function core.register_async_metatable(name, mt)
end
core.known_metatables = known_metatables
-core.register_async_metatable("__builtin:vector", vector.metatable)
+function core.register_async_metatable(...)
+ core.log("deprecated", "minetest.register_async_metatable is deprecated. " ..
+ "Use minetest.register_portable_metatable instead.")
+ return core.register_portable_metatable(...)
+end
+
+core.register_portable_metatable("__builtin:vector", vector.metatable)
diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
index c3ba9f683..6fca3da89 100644
--- a/builtin/common/misc_helpers.lua
+++ b/builtin/common/misc_helpers.lua
@@ -240,12 +240,15 @@ function math.factorial(x)
return v
end
-
function math.round(x)
- if x >= 0 then
- return math.floor(x + 0.5)
- end
- return math.ceil(x - 0.5)
+ if x < 0 then
+ local int = math.ceil(x)
+ local frac = x - int
+ return int - ((frac <= -0.5) and 1 or 0)
+ end
+ local int = math.floor(x)
+ local frac = x - int
+ return int + ((frac >= 0.5) and 1 or 0)
end
local formspec_escapes = {
diff --git a/builtin/common/tests/misc_helpers_spec.lua b/builtin/common/tests/misc_helpers_spec.lua
index 73a66737f..65549c2e3 100644
--- a/builtin/common/tests/misc_helpers_spec.lua
+++ b/builtin/common/tests/misc_helpers_spec.lua
@@ -176,3 +176,17 @@ describe("formspec_escape", function()
assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\["))
end)
end)
+
+describe("math", function()
+ it("round()", function()
+ assert.equal(0, math.round(0))
+ assert.equal(10, math.round(10.3))
+ assert.equal(11, math.round(10.5))
+ assert.equal(11, math.round(10.7))
+ assert.equal(-10, math.round(-10.3))
+ assert.equal(-11, math.round(-10.5))
+ assert.equal(-11, math.round(-10.7))
+ assert.equal(0, math.round(0.49999999999999994))
+ assert.equal(0, math.round(-0.49999999999999994))
+ end)
+end)
diff --git a/builtin/fstk/buttonbar.lua b/builtin/fstk/buttonbar.lua
index c7da41280..59b7a586f 100644
--- a/builtin/fstk/buttonbar.lua
+++ b/builtin/fstk/buttonbar.lua
@@ -102,14 +102,24 @@ local function buttonbar_formspec(self)
end
local function buttonbar_buttonhandler(self, fields)
- if fields[self.btn_prev_name] and self.cur_page > 1 then
- self.cur_page = self.cur_page - 1
- return true
+ if fields[self.btn_prev_name] then
+ if self.cur_page > 1 then
+ self.cur_page = self.cur_page - 1
+ return true
+ elseif self.cur_page == 1 then
+ self.cur_page = self.num_pages
+ return true
+ end
end
- if fields[self.btn_next_name] and self.cur_page < self.num_pages then
- self.cur_page = self.cur_page + 1
- return true
+ if fields[self.btn_next_name] then
+ if self.cur_page < self.num_pages then
+ self.cur_page = self.cur_page + 1
+ return true
+ elseif self.cur_page == self.num_pages then
+ self.cur_page = 1
+ return true
+ end
end
for _, btn in ipairs(self.buttons) do
diff --git a/builtin/fstk/dialog.lua b/builtin/fstk/dialog.lua
index c50d23855..991ec20ab 100644
--- a/builtin/fstk/dialog.lua
+++ b/builtin/fstk/dialog.lua
@@ -78,15 +78,12 @@ function dialog_create(name,get_formspec,buttonhandler,eventhandler)
return self
end
+-- "message" must already be formspec-escaped, e.g. via fgettext or
+-- core.formspec_escape.
function messagebox(name, message)
return dialog_create(name,
function()
- return ([[
- formspec_version[3]
- size[8,3]
- textarea[0.375,0.375;7.25,1.2;;;%s]
- button[3,1.825;2,0.8;ok;%s]
- ]]):format(message, fgettext("OK"))
+ return ui.get_message_formspec("", message, "ok")
end,
function(this, fields)
if fields.ok then
diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua
index a63cb2cd0..a3a8fed77 100644
--- a/builtin/fstk/ui.lua
+++ b/builtin/fstk/ui.lua
@@ -50,6 +50,20 @@ function ui.find_by_name(name)
return ui.childlist[name]
end
+--------------------------------------------------------------------------------
+-- "title" and "message" must already be formspec-escaped, e.g. via fgettext or
+-- core.formspec_escape.
+function ui.get_message_formspec(title, message, btn_id)
+ return table.concat({
+ "size[14,8]",
+ "real_coordinates[true]",
+ "set_focus[", btn_id, ";true]",
+ "box[0.5,1.2;13,5;#000]",
+ ("textarea[0.5,1.2;13,5;;%s;%s]"):format(title, message),
+ "button[5,6.6;4,1;", btn_id, ";" .. fgettext("OK") .. "]",
+ })
+end
+
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Internal functions not to be called from user
@@ -76,6 +90,9 @@ function ui.update()
}
ui.overridden = true
elseif gamedata ~= nil and gamedata.errormessage ~= nil then
+ -- Note to API users:
+ -- "gamedata.errormessage" must not be formspec-escaped yet.
+ -- For translations, fgettext_ne should be used.
local error_message = core.formspec_escape(gamedata.errormessage)
local error_title
@@ -84,15 +101,7 @@ function ui.update()
else
error_title = fgettext("An error occurred:")
end
- formspec = {
- "size[14,8]",
- "real_coordinates[true]",
- "set_focus[btn_error_confirm;true]",
- "box[0.5,1.2;13,5;#000]",
- ("textarea[0.5,1.2;13,5;;%s;%s]"):format(
- error_title, error_message),
- "button[5,6.6;4,1;btn_error_confirm;" .. fgettext("OK") .. "]"
- }
+ formspec = {ui.get_message_formspec(error_title, error_message, "btn_error_confirm")}
ui.overridden = true
else
local active_toplevel_ui_elements = 0
diff --git a/builtin/game/features.lua b/builtin/game/features.lua
index 286c3f146..22bf1859d 100644
--- a/builtin/game/features.lua
+++ b/builtin/game/features.lua
@@ -40,6 +40,8 @@ core.features = {
lsystem_decoration_type = true,
item_meta_range = true,
node_interaction_actor = true,
+ moveresult_new_pos = true,
+ override_item_remove_fields = true,
}
function core.has_feature(arg)
diff --git a/builtin/game/knockback.lua b/builtin/game/knockback.lua
index 979d13a14..b119cdeb9 100644
--- a/builtin/game/knockback.lua
+++ b/builtin/game/knockback.lua
@@ -22,14 +22,16 @@ local function vector_absmax(v)
return max(max(abs(v.x), abs(v.y)), abs(v.z))
end
-core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, unused_dir, damage)
+core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
if player:get_hp() == 0 then
return -- RIP
end
- -- Server::handleCommand_Interact() adds eye offset to one but not the other
- -- so the direction is slightly off, calculate it ourselves
- local dir = vector.subtract(player:get_pos(), hitter:get_pos())
+ if hitter then
+ -- Server::handleCommand_Interact() adds eye offset to one but not the other
+ -- so the direction is slightly off, calculate it ourselves
+ dir = vector.subtract(player:get_pos(), hitter:get_pos())
+ end
local d = vector.length(dir)
if d ~= 0.0 then
dir = vector.divide(dir, d)
diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua
index dc394a00c..a8a6700f9 100644
--- a/builtin/game/misc.lua
+++ b/builtin/game/misc.lua
@@ -272,3 +272,29 @@ function core.get_globals_to_transfer()
}
return all
end
+
+do
+ local function valid_object_iterator(objects)
+ local i = 0
+ local function next_valid_object()
+ i = i + 1
+ local obj = objects[i]
+ if obj == nil then
+ return
+ end
+ if obj:is_valid() then
+ return obj
+ end
+ return next_valid_object()
+ end
+ return next_valid_object
+ end
+
+ function core.objects_inside_radius(center, radius)
+ return valid_object_iterator(core.get_objects_inside_radius(center, radius))
+ end
+
+ function core.objects_in_area(min_pos, max_pos)
+ return valid_object_iterator(core.get_objects_in_area(min_pos, max_pos))
+ end
+end
diff --git a/builtin/game/misc_s.lua b/builtin/game/misc_s.lua
index af4002bf2..ab15b230e 100644
--- a/builtin/game/misc_s.lua
+++ b/builtin/game/misc_s.lua
@@ -97,3 +97,26 @@ function core.encode_png(width, height, data, compression)
return o_encode_png(width, height, data, compression or 6)
end
+
+-- Helper that pushes a collisionMoveResult structure
+if core.set_push_moveresult1 then
+ -- must match CollisionAxis in collision.h
+ local AXES = {"x", "y", "z"}
+ -- <=> script/common/c_content.cpp push_collision_move_result()
+ core.set_push_moveresult1(function(b0, b1, b2, axis, npx, npy, npz, v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z)
+ return {
+ touching_ground = b0,
+ collides = b1,
+ standing_on_object = b2,
+ collisions = {{
+ type = "node",
+ axis = AXES[axis + 1],
+ node_pos = vector.new(npx, npy, npz),
+ new_pos = vector.new(v0x, v0y, v0z),
+ old_velocity = vector.new(v1x, v1y, v1z),
+ new_velocity = vector.new(v2x, v2y, v2z),
+ }},
+ }
+ end)
+ core.set_push_moveresult1 = nil
+end
diff --git a/builtin/game/register.lua b/builtin/game/register.lua
index 4ecf6ec71..76cb72f87 100644
--- a/builtin/game/register.lua
+++ b/builtin/game/register.lua
@@ -404,7 +404,7 @@ core.register_item(":", {
})
-function core.override_item(name, redefinition)
+function core.override_item(name, redefinition, del_fields)
if redefinition.name ~= nil then
error("Attempt to redefine name of "..name.." to "..dump(redefinition.name), 2)
end
@@ -418,6 +418,9 @@ function core.override_item(name, redefinition)
for k, v in pairs(redefinition) do
rawset(item, k, v)
end
+ for _, field in ipairs(del_fields or {}) do
+ rawset(item, field, nil)
+ end
register_item_raw(item)
end
diff --git a/builtin/mainmenu/content/contentdb.lua b/builtin/mainmenu/content/contentdb.lua
index eb06c8b61..e0479cb4c 100644
--- a/builtin/mainmenu/content/contentdb.lua
+++ b/builtin/mainmenu/content/contentdb.lua
@@ -71,7 +71,9 @@ local function download_and_extract(param)
os.remove(filename)
if tempfolder == "" then
return {
- msg = fgettext_ne("Failed to extract \"$1\" (unsupported file type or broken archive)", package.title),
+ msg = fgettext_ne("Failed to extract \"$1\" " ..
+ "(insufficient disk space, unsupported file type or broken archive)",
+ package.title),
}
end
@@ -131,6 +133,8 @@ local function start_install(package, reason)
conf:set("release", package.release)
conf:write()
end
+
+ pkgmgr.reload_by_type(package.type)
end
end
@@ -144,7 +148,6 @@ local function start_install(package, reason)
start_install(next.package, next.reason)
end
-
ui.update()
end
@@ -179,14 +182,26 @@ function contentdb.get_package_by_id(id)
end
-local function get_raw_dependencies(package)
- if package.type ~= "mod" then
- return {}
- end
- if package.raw_deps then
- return package.raw_deps
+-- Create a coroutine from `fn` and provide results to `callback` when complete (dead).
+-- Returns a resumer function.
+local function make_callback_coroutine(fn, callback)
+ local co = coroutine.create(fn)
+
+ local function resumer(...)
+ local ok, result = coroutine.resume(co, ...)
+
+ if not ok then
+ error(result)
+ elseif coroutine.status(co) == "dead" then
+ callback(result)
+ end
end
+ return resumer
+end
+
+
+local function get_raw_dependencies_async(package)
local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
local version = core.get_version()
local base_url = core.settings:get("contentdb_url")
@@ -195,11 +210,25 @@ local function get_raw_dependencies(package)
local http = core.get_http_api()
local response = http.fetch_sync({ url = url })
if not response.succeeded then
- core.log("error", "Unable to fetch dependencies for " .. package.url_part)
- return
+ return nil
end
+ return core.parse_json(response.data) or {}
+end
- local data = core.parse_json(response.data) or {}
+
+local function get_raw_dependencies_co(package, resumer)
+ if package.type ~= "mod" then
+ return {}
+ end
+ if package.raw_deps then
+ return package.raw_deps
+ end
+
+ core.handle_async(get_raw_dependencies_async, package, resumer)
+ local data = coroutine.yield()
+ if not data then
+ return nil
+ end
for id, raw_deps in pairs(data) do
local package2 = contentdb.package_by_id[id:lower()]
@@ -220,8 +249,8 @@ local function get_raw_dependencies(package)
end
-function contentdb.has_hard_deps(package)
- local raw_deps = get_raw_dependencies(package)
+local function has_hard_deps_co(package, resumer)
+ local raw_deps = get_raw_dependencies_co(package, resumer)
if not raw_deps then
return nil
end
@@ -236,8 +265,14 @@ function contentdb.has_hard_deps(package)
end
+function contentdb.has_hard_deps(package, callback)
+ local resumer = make_callback_coroutine(has_hard_deps_co, callback)
+ resumer(package, resumer)
+end
+
+
-- Recursively resolve dependencies, given the installed mods
-local function resolve_dependencies_2(raw_deps, installed_mods, out)
+local function resolve_dependencies_2_co(raw_deps, installed_mods, out, resumer)
local function resolve_dep(dep)
-- Check whether it's already installed
if installed_mods[dep.name] then
@@ -287,9 +322,9 @@ local function resolve_dependencies_2(raw_deps, installed_mods, out)
local result = resolve_dep(dep)
out[dep.name] = result
if result and result.package and not result.installed then
- local raw_deps2 = get_raw_dependencies(result.package)
+ local raw_deps2 = get_raw_dependencies_co(result.package, resumer)
if raw_deps2 then
- resolve_dependencies_2(raw_deps2, installed_mods, out)
+ resolve_dependencies_2_co(raw_deps2, installed_mods, out, resumer)
end
end
end
@@ -299,11 +334,10 @@ local function resolve_dependencies_2(raw_deps, installed_mods, out)
end
--- Resolve dependencies for a package, calls the recursive version.
-function contentdb.resolve_dependencies(package, game)
+local function resolve_dependencies_co(package, game, resumer)
assert(game)
- local raw_deps = get_raw_dependencies(package)
+ local raw_deps = get_raw_dependencies_co(package, resumer)
local installed_mods = {}
local mods = {}
@@ -317,7 +351,7 @@ function contentdb.resolve_dependencies(package, game)
end
local out = {}
- if not resolve_dependencies_2(raw_deps, installed_mods, out) then
+ if not resolve_dependencies_2_co(raw_deps, installed_mods, out, resumer) then
return nil
end
@@ -334,6 +368,13 @@ function contentdb.resolve_dependencies(package, game)
end
+-- Resolve dependencies for a package, calls the recursive version.
+function contentdb.resolve_dependencies(package, game, callback)
+ local resumer = make_callback_coroutine(resolve_dependencies_co, callback)
+ resumer(package, game, resumer)
+end
+
+
local function fetch_pkgs(params)
local version = core.get_version()
local base_url = core.settings:get("contentdb_url")
@@ -425,8 +466,9 @@ end
function contentdb.update_paths()
+ pkgmgr.load_all()
+
local mod_hash = {}
- pkgmgr.refresh_globals()
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
local cdb_id = pkgmgr.get_contentdb_id(mod)
if cdb_id then
@@ -435,7 +477,6 @@ function contentdb.update_paths()
end
local game_hash = {}
- pkgmgr.update_gamelist()
for _, game in pairs(pkgmgr.games) do
local cdb_id = pkgmgr.get_contentdb_id(game)
if cdb_id then
@@ -444,7 +485,7 @@ function contentdb.update_paths()
end
local txp_hash = {}
- for _, txp in pairs(pkgmgr.get_texture_packs()) do
+ for _, txp in pairs(pkgmgr.texture_packs) do
local cdb_id = pkgmgr.get_contentdb_id(txp)
if cdb_id then
txp_hash[contentdb.aliases[cdb_id] or cdb_id] = txp
diff --git a/builtin/mainmenu/content/dlg_contentdb.lua b/builtin/mainmenu/content/dlg_contentdb.lua
index 4dafafc45..84ef96800 100644
--- a/builtin/mainmenu/content/dlg_contentdb.lua
+++ b/builtin/mainmenu/content/dlg_contentdb.lua
@@ -63,21 +63,12 @@ local function install_or_update_package(this, package)
end
local function on_confirm()
- local has_hard_deps = contentdb.has_hard_deps(package)
- if has_hard_deps then
- local dlg = create_install_dialog(package)
- dlg:set_parent(this)
- this:hide()
- dlg:show()
- elseif has_hard_deps == nil then
- local dlg = messagebox("error_checking_deps",
- fgettext("Error getting dependencies for package"))
- dlg:set_parent(this)
- this:hide()
- dlg:show()
- else
- contentdb.queue_download(package, package.path and contentdb.REASON_UPDATE or contentdb.REASON_NEW)
- end
+ local dlg = create_install_dialog(package)
+ dlg:set_parent(this)
+ this:hide()
+ dlg:show()
+
+ dlg:load_deps()
end
if package.type == "mod" and #pkgmgr.games == 0 then
@@ -117,7 +108,7 @@ local function resolve_auto_install_spec()
end
if not resolved then
- gamedata.errormessage = fgettext("The package $1 was not found.", auto_install_spec)
+ gamedata.errormessage = fgettext_ne("The package $1 was not found.", auto_install_spec)
ui.update()
auto_install_spec = nil
diff --git a/builtin/mainmenu/content/dlg_install.lua b/builtin/mainmenu/content/dlg_install.lua
index 8c2a43b09..0549e23be 100644
--- a/builtin/mainmenu/content/dlg_install.lua
+++ b/builtin/mainmenu/content/dlg_install.lua
@@ -15,7 +15,31 @@
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+local function is_still_visible(dlg)
+ local this = ui.find_by_name("install_dialog")
+ return this == dlg and not dlg.hidden
+end
+
+
+local function get_loading_formspec()
+ local ENABLE_TOUCH = core.settings:get_bool("enable_touch")
+ local w = ENABLE_TOUCH and 14 or 7
+
+ local formspec = {
+ "formspec_version[3]",
+ "size[", w, ",9.05]",
+ ENABLE_TOUCH and "padding[0.01,0.01]" or "position[0.5,0.55]",
+ "label[3,4.525;", fgettext("Loading..."), "]",
+ }
+ return table.concat(formspec)
+end
+
+
local function get_formspec(data)
+ if not data.has_hard_deps_ready then
+ return get_loading_formspec()
+ end
+
local selected_game, selected_game_idx = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
if not selected_game_idx then
selected_game_idx = 1
@@ -27,15 +51,35 @@ local function get_formspec(data)
game_list[i] = core.formspec_escape(game.title)
end
+ if not data.deps_ready[selected_game_idx] and
+ not data.deps_loading[selected_game_idx] then
+ data.deps_loading[selected_game_idx] = true
+
+ contentdb.resolve_dependencies(data.package, selected_game, function(deps)
+ if not is_still_visible(data.dlg) then
+ return
+ end
+ data.deps_ready[selected_game_idx] = deps
+ ui.update()
+ end)
+ end
+
+ -- The value of `data.deps_ready[selected_game_idx]` may have changed
+ -- since the last if statement since `contentdb.resolve_dependencies`
+ -- calls the callback immediately if the dependencies are already cached.
+ if not data.deps_ready[selected_game_idx] then
+ return get_loading_formspec()
+ end
+
local package = data.package
local will_install_deps = data.will_install_deps
local deps_to_install = 0
local deps_not_found = 0
- data.dependencies = contentdb.resolve_dependencies(package, selected_game)
+ data.deps_chosen = data.deps_ready[selected_game_idx]
local formatted_deps = {}
- for _, dep in pairs(data.dependencies) do
+ for _, dep in pairs(data.deps_chosen) do
formatted_deps[#formatted_deps + 1] = "#fff"
formatted_deps[#formatted_deps + 1] = core.formspec_escape(dep.name)
if dep.installed then
@@ -66,35 +110,45 @@ local function get_formspec(data)
message_bg = mt_color_orange
end
+ local ENABLE_TOUCH = core.settings:get_bool("enable_touch")
+
+ local w = ENABLE_TOUCH and 14 or 7
+ local padded_w = w - 2*0.375
+ local dropdown_w = ENABLE_TOUCH and 10.2 or 4.25
+ local button_w = (padded_w - 0.25) / 3
+ local button_pad = button_w / 2
+
local formspec = {
"formspec_version[3]",
- "size[7,7.85]",
+ "size[", w, ",9.05]",
+ ENABLE_TOUCH and "padding[0.01,0.01]" or "position[0.5,0.55]",
"style[title;border=false]",
- "box[0,0;7,0.5;#3333]",
- "button[0,0;7,0.5;title;", fgettext("Install $1", package.title) , "]",
+ "box[0,0;", w, ",0.8;#3333]",
+ "button[0,0;", w, ",0.8;title;", fgettext("Install $1", package.title) , "]",
- "container[0.375,0.70]",
+ "container[0.375,1]",
- "label[0,0.25;", fgettext("Base Game:"), "]",
- "dropdown[2,0;4.25,0.5;selected_game;", table.concat(game_list, ","), ";", selected_game_idx, "]",
+ "label[0,0.4;", fgettext("Base Game:"), "]",
+ "dropdown[", padded_w - dropdown_w, ",0;", dropdown_w, ",0.8;selected_game;",
+ table.concat(game_list, ","), ";", selected_game_idx, "]",
- "label[0,0.8;", fgettext("Dependencies:"), "]",
+ "label[0,1.1;", fgettext("Dependencies:"), "]",
"tablecolumns[color;text;color;text]",
- "table[0,1.1;6.25,3;packages;", table.concat(formatted_deps, ","), "]",
+ "table[0,1.4;", padded_w, ",3;packages;", table.concat(formatted_deps, ","), "]",
"container_end[]",
- "checkbox[0.375,5.1;will_install_deps;",
+ "checkbox[0.375,5.7;will_install_deps;",
fgettext("Install missing dependencies"), ";",
will_install_deps and "true" or "false", "]",
- "box[0,5.4;7,1.2;", message_bg, "]",
- "textarea[0.375,5.5;6.25,1;;;", message, "]",
+ "box[0,6;", w, ",1.8;", message_bg, "]",
+ "textarea[0.375,6.1;", padded_w, ",1.6;;;", message, "]",
- "container[1.375,6.85]",
- "button[0,0;2,0.8;install_all;", fgettext("Install"), "]",
- "button[2.25,0;2,0.8;cancel;", fgettext("Cancel"), "]",
+ "container[", 0.375 + button_pad, ",8.05]",
+ "button[0,0;", button_w, ",0.8;install_all;", fgettext("Install"), "]",
+ "button[", 0.25 + button_w, ",0;", button_w, ",0.8;cancel;", fgettext("Cancel"), "]",
"container_end[]",
}
@@ -118,7 +172,7 @@ local function handle_submit(this, fields)
contentdb.queue_download(data.package, contentdb.REASON_NEW)
if data.will_install_deps then
- for _, dep in pairs(data.dependencies) do
+ for _, dep in pairs(data.deps_chosen) do
if not dep.is_optional and not dep.installed and dep.package then
contentdb.queue_download(dep.package, contentdb.REASON_DEPENDENCY)
end
@@ -143,10 +197,50 @@ local function handle_submit(this, fields)
end
+local function load_deps(dlg)
+ local package = dlg.data.package
+
+ contentdb.has_hard_deps(package, function(result)
+ if not is_still_visible(dlg) then
+ return
+ end
+
+ if result == nil then
+ local parent = dlg.parent
+ dlg:delete()
+ local dlg2 = messagebox("error_checking_deps",
+ fgettext("Error getting dependencies for package $1", package.url_part))
+ dlg2:set_parent(parent)
+ parent:hide()
+ dlg2:show()
+ elseif result == false then
+ contentdb.queue_download(package, package.path and contentdb.REASON_UPDATE or contentdb.REASON_NEW)
+ dlg:delete()
+ else
+ assert(result == true)
+ dlg.data.has_hard_deps_ready = true
+ end
+ ui.update()
+ end)
+end
+
+
function create_install_dialog(package)
local dlg = dialog_create("install_dialog", get_formspec, handle_submit, nil)
- dlg.data.dependencies = nil
+ dlg.data.deps_chosen = nil
dlg.data.package = package
dlg.data.will_install_deps = true
+
+ dlg.data.has_hard_deps_ready = false
+ dlg.data.deps_ready = {}
+ dlg.data.deps_loading = {}
+
+ dlg.load_deps = load_deps
+
+ -- `get_formspec` needs to access `dlg` to check whether it's still open.
+ -- It doesn't suffice to check that any "install_dialog" instance is open
+ -- via `ui.find_by_name`, it's necessary to check for this exact instance.
+ dlg.data.dlg = dlg
+
return dlg
end
diff --git a/builtin/mainmenu/content/pkgmgr.lua b/builtin/mainmenu/content/pkgmgr.lua
index b167e1423..5a4aa6c17 100644
--- a/builtin/mainmenu/content/pkgmgr.lua
+++ b/builtin/mainmenu/content/pkgmgr.lua
@@ -110,7 +110,7 @@ pkgmgr = {}
-- @param modpack Currently processing modpack or nil/"" if none (recursion)
function pkgmgr.get_mods(path, virtual_path, listing, modpack)
local mods = core.get_dir_list(path, true)
-
+ local added = {}
for _, name in ipairs(mods) do
if name:sub(1, 1) ~= "." then
local mod_path = path .. DIR_DELIM .. name
@@ -120,6 +120,7 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
parent_dir = path,
}
listing[#listing + 1] = toadd
+ added[#added + 1] = toadd
-- Get config file
local mod_conf
@@ -150,8 +151,6 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
toadd.virtual_path = mod_virtual_path
toadd.type = "mod"
- pkgmgr.update_translations({ toadd })
-
-- Check modpack.txt
-- Note: modpack.conf is already checked above
local modpackfile = io.open(mod_path .. DIR_DELIM .. "modpack.txt")
@@ -171,6 +170,8 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
end
end
+ pkgmgr.update_translations(added)
+
if not modpack then
-- Sort all when the recursion is done
table.sort(listing, function(a, b)
@@ -180,12 +181,13 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
end
--------------------------------------------------------------------------------
-function pkgmgr.get_texture_packs()
+function pkgmgr.reload_texture_packs()
local txtpath = core.get_texturepath()
local txtpath_system = core.get_texturepath_share()
local retval = {}
load_texture_packs(txtpath, retval)
+
-- on portable versions these two paths coincide. It avoids loading the path twice
if txtpath ~= txtpath_system then
load_texture_packs(txtpath_system, retval)
@@ -197,11 +199,13 @@ function pkgmgr.get_texture_packs()
return a.title:lower() < b.title:lower()
end)
- return retval
+ pkgmgr.texture_packs = retval
end
--------------------------------------------------------------------------------
function pkgmgr.get_all()
+ pkgmgr.load_all()
+
local result = {}
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
@@ -210,7 +214,7 @@ function pkgmgr.get_all()
for _, game in pairs(pkgmgr.games) do
result[#result + 1] = game
end
- for _, txp in pairs(pkgmgr.get_texture_packs()) do
+ for _, txp in pairs(pkgmgr.texture_packs) do
result[#result + 1] = txp
end
@@ -288,7 +292,7 @@ end
function pkgmgr.render_packagelist(render_list, use_technical_names, with_icon)
if not render_list then
if not pkgmgr.global_mods then
- pkgmgr.refresh_globals()
+ pkgmgr.reload_global_mods()
end
render_list = pkgmgr.global_mods
end
@@ -549,6 +553,7 @@ function pkgmgr.get_worldconfig(worldpath)
end
--------------------------------------------------------------------------------
+-- Caller is responsible for reloading content types (see reload_by_type)
function pkgmgr.install_dir(expected_type, path, basename, targetpath)
assert(type(expected_type) == "string")
assert(type(path) == "string")
@@ -615,12 +620,6 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
fgettext_ne("Failed to install $1 to $2", basename, targetpath)
end
- if basefolder.type == "game" then
- pkgmgr.update_gamelist()
- else
- pkgmgr.refresh_globals()
- end
-
return targetpath, nil
end
@@ -742,7 +741,7 @@ function pkgmgr.comparemod(elem1,elem2)
end
--------------------------------------------------------------------------------
-function pkgmgr.refresh_globals()
+function pkgmgr.reload_global_mods()
local function is_equal(element,uid) --uid match
if element.name == uid then
return true
@@ -774,7 +773,7 @@ function pkgmgr.get_game_mods(gamespec, retval)
end
--------------------------------------------------------------------------------
-function pkgmgr.update_gamelist()
+function pkgmgr.reload_games()
pkgmgr.games = core.get_games()
table.sort(pkgmgr.games, function(a, b)
return a.title:lower() < b.title:lower()
@@ -782,6 +781,32 @@ function pkgmgr.update_gamelist()
pkgmgr.update_translations(pkgmgr.games)
end
+--------------------------------------------------------------------------------
+function pkgmgr.reload_by_type(type)
+ if type == "game" then
+ pkgmgr.reload_games()
+ elseif type == "txp" then
+ pkgmgr.reload_texture_packs()
+ elseif type == "mod" or type == "modpack" then
+ pkgmgr.reload_global_mods()
+ else
+ error("Unknown package type: " .. type)
+ end
+end
+
+--------------------------------------------------------------------------------
+function pkgmgr.load_all()
+ if not pkgmgr.global_mods then
+ pkgmgr.reload_global_mods()
+ end
+ if not pkgmgr.games then
+ pkgmgr.reload_games()
+ end
+ if not pkgmgr.texture_packs then
+ pkgmgr.reload_texture_packs()
+ end
+end
+
--------------------------------------------------------------------------------
function pkgmgr.update_translations(list)
for _, item in ipairs(list) do
@@ -831,4 +856,4 @@ end
--------------------------------------------------------------------------------
-- read initial data
--------------------------------------------------------------------------------
-pkgmgr.update_gamelist()
+pkgmgr.reload_games()
diff --git a/builtin/mainmenu/content/tests/pkgmgr_spec.lua b/builtin/mainmenu/content/tests/pkgmgr_spec.lua
index 37c1bb784..ccadde38c 100644
--- a/builtin/mainmenu/content/tests/pkgmgr_spec.lua
+++ b/builtin/mainmenu/content/tests/pkgmgr_spec.lua
@@ -46,6 +46,9 @@ local function reset()
function core.get_texturepath()
return txp_dir
end
+ function core.get_texturepath_share()
+ return txp_dir
+ end
function core.get_modpath()
return mods_dir
end
@@ -59,13 +62,6 @@ local function reset()
setfenv(loadfile("builtin/common/misc_helpers.lua"), env)()
setfenv(loadfile("builtin/mainmenu/content/pkgmgr.lua"), env)()
- function env.pkgmgr.update_gamelist()
- table.insert(calls, { "update_gamelist" })
- end
- function env.pkgmgr.refresh_globals()
- table.insert(calls, { "refresh_globals" })
- end
-
function env.assert_calls(list)
assert.are.same(list, calls)
end
@@ -113,7 +109,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", mods_dir .. "/mymod" },
{ "copy_dir", "/tmp/123", mods_dir .. "/mymod", false },
- { "refresh_globals" },
})
end)
@@ -129,7 +124,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", mods_dir .. "/mymod" },
{ "copy_dir", "/tmp/123", mods_dir .. "/mymod", false },
- { "refresh_globals" },
})
end)
@@ -145,7 +139,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", games_dir .. "/mygame" },
{ "copy_dir", "/tmp/123", games_dir .. "/mygame", false },
- { "update_gamelist" },
})
end)
@@ -161,7 +154,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", mods_dir .. "/123" },
{ "copy_dir", "/tmp/123", mods_dir .. "/123", false },
- { "refresh_globals" },
})
end)
@@ -188,7 +180,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", "/tmp/alt-target" },
{ "copy_dir", "/tmp/123", "/tmp/alt-target", false },
- { "refresh_globals" },
})
end)
@@ -238,6 +229,5 @@ describe("install_dir", function()
path, message = env.pkgmgr.install_dir("txp", "/tmp/123", "name", nil)
assert.is._not._nil(path)
assert.is._nil(message)
-
end)
end)
diff --git a/builtin/mainmenu/content/update_detector.lua b/builtin/mainmenu/content/update_detector.lua
index f70220224..1f8672bba 100644
--- a/builtin/mainmenu/content/update_detector.lua
+++ b/builtin/mainmenu/content/update_detector.lua
@@ -31,7 +31,7 @@ local cache_file_path = core.get_cache_path() .. DIR_DELIM .. "cdb" .. DIR_DELIM
local has_fetched = false
local latest_releases
do
- if check_cache_age("cdb_updates_last_checked", 3 * 3600) then
+ if check_cache_age("cdb_updates_last_checked", 24 * 3600) then
local f = io.open(cache_file_path, "r")
local data = ""
if f then
@@ -72,9 +72,6 @@ end
local function has_packages_from_cdb()
- pkgmgr.refresh_globals()
- pkgmgr.update_gamelist()
-
for _, content in pairs(pkgmgr.get_all()) do
if pkgmgr.get_contentdb_id(content) then
return true
@@ -127,9 +124,6 @@ function update_detector.get_all()
return {}
end
- pkgmgr.refresh_globals()
- pkgmgr.update_gamelist()
-
local ret = {}
local all_content = pkgmgr.get_all()
for _, content in ipairs(all_content) do
diff --git a/builtin/mainmenu/dlg_delete_content.lua b/builtin/mainmenu/dlg_delete_content.lua
index 1a6178acc..98e15bf01 100644
--- a/builtin/mainmenu/dlg_delete_content.lua
+++ b/builtin/mainmenu/dlg_delete_content.lua
@@ -37,11 +37,7 @@ local function delete_content_buttonhandler(this, fields)
gamedata.errormessage = fgettext_ne("pkgmgr: failed to delete \"$1\"", this.data.content.path)
end
- if this.data.content.type == "game" then
- pkgmgr.update_gamelist()
- else
- pkgmgr.refresh_globals()
- end
+ pkgmgr.reload_by_type(this.data.content.type)
else
gamedata.errormessage = fgettext_ne("pkgmgr: invalid path \"$1\"", this.data.content.path)
end
diff --git a/builtin/mainmenu/dlg_rename_modpack.lua b/builtin/mainmenu/dlg_rename_modpack.lua
index ca76a8cbe..884140eb3 100644
--- a/builtin/mainmenu/dlg_rename_modpack.lua
+++ b/builtin/mainmenu/dlg_rename_modpack.lua
@@ -45,7 +45,7 @@ local function rename_modpack_buttonhandler(this, fields)
local oldpath = this.data.mod.path
local targetpath = this.data.mod.parent_dir .. DIR_DELIM .. fields["te_modpack_name"]
os.rename(oldpath, targetpath)
- pkgmgr.refresh_globals()
+ pkgmgr.reload_global_mods()
pkgmgr.selected_mod = pkgmgr.global_mods:get_current_index(
pkgmgr.global_mods:raw_index_by_uid(fields["te_modpack_name"]))
diff --git a/builtin/mainmenu/settings/components.lua b/builtin/mainmenu/settings/components.lua
index 51cc0c95b..bfe64285c 100644
--- a/builtin/mainmenu/settings/components.lua
+++ b/builtin/mainmenu/settings/components.lua
@@ -306,18 +306,37 @@ function make.flags(setting)
"label[0,0.1;" .. get_label(setting) .. "]",
}
- local value = core.settings:get(setting.name) or setting.default
self.resettable = core.settings:has(setting.name)
checkboxes = {}
- for _, name in ipairs(value:split(",")) do
- name = name:trim()
- if name:sub(1, 2) == "no" then
- checkboxes[name:sub(3)] = false
- elseif name ~= "" then
- checkboxes[name] = true
+ for _, name in ipairs(setting.possible) do
+ checkboxes[name] = false
+ end
+ local function apply_flags(flag_string, what)
+ local prefixed_flags = {}
+ for _, name in ipairs(flag_string:split(",")) do
+ prefixed_flags[name:trim()] = true
+ end
+ for _, name in ipairs(setting.possible) do
+ local enabled = prefixed_flags[name]
+ local disabled = prefixed_flags["no" .. name]
+ if enabled and disabled then
+ core.log("warning", "Flag " .. name .. " in " .. what .. " " ..
+ setting.name .. " both enabled and disabled, ignoring")
+ elseif enabled then
+ checkboxes[name] = true
+ elseif disabled then
+ checkboxes[name] = false
+ end
end
end
+ -- First apply the default, which is necessary since flags
+ -- which are not overridden may be missing from the value.
+ apply_flags(setting.default, "default for setting")
+ local value = core.settings:get(setting.name)
+ if value then
+ apply_flags(value, "setting")
+ end
local columns = math.max(math.floor(avail_w / 2.5), 1)
local column_width = avail_w / columns
@@ -325,18 +344,16 @@ function make.flags(setting)
local y = 0.55
for _, possible in ipairs(setting.possible) do
- if possible:sub(1, 2) ~= "no" then
- if x >= avail_w then
- x = 0
- y = y + 0.5
- end
-
- local is_checked = checkboxes[possible]
- fs[#fs + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
- x, y, setting.name .. "_" .. possible,
- core.formspec_escape(possible), tostring(is_checked))
- x = x + column_width
+ if x >= avail_w then
+ x = 0
+ y = y + 0.5
end
+
+ local is_checked = checkboxes[possible]
+ fs[#fs + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
+ x, y, setting.name .. "_" .. possible,
+ core.formspec_escape(possible), tostring(is_checked))
+ x = x + column_width
end
return table.concat(fs, ""), y + 0.25
@@ -355,12 +372,10 @@ function make.flags(setting)
if changed then
local values = {}
for _, name in ipairs(setting.possible) do
- if name:sub(1, 2) ~= "no" then
- if checkboxes[name] then
- table.insert(values, name)
- else
- table.insert(values, "no" .. name)
- end
+ if checkboxes[name] then
+ table.insert(values, name)
+ else
+ table.insert(values, "no" .. name)
end
end
diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua
index d6bbf2570..73a72769b 100644
--- a/builtin/mainmenu/settings/dlg_settings.lua
+++ b/builtin/mainmenu/settings/dlg_settings.lua
@@ -22,16 +22,27 @@ local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "shadows_component.lua")
-
-local full_settings = settingtypes.parse_config_file(false, true)
+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 = {}
local page_by_id = {}
local filtered_pages = all_pages
local filtered_page_by_id = page_by_id
+
+local function get_setting_info(name)
+ for _, entry in ipairs(full_settings) do
+ if entry.type ~= "category" and entry.name == name then
+ return entry
+ end
+ end
+
+ return nil
+end
+
+
local function add_page(page)
assert(type(page.id) == "string")
assert(type(page.title) == "string")
@@ -46,49 +57,6 @@ local function add_page(page)
end
-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,
-}
-
-
-add_page({
- id = "accessibility",
- title = fgettext_ne("Accessibility"),
- content = {
- "language",
- { heading = fgettext_ne("General") },
- "font_size",
- "chat_font_size",
- "gui_scaling",
- "hud_scaling",
- "show_nametag_backgrounds",
- { heading = fgettext_ne("Chat") },
- "console_height",
- "console_alpha",
- "console_color",
- { heading = fgettext_ne("Controls") },
- "autojump",
- "safe_dig_and_place",
- { heading = fgettext_ne("Movement") },
- "arm_inertia",
- "view_bobbing_amount",
- "fall_bobbing_amount",
- },
-})
-
-
local function load_settingtypes()
local page = nil
local section = nil
@@ -129,94 +97,134 @@ local function load_settingtypes()
end
end
end
-load_settingtypes()
-table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
-do
- local content = page_by_id.graphics_and_audio_shaders.content
- local idx = table.indexof(content, "enable_dynamic_shadows")
- table.insert(content, idx, shadows_component)
-end
+local function load()
+ if loaded then
+ return
+ 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,
+ }
-local function get_setting_info(name)
- for _, entry in ipairs(full_settings) do
- if entry.type ~= "category" and entry.name == name then
- return entry
- end
+ add_page({
+ id = "accessibility",
+ title = fgettext_ne("Accessibility"),
+ content = {
+ "language",
+ { heading = fgettext_ne("General") },
+ "font_size",
+ "chat_font_size",
+ "gui_scaling",
+ "hud_scaling",
+ "show_nametag_backgrounds",
+ { heading = fgettext_ne("Chat") },
+ "console_height",
+ "console_alpha",
+ "console_color",
+ { heading = fgettext_ne("Controls") },
+ "autojump",
+ "safe_dig_and_place",
+ { 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)
+ do
+ local content = page_by_id.graphics_and_audio_shaders.content
+ local idx = table.indexof(content, "enable_dynamic_shadows")
+ table.insert(content, idx, shadows_component)
end
- return nil
+ -- These must not be translated, as they need to show in the local
+ -- language no matter the user's current language.
+ -- This list must be kept in sync with src/unsupported_language_list.txt.
+ get_setting_info("language").option_labels = {
+ [""] = fgettext_ne("(Use system language)"),
+ --ar = " [ar]", blacklisted
+ be = "Беларуская [be]",
+ bg = "Български [bg]",
+ ca = "Català [ca]",
+ cs = "Česky [cs]",
+ cy = "Cymraeg [cy]",
+ da = "Dansk [da]",
+ de = "Deutsch [de]",
+ --dv = " [dv]", blacklisted
+ el = "Ελληνικά [el]",
+ en = "English [en]",
+ eo = "Esperanto [eo]",
+ es = "Español [es]",
+ et = "Eesti [et]",
+ eu = "Euskara [eu]",
+ fi = "Suomi [fi]",
+ fil = "Wikang Filipino [fil]",
+ fr = "Français [fr]",
+ gd = "Gàidhlig [gd]",
+ gl = "Galego [gl]",
+ --he = " [he]", blacklisted
+ --hi = " [hi]", blacklisted
+ hu = "Magyar [hu]",
+ id = "Bahasa Indonesia [id]",
+ it = "Italiano [it]",
+ ja = "日本語 [ja]",
+ jbo = "Lojban [jbo]",
+ kk = "Қазақша [kk]",
+ --kn = " [kn]", blacklisted
+ ko = "한국어 [ko]",
+ ky = "Kırgızca / Кыргызча [ky]",
+ lt = "Lietuvių [lt]",
+ lv = "Latviešu [lv]",
+ mn = "Монгол [mn]",
+ mr = "मराठी [mr]",
+ ms = "Bahasa Melayu [ms]",
+ --ms_Arab = " [ms_Arab]", blacklisted
+ nb = "Norsk Bokmål [nb]",
+ nl = "Nederlands [nl]",
+ nn = "Norsk Nynorsk [nn]",
+ oc = "Occitan [oc]",
+ pl = "Polski [pl]",
+ pt = "Português [pt]",
+ pt_BR = "Português do Brasil [pt_BR]",
+ ro = "Română [ro]",
+ ru = "Русский [ru]",
+ sk = "Slovenčina [sk]",
+ sl = "Slovenščina [sl]",
+ sr_Cyrl = "Српски [sr_Cyrl]",
+ sr_Latn = "Srpski (Latinica) [sr_Latn]",
+ sv = "Svenska [sv]",
+ sw = "Kiswahili [sw]",
+ --th = " [th]", blacklisted
+ tr = "Türkçe [tr]",
+ tt = "Tatarça [tt]",
+ uk = "Українська [uk]",
+ vi = "Tiếng Việt [vi]",
+ zh_CN = "中文 (简体) [zh_CN]",
+ zh_TW = "正體中文 (繁體) [zh_TW]",
+ }
end
--- These must not be translated, as they need to show in the local
--- language no matter the user's current language.
--- This list must be kept in sync with src/unsupported_language_list.txt.
-get_setting_info("language").option_labels = {
- [""] = fgettext_ne("(Use system language)"),
- --ar = " [ar]", blacklisted
- be = "Беларуская [be]",
- bg = "Български [bg]",
- ca = "Català [ca]",
- cs = "Česky [cs]",
- cy = "Cymraeg [cy]",
- da = "Dansk [da]",
- de = "Deutsch [de]",
- --dv = " [dv]", blacklisted
- el = "Ελληνικά [el]",
- en = "English [en]",
- eo = "Esperanto [eo]",
- es = "Español [es]",
- et = "Eesti [et]",
- eu = "Euskara [eu]",
- fi = "Suomi [fi]",
- fil = "Wikang Filipino [fil]",
- fr = "Français [fr]",
- gd = "Gàidhlig [gd]",
- gl = "Galego [gl]",
- --he = " [he]", blacklisted
- --hi = " [hi]", blacklisted
- hu = "Magyar [hu]",
- id = "Bahasa Indonesia [id]",
- it = "Italiano [it]",
- ja = "日本語 [ja]",
- jbo = "Lojban [jbo]",
- kk = "Қазақша [kk]",
- --kn = " [kn]", blacklisted
- ko = "한국어 [ko]",
- ky = "Kırgızca / Кыргызча [ky]",
- lt = "Lietuvių [lt]",
- lv = "Latviešu [lv]",
- mn = "Монгол [mn]",
- mr = "मराठी [mr]",
- ms = "Bahasa Melayu [ms]",
- --ms_Arab = " [ms_Arab]", blacklisted
- nb = "Norsk Bokmål [nb]",
- nl = "Nederlands [nl]",
- nn = "Norsk Nynorsk [nn]",
- oc = "Occitan [oc]",
- pl = "Polski [pl]",
- pt = "Português [pt]",
- pt_BR = "Português do Brasil [pt_BR]",
- ro = "Română [ro]",
- ru = "Русский [ru]",
- sk = "Slovenčina [sk]",
- sl = "Slovenščina [sl]",
- sr_Cyrl = "Српски [sr_Cyrl]",
- sr_Latn = "Srpski (Latinica) [sr_Latn]",
- sv = "Svenska [sv]",
- sw = "Kiswahili [sw]",
- --th = " [th]", blacklisted
- tr = "Türkçe [tr]",
- tt = "Tatarça [tt]",
- uk = "Українська [uk]",
- vi = "Tiếng Việt [vi]",
- zh_CN = "中文 (简体) [zh_CN]",
- zh_TW = "正體中文 (繁體) [zh_TW]",
-}
-
-
-- See if setting matches keywords
local function get_setting_match_weight(entry, query_keywords)
local setting_score = 0
@@ -723,12 +731,18 @@ local function eventhandler(event)
mm_game_theme.set_engine(true)
return true
end
+ if event == "FullscreenChange" then
+ -- Refresh the formspec to keep the fullscreen checkbox up to date.
+ ui.update()
+ return true
+ end
return false
end
function create_settings_dlg()
+ load()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
dlg.data.page_id = update_filtered_pages("")
diff --git a/builtin/mainmenu/settings/settingtypes.lua b/builtin/mainmenu/settings/settingtypes.lua
index 1174c9b76..eacd96d09 100644
--- a/builtin/mainmenu/settings/settingtypes.lua
+++ b/builtin/mainmenu/settings/settingtypes.lua
@@ -439,9 +439,9 @@ function settingtypes.parse_config_file(read_all, parse_mods)
end
-- Parse mods
+ pkgmgr.load_all()
local mods_category_initialized = false
- local mods = {}
- pkgmgr.get_mods(core.get_modpath(), "mods", mods)
+ local mods = pkgmgr.global_mods:get_list()
table.sort(mods, function(a, b) return a.name < b.name end)
for _, mod in ipairs(mods) do
diff --git a/builtin/mainmenu/tab_about.lua b/builtin/mainmenu/tab_about.lua
index 1805acc81..d798b5b09 100644
--- a/builtin/mainmenu/tab_about.lua
+++ b/builtin/mainmenu/tab_about.lua
@@ -30,6 +30,7 @@ local core_developers = {
"Desour/DS",
"srifqi",
"Gregor Parzefall (grorp)",
+ "Lars Müller (luatic)",
}
-- currently only https://github.com/orgs/minetest/teams/triagers/members
@@ -43,19 +44,24 @@ local core_team = {
-- For updating active/previous contributors, see the script in ./util/gather_git_credits.py
local active_contributors = {
- "Wuzzy [Features, translations, documentation]",
- "numzero [Optimizations, work on OpenGL driver]",
- "ROllerozxa [Bugfixes, Mainmenu]",
- "Lars Müller [Bugfixes]",
- "AFCMS [Documentation]",
- "savilli [Bugfixes]",
- "fluxionary [Bugfixes]",
- "Bradley Pierce (Thresher) [Documentation]",
- "Stvk imension [Android]",
- "JosiahWI [Code cleanups]",
- "OgelGames [UI, Bugfixes]",
- "ndren [Bugfixes]",
- "Abdou-31 [Documentation]",
+ "cx384",
+ "numzero",
+ "AFCMS",
+ "sfence",
+ "Wuzzy",
+ "ROllerozxa",
+ "JosiahWI",
+ "OgelGames",
+ "David Heidelberg",
+ "1F616EMO",
+ "HybridDog",
+ "Bradley Pierce (Thresher)",
+ "savilli",
+ "Stvk imension",
+ "y5nw",
+ "chmodsayshello",
+ "jordan4ibanez",
+ "superfloh247",
}
local previous_core_developers = {
diff --git a/builtin/mainmenu/tab_content.lua b/builtin/mainmenu/tab_content.lua
index 18ff9cc8a..b38f12884 100644
--- a/builtin/mainmenu/tab_content.lua
+++ b/builtin/mainmenu/tab_content.lua
@@ -29,16 +29,11 @@ end
local packages_raw, packages
local function update_packages()
- if not pkgmgr.global_mods then
- pkgmgr.refresh_globals()
- end
- if not pkgmgr.games then
- pkgmgr.update_gamelist()
- end
+ pkgmgr.load_all()
packages_raw = {}
table.insert_all(packages_raw, pkgmgr.games)
- table.insert_all(packages_raw, pkgmgr.get_texture_packs())
+ table.insert_all(packages_raw, pkgmgr.texture_packs)
table.insert_all(packages_raw, pkgmgr.global_mods:get_list())
local function get_data()
@@ -207,6 +202,7 @@ local function handle_doubleclick(pkg)
core.settings:set("texture_path", pkg.path)
end
packages = nil
+ pkgmgr.reload_texture_packs()
mm_game_theme.init()
mm_game_theme.set_engine()
@@ -271,6 +267,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
core.settings:set("texture_path", txp_path)
packages = nil
+ pkgmgr.reload_texture_packs()
mm_game_theme.init()
mm_game_theme.set_engine()
@@ -283,7 +280,7 @@ end
return {
name = "content",
caption = function()
- local update_count = update_detector.get_count()
+ local update_count = core.settings:get_bool("contentdb_enable_updates_indicator") and update_detector.get_count() or 0
if update_count == 0 then
return fgettext("Content")
else
diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua
index 98ba17299..1ed08d825 100644
--- a/builtin/mainmenu/tab_local.lua
+++ b/builtin/mainmenu/tab_local.lua
@@ -156,10 +156,18 @@ local function get_formspec(tabview, name, tabdata)
-- Point the player to ContentDB when no games are found
if #pkgmgr.games == 0 then
+ local W = tabview.width
+ local H = tabview.height
+
+ local hypertext = "" ..
+ fgettext_ne("Minetest is a game-creation platform that allows you to play many different games.") .. "\n" ..
+ fgettext_ne("Minetest doesn't come with a game by default.") .. " " ..
+ fgettext_ne("You need to install a game before you can create a world.")
+
+ local button_y = H * 2/3 - 0.6
return table.concat({
- "style[label_button;border=false]",
- "button[2.75,1.5;10,1;label_button;", fgettext("You have no games installed."), "]",
- "button[5.25,3.5;5,1.2;game_open_cdb;", fgettext("Install a game"), "]"})
+ "hypertext[0.375,0;", W - 2*0.375, ",", button_y, ";ht;", core.formspec_escape(hypertext), "]",
+ "button[5.25,", button_y, ";5,1.2;game_open_cdb;", fgettext("Install a game"), "]"})
end
local retval = ""
diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt
index 4feb1274a..81dcaafb5 100644
--- a/builtin/settingtypes.txt
+++ b/builtin/settingtypes.txt
@@ -132,11 +132,11 @@ always_fly_fast (Always fly fast) bool true
# the place button.
#
# Requires: keyboard_mouse
-repeat_place_time (Place repetition interval) float 0.25 0.15 2.0
+repeat_place_time (Place repetition interval) float 0.25 0.16 2.0
# The minimum time in seconds it takes between digging nodes when holding
# the dig button.
-repeat_dig_time (Dig repetition interval) float 0.15 0.15 2.0
+repeat_dig_time (Minimum dig repetition interval) float 0.0 0.0 2.0
# Automatically jump up single-node obstacles.
autojump (Automatic jumping) bool false
@@ -171,6 +171,8 @@ invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
[*Touchscreen]
# Enables touchscreen mode, allowing you to play the game with a touchscreen.
+#
+# Requires: !android
enable_touch (Enable touchscreen) bool true
# Touchscreen sensitivity multiplier.
@@ -206,7 +208,7 @@ fixed_virtual_joystick (Fixed virtual joystick) bool false
# Requires: touchscreen_gui
virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false
-# The gesture for for punching players/entities.
+# The gesture for punching players/entities.
# This can be overridden by games and mods.
#
# * short_tap
@@ -283,8 +285,8 @@ undersampling (Undersampling) int 1 1 8
[**Graphics Effects]
-# Makes all liquids opaque
-opaque_water (Opaque liquids) bool false
+# Allows liquids to be translucent.
+translucent_liquids (Translucent liquids) bool true
# Leaves style:
# - Fancy: all faces visible
@@ -782,6 +784,10 @@ chat_font_size (Chat font size) int 0 0 72
# The URL for the content repository
contentdb_url (ContentDB URL) string https://content.minetest.net
+# If enabled and you have ContentDB packages installed, Minetest may contact ContentDB to
+# check for package updates when opening the mainmenu.
+contentdb_enable_updates_indicator (Enable updates available indicator on content tab) bool true
+
# Comma-separated list of flags to hide in the content repository.
# "nonfree" can be used to hide packages which do not qualify as 'free software',
# as defined by the Free Software Foundation.
@@ -809,7 +815,7 @@ serverlist_url (Serverlist URL) string servers.minetest.net
# If disabled, new accounts will be registered automatically when logging in.
enable_split_login_register (Enable split login/register) bool true
-# URL to JSON file which provides information about the newest Minetest release
+# URL to JSON file which provides information about the newest Minetest release.
# If this is empty the engine will never check for updates.
update_information_url (Update information URL) string https://www.minetest.net/release_info.json
@@ -1778,6 +1784,9 @@ deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,erro
# Enable random user input (only used for testing).
random_input (Random input) bool false
+# Enable random mod loading (mainly used for testing).
+random_mod_load_order (Random mod load order) bool false
+
# Enable mod channels support.
enable_mod_channels (Mod channels) bool false
@@ -2202,9 +2211,6 @@ curl_file_download_timeout (cURL file download timeout) int 300000 5000 21474836
[**Miscellaneous]
-# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.
-screen_dpi (DPI) int 72 1
-
# Adjust the detected display density, used for scaling UI elements.
display_density_factor (Display Density Scaling Factor) float 1 0.5 5.0
@@ -2414,6 +2420,9 @@ 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
@@ -2537,6 +2546,9 @@ 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
diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl
index 3b0d4c844..0a73aa393 100644
--- a/client/shaders/second_stage/opengl_fragment.glsl
+++ b/client/shaders/second_stage/opengl_fragment.glsl
@@ -61,7 +61,8 @@ vec4 applyBloom(vec4 color, vec2 uv)
equation used: ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
*/
-vec3 uncharted2Tonemap(vec3 x)
+// see https://github.com/minetest/minetest/pull/14688
+highp vec3 uncharted2Tonemap(highp vec3 x)
{
return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
}
diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md
index 99f341de9..22f198871 100644
--- a/doc/client_lua_api.md
+++ b/doc/client_lua_api.md
@@ -314,7 +314,8 @@ Minetest namespace reference
Call these functions only at load time!
* `minetest.register_globalstep(function(dtime))`
- * Called every client environment step, usually interval of 0.1s
+ * Called every client environment step
+ * `dtime` is the time since last execution in seconds.
* `minetest.register_on_mods_loaded(function())`
* Called just after mods have finished loading.
* `minetest.register_on_shutdown(function())`
@@ -884,7 +885,7 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
"node1",
"node2"
},
- post_effect_color = Color, -- Color overlayed on the screen when the player is in the node
+ post_effect_color = Color, -- Color overlaid on the screen when the player is in the node
leveled = number, -- Max level for node
sunlight_propogates = bool, -- Whether light passes through the block
light_source = number, -- Light emitted by the block
diff --git a/doc/compiling/README.md b/doc/compiling/README.md
index 3652c2d74..a1ab1ebbd 100644
--- a/doc/compiling/README.md
+++ b/doc/compiling/README.md
@@ -20,6 +20,8 @@ General options and their default values:
SemiDebug - Partially optimized debug build
RelWithDebInfo - Release build with debug information
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
+ 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)
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 3bb796a16..03a85a32c 100644
--- a/doc/compiling/linux.md
+++ b/doc/compiling/linux.md
@@ -29,7 +29,7 @@ For Fedora users:
For openSUSE users:
- sudo zypper install gcc cmake libjpeg8-devel libpng16-devel openal-soft-devel libcurl-devel sqlite3-devel luajit-devel libzstd-devel Mesa-libGL-devel libvorbis-devel freetype2-devel SDL2-devel
+ sudo zypper install gcc gcc-c++ cmake libjpeg8-devel libpng16-devel openal-soft-devel libcurl-devel sqlite3-devel luajit-devel libzstd-devel Mesa-libGL-devel libvorbis-devel freetype2-devel SDL2-devel
For Arch users:
@@ -41,7 +41,7 @@ For Alpine users:
For Void users:
- sudo xbps-install cmake libpng-devel jpeg-devel mesa sqlite-devel libogg-devel libvorbis-devel libopenal-devel libcurl-devel freetype-devel zlib-devel gmp-devel jsoncpp-devel LuaJIT-devel libzstd-devel gettext SDL2-devel
+ sudo xbps-install cmake libpng-devel jpeg-devel mesa sqlite-devel libogg-devel libvorbis-devel libopenal-devel libcurl-devel freetype-devel zlib-devel gmp-devel jsoncpp-devel LuaJIT-devel zstd libzstd-devel gettext SDL2-devel
## Download
diff --git a/doc/ides/images/jetbrains_cmake_profiles.png b/doc/ides/images/jetbrains_cmake_profiles.png
new file mode 100644
index 000000000..f0e969a6b
Binary files /dev/null and b/doc/ides/images/jetbrains_cmake_profiles.png differ
diff --git a/doc/ides/images/jetbrains_ide.png b/doc/ides/images/jetbrains_ide.png
new file mode 100644
index 000000000..8b9b11c0f
Binary files /dev/null and b/doc/ides/images/jetbrains_ide.png differ
diff --git a/doc/ides/images/jetbrains_notification_profiles.png b/doc/ides/images/jetbrains_notification_profiles.png
new file mode 100644
index 000000000..8aeb13073
Binary files /dev/null and b/doc/ides/images/jetbrains_notification_profiles.png differ
diff --git a/doc/ides/images/jetbrains_open_project_wizard_profiles.png b/doc/ides/images/jetbrains_open_project_wizard_profiles.png
new file mode 100644
index 000000000..dafc2c0c4
Binary files /dev/null and b/doc/ides/images/jetbrains_open_project_wizard_profiles.png differ
diff --git a/doc/ides/images/jetbrains_open_project_wizard_windows_cmake.png b/doc/ides/images/jetbrains_open_project_wizard_windows_cmake.png
new file mode 100644
index 000000000..2366908ec
Binary files /dev/null and b/doc/ides/images/jetbrains_open_project_wizard_windows_cmake.png differ
diff --git a/doc/ides/images/jetbrains_open_project_wizard_windows_compiler.png b/doc/ides/images/jetbrains_open_project_wizard_windows_compiler.png
new file mode 100644
index 000000000..928e0788f
Binary files /dev/null and b/doc/ides/images/jetbrains_open_project_wizard_windows_compiler.png differ
diff --git a/doc/ides/images/jetbrains_vcpkg.png b/doc/ides/images/jetbrains_vcpkg.png
new file mode 100644
index 000000000..31ab265ce
Binary files /dev/null and b/doc/ides/images/jetbrains_vcpkg.png differ
diff --git a/doc/ides/images/vscode_cmake_preset_selection.png b/doc/ides/images/vscode_cmake_preset_selection.png
new file mode 100644
index 000000000..f73ede5be
Binary files /dev/null and b/doc/ides/images/vscode_cmake_preset_selection.png differ
diff --git a/doc/ides/images/vscode_toolbar.png b/doc/ides/images/vscode_toolbar.png
new file mode 100644
index 000000000..13bfcbbde
Binary files /dev/null and b/doc/ides/images/vscode_toolbar.png differ
diff --git a/doc/ides/jetbrains.md b/doc/ides/jetbrains.md
new file mode 100644
index 000000000..09a95b92f
--- /dev/null
+++ b/doc/ides/jetbrains.md
@@ -0,0 +1,81 @@
+# [Jetbrains IntellIJ CLion](https://www.jetbrains.com/clion)
+
+## Linux
+
+When opening the folder for the first time, select `Open as CMake project` if the IDE ask you between Make and CMake.
+
+The IDE will open the folder and display the open project wizard:
+
+
+
+CLion try to determine a base configuration, but Minetest define it's own presets for easier setup. So you need to
+delete the `Debug` profile with the `-` sign and close the dialog.
+
+You should notice a notification telling you 4 presets have been loaded in the bottom right corner.
+
+
+
+Clicking on the `View` link or going to `Settings > Build, Execution, Deployment > CMake` you should get a window
+similar to the Open Project Wizard, but with the readonly presets listed.
+
+
+
+By default, none of the presets are enabled. You can select them and enable the ones you want. Keep in mind that
+triggering the CMake project reload (VCS updates, config changes, etc) will reload all the enabled profiles, so unless
+you need the other ones you can enable just `Debug` and `Release`.
+
+If none of the availlable profiles fit your needs, you can create a `CMakeUserPresets.json` file, edit it by hand and
+CLion will load the presets in this window. But the easiest solution is to create an editable copy of one of the availlable
+presets with the `Copy` button icon.
+
+After these steps you should get an IDE like this.
+
+On the main toolbar at the top right, you have a dropdown for selecting the CMake profile to use for the build. You have another dropdown next to it to select the build target; by default the `minetest` executable will be selected, but you may also have to use `IrrlichtMt` for building just the library .
+
+
+
+You can rightclick the topbar to change the project icon and color, for fancier looking IDE.
+
+## Windows
+
+Under Windows, the recommended compiler is the [Visual Studio](https://visualstudio.microsoft.com) compiler.
+
+From the Visual Studio installer, you need to install the `Desktop development with C++` Workload. CMake is already
+bundled in CLion.
+
+By default, CLion have a MinGW compiler bundled, so if you want to use Visual Studio, you need to configure it as the default compiler.
+
+CLion may ask you in the open project wisard for your compilers, with MinGW and Visual Studio if you have installed it predefined. You can use the arrows to make `Visual Studio` the default.
+
+If not you can go to `Settings > Build, Execution, Deployment > Toolchains` to change it.
+
+
+
+
+
+
+Then, the process is roughly similar to Linux, you just need to pick `Visual Studio` as toolchain.
+
+
+
+
+
+
+[Vcpkg](https://vcpkg.io) is the recommended way of installing Minetest dependencies.
+
+You need to let CLion know about a `vcpkg` installation to let the bundled CMake use the dependencies seamlessly and get
+IDE integration. (Require CLion 2023 or later)
+
+Go to `View > Tool Windows > Vcpkg` and click the add button. I will open a popup allowing you to add a Vcpkg
+installation. By default it will download a new one that you can use to install your dependencies, but if you already
+have one installed or you do not plan on using CLion only then install Vcpkg by hand and select your installation
+directory. Don't forget to check `Add vcpkg installation to existing CMake profiles`. If you haven't already installed
+Minetest dependencies in your vcpkg installation, you can do it right from CLion's Vcpkg tool window.
+
+
+
+Reloading the CMake project (should happen automatically, or display a notification for outdated CMake project) will now
+load the dependencies.
+
+[More infos on Vcpkg integration in CLion](https://blog.jetbrains.com/clion/2023/01/support-for-vcpkg-in-clion)
+
diff --git a/doc/ides/visual_studio.md b/doc/ides/visual_studio.md
new file mode 100644
index 000000000..a08a58ea2
--- /dev/null
+++ b/doc/ides/visual_studio.md
@@ -0,0 +1,7 @@
+# [Visual Studio](https://visualstudio.microsoft.com)
+
+From the Visual Studio installer, you need to install the `Desktop development with C++` Workload. You need to make sure the `C++ CMake tools for Windows` component is included in the installation details panel.
+
+You need to install [Vcpkg](https://vcpkg.io) and install Minetest dependencies as stated in the compilation documentation.
+
+For the packages to be discoverable and used by Visual Studio, you need to run `vcpkg integrate install`.
diff --git a/doc/ides/vscode.md b/doc/ides/vscode.md
new file mode 100644
index 000000000..6814a7a88
--- /dev/null
+++ b/doc/ides/vscode.md
@@ -0,0 +1,51 @@
+# [Visual Studio Code](https://code.visualstudio.com)
+
+VSCode suppport for C/C++ and CMake is provided by
+the [Microsoft C/C++ extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools-extension-pack).
+You can install it from the VSCode extensions tab.
+
+If you use a unofficial VSCode distribution like [VSCodium](https://vscodium.com), you will need to install the
+extension pack manually by downloading the VSIX files and going to `Extensions > ... > Install from VSIX`.
+
+CMake support for VSCode uses CMake presets provided by the project by default.
+
+When you open the Minetest folder with VSCode, you should get a quick pick asking you for the default preset.
+
+
+
+You can use the bottom bar to change the CMake profile, change the build target, build, run and debug (running/debugging doesn't build first).
+
+
+
+Like most of the VSCode experience, it may be faster to use commands directly (most of the VSCode UI just trigger commands).
+
+| Command Name | Usecase |
+|----------------------------------|----------------------------------|
+| `CMake: Select Configure Preset` | Change the current CMake profile |
+| `CMake: Set Build Target` | Change the current build target |
+| `CMake: Build` | Build current target |
+| `CMake: Clean` | Clean build files |
+| `CMake: Run Without Debugging` | Run selected run target |
+| `CMake: Debug` | Debug selected run target |
+
+## Windows
+
+Under Windows, the recommended compiler is the [Visual Studio](https://visualstudio.microsoft.com) compiler.
+
+From the Visual Studio installer, you need to install the `Desktop development with C++` Workload.
+
+[Vcpkg](https://vcpkg.io) is the recommended way of installing Minetest dependencies.
+
+Follow the official documentation to install it and install Minetest dependencies as explained in [Windows compilation process](../compiling/windows.md).
+
+You need to let CMake know about the `vcpkg` installation in VSCode.
+
+Modify your `.vscode/settings.json`:
+
+```json
+{
+ "cmake.configureSettings": {
+ "CMAKE_TOOLCHAIN_FILE": "C:/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake"
+ }
+}
+```
diff --git a/doc/lua_api.md b/doc/lua_api.md
index 43a103c3f..81294d365 100644
--- a/doc/lua_api.md
+++ b/doc/lua_api.md
@@ -4,6 +4,7 @@ Minetest Lua Modding API Reference
* More information at
* Developer Wiki:
* (Unofficial) Minetest Modding Book by rubenwardy:
+* Modding tools:
Introduction
------------
@@ -63,6 +64,8 @@ The game directory can contain the following files:
* `name`: (Deprecated) same as title.
* `description`: Short description to be shown in the content tab.
See [Translating content meta](#translating-content-meta).
+ * `first_mod`: Use this to specify the mod that must be loaded before any other mod.
+ * `last_mod`: Use this to specify the mod that must be loaded after all other mods
* `allowed_mapgens = `
e.g. `allowed_mapgens = v5,v6,flat`
Mapgens not in this list are removed from the list of mapgens for the
@@ -454,6 +457,10 @@ i.e. without gamma-correction.
Textures can be overlaid by putting a `^` between them.
+Warning: If the lower and upper pixels are both semi-transparent, this operation
+does *not* do alpha blending, and it is *not* associative. Otherwise it does
+alpha blending in srgb color space.
+
Example:
default_dirt.png^default_grass_side.png
@@ -1497,7 +1504,7 @@ Look for examples in `games/devtest` or `games/minetest_game`.
* For supported model formats see Irrlicht engine documentation.
* `plantlike_rooted`
* Enables underwater `plantlike` without air bubbles around the nodes.
- * Consists of a base cube at the co-ordinates of the node plus a
+ * Consists of a base cube at the coordinates of the node plus a
`plantlike` extension above
* If `paramtype2="leveled", the `plantlike` extension has a height
of `param2 / 16` nodes, otherwise it's the height of 1 node
@@ -2350,9 +2357,12 @@ for this group, and unable to dig the rating `1`, which is the toughest.
Unless there is a matching group that enables digging otherwise.
If the result digging time is 0, a delay of 0.15 seconds is added between
-digging nodes; If the player releases LMB after digging, this delay is set to 0,
+digging nodes. If the player releases LMB after digging, this delay is set to 0,
i.e. players can more quickly click the nodes away instead of holding LMB.
+This extra delay is not applied in case of a digging time between 0 and 0.15,
+so a digging time of 0.01 is actually faster than a digging time of 0.
+
### Damage groups
List of damage for groups of entities. See [Entity damage mechanism].
@@ -3068,7 +3078,7 @@ Elements
### `textlist[,;,;;,,...,]`
* Scrollable item list showing arbitrary text elements
-* `name` fieldname sent to server on doubleclick value is current selected
+* `name` fieldname sent to server on double-click value is current selected
element.
* `listelements` can be prepended by #color in hexadecimal format RRGGBB
(only).
@@ -3077,7 +3087,7 @@ Elements
### `textlist[,;,;;,,...,;;]`
* Scrollable itemlist showing arbitrary text elements
-* `name` fieldname sent to server on doubleclick value is current selected
+* `name` fieldname sent to server on double-click value is current selected
element.
* `listelements` can be prepended by #RRGGBB (only) in hexadecimal format
* if you want a listelement to start with "#" write "##"
@@ -3214,7 +3224,7 @@ Elements
* Show scrollable table using options defined by the previous `tableoptions[]`
* Displays cells as defined by the previous `tablecolumns[]`
-* `name`: fieldname sent to server on row select or doubleclick
+* `name`: fieldname sent to server on row select or double-click
* `cell 1`...`cell n`: cell contents given in row-major order
* `selected idx`: index of row to be selected within table (first row = `1`)
* See also `minetest.explode_table_event`
@@ -3243,7 +3253,7 @@ Elements
* Types: `text`, `image`, `color`, `indent`, `tree`
* `text`: show cell contents as text
* `image`: cell contents are an image index, use column options to define
- images.
+ images. images are scaled down to fit the row height if necessary.
* `color`: cell contents are a ColorString and define color of following
cell.
* `indent`: cell contents are a number and define indentation of following
@@ -3264,7 +3274,7 @@ Elements
* `0=` sets image for image index 0
* `1=` sets image for image index 1
* `2=` sets image for image index 2
- * and so on; defined indices need not be contiguous empty or
+ * and so on; defined indices need not be contiguous. empty or
non-numeric cells are treated as `0`.
* `color` column options:
* `span=`: number of following columns to affect
@@ -3535,10 +3545,12 @@ Markup language used in `hypertext[]` elements uses tags that look like HTML tag
The markup language is currently unstable and subject to change. Use with caution.
Some tags can enclose text, they open with `` and close with ``.
Tags can have attributes, in that case, attributes are in the opening tag in
-form of a key/value separated with equal signs. Attribute values should not be quoted.
+form of a key/value separated with equal signs.
+Attribute values should be quoted using either " or '.
-If you want to insert a literal greater-than sign or a backslash into the text,
-you must escape it by preceding it with a backslash.
+If you want to insert a literal greater-than, less-than, or a backslash into the text,
+you must escape it by preceding it with a backslash. In a quoted attribute value, you
+can insert a literal quote mark by preceding it with a backslash.
These are the technically basic tags but see below for usual tags. Base tags are:
@@ -3898,6 +3910,7 @@ Operators
---------
Operators can be used if all of the involved vectors have metatables:
+
* `v1 == v2`:
* Returns whether `v1` and `v2` are identical.
* `-v`:
@@ -4003,8 +4016,9 @@ Helper functions
* X1, Y1, ... Z2 are coordinates
* `relative_to`: Optional. If set to a position, each coordinate
can use the tilde notation for relative positions
- * Tilde notation: "~": Relative coordinate
- "~": Relative coordinate plus
+ * Tilde notation
+ * `"~"`: Relative coordinate
+ * `"~"`: Relative coordinate plus ``
* Example: `minetest.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})`
returns `{x=1,y=2,z=3}, {x=15,y=5,z=10}`
* `minetest.formspec_escape(string)`: returns a string
@@ -4075,9 +4089,9 @@ Translations
Texts can be translated client-side with the help of `minetest.translate` and
translation files.
-Consider using the script `util/mod_translation_updater.py` in the Minetest
-repository to generate and update translation files automatically from the Lua
-sources. See `util/README_mod_translation_updater.md` for an explanation.
+Consider using the script `mod_translation_updater.py` in the Minetest
+[modtools](https://github.com/minetest/modtools) repository to generate and
+update translation files automatically from the Lua sources.
Translating a string
--------------------
@@ -4241,7 +4255,7 @@ Perlin noise
============
Perlin noise creates a continuously-varying value depending on the input values.
-Usually in Minetest the input values are either 2D or 3D co-ordinates in nodes.
+Usually in Minetest the input values are either 2D or 3D coordinates in nodes.
The result is used during map generation to create the terrain shape, vary heat
and humidity to distribute biomes, vary the density of decorations or vary the
structure of ores.
@@ -4342,6 +4356,8 @@ the previous octave.
This may need to be tuned when altering `lacunarity`; when doing so consider
that a common medium value is 1 / lacunarity.
+Instead of `persistence`, the key `persist` may be used to the same effect.
+
### `lacunarity`
Each additional octave has a 'wavelength' that is the 'wavelength' of the
@@ -4502,7 +4518,7 @@ computationally expensive than any other ore.
Creates a single undulating ore stratum that is continuous across mapchunk
borders and horizontally spans the world.
-The 2D perlin noise described by `noise_params` defines the Y co-ordinate of
+The 2D perlin noise described by `noise_params` defines the Y coordinate of
the stratum midpoint. The 2D perlin noise described by `np_stratum_thickness`
defines the stratum's vertical thickness (in units of nodes). Due to being
continuous across mapchunk borders the stratum's vertical thickness is
@@ -5124,6 +5140,9 @@ Collision info passed to `on_step` (`moveresult` argument):
axis = string, -- "x", "y" or "z"
node_pos = vector, -- if type is "node"
object = ObjectRef, -- if type is "object"
+ -- The position of the entity when the collision occurred.
+ -- Available since feature "moveresult_new_pos".
+ new_pos = vector,
old_velocity = vector,
new_velocity = vector,
},
@@ -5432,11 +5451,15 @@ Utilities
dynamic_add_media_filepath = true,
-- L-system decoration type (5.9.0)
lsystem_decoration_type = true,
- -- Overrideable pointing range using the itemstack meta key `"range"` (5.9.0)
+ -- Overridable pointing range using the itemstack meta key `"range"` (5.9.0)
item_meta_range = true,
-- Allow passing an optional "actor" ObjectRef to the following functions:
-- minetest.place_node, minetest.dig_node, minetest.punch_node (5.9.0)
node_interaction_actor = true,
+ -- "new_pos" field in entity moveresult (5.9.0)
+ moveresult_new_pos = true,
+ -- Allow removing definition fields in `minetest.override_item` (5.9.0)
+ override_item_remove_fields = true,
}
```
@@ -5606,11 +5629,17 @@ Call these functions only at load time!
* `minetest.register_node(name, node definition)`
* `minetest.register_craftitem(name, item definition)`
* `minetest.register_tool(name, item definition)`
-* `minetest.override_item(name, redefinition)`
+* `minetest.override_item(name, redefinition, del_fields)`
+ * `redefinition` is a table of fields `[name] = new_value`,
+ overwriting fields of or adding fields to the existing definition.
+ * `del_fields` is a list of field names to be set
+ to `nil` ("deleted from") the original definition.
* Overrides fields of an item registered with register_node/tool/craftitem.
* Note: Item must already be defined, (opt)depend on the mod defining it.
* Example: `minetest.override_item("default:mese",
- {light_source=minetest.LIGHT_MAX})`
+ {light_source=minetest.LIGHT_MAX}, {"sounds"})`:
+ Overwrites the `light_source` field,
+ removes the sounds from the definition of the mese block.
* `minetest.unregister_item(name)`
* Unregisters the item from the engine, and deletes the entry with key
`name` from `minetest.registered_items` and from the associated item table
@@ -5697,12 +5726,15 @@ Global callback registration functions
Call these functions only at load time!
* `minetest.register_globalstep(function(dtime))`
- * Called every server step, usually interval of 0.1s
+ * Called every server step, usually interval of 0.1s.
+ * `dtime` is the time since last execution in seconds.
* `minetest.register_on_mods_loaded(function())`
* Called after mods have finished loading and before the media is cached or the
aliases handled.
* `minetest.register_on_shutdown(function())`
* Called before server shutdown
+ * Players that were kicked by the shutdown procedure are still fully accessible
+ in `minetest.get_connected_players()`.
* **Warning**: If the server terminates abnormally (i.e. crashes), the
registered callbacks **will likely not be run**. Data should be saved at
semi-frequent intervals as well as on server shutdown.
@@ -5721,7 +5753,7 @@ Call these functions only at load time!
* `minetest.register_on_generated(function(minp, maxp, blockseed))`
* Called after generating a piece of world between `minp` and `maxp`.
* **Avoid using this** whenever possible. As with other callbacks this blocks
- the main thread and introduces noticable latency.
+ the main thread and introduces noticeable latency.
Consider [Mapgen environment] for an alternative.
* `minetest.register_on_newplayer(function(ObjectRef))`
* Called when a new player enters the world for the first time
@@ -5729,7 +5761,7 @@ Call these functions only at load time!
* Called when a player is punched
* Note: This callback is invoked even if the punched player is dead.
* `player`: ObjectRef - Player that was punched
- * `hitter`: ObjectRef - Player that hit
+ * `hitter`: ObjectRef - Player that hit. Can be nil.
* `time_from_last_punch`: Meant for disallowing spamming of clicks
(can be nil).
* `tool_capabilities`: Capability table of used item (can be nil)
@@ -5779,6 +5811,7 @@ Call these functions only at load time!
* `last_login`: The timestamp of the previous login, or nil if player is new
* `minetest.register_on_leaveplayer(function(ObjectRef, timed_out))`
* Called when a player leaves the game
+ * Does not get executed for connected players on shutdown.
* `timed_out`: True for timeout, false for other reasons.
* `minetest.register_on_authplayer(function(name, ip, is_success))`
* Called when a client attempts to log into an account.
@@ -5808,8 +5841,8 @@ Call these functions only at load time!
* Return `true` to mark the command as handled, which means that the default
handlers will be prevented.
* `minetest.register_on_player_receive_fields(function(player, formname, fields))`
- * Called when the server received input from `player` in a formspec with
- the given `formname`. Specifically, this is called on any of the
+ * Called when the server received input from `player`.
+ Specifically, this is called on any of the
following events:
* a button was pressed,
* Enter was pressed while the focus was on a text field
@@ -5820,6 +5853,9 @@ Call these functions only at load time!
* an entry was double-clicked in a textlist or table,
* a scrollbar was moved, or
* the form was actively closed by the player.
+ * `formname` is the name passed to `minetest.show_formspec`.
+ Special case: The empty string refers to the player inventory
+ (the formspec set by the `set_inventory_formspec` player method).
* Fields are sent for formspec elements which define a field. `fields`
is a table containing each formspecs element value (as string), with
the `name` parameter as index for each. The value depends on the
@@ -6102,12 +6138,24 @@ Environment access
* Items can be added also to unloaded and non-generated blocks.
* `minetest.get_player_by_name(name)`: Get an `ObjectRef` to a player
* Returns nothing in case of error (player offline, doesn't exist, ...).
-* `minetest.get_objects_inside_radius(pos, radius)`
- * returns a list of ObjectRefs.
+* `minetest.get_objects_inside_radius(center, radius)`
+ * returns a list of ObjectRefs
* `radius`: using a Euclidean metric
-* `minetest.get_objects_in_area(pos1, pos2)`
- * returns a list of ObjectRefs.
- * `pos1` and `pos2` are the min and max positions of the area to search.
+ * **Warning**: Any kind of interaction with the environment or other APIs
+ can cause later objects in the list to become invalid while you're iterating it.
+ (e.g. punching an entity removes its children)
+ It is recommended to use `minetest.objects_inside_radius` instead, which
+ transparently takes care of this possibility.
+* `minetest.objects_inside_radius(center, radius)`
+ * returns an iterator of valid objects
+ * example: `for obj in minetest.objects_inside_radius(center, radius) do obj:punch(...) end`
+* `minetest.get_objects_in_area(min_pos, max_pos)`
+ * returns a list of ObjectRefs
+ * `min_pos` and `max_pos` are the min and max positions of the area to search
+ * **Warning**: The same warning as for `minetest.get_objects_inside_radius` applies.
+ Use `minetest.objects_in_area` instead to iterate only valid objects.
+* `minetest.objects_in_area(min_pos, max_pos)`
+ * returns an iterator of valid objects
* `minetest.set_timeofday(val)`: set time of day
* `val` is between `0` and `1`; `0` for midnight, `0.5` for midday
* `minetest.get_timeofday()`: get time of day
@@ -6369,11 +6417,11 @@ Environment access
* spread these updates to neighbors and can cause a cascade
of nodes to fall.
* `minetest.get_spawn_level(x, z)`
- * Returns a player spawn y co-ordinate for the provided (x, z)
- co-ordinates, or `nil` for an unsuitable spawn point.
+ * Returns a player spawn y coordinate for the provided (x, z)
+ coordinates, or `nil` for an unsuitable spawn point.
* For most mapgens a 'suitable spawn point' is one with y between
`water_level` and `water_level + 16`, and in mgv7 well away from rivers,
- so `nil` will be returned for many (x, z) co-ordinates.
+ so `nil` will be returned for many (x, z) coordinates.
* The spawn level returned is for a player spawn in unmodified terrain.
* The spawn level is intentionally above terrain level to cope with
full-node biome 'dust' nodes.
@@ -6418,7 +6466,8 @@ Formspec
* `minetest.show_formspec(playername, formname, formspec)`
* `playername`: name of player to show formspec
* `formname`: name passed to `on_player_receive_fields` callbacks.
- It should follow the `"modname:"` naming convention
+ It should follow the `"modname:"` naming convention.
+ `formname` must not be empty.
* `formspec`: formspec to display
* `minetest.close_formspec(playername, formname)`
* `playername`: name of player to close formspec
@@ -6682,7 +6731,7 @@ This allows you easy interoperability for delegating work to jobs.
* Register a path to a Lua file to be imported when an async environment
is initialized. You can use this to preload code which you can then call
later using `minetest.handle_async()`.
-* `minetest.register_async_metatable(name, mt)`:
+* `minetest.register_portable_metatable(name, mt)`:
* Register a metatable that should be preserved when data is transferred
between the main thread and the async environment.
* `name` is a string that identifies the metatable. It is recommended to
@@ -6698,6 +6747,7 @@ This allows you easy interoperability for delegating work to jobs.
### List of APIs available in an async environment
Classes:
+
* `AreaStore`
* `ItemStack`
* `PerlinNoise`
@@ -6711,17 +6761,20 @@ Classes:
* `Settings`
Class instances that can be transferred between environments:
+
* `ItemStack`
* `PerlinNoise`
* `PerlinNoiseMap`
* `VoxelManip`
Functions:
+
* Standalone helpers such as logging, filesystem, encoding,
hashing or compression APIs
-* `minetest.register_async_metatable` (see above)
+* `minetest.register_portable_metatable` (see above)
Variables:
+
* `minetest.settings`
* `minetest.registered_items`, `registered_nodes`, `registered_tools`,
`registered_craftitems` and `registered_aliases`
@@ -6773,6 +6826,7 @@ does not have a global step or timer.
### List of APIs available in the mapgen env
Classes:
+
* `AreaStore`
* `ItemStack`
* `PerlinNoise`
@@ -6786,6 +6840,7 @@ Classes:
* `Settings`
Functions:
+
* Standalone helpers such as logging, filesystem, encoding,
hashing or compression APIs
* `minetest.get_biome_id`, `get_biome_name`, `get_heat`, `get_humidity`,
@@ -6796,6 +6851,7 @@ Functions:
* these only operate on the current chunk (if inside a callback)
Variables:
+
* `minetest.settings`
* `minetest.registered_items`, `registered_nodes`, `registered_tools`,
`registered_craftitems` and `registered_aliases`
@@ -6845,7 +6901,7 @@ Server
all players (optional)
* `ephemeral`: boolean that marks the media as ephemeral,
it will not be cached on the client (optional, default false)
- * Exactly one of the paramters marked [*] must be specified.
+ * Exactly one of the parameters marked [*] must be specified.
* `callback`: function with arguments `name`, which is a player name
* Pushes the specified media file to client(s). (details below)
The file must be a supported image, sound or model format.
@@ -7527,9 +7583,12 @@ an itemstring, a table or `nil`.
* `set_wear(wear)`: returns boolean indicating whether item was cleared
* `wear`: number, unsigned 16 bit integer
* `get_meta()`: returns ItemStackMetaRef. See section for more details
-* `get_metadata()`: (DEPRECATED) Returns metadata (a string attached to an item
- stack).
-* `set_metadata(metadata)`: (DEPRECATED) Returns true.
+* `get_metadata()`: **Deprecated.** Returns metadata (a string attached to an item stack).
+ * If you need to access this to maintain backwards compatibility,
+ use `stack:get_meta():get_string("")` instead.
+* `set_metadata(metadata)`: **Deprecated.** Returns true.
+ * If you need to set this to maintain backwards compatibility,
+ use `stack:get_meta():set_string("", metadata)` instead.
* `get_description()`: returns the description shown in inventory list tooltips.
* The engine uses this when showing item descriptions in tooltips.
* Fields for finding the description, in order:
@@ -7673,7 +7732,7 @@ metadata_table = {
-- metadata fields (key/value store)
fields = {
infotext = "Container",
- anoter_key = "Another Value",
+ another_key = "Another Value",
},
-- inventory data (for nodes)
@@ -7768,13 +7827,18 @@ When you receive an `ObjectRef` as a callback argument or from another API
function, it is possible to store the reference somewhere and keep it around.
It will keep functioning until the object is unloaded or removed.
-However, doing this is **NOT** recommended as there is (intentionally) no method
-to test if a previously acquired `ObjectRef` is still valid.
-Instead, `ObjectRefs` should be "let go" of as soon as control is returned from
-Lua back to the engine.
+However, doing this is **NOT** recommended - `ObjectRefs` should be "let go"
+of as soon as control is returned from Lua back to the engine.
+
Doing so is much less error-prone and you will never need to wonder if the
object you are working with still exists.
+If this is not feasible, you can test whether an `ObjectRef` is still valid
+via `object:is_valid()`.
+
+Getters may be called for invalid objects and will return nothing then.
+All other methods should not be called on invalid objects.
+
### Attachments
It is possible to attach objects to other objects (`set_attach` method).
@@ -7793,6 +7857,8 @@ child will follow movement and rotation of that bone.
### Methods
+* `is_valid()`: returns whether the object is valid.
+ * See "Advice on handling `ObjectRefs`" above.
* `get_pos()`: returns position as vector `{x=num, y=num, z=num}`
* `set_pos(pos)`:
* Sets the position of the object.
@@ -7825,11 +7891,11 @@ child will follow movement and rotation of that bone.
* no-op if object is attached
* `punch(puncher, time_from_last_punch, tool_capabilities, dir)`
* punches the object, triggering all consequences a normal punch would have
- * `puncher`: another `ObjectRef` which punched the object
+ * `puncher`: another `ObjectRef` which punched the object or `nil`
* `dir`: direction vector of punch
* Other arguments: See `on_punch` for entities
- * All arguments except `puncher` can be `nil`, in which case a default
- value will be used
+ * Arguments `time_from_last_punch`, `tool_capabilities`, and `dir`
+ will be replaced with a default value when the caller sets them to `nil`.
* `right_click(clicker)`:
* simulates using the 'place/use' key on the object
* triggers all consequences as if a real player had done this
@@ -7966,7 +8032,7 @@ child will follow movement and rotation of that bone.
* `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading)
and Z is roll (bank).
* Does not reset rotation incurred through `automatic_rotate`.
- Remove & readd your objects to force a certain rotation.
+ Remove & re-add your objects to force a certain rotation.
* `get_rotation()`: returns the rotation, a vector (radians)
* `set_yaw(yaw)`: sets the yaw in radians (heading).
* `get_yaw()`: returns number in radians
@@ -8204,7 +8270,11 @@ child will follow movement and rotation of that bone.
* `"plain"`: Uses 0 textures, `base_color` used as both fog and sky.
(default: `"regular"`)
* `textures`: A table containing up to six textures in the following
- order: Y+ (top), Y- (bottom), X- (west), X+ (east), Z+ (north), Z- (south).
+ order: Y+ (top), Y- (bottom), X+ (east), X- (west), Z- (south), Z+ (north).
+ The top and bottom textures are oriented in-line with the east (X+) face (the top edge of the
+ bottom texture and the bottom edge of the top texture touch the east face).
+ Some top and bottom textures expect to be aligned with the north face and will need to be rotated
+ by -90 and 90 degrees, respectively, to fit the eastward orientation.
* `clouds`: Boolean for whether clouds appear. (default: `true`)
* `sky_color`: A table used in `"regular"` type only, containing the
following values (alpha is ignored):
@@ -9660,6 +9730,7 @@ Used by `minetest.register_node`.
on_receive_fields = function(pos, formname, fields, sender),
-- fields = {name1 = value1, name2 = value2, ...}
+ -- formname should be the empty string; you **must not** use formname.
-- Called when an UI form (e.g. sign text input) returns data.
-- See minetest.register_on_player_receive_fields for more info.
-- default: nil
@@ -10284,7 +10355,7 @@ See [Decoration types]. Used by `minetest.register_decoration`.
y_min = -31000,
y_max = 31000,
-- Lower and upper limits for decoration (inclusive).
- -- These parameters refer to the Y co-ordinate of the 'place_on' node.
+ -- These parameters refer to the Y coordinate of the 'place_on' node.
spawn_by = "default:water",
-- Node (or list of nodes) that the decoration only spawns next to.
@@ -10614,7 +10685,10 @@ Used by `minetest.add_particle`.
texture = "image.png",
-- The texture of the particle
-- v5.6.0 and later: also supports the table format described in the
- -- following section
+ -- following section, but due to a bug this did not take effect
+ -- (beyond the texture name).
+ -- v5.9.0 and later: fixes the bug.
+ -- Note: "texture.animation" is ignored here. Use "animation" below instead.
playername = "singleplayer",
-- Optional, if specified spawns particle only on the player's client
@@ -10639,6 +10713,11 @@ Used by `minetest.add_particle`.
drag = {x=0, y=0, z=0},
-- v5.6.0 and later: Optional drag value, consult the following section
+ -- Note: Only a vector is supported here. Alternative forms like a single
+ -- number are not supported.
+
+ jitter = {min = ..., max = ..., bias = 0},
+ -- v5.6.0 and later: Optional jitter range, consult the following section
bounce = {min = ..., max = ..., bias = 0},
-- v5.6.0 and later: Optional bounce range, consult the following section
@@ -10664,7 +10743,10 @@ will be ignored.
```lua
{
- -- Common fields (same name and meaning in both new and legacy syntax)
+ -------------------
+ -- Common fields --
+ -------------------
+ -- (same name and meaning in both new and legacy syntax)
amount = 1,
-- Number of particles spawned over the time period `time`.
@@ -10696,6 +10778,8 @@ will be ignored.
texture = "image.png",
-- The texture of the particle
+ -- v5.6.0 and later: also supports the table format described in the
+ -- following section.
playername = "singleplayer",
-- Optional, if specified spawns particles only on the player's client
@@ -10721,7 +10805,9 @@ will be ignored.
-- particle texture is picked.
-- Otherwise, the default behavior is used. (currently: any random tile)
- -- Legacy definition fields
+ -------------------
+ -- Legacy fields --
+ -------------------
minpos = {x=0, y=0, z=0},
maxpos = {x=0, y=0, z=0},
@@ -10849,32 +10935,46 @@ All of the properties that can be defined in this way are listed in the next
section, along with the datatypes they accept.
#### List of particlespawner properties
-All of the properties in this list can be animated with `*_tween` tables
-unless otherwise specified. For example, `jitter` can be tweened by setting
-a `jitter_tween` table instead of (or in addition to) a `jitter` table/value.
+
+All properties in this list of type "vec3 range", "float range" or "vec3" can
+be animated with `*_tween` tables. For example, `jitter` can be tweened by
+setting a `jitter_tween` table instead of (or in addition to) a `jitter`
+table/value.
+
In this section, a float range is a table defined as so: { min = A, max = B }
A and B are your supplemented values. For a vec3 range this means they are vectors.
Types used are defined in the previous section.
* vec3 range `pos`: the position at which particles can appear
+
* vec3 range `vel`: the initial velocity of the particle
+
* vec3 range `acc`: the direction and speed with which the particle
accelerates
+
+* vec3 range `size`: scales the visual size of the particle texture.
+ if `node` is set, this can be set to 0 to spawn randomly-sized particles
+ (just like actual node dig particles).
+
* vec3 range `jitter`: offsets the velocity of each particle by a random
amount within the specified range each frame. used to create Brownian motion.
+
* vec3 range `drag`: the amount by which absolute particle velocity along
each axis is decreased per second. a value of 1.0 means that the particle
will be slowed to a stop over the space of a second; a value of -1.0 means
that the particle speed will be doubled every second. to avoid interfering
with gravity provided by `acc`, a drag vector like `vector.new(1,0,1)` can
be used instead of a uniform value.
+
* float range `bounce`: how bouncy the particles are when `collisiondetection`
is turned on. values less than or equal to `0` turn off particle bounce;
`1` makes the particles bounce without losing any velocity, and `2` makes
them double their velocity with every bounce. `bounce` is not bounded but
values much larger than `1.0` probably aren't very useful.
+
* float range `exptime`: the number of seconds after which the particle
disappears.
+
* table `attract`: sets the birth orientation of particles relative to various
shapes defined in world coordinate space. this is an alternative means of
setting the velocity which allows particles to emerge from or enter into
@@ -10882,8 +10982,10 @@ Types used are defined in the previous section.
velocity values within a range. the velocity calculated by this method will
be **added** to that specified by `vel` if `vel` is also set, so in most
cases **`vel` should be set to 0**. `attract` has the fields:
+
* string `kind`: selects the kind of shape towards which the particles will
be oriented. it must have one of the following values:
+
* `"none"`: no attractor is set and the `attractor` table is ignored
* `"point"`: the particles are attracted to a specific point in space.
use this also if you want a sphere-like effect, in combination with
@@ -10894,25 +10996,32 @@ Types used are defined in the previous section.
* `"plane"`: the particles are attracted to an (infinite) plane on whose
surface `origin` designates a point in world coordinate space. use this
for e.g. particles entering or emerging from a portal.
+
* float range `strength`: the speed with which particles will move towards
`attractor`. If negative, the particles will instead move away from that
point.
+
* vec3 `origin`: the origin point of the shape towards which particles will
initially be oriented. functions as an offset if `origin_attached` is also
set.
+
* vec3 `direction`: sets the direction in which the attractor shape faces. for
lines, this sets the angle of the line; e.g. a vector of (0,1,0) will
create a vertical line that passes through `origin`. for planes, `direction`
is the surface normal of an infinite plane on whose surface `origin` is
a point. functions as an offset if `direction_attached` is also set.
- * entity `origin_attached`: allows the origin to be specified as an offset
- from the position of an entity rather than a coordinate in world space.
- * entity `direction_attached`: allows the direction to be specified as an offset
+
+ * ObjectRef `origin_attached`: allows the origin to be specified as an offset
from the position of an entity rather than a coordinate in world space.
+
+ * ObjectRef `direction_attached`: allows the direction to be specified as an
+ offset from the position of an entity rather than a coordinate in world space.
+
* bool `die_on_contact`: if true, the particles' lifetimes are adjusted so
that they will die as they cross the attractor threshold. this behavior
is the default but is undesirable for some kinds of animations; set it to
false to allow particles to live out their natural lives.
+
* vec3 range `radius`: if set, particles will be arranged in a sphere around
`pos`. A constant can be used to create a spherical shell of particles, a
vector to create an ovoid shell, and a range to create a volume; e.g.
@@ -10924,9 +11033,10 @@ Types used are defined in the previous section.
### Textures
-In versions before v5.6.0, particlespawner textures could only be specified as a single
-texture string. After v5.6.0, textures can now be specified as a table as well. This
-table contains options that allow simple animations to be applied to the texture.
+In versions before v5.6.0, particle/particlespawner textures could only be
+specified as a single texture string. After v5.6.0, textures can now be
+specified as a table as well. This table contains options that allow simple
+animations to be applied to the texture.
```lua
texture = {
@@ -10981,18 +11091,18 @@ texture = {
}
```
-Instead of setting a single texture definition, it is also possible to set a
-`texpool` property. A `texpool` consists of a list of possible particle textures.
-Every time a particle is spawned, the engine will pick a texture at random from
-the `texpool` and assign it as that particle's texture. You can also specify a
-`texture` in addition to a `texpool`; the `texture` value will be ignored on newer
-clients but will be sent to older (pre-v5.6.0) clients that do not implement
-texpools.
+For particlespawners, it is also possible to set the `texpool` property instead
+of a single texture definition. A `texpool` consists of a list of possible
+particle textures. Every time a particle is spawned, the engine will pick a
+texture at random from the `texpool` and assign it as that particle's texture.
+You can also specify a `texture` in addition to a `texpool`; the `texture`
+value will be ignored on newer clients but will be sent to older (pre-v5.6.0)
+clients that do not implement texpools.
```lua
texpool = {
"mymod_particle_texture.png";
- { name = "mymod_spark.png", fade = "out" },
+ { name = "mymod_spark.png", alpha_tween = {1, 0} },
{
name = "mymod_dust.png",
alpha = 0.3,
diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md
index 439ef3bdf..5017d2e5b 100644
--- a/doc/menu_lua_api.md
+++ b/doc/menu_lua_api.md
@@ -14,7 +14,8 @@ Callbacks
* `core.button_handler(fields)`: called when a button is pressed.
* `fields` = `{name1 = value1, name2 = value2, ...}`
* `core.event_handler(event)`
- * `event`: `"MenuQuit"`, `"KeyEnter"`, `"ExitButton"` or `"EditBoxEnter"`
+ * `event`: `"MenuQuit"`, `"KeyEnter"`, `"ExitButton"`, `"EditBoxEnter"` or
+ `"FullscreenChange"`
Gamedata
diff --git a/games/devtest/game.conf b/games/devtest/game.conf
index 0f5656c99..8184881a2 100644
--- a/games/devtest/game.conf
+++ b/games/devtest/game.conf
@@ -1,2 +1,4 @@
title = Development Test
description = Testing environment to help with testing the engine features of Minetest. It can also be helpful in mod development.
+first_mod = first_mod
+last_mod = last_mod
diff --git a/games/devtest/mods/basetools/init.lua b/games/devtest/mods/basetools/init.lua
index aa91d5e92..17e53c1fe 100644
--- a/games/devtest/mods/basetools/init.lua
+++ b/games/devtest/mods/basetools/init.lua
@@ -71,7 +71,7 @@ end
-- Mese Pickaxe: special tool that digs "everything" instantly
minetest.register_tool("basetools:pick_mese", {
description = "Mese Pickaxe".."\n"..
- "Digs diggable nodes instantly",
+ "Digs diggable nodes instantly.",
inventory_image = "basetools_mesepick.png",
tool_capabilities = {
full_punch_interval = 1.0,
@@ -88,6 +88,28 @@ minetest.register_tool("basetools:pick_mese", {
})
+-- A variant of the mese pickaxe that is not affected by the 0.15s digging delay
+minetest.register_tool("basetools:pick_mese_no_delay", {
+ description = "Mese Pickaxe (no delay)".."\n"..
+ "Digs diggable nodes instantly.".."\n"..
+ "There is no delay between digging each node,\n"..
+ 'but the "repeat_dig_time" setting is still respected.',
+ inventory_image = "basetools_mesepick_no_delay.png",
+ tool_capabilities = {
+ full_punch_interval = 1.0,
+ max_drop_level=3,
+ groupcaps={
+ cracky={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
+ crumbly={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
+ snappy={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
+ choppy={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
+ dig_immediate={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
+ },
+ damage_groups = {fleshy=100},
+ },
+})
+
+
--
-- Pickaxes: Dig cracky
--
diff --git a/games/devtest/mods/basetools/textures/basetools_mesepick_no_delay.png b/games/devtest/mods/basetools/textures/basetools_mesepick_no_delay.png
new file mode 100644
index 000000000..5a7db7e0a
Binary files /dev/null and b/games/devtest/mods/basetools/textures/basetools_mesepick_no_delay.png differ
diff --git a/games/devtest/mods/callbacks/entities.lua b/games/devtest/mods/callbacks/entities.lua
index e8379cb6f..528c985da 100644
--- a/games/devtest/mods/callbacks/entities.lua
+++ b/games/devtest/mods/callbacks/entities.lua
@@ -76,3 +76,26 @@ minetest.register_entity("callbacks:callback_step", {
message("on_step callback entity: on_step! pos="..spos(self).."; dtime="..dtime)
end,
})
+
+-- Callback punch with nil puncher
+minetest.register_entity("callbacks:callback_puncher", {
+ initial_properties = {
+ visual = "upright_sprite",
+ textures = { "callbacks_callback_entity.png" },
+ infotext = "Callback entity for nil puncher test.",
+ },
+
+ on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
+ if puncher then
+ puncher:punch(nil, time_from_last_punch, tool_capabilities, dir)
+ self.object:punch(nil, time_from_last_punch, tool_capabilities, dir)
+ else
+ message(
+ "Callback entity: on_punch with nil puncher "..
+ "pos="..spos(self).."; "..
+ "time_from_last_punch="..time_from_last_punch.."; "..
+ "tool_capabilities="..dump(tool_capabilities).."; "..
+ "dir="..dump(dir).."; damage="..damage)
+ end
+ end,
+})
diff --git a/games/devtest/mods/callbacks/init.lua b/games/devtest/mods/callbacks/init.lua
index 8f719a3f8..77cee1db2 100644
--- a/games/devtest/mods/callbacks/init.lua
+++ b/games/devtest/mods/callbacks/init.lua
@@ -1,3 +1,4 @@
dofile(minetest.get_modpath("callbacks").."/items.lua")
dofile(minetest.get_modpath("callbacks").."/nodes.lua")
dofile(minetest.get_modpath("callbacks").."/entities.lua")
+dofile(minetest.get_modpath("callbacks").."/players.lua")
diff --git a/games/devtest/mods/callbacks/players.lua b/games/devtest/mods/callbacks/players.lua
new file mode 100644
index 000000000..15bb076dc
--- /dev/null
+++ b/games/devtest/mods/callbacks/players.lua
@@ -0,0 +1,11 @@
+
+local message = function(msg)
+ minetest.log("action", "[callbacks] "..msg)
+ minetest.chat_send_all(msg)
+end
+
+core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
+ if not hitter then
+ message("Player "..player:get_player_name().." punched without hitter.")
+ end
+end)
diff --git a/games/devtest/mods/first_mod/init.lua b/games/devtest/mods/first_mod/init.lua
new file mode 100644
index 000000000..3909a39cc
--- /dev/null
+++ b/games/devtest/mods/first_mod/init.lua
@@ -0,0 +1 @@
+-- Nothing to do here, loading order is tested in C++ unittests.
diff --git a/games/devtest/mods/first_mod/mod.conf b/games/devtest/mods/first_mod/mod.conf
new file mode 100644
index 000000000..d35fa8d28
--- /dev/null
+++ b/games/devtest/mods/first_mod/mod.conf
@@ -0,0 +1,2 @@
+name = first_mod
+description = Mod which should be loaded before every other mod.
diff --git a/games/devtest/mods/last_mod/init.lua b/games/devtest/mods/last_mod/init.lua
new file mode 100644
index 000000000..3909a39cc
--- /dev/null
+++ b/games/devtest/mods/last_mod/init.lua
@@ -0,0 +1 @@
+-- Nothing to do here, loading order is tested in C++ unittests.
diff --git a/games/devtest/mods/last_mod/mod.conf b/games/devtest/mods/last_mod/mod.conf
new file mode 100644
index 000000000..734bf4c0c
--- /dev/null
+++ b/games/devtest/mods/last_mod/mod.conf
@@ -0,0 +1,5 @@
+name = last_mod
+description = Mod which should be loaded as last mod.
+# Test dependencies
+optional_depends = unittests
+depends = first_mod
diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua
index 758bad631..99ee691f1 100644
--- a/games/devtest/mods/testformspec/formspec.lua
+++ b/games/devtest/mods/testformspec/formspec.lua
@@ -64,14 +64,49 @@ local inv_style_fs = [[
list[current_player;main;.5,7;8,4]
]]
+-- Some textures from textures/base/pack and Devtest, with many different sizes
+-- and aspect ratios.
+local image_column = "image,0=logo.png,1=rare_controls.png,2=checkbox_16.png," ..
+ "3=checkbox_32.png,4=checkbox_64.png,5=default_lava.png," ..
+ "6=progress_bar.png,7=progress_bar_bg.png"
+local words = {
+ "esciunt", "repudiandae", "repellat", "voluptatem", "autem", "vitae", "et",
+ "minima", "quasi", "facere", "nihil", "ea", "nemo", "rem", "non", "eos",
+ "laudantium", "eveniet", "veritatis",
+}
+
+local reseed = math.random(2^31-1)
+math.randomseed(1337)
+
+local table_content = {}
+for i = 1, 100 do
+ table.insert(table_content, words[math.random(#words)])
+ table.insert(table_content, words[math.random(#words)])
+ table.insert(table_content, words[math.random(#words)])
+ table.insert(table_content, math.random(0, 7))
+ table.insert(table_content, math.random(0, 7))
+ table.insert(table_content, math.random(0, 7))
+ table.insert(table_content, words[math.random(#words)])
+end
+
+math.randomseed(reseed)
+
+local table_fs = table.concat({
+ "tablecolumns[text,align=left;text,align=right;text,align=center;",
+ image_column, ",align=left;",
+ image_column, ",align=right;",
+ image_column, ",align=center;text,align=right]",
+ "table[0,0;17,12;the_table;", table.concat(table_content, ","), ";1]"
+})
+
local hypertext_basic = [[A hypertext element
Normal test
This is a normal text.
style test
-
- .
-
+
+ .
+
Tag test
normal
@@ -88,20 +123,20 @@ This is a normal text.
Custom tag test
-
-
-
-
+
+
+
+
color=green
-Action: color=green
-Action: hovercolor=yellow
-Action URL: open URL
+Action: color=green
+Action: hovercolor=yellow
+Action URL: open URL
size=24
font=mono
color=green font=mono size=24
action test
-action
+action
img test
Normal:
@@ -350,6 +385,10 @@ local pages = {
"label[11,0.5;Noclip]" ..
"container[11.5,1]" .. clip_fs:gsub("%%c", "true") .. "container_end[]",
+ -- Table
+ "size[18,13]real_coordinates[true]" ..
+ "container[0.5,0.5]" .. table_fs.. "container_end[]",
+
-- Hypertext
"size[12,13]real_coordinates[true]" ..
"container[0.5,0.5]" .. hypertext_fs .. "container_end[]",
@@ -477,7 +516,7 @@ local function show_test_formspec(pname)
page = page()
end
- local fs = page .. "tabheader[0,0;11,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound,Background,Unsized;" .. page_id .. ";false;false]"
+ local fs = page .. "tabheader[0,0;11,0.65;maintabs;Real Coord,Styles,Noclip,Table,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound,Background,Unsized;" .. page_id .. ";false;false]"
minetest.show_formspec(pname, "testformspec:formspec", fs)
end
diff --git a/games/devtest/mods/testnodes/textures.lua b/games/devtest/mods/testnodes/textures.lua
index 0caaaed9d..86bc3e343 100644
--- a/games/devtest/mods/testnodes/textures.lua
+++ b/games/devtest/mods/testnodes/textures.lua
@@ -181,14 +181,6 @@ fractal = nil
frac_emb = nil
checker = nil
-do
- -- we used to write the textures to our mod folder. in order to avoid
- -- duplication errors delete them if they still exist.
- local path = core.get_modpath(core.get_current_modname()) .. "/textures/"
- os.remove(path .. "testnodes_generated_mb.png")
- os.remove(path .. "testnodes_generated_ck.png")
-end
-
local textures_path = core.get_worldpath() .. "/"
core.safe_file_write(
textures_path .. "testnodes1.png",
diff --git a/games/devtest/mods/unittests/async_env.lua b/games/devtest/mods/unittests/async_env.lua
index d7a714941..b00deb3b6 100644
--- a/games/devtest/mods/unittests/async_env.lua
+++ b/games/devtest/mods/unittests/async_env.lua
@@ -167,18 +167,18 @@ local function test_userdata_passing2(cb, _, pos)
end
unittests.register("test_userdata_passing2", test_userdata_passing2, {map=true, async=true})
-local function test_async_metatable_override()
- assert(pcall(core.register_async_metatable, "__builtin:vector", vector.metatable),
+local function test_portable_metatable_override()
+ assert(pcall(core.register_portable_metatable, "__builtin:vector", vector.metatable),
"Metatable name aliasing throws an error when it should be allowed")
- assert(not pcall(core.register_async_metatable, "__builtin:vector", {}),
+ assert(not pcall(core.register_portable_metatable, "__builtin:vector", {}),
"Illegal metatable overriding allowed")
end
-unittests.register("test_async_metatable_override", test_async_metatable_override)
+unittests.register("test_portable_metatable_override", test_portable_metatable_override)
-local function test_async_metatable_registration(cb)
+local function test_portable_metatable_registration(cb)
local custom_metatable = {}
- core.register_async_metatable("unittests:custom_metatable", custom_metatable)
+ core.register_portable_metatable("unittests:custom_metatable", custom_metatable)
core.handle_async(function(x)
-- unittests.custom_metatable is registered in inside_async_env.lua
@@ -193,7 +193,7 @@ local function test_async_metatable_registration(cb)
cb()
end, setmetatable({}, custom_metatable))
end
-unittests.register("test_async_metatable_registration", test_async_metatable_registration, {async=true})
+unittests.register("test_portable_metatable_registration", test_portable_metatable_registration, {async=true})
local function test_vector_preserve(cb)
local vec = vector.new(1, 2, 3)
diff --git a/games/devtest/mods/unittests/entity.lua b/games/devtest/mods/unittests/entity.lua
index 565dd6adf..38d026663 100644
--- a/games/devtest/mods/unittests/entity.lua
+++ b/games/devtest/mods/unittests/entity.lua
@@ -71,13 +71,13 @@ local function test_entity_lifecycle(_, pos)
-- with binary in staticdata
local obj = core.add_entity(pos, "unittests:callbacks", "abc\000def")
+ assert(obj and obj:is_valid())
check_log({"on_activate(7)"})
obj:set_hp(0)
check_log({"on_death(nil)", "on_deactivate(true)"})
- -- objectref must be invalid now
- assert(obj:get_velocity() == nil)
+ assert(not obj:is_valid())
end
unittests.register("test_entity_lifecycle", test_entity_lifecycle, {map=true})
@@ -130,3 +130,57 @@ local function test_entity_attach(player, pos)
obj:remove()
end
unittests.register("test_entity_attach", test_entity_attach, {player=true, map=true})
+
+core.register_entity("unittests:dummy", {
+ initial_properties = {
+ hp_max = 1,
+ visual = "upright_sprite",
+ textures = { "no_texture.png" },
+ static_save = false,
+ },
+})
+
+local function test_entity_raycast(_, pos)
+ local obj1 = core.add_entity(pos, "unittests:dummy")
+ local obj2 = core.add_entity(pos:offset(1, 0, 0), "unittests:dummy")
+ local raycast = core.raycast(pos:offset(-1, 0, 0), pos:offset(2, 0, 0), true, false)
+ for pt in raycast do
+ if pt.type == "object" then
+ assert(pt.ref == obj1)
+ obj1:remove()
+ obj2:remove()
+ obj1 = nil -- object should be hit exactly one
+ end
+ end
+ assert(obj1 == nil)
+end
+unittests.register("test_entity_raycast", test_entity_raycast, {map=true})
+
+local function test_object_iterator(pos, make_iterator)
+ local obj1 = core.add_entity(pos, "unittests:dummy")
+ local obj2 = core.add_entity(pos, "unittests:dummy")
+ assert(obj1 and obj2)
+ local found = false
+ -- As soon as we find one of the objects, we remove both, invalidating the other.
+ for obj in make_iterator() do
+ assert(obj:is_valid())
+ if obj == obj1 or obj == obj2 then
+ obj1:remove()
+ obj2:remove()
+ found = true
+ end
+ end
+ assert(found)
+end
+
+unittests.register("test_objects_inside_radius", function(_, pos)
+ test_object_iterator(pos, function()
+ return core.objects_inside_radius(pos, 1)
+ end)
+end, {map=true})
+
+unittests.register("test_objects_in_area", function(_, pos)
+ test_object_iterator(pos, function()
+ return core.objects_in_area(pos:offset(-1, -1, -1), pos:offset(1, 1, 1))
+ end)
+end, {map=true})
diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua
index 6b2cd7a79..eae003a2a 100644
--- a/games/devtest/mods/unittests/init.lua
+++ b/games/devtest/mods/unittests/init.lua
@@ -160,7 +160,7 @@ function unittests.run_all()
-- Print stats
assert(#unittests.list == counters.total)
print(string.rep("+", 80))
- print(string.format("Unit Test Results: %s",
+ print(string.format("Devtest Unit Test Results: %s",
counters.total == counters.passed and "PASSED" or "FAILED"))
print(string.format(" %d / %d failed tests.",
counters.total - counters.passed, counters.total))
@@ -185,22 +185,47 @@ dofile(modpath .. "/content_ids.lua")
dofile(modpath .. "/metadata.lua")
dofile(modpath .. "/raycast.lua")
dofile(modpath .. "/inventory.lua")
+dofile(modpath .. "/load_time.lua")
+dofile(modpath .. "/on_shutdown.lua")
--------------
+local function send_results(name, ok)
+ core.chat_send_player(name,
+ minetest.colorize(ok and "green" or "red",
+ (ok and "All devtest unit tests passed." or
+ "There were devtest unit test failures.") ..
+ " Check the console for detailed output."))
+end
+
if core.settings:get_bool("devtest_unittests_autostart", false) then
+ local test_results = nil
core.after(0, function()
+ -- CI adds a mod which sets `unittests.on_finished`
+ -- to write status information to the filesystem
+ local old_on_finished = unittests.on_finished
+ unittests.on_finished = function(ok)
+ for _, player in ipairs(minetest.get_connected_players()) do
+ send_results(player:get_player_name(), ok)
+ end
+ test_results = ok
+ old_on_finished(ok)
+ end
coroutine.wrap(unittests.run_all)()
end)
+ minetest.register_on_joinplayer(function(player)
+ if test_results == nil then
+ return -- tests haven't completed yet
+ end
+ send_results(player:get_player_name(), test_results)
+ end)
else
core.register_chatcommand("unittests", {
privs = {basic_privs=true},
description = "Runs devtest unittests (may modify player or map state)",
func = function(name, param)
unittests.on_finished = function(ok)
- core.chat_send_player(name,
- (ok and "All tests passed." or "There were test failures.") ..
- " Check the console for detailed output.")
+ send_results(name, ok)
end
coroutine.wrap(unittests.run_all)()
return true, ""
diff --git a/games/devtest/mods/unittests/inside_async_env.lua b/games/devtest/mods/unittests/inside_async_env.lua
index 5f97ec15d..fb610604d 100644
--- a/games/devtest/mods/unittests/inside_async_env.lua
+++ b/games/devtest/mods/unittests/inside_async_env.lua
@@ -3,7 +3,7 @@ unittests = {}
core.log("info", "Hello World")
unittests.custom_metatable = {}
-core.register_async_metatable("unittests:custom_metatable", unittests.custom_metatable)
+core.register_portable_metatable("unittests:custom_metatable", unittests.custom_metatable)
local function do_tests()
assert(core == minetest)
diff --git a/games/devtest/mods/unittests/load_time.lua b/games/devtest/mods/unittests/load_time.lua
new file mode 100644
index 000000000..d437fba06
--- /dev/null
+++ b/games/devtest/mods/unittests/load_time.lua
@@ -0,0 +1,13 @@
+-- Test item (un)registration and overriding
+do
+ local itemname = "unittests:test_override_item"
+ minetest.register_craftitem(":" .. itemname, {description = "foo"})
+ assert(assert(minetest.registered_items[itemname]).description == "foo")
+ minetest.override_item(itemname, {description = "bar"})
+ assert(assert(minetest.registered_items[itemname]).description == "bar")
+ minetest.override_item(itemname, {}, {"description"})
+ -- description has the empty string as a default
+ assert(assert(minetest.registered_items[itemname]).description == "")
+ minetest.unregister_item("unittests:test_override_item")
+ assert(minetest.registered_items["unittests:test_override_item"] == nil)
+end
diff --git a/games/devtest/mods/unittests/mod.conf b/games/devtest/mods/unittests/mod.conf
index fa94e01a6..ccff73701 100644
--- a/games/devtest/mods/unittests/mod.conf
+++ b/games/devtest/mods/unittests/mod.conf
@@ -1,3 +1,4 @@
name = unittests
description = Adds automated unit tests for the engine
-depends = basenodes
+# Also test that it is possible to depend on first_mod
+depends = first_mod, basenodes
diff --git a/games/devtest/mods/unittests/on_shutdown.lua b/games/devtest/mods/unittests/on_shutdown.lua
new file mode 100644
index 000000000..6d5d88638
--- /dev/null
+++ b/games/devtest/mods/unittests/on_shutdown.lua
@@ -0,0 +1,22 @@
+-- Test whether players still exist on shutdown
+local players = {}
+
+core.register_on_joinplayer(function(player)
+ players[player:get_player_name()] = true
+end)
+
+core.register_on_leaveplayer(function(player)
+ local name = player:get_player_name();
+ assert(players[name], "Unrecorded player join.")
+ players[name] = nil
+end)
+
+core.register_on_shutdown(function()
+ for _, player in pairs(core.get_connected_players()) do
+ local name = player:get_player_name()
+ assert(players[name], "Unrecorded player join or left too early.")
+ players[name] = nil
+ end
+
+ assert(not next(players), "Invalid connected players on shutdown.")
+end)
diff --git a/irr/include/IEventReceiver.h b/irr/include/IEventReceiver.h
index e0660a6f1..a484bfb84 100644
--- a/irr/include/IEventReceiver.h
+++ b/irr/include/IEventReceiver.h
@@ -205,6 +205,9 @@ enum EAPPLICATION_EVENT_TYPE
//! The application received a memory warning.
EAET_MEMORY_WARNING,
+ //! The display density changed (only works on SDL).
+ EAET_DPI_CHANGED,
+
//! No real event, but to get number of event types.
EAET_COUNT
};
@@ -331,7 +334,6 @@ struct SEvent
//! A bitmap of button states. You can use isButtonPressed() to determine
//! if a button is pressed or not.
- //! Currently only valid if the event was EMIE_MOUSE_MOVED
u32 ButtonStates;
//! Is the left button pressed down?
diff --git a/irr/include/IGUIButton.h b/irr/include/IGUIButton.h
index 0fbf866ac..8870f8b1c 100644
--- a/irr/include/IGUIButton.h
+++ b/irr/include/IGUIButton.h
@@ -200,7 +200,7 @@ class IGUIButton : public IGUIElement
\param loop: True if the animation should loop, false if not
\param scale: True if the sprite should scale to button size, false if not */
virtual void setSprite(EGUI_BUTTON_STATE state, s32 index,
- video::SColor color = video::SColor(255, 255, 255, 255), bool loop = false, bool scale = false) = 0;
+ video::SColor color = video::SColor(255, 255, 255, 255), bool loop = false) = 0;
//! Get the sprite-index for the given state or -1 when no sprite is set
virtual s32 getSpriteIndex(EGUI_BUTTON_STATE state) const = 0;
@@ -211,9 +211,6 @@ class IGUIButton : public IGUIElement
//! Returns if the sprite in the given state does loop
virtual bool getSpriteLoop(EGUI_BUTTON_STATE state) const = 0;
- //! Returns if the sprite in the given state is scaled
- virtual bool getSpriteScale(EGUI_BUTTON_STATE state) const = 0;
-
//! Sets if the button should behave like a push button.
/** Which means it can be in two states: Normal or Pressed. With a click on the button,
the user can change the state of the button. */
diff --git a/irr/include/IGUISkin.h b/irr/include/IGUISkin.h
index 6ec456472..36b510606 100644
--- a/irr/include/IGUISkin.h
+++ b/irr/include/IGUISkin.h
@@ -374,6 +374,12 @@ const c8 *const GUISkinFontNames[EGDF_COUNT + 1] = {
class IGUISkin : virtual public IReferenceCounted
{
public:
+ //! returns display density scaling factor
+ virtual float getScale() const = 0;
+
+ //! sets display density scaling factor
+ virtual void setScale(float scale) = 0;
+
//! returns default color
virtual video::SColor getColor(EGUI_DEFAULT_COLOR color) const = 0;
diff --git a/irr/include/IMeshBuffer.h b/irr/include/IMeshBuffer.h
index 3e2e3d74e..c69a12d1d 100644
--- a/irr/include/IMeshBuffer.h
+++ b/irr/include/IMeshBuffer.h
@@ -176,6 +176,32 @@ class IMeshBuffer : public virtual IReferenceCounted
}
return 0;
}
+
+ //! Calculate size of vertices and indices in memory
+ virtual size_t getSize() const
+ {
+ size_t ret = 0;
+ switch (getVertexType()) {
+ case video::EVT_STANDARD:
+ ret += sizeof(video::S3DVertex) * getVertexCount();
+ break;
+ case video::EVT_2TCOORDS:
+ ret += sizeof(video::S3DVertex2TCoords) * getVertexCount();
+ break;
+ case video::EVT_TANGENTS:
+ ret += sizeof(video::S3DVertexTangents) * getVertexCount();
+ break;
+ }
+ switch (getIndexType()) {
+ case video::EIT_16BIT:
+ ret += sizeof(u16) * getIndexCount();
+ break;
+ case video::EIT_32BIT:
+ ret += sizeof(u32) * getIndexCount();
+ break;
+ }
+ return ret;
+ }
};
} // end namespace scene
diff --git a/irr/include/IrrlichtDevice.h b/irr/include/IrrlichtDevice.h
index 38ba8c63d..11619010c 100644
--- a/irr/include/IrrlichtDevice.h
+++ b/irr/include/IrrlichtDevice.h
@@ -179,6 +179,11 @@ class IrrlichtDevice : public virtual IReferenceCounted
/** \return True if window is fullscreen. */
virtual bool isFullscreen() const = 0;
+ //! Enables or disables fullscreen mode.
+ /** Only works on SDL.
+ \return True on success. */
+ virtual bool setFullscreen(bool fullscreen) { return false; }
+
//! Checks if the window could possibly be visible.
/** If this returns false, you should not do any rendering. */
virtual bool isWindowVisible() const { return true; };
diff --git a/irr/src/CGUIButton.cpp b/irr/src/CGUIButton.cpp
index f023024f9..60bab5f83 100644
--- a/irr/src/CGUIButton.cpp
+++ b/irr/src/CGUIButton.cpp
@@ -75,12 +75,11 @@ void CGUIButton::setSpriteBank(IGUISpriteBank *sprites)
SpriteBank = sprites;
}
-void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale)
+void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop)
{
ButtonSprites[(u32)state].Index = index;
ButtonSprites[(u32)state].Color = color;
ButtonSprites[(u32)state].Loop = loop;
- ButtonSprites[(u32)state].Scale = scale;
}
//! Get the sprite-index for the given state or -1 when no sprite is set
@@ -101,12 +100,6 @@ bool CGUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const
return ButtonSprites[(u32)state].Loop;
}
-//! Returns if the sprite in the given state is scaled
-bool CGUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const
-{
- return ButtonSprites[(u32)state].Scale;
-}
-
//! called if an event happened.
bool CGUIButton::OnEvent(const SEvent &event)
{
@@ -294,19 +287,26 @@ void CGUIButton::draw()
void CGUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di ¢er)
{
u32 stateIdx = (u32)state;
+ s32 spriteIdx = ButtonSprites[stateIdx].Index;
+ if (spriteIdx == -1)
+ return;
- if (ButtonSprites[stateIdx].Index != -1) {
- if (ButtonSprites[stateIdx].Scale) {
- const video::SColor colors[] = {ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color};
- SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, AbsoluteRect,
- &AbsoluteClippingRect, colors,
- os::Timer::getTime() - startTime, ButtonSprites[stateIdx].Loop);
- } else {
- SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, center,
- &AbsoluteClippingRect, ButtonSprites[stateIdx].Color, startTime, os::Timer::getTime(),
- ButtonSprites[stateIdx].Loop, true);
- }
- }
+ u32 rectIdx = SpriteBank->getSprites()[spriteIdx].Frames[0].rectNumber;
+ core::rect srcRect = SpriteBank->getPositions()[rectIdx];
+
+ IGUISkin *skin = Environment->getSkin();
+ s32 scale = std::max(std::floor(skin->getScale()), 1.0f);
+ core::rect rect(center, srcRect.getSize() * scale);
+ rect -= rect.getSize() / 2;
+
+ const video::SColor colors[] = {
+ ButtonSprites[stateIdx].Color,
+ ButtonSprites[stateIdx].Color,
+ ButtonSprites[stateIdx].Color,
+ ButtonSprites[stateIdx].Color,
+ };
+ SpriteBank->draw2DSprite(spriteIdx, rect, &AbsoluteClippingRect, colors,
+ os::Timer::getTime() - startTime, ButtonSprites[stateIdx].Loop);
}
EGUI_BUTTON_IMAGE_STATE CGUIButton::getImageState(bool pressed) const
diff --git a/irr/src/CGUIButton.h b/irr/src/CGUIButton.h
index 8ff36c9a7..8a1434025 100644
--- a/irr/src/CGUIButton.h
+++ b/irr/src/CGUIButton.h
@@ -92,7 +92,7 @@ class CGUIButton : public IGUIButton
*/
virtual void setSprite(EGUI_BUTTON_STATE state, s32 index,
video::SColor color = video::SColor(255, 255, 255, 255),
- bool loop = false, bool scale = false) override;
+ bool loop = false) override;
//! Get the sprite-index for the given state or -1 when no sprite is set
s32 getSpriteIndex(EGUI_BUTTON_STATE state) const override;
@@ -103,9 +103,6 @@ class CGUIButton : public IGUIButton
//! Returns if the sprite in the given state does loop
bool getSpriteLoop(EGUI_BUTTON_STATE state) const override;
- //! Returns if the sprite in the given state is scaled
- bool getSpriteScale(EGUI_BUTTON_STATE state) const override;
-
//! Sets if the button should behave like a push button. Which means it
//! can be in two states: Normal or Pressed. With a click on the button,
//! the user can change the state of the button.
@@ -158,19 +155,18 @@ class CGUIButton : public IGUIButton
struct ButtonSprite
{
ButtonSprite() :
- Index(-1), Loop(false), Scale(false)
+ Index(-1), Loop(false)
{
}
bool operator==(const ButtonSprite &other) const
{
- return Index == other.Index && Color == other.Color && Loop == other.Loop && Scale == other.Scale;
+ return Index == other.Index && Color == other.Color && Loop == other.Loop;
}
s32 Index;
video::SColor Color;
bool Loop;
- bool Scale;
};
ButtonSprite ButtonSprites[EGBS_COUNT];
diff --git a/irr/src/CGUISkin.h b/irr/src/CGUISkin.h
index 130e5fada..68eae1c73 100644
--- a/irr/src/CGUISkin.h
+++ b/irr/src/CGUISkin.h
@@ -24,6 +24,12 @@ class CGUISkin : public IGUISkin
//! destructor
virtual ~CGUISkin();
+ //! returns display density scaling factor
+ virtual float getScale() const override { return Scale; }
+
+ //! sets display density scaling factor
+ virtual void setScale(float scale) override { Scale = scale; }
+
//! returns default color
video::SColor getColor(EGUI_DEFAULT_COLOR color) const override;
@@ -210,6 +216,7 @@ class CGUISkin : public IGUISkin
EGUI_SKIN_TYPE getType() const override;
private:
+ float Scale = 1.0f;
video::SColor Colors[EGDC_COUNT];
s32 Sizes[EGDS_COUNT];
u32 Icons[EGDI_COUNT];
diff --git a/irr/src/CIrrDeviceOSX.mm b/irr/src/CIrrDeviceOSX.mm
index 8e4843441..67c0ce05c 100644
--- a/irr/src/CIrrDeviceOSX.mm
+++ b/irr/src/CIrrDeviceOSX.mm
@@ -852,6 +852,7 @@ - (BOOL)isQuit
ievent.MouseInput.Wheel *= 10.0f;
else
ievent.MouseInput.Wheel *= 5.0f;
+ ievent.MouseInput.ButtonStates = MouseButtonStates;
postMouseEvent(event, ievent);
break;
@@ -1048,6 +1049,7 @@ - (BOOL)isQuit
ievent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
ievent.MouseInput.X = x;
ievent.MouseInput.Y = y;
+ ievent.MouseInput.ButtonStates = MouseButtonStates;
postEventFromUser(ievent);
}
}
diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp
index b50fd4b4a..71573a803 100644
--- a/irr/src/CIrrDeviceSDL.cpp
+++ b/irr/src/CIrrDeviceSDL.cpp
@@ -129,9 +129,9 @@ EM_BOOL CIrrDeviceSDL::MouseLeaveCallback(int eventType, const EmscriptenMouseEv
}
#endif
-bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
+bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE irrlichtKey)
{
- switch (key) {
+ switch (irrlichtKey) {
// keys which are known to have safe special character interpretation
// could need changes over time (removals and additions!)
case KEY_RETURN:
@@ -189,24 +189,68 @@ bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
}
}
-int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
+int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock)
{
+ switch (irrlichtKey) {
// special cases that always return a char regardless of how the SDL keycode
// looks
- switch (key) {
case KEY_RETURN:
case KEY_ESCAPE:
- return (int)key;
+ return (int)irrlichtKey;
+
+ // This is necessary for keys on the numpad because they don't use the same
+ // keycodes as their non-numpad versions (whose keycodes correspond to chars),
+ // but have their own SDL keycodes and their own Irrlicht keycodes (which
+ // don't correspond to chars).
+ case KEY_MULTIPLY:
+ return '*';
+ case KEY_ADD:
+ return '+';
+ case KEY_SUBTRACT:
+ return '-';
+ case KEY_DIVIDE:
+ return '/';
+
default:
break;
}
+ if (numlock) {
+ // Number keys on the numpad are also affected, but we only want them
+ // to produce number chars when numlock is enabled.
+ switch (irrlichtKey) {
+ case KEY_NUMPAD0:
+ return '0';
+ case KEY_NUMPAD1:
+ return '1';
+ case KEY_NUMPAD2:
+ return '2';
+ case KEY_NUMPAD3:
+ return '3';
+ case KEY_NUMPAD4:
+ return '4';
+ case KEY_NUMPAD5:
+ return '5';
+ case KEY_NUMPAD6:
+ return '6';
+ case KEY_NUMPAD7:
+ return '7';
+ case KEY_NUMPAD8:
+ return '8';
+ case KEY_NUMPAD9:
+ return '9';
+ default:
+ break;
+ }
+ }
+
// SDL in-place ORs values with no character representation with 1<<30
// https://wiki.libsdl.org/SDL2/SDLKeycodeLookup
- if (assumedChar & (1 << 30))
+ // This also affects the numpad keys btw.
+ if (sdlKey & (1 << 30))
return 0;
- switch (key) {
+ switch (irrlichtKey) {
case KEY_PRIOR:
case KEY_NEXT:
case KEY_HOME:
@@ -218,7 +262,7 @@ int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
case KEY_NUMLOCK:
return 0;
default:
- return assumedChar;
+ return sdlKey;
}
}
@@ -272,11 +316,33 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m) :
SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0");
#endif
+ // Minetest has its own signal handler
+ SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
+
+ // Disabling the compositor is not a good idea in windowed mode.
+ // See https://github.com/minetest/minetest/issues/14596
+ SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
+
+#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
+ // These are not interesting for our use
+ SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
+ SDL_SetHint(SDL_HINT_TV_REMOTE_AS_JOYSTICK, "0");
+#endif
+
+#if SDL_VERSION_ATLEAST(2, 24, 0)
+ // highdpi support on Windows
+ SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "1");
+#endif
+
// Minetest has its own code to synthesize mouse events from touch events,
// so we prevent SDL from doing it.
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
+#if defined(SDL_HINT_APP_NAME)
+ SDL_SetHint(SDL_HINT_APP_NAME, "Minetest");
+#endif
+
u32 flags = SDL_INIT_TIMER | SDL_INIT_EVENTS;
if (CreationParams.DriverType != video::EDT_NULL)
flags |= SDL_INIT_VIDEO;
@@ -462,14 +528,9 @@ bool CIrrDeviceSDL::createWindow()
bool CIrrDeviceSDL::createWindowWithContext()
{
u32 SDL_Flags = 0;
+ SDL_Flags |= SDL_WINDOW_ALLOW_HIGHDPI;
- if (CreationParams.Fullscreen) {
-#ifdef _IRR_EMSCRIPTEN_PLATFORM_
- SDL_Flags |= SDL_WINDOW_FULLSCREEN;
-#else
- SDL_Flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
-#endif
- }
+ SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen);
if (Resizable)
SDL_Flags |= SDL_WINDOW_RESIZABLE;
if (CreationParams.WindowMaximized)
@@ -582,13 +643,17 @@ bool CIrrDeviceSDL::createWindowWithContext()
return false;
}
- // Update Width and Height to match the actual window size.
- // In fullscreen mode, the window size specified in SIrrlichtCreationParameters
- // is ignored, so we cannot rely on it.
- int w = 0, h = 0;
- SDL_GetWindowSize(Window, &w, &h);
- Width = w;
- Height = h;
+ updateSizeAndScale();
+ if (ScaleX != 1.0f || ScaleY != 1.0f) {
+ // The given window size is in pixels, not in screen coordinates.
+ // We can only do the conversion now since we didn't know the scale before.
+ SDL_SetWindowSize(Window,
+ static_cast(CreationParams.WindowSize.Width / ScaleX),
+ static_cast(CreationParams.WindowSize.Height / ScaleY));
+ // Re-center, otherwise large, non-maximized windows go offscreen.
+ SDL_SetWindowPosition(Window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
+ updateSizeAndScale();
+ }
return true;
#endif // !_IRR_EMSCRIPTEN_PLATFORM_
@@ -645,6 +710,7 @@ bool CIrrDeviceSDL::run()
while (!Close && wrap_PollEvent(&SDL_event)) {
// os::Printer::log("event: ", core::stringc((int)SDL_event.type).c_str(), ELL_INFORMATION); // just for debugging
+ memset(&irrevent, 0, sizeof(irrevent));
switch (SDL_event.type) {
case SDL_MOUSEMOTION: {
@@ -652,10 +718,12 @@ bool CIrrDeviceSDL::run()
irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT;
irrevent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
- MouseX = irrevent.MouseInput.X = SDL_event.motion.x;
- MouseY = irrevent.MouseInput.Y = SDL_event.motion.y;
- MouseXRel = SDL_event.motion.xrel;
- MouseYRel = SDL_event.motion.yrel;
+ MouseX = irrevent.MouseInput.X =
+ static_cast(SDL_event.motion.x * ScaleX);
+ MouseY = irrevent.MouseInput.Y =
+ static_cast(SDL_event.motion.y * ScaleY);
+ MouseXRel = static_cast(SDL_event.motion.xrel * ScaleX);
+ MouseYRel = static_cast(SDL_event.motion.yrel * ScaleY);
irrevent.MouseInput.ButtonStates = MouseButtonStates;
irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0;
irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0;
@@ -673,6 +741,7 @@ bool CIrrDeviceSDL::run()
#else
irrevent.MouseInput.Wheel = SDL_event.wheel.y;
#endif
+ irrevent.MouseInput.ButtonStates = MouseButtonStates;
irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0;
irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0;
irrevent.MouseInput.X = MouseX;
@@ -686,8 +755,8 @@ bool CIrrDeviceSDL::run()
SDL_Keymod keymod = SDL_GetModState();
irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT;
- irrevent.MouseInput.X = SDL_event.button.x;
- irrevent.MouseInput.Y = SDL_event.button.y;
+ irrevent.MouseInput.X = static_cast(SDL_event.button.x * ScaleX);
+ irrevent.MouseInput.Y = static_cast(SDL_event.button.y * ScaleY);
irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0;
irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0;
@@ -808,7 +877,8 @@ bool CIrrDeviceSDL::run()
irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN);
irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0;
irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL) != 0;
- irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key);
+ irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key,
+ (SDL_event.key.keysym.mod & KMOD_NUM) != 0);
postEventFromUser(irrevent);
} break;
@@ -819,14 +889,25 @@ bool CIrrDeviceSDL::run()
case SDL_WINDOWEVENT:
switch (SDL_event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
- if ((SDL_event.window.data1 != (int)Width) || (SDL_event.window.data2 != (int)Height)) {
- Width = SDL_event.window.data1;
- Height = SDL_event.window.data2;
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+#if SDL_VERSION_ATLEAST(2, 0, 18)
+ case SDL_WINDOWEVENT_DISPLAY_CHANGED:
+#endif
+ u32 old_w = Width, old_h = Height;
+ f32 old_scale_x = ScaleX, old_scale_y = ScaleY;
+ updateSizeAndScale();
+ if (old_w != Width || old_h != Height) {
if (VideoDriver)
VideoDriver->OnResize(core::dimension2d(Width, Height));
}
+ if (old_scale_x != ScaleX || old_scale_y != ScaleY) {
+ irrevent.EventType = EET_APPLICATION_EVENT;
+ irrevent.ApplicationEvent.EventType = EAET_DPI_CHANGED;
+ postEventFromUser(irrevent);
+ }
break;
}
+ break;
case SDL_USEREVENT:
irrevent.EventType = irr::EET_USER_EVENT;
@@ -840,8 +921,8 @@ bool CIrrDeviceSDL::run()
irrevent.EventType = EET_TOUCH_INPUT_EVENT;
irrevent.TouchInput.Event = ETIE_PRESSED_DOWN;
irrevent.TouchInput.ID = SDL_event.tfinger.fingerId;
- irrevent.TouchInput.X = SDL_event.tfinger.x * Width;
- irrevent.TouchInput.Y = SDL_event.tfinger.y * Height;
+ irrevent.TouchInput.X = static_cast(SDL_event.tfinger.x * Width);
+ irrevent.TouchInput.Y = static_cast(SDL_event.tfinger.y * Height);
CurrentTouchCount++;
irrevent.TouchInput.touchedCount = CurrentTouchCount;
@@ -852,8 +933,8 @@ bool CIrrDeviceSDL::run()
irrevent.EventType = EET_TOUCH_INPUT_EVENT;
irrevent.TouchInput.Event = ETIE_MOVED;
irrevent.TouchInput.ID = SDL_event.tfinger.fingerId;
- irrevent.TouchInput.X = SDL_event.tfinger.x * Width;
- irrevent.TouchInput.Y = SDL_event.tfinger.y * Height;
+ irrevent.TouchInput.X = static_cast(SDL_event.tfinger.x * Width);
+ irrevent.TouchInput.Y = static_cast(SDL_event.tfinger.y * Height);
irrevent.TouchInput.touchedCount = CurrentTouchCount;
postEventFromUser(irrevent);
@@ -863,8 +944,8 @@ bool CIrrDeviceSDL::run()
irrevent.EventType = EET_TOUCH_INPUT_EVENT;
irrevent.TouchInput.Event = ETIE_LEFT_UP;
irrevent.TouchInput.ID = SDL_event.tfinger.fingerId;
- irrevent.TouchInput.X = SDL_event.tfinger.x * Width;
- irrevent.TouchInput.Y = SDL_event.tfinger.y * Height;
+ irrevent.TouchInput.X = static_cast(SDL_event.tfinger.x * Width);
+ irrevent.TouchInput.Y = static_cast(SDL_event.tfinger.y * Height);
// To match Android behavior, still count the pointer that was
// just released.
irrevent.TouchInput.touchedCount = CurrentTouchCount;
@@ -889,6 +970,14 @@ bool CIrrDeviceSDL::run()
IsInBackground = false;
break;
+ case SDL_RENDER_TARGETS_RESET:
+ os::Printer::log("Received SDL_RENDER_TARGETS_RESET. Rendering is probably broken.", ELL_ERROR);
+ break;
+
+ case SDL_RENDER_DEVICE_RESET:
+ os::Printer::log("Received SDL_RENDER_DEVICE_RESET. Rendering is probably broken.", ELL_ERROR);
+ break;
+
default:
break;
} // end switch
@@ -1014,25 +1103,26 @@ bool CIrrDeviceSDL::activateJoysticks(core::array &joystickInfo)
return false;
}
-//! Get the display density in dots per inch.
-float CIrrDeviceSDL::getDisplayDensity() const
+void CIrrDeviceSDL::updateSizeAndScale()
{
- if (!Window)
- return 0.0f;
-
- int window_w;
- int window_h;
+ int window_w, window_h;
SDL_GetWindowSize(Window, &window_w, &window_h);
- int drawable_w;
- int drawable_h;
+ int drawable_w, drawable_h;
SDL_GL_GetDrawableSize(Window, &drawable_w, &drawable_h);
- // assume 96 dpi
- float dpi_w = (float)drawable_w / (float)window_w * 96.0f;
- float dpi_h = (float)drawable_h / (float)window_h * 96.0f;
+ ScaleX = (float)drawable_w / (float)window_w;
+ ScaleY = (float)drawable_h / (float)window_h;
+
+ Width = drawable_w;
+ Height = drawable_h;
+}
- return std::max(dpi_w, dpi_h);
+//! Get the display density in dots per inch.
+float CIrrDeviceSDL::getDisplayDensity() const
+{
+ // assume 96 dpi
+ return std::max(ScaleX * 96.0f, ScaleY * 96.0f);
}
void CIrrDeviceSDL::SwapWindow()
@@ -1157,14 +1247,37 @@ bool CIrrDeviceSDL::isWindowMaximized() const
bool CIrrDeviceSDL::isFullscreen() const
{
+ if (!Window)
+ return false;
+ u32 flags = SDL_GetWindowFlags(Window);
+ return (flags & SDL_WINDOW_FULLSCREEN) != 0 ||
+ (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
+}
+
+u32 CIrrDeviceSDL::getFullscreenFlag(bool fullscreen)
+{
+ if (!fullscreen)
+ return 0;
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
- return SDL_GetWindowFlags(0) == SDL_WINDOW_FULLSCREEN;
+ return SDL_WINDOW_FULLSCREEN;
#else
-
- return CIrrDeviceStub::isFullscreen();
+ return SDL_WINDOW_FULLSCREEN_DESKTOP;
#endif
}
+bool CIrrDeviceSDL::setFullscreen(bool fullscreen)
+{
+ if (!Window)
+ return false;
+ // The SDL wiki says that this may trigger SDL_RENDER_TARGETS_RESET, but
+ // looking at the SDL source, this only happens with D3D, so it's not
+ // relevant to us.
+ bool success = SDL_SetWindowFullscreen(Window, getFullscreenFlag(fullscreen)) == 0;
+ if (!success)
+ os::Printer::log("SDL_SetWindowFullscreen failed", SDL_GetError(), ELL_ERROR);
+ return success;
+}
+
bool CIrrDeviceSDL::isWindowVisible() const
{
return !IsInBackground;
@@ -1228,7 +1341,8 @@ void CIrrDeviceSDL::createKeyMap()
// buttons missing
- KeyMap.push_back(SKeyMap(SDLK_AC_BACK, KEY_CANCEL));
+ // Android back button = ESC
+ KeyMap.push_back(SKeyMap(SDLK_AC_BACK, KEY_ESCAPE));
KeyMap.push_back(SKeyMap(SDLK_BACKSPACE, KEY_BACK));
KeyMap.push_back(SKeyMap(SDLK_TAB, KEY_TAB));
diff --git a/irr/src/CIrrDeviceSDL.h b/irr/src/CIrrDeviceSDL.h
index a83e8cf5e..f881bba5c 100644
--- a/irr/src/CIrrDeviceSDL.h
+++ b/irr/src/CIrrDeviceSDL.h
@@ -86,6 +86,10 @@ class CIrrDeviceSDL : public CIrrDeviceStub
/** \return True if window is fullscreen. */
bool isFullscreen() const override;
+ //! Enables or disables fullscreen mode.
+ /** \return True on success. */
+ bool setFullscreen(bool fullscreen) override;
+
//! Checks if the window could possibly be visible.
bool isWindowVisible() const override;
@@ -154,7 +158,9 @@ class CIrrDeviceSDL : public CIrrDeviceStub
//! Sets the new position of the cursor.
void setPosition(s32 x, s32 y) override
{
- SDL_WarpMouseInWindow(Device->Window, x, y);
+ SDL_WarpMouseInWindow(Device->Window,
+ static_cast(x / Device->ScaleX),
+ static_cast(y / Device->ScaleY));
if (SDL_GetRelativeMouseMode()) {
// There won't be an event for this warp (details on libsdl-org/SDL/issues/6034)
@@ -187,7 +193,7 @@ class CIrrDeviceSDL : public CIrrDeviceStub
virtual void setRelativeMode(bool relative) _IRR_OVERRIDE_
{
// Only change it when necessary, as it flushes mouse motion when enabled
- if (relative != SDL_GetRelativeMouseMode()) {
+ if (relative != static_cast(SDL_GetRelativeMouseMode())) {
if (relative)
SDL_SetRelativeMouseMode(SDL_TRUE);
else
@@ -268,10 +274,10 @@ class CIrrDeviceSDL : public CIrrDeviceStub
#endif
// Check if a key is a known special character with no side effects on text boxes.
- static bool keyIsKnownSpecial(EKEY_CODE key);
+ static bool keyIsKnownSpecial(EKEY_CODE irrlichtKey);
// Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0).
- static int findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key);
+ static int findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock);
// Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus.
void resetReceiveTextInputEvents();
@@ -296,9 +302,13 @@ class CIrrDeviceSDL : public CIrrDeviceStub
u32 MouseButtonStates;
u32 Width, Height;
+ f32 ScaleX = 1.0f, ScaleY = 1.0f;
+ void updateSizeAndScale();
bool Resizable;
+ static u32 getFullscreenFlag(bool fullscreen);
+
core::rect lastElemPos;
struct SKeyMap
diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt
index 10a841669..d5e9d47e7 100644
--- a/irr/src/CMakeLists.txt
+++ b/irr/src/CMakeLists.txt
@@ -1,3 +1,5 @@
+# When enabling SDL2 by default on macOS, don't forget to change
+# "NSHighResolutionCapable" to true in "Info.plist".
if(NOT APPLE)
set(DEFAULT_SDL2 ON)
endif()
@@ -31,7 +33,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
elseif(MSVC)
string(APPEND CMAKE_CXX_STANDARD_LIBRARIES " msvcrt.lib") # ???? fuck off
- add_compile_options(/GR- /Zl)
+ add_compile_options(/Zl)
# Enable SSE for floating point math on 32-bit x86 by default
# reasoning see minetest issue #11810 and https://gcc.gnu.org/wiki/FloatingPointMath
diff --git a/irr/src/COSOperator.cpp b/irr/src/COSOperator.cpp
index b26463799..d53223916 100644
--- a/irr/src/COSOperator.cpp
+++ b/irr/src/COSOperator.cpp
@@ -29,18 +29,6 @@
#include "fast_atof.h"
-#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_)
-static const bool sdl_supports_primary_selection = [] {
-#if SDL_VERSION_ATLEAST(2, 25, 0)
- SDL_version linked_version;
- SDL_GetVersion(&linked_version);
- return (linked_version.major == 2 && linked_version.minor >= 25) || linked_version.major > 2;
-#else
- return false;
-#endif
-}();
-#endif
-
namespace irr
{
@@ -131,8 +119,7 @@ void COSOperator::copyToPrimarySelection(const c8 *text) const
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_)
#if SDL_VERSION_ATLEAST(2, 25, 0)
- if (sdl_supports_primary_selection)
- SDL_SetPrimarySelectionText(text);
+ SDL_SetPrimarySelectionText(text);
#endif
#elif defined(_IRR_COMPILE_WITH_X11_DEVICE_)
@@ -195,11 +182,9 @@ const c8 *COSOperator::getTextFromPrimarySelection() const
{
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_)
#if SDL_VERSION_ATLEAST(2, 25, 0)
- if (sdl_supports_primary_selection) {
- SDL_free(PrimarySelectionText);
- PrimarySelectionText = SDL_GetPrimarySelectionText();
- return PrimarySelectionText;
- }
+ SDL_free(PrimarySelectionText);
+ PrimarySelectionText = SDL_GetPrimarySelectionText();
+ return PrimarySelectionText;
#endif
return 0;
diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp
index 999e03abd..736370f8f 100644
--- a/irr/src/COpenGLDriver.cpp
+++ b/irr/src/COpenGLDriver.cpp
@@ -3334,7 +3334,7 @@ IImage *COpenGLDriver::createScreenShot(video::ECOLOR_FORMAT format, video::E_RE
if (newImage)
pixels = static_cast(newImage->getData());
if (pixels) {
- glReadBuffer(GL_FRONT);
+ glReadBuffer(Params.Doublebuffer ? GL_BACK : GL_FRONT);
glReadPixels(0, 0, ScreenSize.Width, ScreenSize.Height, fmt, type, pixels);
testGLError(__LINE__);
glReadBuffer(GL_BACK);
diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp
index bf9334e15..0dd6ea1bb 100644
--- a/irr/src/OpenGL/Driver.cpp
+++ b/irr/src/OpenGL/Driver.cpp
@@ -704,12 +704,6 @@ IRenderTarget *COpenGL3DriverBase::addRenderTarget()
return renderTarget;
}
-// small helper function to create vertex buffer object adress offsets
-static inline u8 *buffer_offset(const long offset)
-{
- return ((u8 *)0 + offset);
-}
-
//! draws a vertex primitive list
void COpenGL3DriverBase::drawVertexPrimitiveList(const void *vertices, u32 vertexCount,
const void *indexList, u32 primitiveCount,
diff --git a/irr/src/OpenGL/Driver.h b/irr/src/OpenGL/Driver.h
index 80eeaf0da..51554334b 100644
--- a/irr/src/OpenGL/Driver.h
+++ b/irr/src/OpenGL/Driver.h
@@ -102,6 +102,8 @@ class COpenGL3DriverBase : public CNullDriver, public IMaterialRendererServices,
// internally used
virtual void draw2DImage(const video::ITexture *texture, u32 layer, bool flip);
+ using CNullDriver::draw2DImage;
+
void draw2DImageBatch(const video::ITexture *texture,
const core::array> &positions,
const core::array> &sourceRects,
diff --git a/irr/src/OpenGL/ExtensionHandler.cpp b/irr/src/OpenGL/ExtensionHandler.cpp
index 3ac212987..3c248ba0a 100644
--- a/irr/src/OpenGL/ExtensionHandler.cpp
+++ b/irr/src/OpenGL/ExtensionHandler.cpp
@@ -12,47 +12,14 @@
#include "os.h"
#include
-// FIXME: this basically duplicates what mt_opengl.h already does
-
namespace irr
{
namespace video
{
-void COpenGL3ExtensionHandler::initExtensionsOld()
-{
- auto extensions_string = reinterpret_cast(GL.GetString(GL_EXTENSIONS));
- const char *pos = extensions_string;
- while (const char *next = strchr(pos, ' ')) {
- addExtension(std::string{pos, next});
- pos = next + 1;
- }
- addExtension(pos);
- extensionsLoaded();
-}
-
-void COpenGL3ExtensionHandler::initExtensionsNew()
-{
- int ext_count = GetInteger(GL_NUM_EXTENSIONS);
- for (int k = 0; k < ext_count; k++)
- addExtension(reinterpret_cast(GL.GetStringi(GL_EXTENSIONS, k)));
- extensionsLoaded();
-}
-
-void COpenGL3ExtensionHandler::addExtension(std::string &&name)
-{
- Extensions.emplace(std::move(name));
-}
-
-bool COpenGL3ExtensionHandler::queryExtension(const std::string &name) const noexcept
-{
- return Extensions.find(name) != Extensions.end();
-}
-void COpenGL3ExtensionHandler::extensionsLoaded()
+void COpenGL3ExtensionHandler::initExtensions()
{
- os::Printer::log((std::string("Loaded ") + std::to_string(Extensions.size()) + " extensions:").c_str(), ELL_DEBUG);
- for (const auto &it : Extensions)
- os::Printer::log((std::string(" ") + it).c_str(), ELL_DEBUG);
+ // reading extensions happens in mt_opengl.cpp
for (size_t j = 0; j < IRR_OGLES_Feature_Count; ++j)
FeatureAvailable[j] = queryExtension(getFeatureString(j));
}
diff --git a/irr/src/OpenGL/ExtensionHandler.h b/irr/src/OpenGL/ExtensionHandler.h
index 8f96f0d4a..8f76c0eea 100644
--- a/irr/src/OpenGL/ExtensionHandler.h
+++ b/irr/src/OpenGL/ExtensionHandler.h
@@ -28,11 +28,13 @@ class COpenGL3ExtensionHandler : public COGLESCoreExtensionHandler
COpenGL3ExtensionHandler() :
COGLESCoreExtensionHandler() {}
- void initExtensionsOld();
- void initExtensionsNew();
+ void initExtensions();
/// Checks whether a named extension is present
- bool queryExtension(const std::string &name) const noexcept;
+ inline bool queryExtension(const std::string &name) const noexcept
+ {
+ return GL.IsExtensionPresent(name);
+ }
bool queryFeature(video::E_VIDEO_DRIVER_FEATURE feature) const
{
@@ -138,7 +140,8 @@ class COpenGL3ExtensionHandler : public COGLESCoreExtensionHandler
inline void irrGlDrawBuffer(GLenum mode)
{
- GL.DrawBuffer(mode);
+ // GLES only has DrawBuffers, so use that
+ GL.DrawBuffers(1, &mode);
}
inline void irrGlDrawBuffers(GLsizei n, const GLenum *bufs)
@@ -158,12 +161,6 @@ class COpenGL3ExtensionHandler : public COGLESCoreExtensionHandler
bool AnisotropicFilterSupported = false;
bool BlendMinMaxSupported = false;
-
-private:
- void addExtension(std::string &&name);
- void extensionsLoaded();
-
- std::unordered_set Extensions;
};
}
diff --git a/irr/src/OpenGL/MaterialRenderer.h b/irr/src/OpenGL/MaterialRenderer.h
index be74461e3..fa37cfff3 100644
--- a/irr/src/OpenGL/MaterialRenderer.h
+++ b/irr/src/OpenGL/MaterialRenderer.h
@@ -37,15 +37,15 @@ class COpenGL3MaterialRenderer : public IMaterialRenderer, public IMaterialRende
GLuint getProgram() const;
virtual void OnSetMaterial(const SMaterial &material, const SMaterial &lastMaterial,
- bool resetAllRenderstates, IMaterialRendererServices *services);
+ bool resetAllRenderstates, IMaterialRendererServices *services) override;
- virtual bool OnRender(IMaterialRendererServices *service, E_VERTEX_TYPE vtxtype);
+ virtual bool OnRender(IMaterialRendererServices *service, E_VERTEX_TYPE vtxtype) override;
- virtual void OnUnsetMaterial();
+ virtual void OnUnsetMaterial() override;
- virtual bool isTransparent() const;
+ virtual bool isTransparent() const override;
- virtual s32 getRenderCapability() const;
+ virtual s32 getRenderCapability() const override;
void setBasicRenderStates(const SMaterial &material, const SMaterial &lastMaterial, bool resetAllRenderstates) override;
diff --git a/irr/src/OpenGL3/Driver.cpp b/irr/src/OpenGL3/Driver.cpp
index 8196a8117..fd018b91d 100644
--- a/irr/src/OpenGL3/Driver.cpp
+++ b/irr/src/OpenGL3/Driver.cpp
@@ -36,7 +36,7 @@ void COpenGL3Driver::initFeatures()
{
assert(Version.Spec == OpenGLSpec::Compat);
assert(isVersionAtLeast(3, 2));
- initExtensionsNew();
+ initExtensions();
TextureFormats[ECF_A1R5G5B5] = {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}; // WARNING: may not be renderable
TextureFormats[ECF_R5G6B5] = {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}; // GL_RGB565 is an extension until 4.1
diff --git a/irr/src/OpenGLES2/Driver.cpp b/irr/src/OpenGLES2/Driver.cpp
index 6278ee9c3..249b6caef 100644
--- a/irr/src/OpenGLES2/Driver.cpp
+++ b/irr/src/OpenGLES2/Driver.cpp
@@ -31,10 +31,7 @@ void COpenGLES2Driver::initFeatures()
{
assert(Version.Spec == OpenGLSpec::ES);
assert(Version.Major >= 2);
- if (Version.Major >= 3)
- initExtensionsNew();
- else
- initExtensionsOld();
+ initExtensions();
static const GLenum BGRA8_EXT = 0x93A1;
diff --git a/lib/catch2/CMakeLists.txt b/lib/catch2/CMakeLists.txt
index 3c471d49d..252dc11e3 100644
--- a/lib/catch2/CMakeLists.txt
+++ b/lib/catch2/CMakeLists.txt
@@ -1,8 +1,8 @@
-# catch2 is distributed as a standalone header.
+# catch2 is distributed as a standalone header / source amalgamation.
#
# Downloaded from:
#
-# https://github.com/catchorg/Catch2/releases/download/v2.13.9/catch.hpp
+# https://github.com/catchorg/Catch2/releases/tag/v3.6.0
#
# The following changes were made to always print in microseconds, fixed format:
#
@@ -12,5 +12,7 @@
# - return os << duration.value() << ' ' << duration.unitsAsString();
# + return os << std::fixed << duration.value() << ' ' << duration.unitsAsString();
-add_library(catch2 INTERFACE)
-target_include_directories(catch2 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(catch2 STATIC catch_amalgamated.cpp)
+target_compile_definitions(catch2 PRIVATE CATCH_CONFIG_NOSTDOUT CATCH_AMALGAMATED_CUSTOM_MAIN)
+add_library(Catch2::Catch2 ALIAS catch2)
+target_include_directories(catch2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
diff --git a/lib/catch2/catch.hpp b/lib/catch2/catch.hpp
deleted file mode 100644
index 9b309bddc..000000000
--- a/lib/catch2/catch.hpp
+++ /dev/null
@@ -1,17976 +0,0 @@
-/*
- * Catch v2.13.10
- * Generated: 2022-10-16 11:01:23.452308
- * ----------------------------------------------------------
- * This file has been merged from multiple headers. Please don't edit it directly
- * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved.
- *
- * Distributed under the Boost Software License, Version 1.0. (See accompanying
- * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
-#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-// start catch.hpp
-
-
-#define CATCH_VERSION_MAJOR 2
-#define CATCH_VERSION_MINOR 13
-#define CATCH_VERSION_PATCH 10
-
-#ifdef __clang__
-# pragma clang system_header
-#elif defined __GNUC__
-# pragma GCC system_header
-#endif
-
-// start catch_suppress_warnings.h
-
-#ifdef __clang__
-# ifdef __ICC // icpc defines the __clang__ macro
-# pragma warning(push)
-# pragma warning(disable: 161 1682)
-# else // __ICC
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wpadded"
-# pragma clang diagnostic ignored "-Wswitch-enum"
-# pragma clang diagnostic ignored "-Wcovered-switch-default"
-# endif
-#elif defined __GNUC__
- // Because REQUIREs trigger GCC's -Wparentheses, and because still
- // supported version of g++ have only buggy support for _Pragmas,
- // Wparentheses have to be suppressed globally.
-# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
-
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wunused-variable"
-# pragma GCC diagnostic ignored "-Wpadded"
-#endif
-// end catch_suppress_warnings.h
-#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
-# define CATCH_IMPL
-# define CATCH_CONFIG_ALL_PARTS
-#endif
-
-// In the impl file, we want to have access to all parts of the headers
-// Can also be used to sanely support PCHs
-#if defined(CATCH_CONFIG_ALL_PARTS)
-# define CATCH_CONFIG_EXTERNAL_INTERFACES
-# if defined(CATCH_CONFIG_DISABLE_MATCHERS)
-# undef CATCH_CONFIG_DISABLE_MATCHERS
-# endif
-# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
-# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
-# endif
-#endif
-
-#if !defined(CATCH_CONFIG_IMPL_ONLY)
-// start catch_platform.h
-
-// See e.g.:
-// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
-#ifdef __APPLE__
-# include
-# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
- (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
-# define CATCH_PLATFORM_MAC
-# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
-# define CATCH_PLATFORM_IPHONE
-# endif
-
-#elif defined(linux) || defined(__linux) || defined(__linux__)
-# define CATCH_PLATFORM_LINUX
-
-#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
-# define CATCH_PLATFORM_WINDOWS
-#endif
-
-// end catch_platform.h
-
-#ifdef CATCH_IMPL
-# ifndef CLARA_CONFIG_MAIN
-# define CLARA_CONFIG_MAIN_NOT_DEFINED
-# define CLARA_CONFIG_MAIN
-# endif
-#endif
-
-// start catch_user_interfaces.h
-
-namespace Catch {
- unsigned int rngSeed();
-}
-
-// end catch_user_interfaces.h
-// start catch_tag_alias_autoregistrar.h
-
-// start catch_common.h
-
-// start catch_compiler_capabilities.h
-
-// Detect a number of compiler features - by compiler
-// The following features are defined:
-//
-// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
-// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
-// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
-// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
-// ****************
-// Note to maintainers: if new toggles are added please document them
-// in configuration.md, too
-// ****************
-
-// In general each macro has a _NO_ form
-// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
-// Many features, at point of detection, define an _INTERNAL_ macro, so they
-// can be combined, en-mass, with the _NO_ forms later.
-
-#ifdef __cplusplus
-
-# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
-# define CATCH_CPP14_OR_GREATER
-# endif
-
-# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
-# define CATCH_CPP17_OR_GREATER
-# endif
-
-#endif
-
-// Only GCC compiler should be used in this block, so other compilers trying to
-// mask themselves as GCC should be ignored.
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
-
-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
-
-#endif
-
-#if defined(__clang__)
-
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
-
-// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
-// which results in calls to destructors being emitted for each temporary,
-// without a matching initialization. In practice, this can result in something
-// like `std::string::~string` being called on an uninitialized value.
-//
-// For example, this code will likely segfault under IBM XL:
-// ```
-// REQUIRE(std::string("12") + "34" == "1234")
-// ```
-//
-// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
-# if !defined(__ibmxl__) && !defined(__CUDACC__)
-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
-# endif
-
-# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
- _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
-
-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
-
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
-
-# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
-
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
-
-#endif // __clang__
-
-////////////////////////////////////////////////////////////////////////////////
-// Assume that non-Windows platforms support posix signals by default
-#if !defined(CATCH_PLATFORM_WINDOWS)
- #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// We know some environments not to support full POSIX signals
-#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
- #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
-#endif
-
-#ifdef __OS400__
-# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
-# define CATCH_CONFIG_COLOUR_NONE
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Android somehow still does not support std::to_string
-#if defined(__ANDROID__)
-# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
-# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Not all Windows environments support SEH properly
-#if defined(__MINGW32__)
-# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// PS4
-#if defined(__ORBIS__)
-# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Cygwin
-#ifdef __CYGWIN__
-
-// Required for some versions of Cygwin to declare gettimeofday
-// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
-# define _BSD_SOURCE
-// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
-// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
-# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
- && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
-
-# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
-
-# endif
-#endif // __CYGWIN__
-
-////////////////////////////////////////////////////////////////////////////////
-// Visual C++
-#if defined(_MSC_VER)
-
-// Universal Windows platform does not support SEH
-// Or console colours (or console at all...)
-# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
-# define CATCH_CONFIG_COLOUR_NONE
-# else
-# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
-# endif
-
-# if !defined(__clang__) // Handle Clang masquerading for msvc
-
-// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
-// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
-// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
-# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
-# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-# endif // MSVC_TRADITIONAL
-
-// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop`
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
-# endif // __clang__
-
-#endif // _MSC_VER
-
-#if defined(_REENTRANT) || defined(_MSC_VER)
-// Enable async processing, as -pthread is specified or no additional linking is required
-# define CATCH_INTERNAL_CONFIG_USE_ASYNC
-#endif // _MSC_VER
-
-////////////////////////////////////////////////////////////////////////////////
-// Check if we are compiled with -fno-exceptions or equivalent
-#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
-# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// DJGPP
-#ifdef __DJGPP__
-# define CATCH_INTERNAL_CONFIG_NO_WCHAR
-#endif // __DJGPP__
-
-////////////////////////////////////////////////////////////////////////////////
-// Embarcadero C++Build
-#if defined(__BORLANDC__)
- #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Use of __COUNTER__ is suppressed during code analysis in
-// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
-// handled by it.
-// Otherwise all supported compilers support COUNTER macro,
-// but user still might want to turn it off
-#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
- #define CATCH_INTERNAL_CONFIG_COUNTER
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-// RTX is a special version of Windows that is real time.
-// This means that it is detected as Windows, but does not provide
-// the same set of capabilities as real Windows does.
-#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
- #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
- #define CATCH_INTERNAL_CONFIG_NO_ASYNC
- #define CATCH_CONFIG_COLOUR_NONE
-#endif
-
-#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
-#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
-#endif
-
-// Various stdlib support checks that require __has_include
-#if defined(__has_include)
- // Check if string_view is available and usable
- #if __has_include() && defined(CATCH_CPP17_OR_GREATER)
- # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
- #endif
-
- // Check if optional is available and usable
- # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
- # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
- # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
-
- // Check if byte is available and usable
- # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
- # include
- # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
- # define CATCH_INTERNAL_CONFIG_CPP17_BYTE
- # endif
- # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
-
- // Check if variant is available and usable
- # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
- # if defined(__clang__) && (__clang_major__ < 8)
- // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
- // fix should be in clang 8, workaround in libstdc++ 8.2
- # include
- # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
- # define CATCH_CONFIG_NO_CPP17_VARIANT
- # else
- # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
- # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
- # else
- # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
- # endif // defined(__clang__) && (__clang_major__ < 8)
- # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
-#endif // defined(__has_include)
-
-#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
-# define CATCH_CONFIG_COUNTER
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
-# define CATCH_CONFIG_WINDOWS_SEH
-#endif
-// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
-#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
-# define CATCH_CONFIG_POSIX_SIGNALS
-#endif
-// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
-#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
-# define CATCH_CONFIG_WCHAR
-#endif
-
-#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
-# define CATCH_CONFIG_CPP11_TO_STRING
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
-# define CATCH_CONFIG_CPP17_OPTIONAL
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
-# define CATCH_CONFIG_CPP17_STRING_VIEW
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
-# define CATCH_CONFIG_CPP17_VARIANT
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
-# define CATCH_CONFIG_CPP17_BYTE
-#endif
-
-#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
-# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
-# define CATCH_CONFIG_NEW_CAPTURE
-#endif
-
-#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-# define CATCH_CONFIG_DISABLE_EXCEPTIONS
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN)
-# define CATCH_CONFIG_POLYFILL_ISNAN
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
-# define CATCH_CONFIG_USE_ASYNC
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
-# define CATCH_CONFIG_ANDROID_LOGWRITE
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
-# define CATCH_CONFIG_GLOBAL_NEXTAFTER
-#endif
-
-// Even if we do not think the compiler has that warning, we still have
-// to provide a macro that can be used by the code.
-#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
-#endif
-#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
-#endif
-
-// The goal of this macro is to avoid evaluation of the arguments, but
-// still have the compiler warn on problems inside...
-#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
-#endif
-
-#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
-# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
-#elif defined(__clang__) && (__clang_major__ < 5)
-# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
-#endif
-
-#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
-#endif
-
-#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-#define CATCH_TRY if ((true))
-#define CATCH_CATCH_ALL if ((false))
-#define CATCH_CATCH_ANON(type) if ((false))
-#else
-#define CATCH_TRY try
-#define CATCH_CATCH_ALL catch (...)
-#define CATCH_CATCH_ANON(type) catch (type)
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR)
-#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#endif
-
-// end catch_compiler_capabilities.h
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#ifdef CATCH_CONFIG_COUNTER
-# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
-#else
-# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
-#endif
-
-#include
-#include
-#include
-
-// We need a dummy global operator<< so we can bring it into Catch namespace later
-struct Catch_global_namespace_dummy {};
-std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
-
-namespace Catch {
-
- struct CaseSensitive { enum Choice {
- Yes,
- No
- }; };
-
- class NonCopyable {
- NonCopyable( NonCopyable const& ) = delete;
- NonCopyable( NonCopyable && ) = delete;
- NonCopyable& operator = ( NonCopyable const& ) = delete;
- NonCopyable& operator = ( NonCopyable && ) = delete;
-
- protected:
- NonCopyable();
- virtual ~NonCopyable();
- };
-
- struct SourceLineInfo {
-
- SourceLineInfo() = delete;
- SourceLineInfo( char const* _file, std::size_t _line ) noexcept
- : file( _file ),
- line( _line )
- {}
-
- SourceLineInfo( SourceLineInfo const& other ) = default;
- SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
- SourceLineInfo( SourceLineInfo&& ) noexcept = default;
- SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
-
- bool empty() const noexcept { return file[0] == '\0'; }
- bool operator == ( SourceLineInfo const& other ) const noexcept;
- bool operator < ( SourceLineInfo const& other ) const noexcept;
-
- char const* file;
- std::size_t line;
- };
-
- std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
-
- // Bring in operator<< from global namespace into Catch namespace
- // This is necessary because the overload of operator<< above makes
- // lookup stop at namespace Catch
- using ::operator<<;
-
- // Use this in variadic streaming macros to allow
- // >> +StreamEndStop
- // as well as
- // >> stuff +StreamEndStop
- struct StreamEndStop {
- std::string operator+() const;
- };
- template
- T const& operator + ( T const& value, StreamEndStop ) {
- return value;
- }
-}
-
-#define CATCH_INTERNAL_LINEINFO \
- ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) )
-
-// end catch_common.h
-namespace Catch {
-
- struct RegistrarForTagAliases {
- RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
- };
-
-} // end namespace Catch
-
-#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
-// end catch_tag_alias_autoregistrar.h
-// start catch_test_registry.h
-
-// start catch_interfaces_testcase.h
-
-#include
-
-namespace Catch {
-
- class TestSpec;
-
- struct ITestInvoker {
- virtual void invoke () const = 0;
- virtual ~ITestInvoker();
- };
-
- class TestCase;
- struct IConfig;
-
- struct ITestCaseRegistry {
- virtual ~ITestCaseRegistry();
- virtual std::vector const& getAllTests() const = 0;
- virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0;
- };
-
- bool isThrowSafe( TestCase const& testCase, IConfig const& config );
- bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
- std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config );
- std::vector const& getAllTestCasesSorted( IConfig const& config );
-
-}
-
-// end catch_interfaces_testcase.h
-// start catch_stringref.h
-
-#include
-#include
-#include
-#include
-
-namespace Catch {
-
- /// A non-owning string class (similar to the forthcoming std::string_view)
- /// Note that, because a StringRef may be a substring of another string,
- /// it may not be null terminated.
- class StringRef {
- public:
- using size_type = std::size_t;
- using const_iterator = const char*;
-
- private:
- static constexpr char const* const s_empty = "";
-
- char const* m_start = s_empty;
- size_type m_size = 0;
-
- public: // construction
- constexpr StringRef() noexcept = default;
-
- StringRef( char const* rawChars ) noexcept;
-
- constexpr StringRef( char const* rawChars, size_type size ) noexcept
- : m_start( rawChars ),
- m_size( size )
- {}
-
- StringRef( std::string const& stdString ) noexcept
- : m_start( stdString.c_str() ),
- m_size( stdString.size() )
- {}
-
- explicit operator std::string() const {
- return std::string(m_start, m_size);
- }
-
- public: // operators
- auto operator == ( StringRef const& other ) const noexcept -> bool;
- auto operator != (StringRef const& other) const noexcept -> bool {
- return !(*this == other);
- }
-
- auto operator[] ( size_type index ) const noexcept -> char {
- assert(index < m_size);
- return m_start[index];
- }
-
- public: // named queries
- constexpr auto empty() const noexcept -> bool {
- return m_size == 0;
- }
- constexpr auto size() const noexcept -> size_type {
- return m_size;
- }
-
- // Returns the current start pointer. If the StringRef is not
- // null-terminated, throws std::domain_exception
- auto c_str() const -> char const*;
-
- public: // substrings and searches
- // Returns a substring of [start, start + length).
- // If start + length > size(), then the substring is [start, size()).
- // If start > size(), then the substring is empty.
- auto substr( size_type start, size_type length ) const noexcept -> StringRef;
-
- // Returns the current start pointer. May not be null-terminated.
- auto data() const noexcept -> char const*;
-
- constexpr auto isNullTerminated() const noexcept -> bool {
- return m_start[m_size] == '\0';
- }
-
- public: // iterators
- constexpr const_iterator begin() const { return m_start; }
- constexpr const_iterator end() const { return m_start + m_size; }
- };
-
- auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
- auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
-
- constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
- return StringRef( rawChars, size );
- }
-} // namespace Catch
-
-constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
- return Catch::StringRef( rawChars, size );
-}
-
-// end catch_stringref.h
-// start catch_preprocessor.hpp
-
-
-#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
-#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
-
-#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
-// MSVC needs more evaluations
-#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
-#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
-#else
-#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__)
-#endif
-
-#define CATCH_REC_END(...)
-#define CATCH_REC_OUT
-
-#define CATCH_EMPTY()
-#define CATCH_DEFER(id) id CATCH_EMPTY()
-
-#define CATCH_REC_GET_END2() 0, CATCH_REC_END
-#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
-#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
-#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
-#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
-#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
-
-#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
-
-#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-
-// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
-// and passes userdata as the first parameter to each invocation,
-// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
-#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
-#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
-#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
-#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
-#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
-#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
-#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
-#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
-#else
-// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
-#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
-#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
-#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
-#endif
-
-#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
-#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
-
-#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper())
-#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
-#else
-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper()))
-#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
-#endif
-
-#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
- CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
-
-#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
-#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
-#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
-#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
-#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
-#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
-#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
-#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
-#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
-#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
-#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
-
-#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
-
-#define INTERNAL_CATCH_TYPE_GEN\
- template struct TypeList {};\
- template\
- constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\
- template class...> struct TemplateTypeList{};\
- template class...Cs>\
- constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\
- template\
- struct append;\
- template\
- struct rewrap;\
- template class, typename...>\
- struct create;\
- template class, typename>\
- struct convert;\
- \
- template \
- struct append { using type = T; };\
- template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\
- struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\
- template< template class L1, typename...E1, typename...Rest>\
- struct append, TypeList, Rest...> { using type = L1; };\
- \
- template< template class Container, template class List, typename...elems>\
- struct rewrap, List> { using type = TypeList>; };\
- template< template class Container, template class List, class...Elems, typename...Elements>\
- struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\
- \
- template class Final, template< typename...> class...Containers, typename...Types>\
- struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };\
- template class Final, template class List, typename...Ts>\
- struct convert> { using type = typename append,TypeList...>::type; };
-
-#define INTERNAL_CATCH_NTTP_1(signature, ...)\
- template struct Nttp{};\
- template\
- constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
- template class...> struct NttpTemplateTypeList{};\
- template class...Cs>\
- constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList { return {}; } \
- \
- template< template class Container, template class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- struct rewrap, List<__VA_ARGS__>> { using type = TypeList>; };\
- template< template class Container, template class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
- struct rewrap, List<__VA_ARGS__>, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\
- template class Final, template class...Containers, typename...Types>\
- struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
- template\
- static void TestName()
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
- template\
- static void TestName()
-
-#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
- template\
- static void TestName()
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
- template\
- static void TestName()
-
-#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
- template\
- void reg_test(TypeList, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestFunc), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
- template\
- void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
- template\
- void reg_test(TypeList, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestName::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
- template\
- void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
- }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
- void test();\
- }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
- void test();\
- }
-
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
- template \
- void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName::test()
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
- template \
- void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_NTTP_0
-#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
-#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
-#else
-#define INTERNAL_CATCH_NTTP_0(signature)
-#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
-#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
-#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
-#endif
-
-// end catch_preprocessor.hpp
-// start catch_meta.hpp
-
-
-#include
-
-namespace Catch {
- template
- struct always_false : std::false_type {};
-
- template struct true_given : std::true_type {};
- struct is_callable_tester {
- template
- true_given()(std::declval()...))> static test(int);
- template
- std::false_type static test(...);
- };
-
- template
- struct is_callable;
-
- template
- struct is_callable : decltype(is_callable_tester::test(0)) {};
-
-#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
- // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
- // replaced with std::invoke_result here.
- template
- using FunctionReturnType = std::remove_reference_t>>;
-#else
- // Keep ::type here because we still support C++11
- template
- using FunctionReturnType = typename std::remove_reference::type>::type>::type;
-#endif
-
-} // namespace Catch
-
-namespace mpl_{
- struct na;
-}
-
-// end catch_meta.hpp
-namespace Catch {
-
-template
-class TestInvokerAsMethod : public ITestInvoker {
- void (C::*m_testAsMethod)();
-public:
- TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
-
- void invoke() const override {
- C obj;
- (obj.*m_testAsMethod)();
- }
-};
-
-auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
-
-template
-auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
- return new(std::nothrow) TestInvokerAsMethod( testAsMethod );
-}
-
-struct NameAndTags {
- NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
- StringRef name;
- StringRef tags;
-};
-
-struct AutoReg : NonCopyable {
- AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
- ~AutoReg();
-};
-
-} // end namespace Catch
-
-#if defined(CATCH_CONFIG_DISABLE)
- #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
- static void TestName()
- #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
- namespace{ \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
- void test(); \
- }; \
- } \
- void TestName::test()
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
- namespace{ \
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
- INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
- } \
- } \
- INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
- #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
- #else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
- #endif
-
- #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
- #else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
- #endif
-
- #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
- #else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
- #endif
-
- #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
- #else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
- #endif
-#endif
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
- static void TestName(); \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- static void TestName()
- #define INTERNAL_CATCH_TESTCASE( ... ) \
- INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ )
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
- void test(); \
- }; \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
- } \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- void TestName::test()
- #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
- INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ )
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
- INTERNAL_CATCH_TYPE_GEN\
- INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- template \
- struct TestName{\
- TestName(){\
- int index = 0; \
- constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
- using expander = int[];\
- (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
- }\
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- TestName();\
- return 0;\
- }();\
- }\
- }\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- template static void TestFuncName(); \
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
- INTERNAL_CATCH_TYPE_GEN \
- INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
- template \
- struct TestName { \
- void reg_tests() { \
- int index = 0; \
- using expander = int[]; \
- constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
- constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
- constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
- } \
- }; \
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
- using TestInit = typename create()), TypeList>::type; \
- TestInit t; \
- t.reg_tests(); \
- return 0; \
- }(); \
- } \
- } \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- template \
- static void TestFuncName()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T,__VA_ARGS__)
-#else
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__)
-#else
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
- #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- template static void TestFunc(); \
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
- INTERNAL_CATCH_TYPE_GEN\
- template \
- struct TestName { \
- void reg_tests() { \
- int index = 0; \
- using expander = int[]; \
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
- } \
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
- using TestInit = typename convert::type; \
- TestInit t; \
- t.reg_tests(); \
- return 0; \
- }(); \
- }}\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- template \
- static void TestFunc()
-
- #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
- INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, TmplList )
-
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
- INTERNAL_CATCH_TYPE_GEN\
- INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
- INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- template \
- struct TestNameClass{\
- TestNameClass(){\
- int index = 0; \
- constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
- using expander = int[];\
- (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
- }\
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- TestNameClass();\
- return 0;\
- }();\
- }\
- }\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
- void test();\
- };\
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
- INTERNAL_CATCH_TYPE_GEN \
- INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- template\
- struct TestNameClass{\
- void reg_tests(){\
- int index = 0;\
- using expander = int[];\
- constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
- constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
- constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
- }\
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- using TestInit = typename create()), TypeList>::type;\
- TestInit t;\
- t.reg_tests();\
- return 0;\
- }(); \
- }\
- }\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- template \
- void TestName::test()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
-#endif
-
- #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
- void test();\
- };\
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
- INTERNAL_CATCH_TYPE_GEN\
- template\
- struct TestNameClass{\
- void reg_tests(){\
- int index = 0;\
- using expander = int[];\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
- }\
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- using TestInit = typename convert::type;\
- TestInit t;\
- t.reg_tests();\
- return 0;\
- }(); \
- }}\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- template \
- void TestName::test()
-
-#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
- INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, TmplList )
-
-// end catch_test_registry.h
-// start catch_capture.hpp
-
-// start catch_assertionhandler.h
-
-// start catch_assertioninfo.h
-
-// start catch_result_type.h
-
-namespace Catch {
-
- // ResultWas::OfType enum
- struct ResultWas { enum OfType {
- Unknown = -1,
- Ok = 0,
- Info = 1,
- Warning = 2,
-
- FailureBit = 0x10,
-
- ExpressionFailed = FailureBit | 1,
- ExplicitFailure = FailureBit | 2,
-
- Exception = 0x100 | FailureBit,
-
- ThrewException = Exception | 1,
- DidntThrowException = Exception | 2,
-
- FatalErrorCondition = 0x200 | FailureBit
-
- }; };
-
- bool isOk( ResultWas::OfType resultType );
- bool isJustInfo( int flags );
-
- // ResultDisposition::Flags enum
- struct ResultDisposition { enum Flags {
- Normal = 0x01,
-
- ContinueOnFailure = 0x02, // Failures fail test, but execution continues
- FalseTest = 0x04, // Prefix expression with !
- SuppressFail = 0x08 // Failures are reported but do not fail the test
- }; };
-
- ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
-
- bool shouldContinueOnFailure( int flags );
- inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
- bool shouldSuppressFailure( int flags );
-
-} // end namespace Catch
-
-// end catch_result_type.h
-namespace Catch {
-
- struct AssertionInfo
- {
- StringRef macroName;
- SourceLineInfo lineInfo;
- StringRef capturedExpression;
- ResultDisposition::Flags resultDisposition;
-
- // We want to delete this constructor but a compiler bug in 4.8 means
- // the struct is then treated as non-aggregate
- //AssertionInfo() = delete;
- };
-
-} // end namespace Catch
-
-// end catch_assertioninfo.h
-// start catch_decomposer.h
-
-// start catch_tostring.h
-
-#include
-#include
-#include
-#include
-// start catch_stream.h
-
-#include
-#include
-#include
-
-namespace Catch {
-
- std::ostream& cout();
- std::ostream& cerr();
- std::ostream& clog();
-
- class StringRef;
-
- struct IStream {
- virtual ~IStream();
- virtual std::ostream& stream() const = 0;
- };
-
- auto makeStream( StringRef const &filename ) -> IStream const*;
-
- class ReusableStringStream : NonCopyable {
- std::size_t m_index;
- std::ostream* m_oss;
- public:
- ReusableStringStream();
- ~ReusableStringStream();
-
- auto str() const -> std::string;
-
- template
- auto operator << ( T const& value ) -> ReusableStringStream& {
- *m_oss << value;
- return *this;
- }
- auto get() -> std::ostream& { return *m_oss; }
- };
-}
-
-// end catch_stream.h
-// start catch_interfaces_enum_values_registry.h
-
-#include
-
-namespace Catch {
-
- namespace Detail {
- struct EnumInfo {
- StringRef m_name;
- std::vector> m_values;
-
- ~EnumInfo();
-
- StringRef lookup( int value ) const;
- };
- } // namespace Detail
-
- struct IMutableEnumValuesRegistry {
- virtual ~IMutableEnumValuesRegistry();
-
- virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0;
-
- template
- Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) {
- static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
- std::vector intValues;
- intValues.reserve( values.size() );
- for( auto enumValue : values )
- intValues.push_back( static_cast( enumValue ) );
- return registerEnum( enumName, allEnums, intValues );
- }
- };
-
-} // Catch
-
-// end catch_interfaces_enum_values_registry.h
-
-#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
-#include
-#endif
-
-#ifdef __OBJC__
-// start catch_objc_arc.hpp
-
-#import
-
-#ifdef __has_feature
-#define CATCH_ARC_ENABLED __has_feature(objc_arc)
-#else
-#define CATCH_ARC_ENABLED 0
-#endif
-
-void arcSafeRelease( NSObject* obj );
-id performOptionalSelector( id obj, SEL sel );
-
-#if !CATCH_ARC_ENABLED
-inline void arcSafeRelease( NSObject* obj ) {
- [obj release];
-}
-inline id performOptionalSelector( id obj, SEL sel ) {
- if( [obj respondsToSelector: sel] )
- return [obj performSelector: sel];
- return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED
-#define CATCH_ARC_STRONG
-#else
-inline void arcSafeRelease( NSObject* ){}
-inline id performOptionalSelector( id obj, SEL sel ) {
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
-#endif
- if( [obj respondsToSelector: sel] )
- return [obj performSelector: sel];
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
-#define CATCH_ARC_STRONG __strong
-#endif
-
-// end catch_objc_arc.hpp
-#endif
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
-#endif
-
-namespace Catch {
- namespace Detail {
-
- extern const std::string unprintableString;
-
- std::string rawMemoryToString( const void *object, std::size_t size );
-
- template
- std::string rawMemoryToString( const T& object ) {
- return rawMemoryToString( &object, sizeof(object) );
- }
-
- template
- class IsStreamInsertable {
- template
- static auto test(int)
- -> decltype(std::declval() << std::declval(), std::true_type());
-
- template
- static auto test(...)->std::false_type;
-
- public:
- static const bool value = decltype(test(0))::value;
- };
-
- template
- std::string convertUnknownEnumToString( E e );
-
- template
- typename std::enable_if<
- !std::is_enum::value && !std::is_base_of::value,
- std::string>::type convertUnstreamable( T const& ) {
- return Detail::unprintableString;
- }
- template
- typename std::enable_if<
- !std::is_enum