mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
80c0410442
769 changed files with 137450 additions and 85683 deletions
|
@ -1,4 +1,4 @@
|
||||||
Checks: '-*,modernize-use-emplace,modernize-avoid-bind,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,performance-*'
|
Checks: '-*,modernize-use-emplace,modernize-avoid-bind,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,performance-*,-performance-avoid-endl,performance-inefficient-string-concatenation'
|
||||||
WarningsAsErrors: '-*,modernize-use-emplace,performance-type-promotion-in-math-fn,performance-faster-string-find,performance-implicit-cast-in-loop'
|
WarningsAsErrors: '-*,modernize-use-emplace,performance-type-promotion-in-math-fn,performance-faster-string-find,performance-implicit-cast-in-loop'
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
- key: performance-unnecessary-value-param.AllowedTypes
|
- key: performance-unnecessary-value-param.AllowedTypes
|
||||||
|
|
40
.github/CONTRIBUTING.md
vendored
40
.github/CONTRIBUTING.md
vendored
|
@ -14,7 +14,7 @@ Contributions are welcome! Here's how you can help:
|
||||||
[clone](https://help.github.com/articles/cloning-a-repository/) your fork.
|
[clone](https://help.github.com/articles/cloning-a-repository/) your fork.
|
||||||
|
|
||||||
2. Before you start coding, consider opening an
|
2. Before you start coding, consider opening an
|
||||||
[issue at Github](https://github.com/minetest/minetest/issues) to discuss the
|
[issue on Github](https://github.com/luanti-org/luanti/issues) to discuss the
|
||||||
suitability and implementation of your intended contribution with the core
|
suitability and implementation of your intended contribution with the core
|
||||||
developers.
|
developers.
|
||||||
|
|
||||||
|
@ -25,16 +25,16 @@ Contributions are welcome! Here's how you can help:
|
||||||
the work, to avoid disappointment.
|
the work, to avoid disappointment.
|
||||||
|
|
||||||
You may also benefit from discussing on our IRC development channel
|
You may also benefit from discussing on our IRC development channel
|
||||||
[#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client
|
[#luanti-dev](http://www.luanti.org/irc/). Note that a proper IRC client
|
||||||
is required to speak on this channel.
|
is required to speak on this channel.
|
||||||
|
|
||||||
3. Start coding!
|
3. Start coding!
|
||||||
- Refer to the
|
- Refer to the
|
||||||
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.md),
|
[Lua API](https://github.com/luanti-org/luanti/blob/master/doc/lua_api.md),
|
||||||
[Developer Wiki](http://dev.minetest.net/Main_Page) and other
|
[Developer Wiki](https://dev.luanti.org/) and other
|
||||||
[documentation](https://github.com/minetest/minetest/tree/master/doc).
|
[documentation](https://github.com/luanti-org/luanti/tree/master/doc).
|
||||||
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and
|
- Follow the [C/C++](https://dev.luanti.org/Code_style_guidelines) and
|
||||||
[Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
|
[Lua](https://dev.luanti.org/Lua_code_style_guidelines) code style guidelines.
|
||||||
- Check your code works as expected and document any changes to the Lua API.
|
- 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.
|
- To avoid conflicting changes between contributions, do not do the following manually. They will be done before each release.
|
||||||
- Run `updatepo.sh` or update `luanti.po{,t}` even if your code adds new translatable strings.
|
- Run `updatepo.sh` or update `luanti.po{,t}` even if your code adds new translatable strings.
|
||||||
|
@ -53,7 +53,7 @@ Contributions are welcome! Here's how you can help:
|
||||||
- The following lines should describe the commit, starting a new line for each point.
|
- The following lines should describe the commit, starting a new line for each point.
|
||||||
|
|
||||||
5. Once you are happy with your changes, submit a pull request.
|
5. Once you are happy with your changes, submit a pull request.
|
||||||
- Open the [pull-request form](https://github.com/minetest/minetest/pull/new/master).
|
- Open the [pull-request form](https://github.com/luanti-org/luanti/pull/new/master).
|
||||||
- Add a description explaining what you've done (or if it's a
|
- Add a description explaining what you've done (or if it's a
|
||||||
work-in-progress - what you need to do).
|
work-in-progress - what you need to do).
|
||||||
- Make sure to fill out the pull request template.
|
- Make sure to fill out the pull request template.
|
||||||
|
@ -64,8 +64,8 @@ Contributions are welcome! Here's how you can help:
|
||||||
picture of the project.
|
picture of the project.
|
||||||
2. It works.
|
2. It works.
|
||||||
3. It follows the code style for
|
3. It follows the code style for
|
||||||
[C/C++](http://dev.minetest.net/Code_style_guidelines) or
|
[C/C++](https://dev.luanti.org/Code_style_guidelines) or
|
||||||
[Lua](http://dev.minetest.net/Lua_code_style_guidelines).
|
[Lua](https://dev.luanti.org/Lua_code_style_guidelines).
|
||||||
4. The code's interfaces are well designed, regardless of other aspects that
|
4. The code's interfaces are well designed, regardless of other aspects that
|
||||||
might need more work in the future.
|
might need more work in the future.
|
||||||
5. It uses protocols and formats which include the required compatibility.
|
5. It uses protocols and formats which include the required compatibility.
|
||||||
|
@ -76,9 +76,9 @@ If you experience an issue, we would like to know the details - especially when
|
||||||
a stable release is on the way.
|
a stable release is on the way.
|
||||||
|
|
||||||
1. Do a quick search on GitHub to check if the issue has already been reported.
|
1. Do a quick search on GitHub to check if the issue has already been reported.
|
||||||
2. Is it an issue with the Minetest *engine*? If not, report it
|
2. Is it an issue with the Luanti *engine*? If not, report it
|
||||||
[elsewhere](http://www.minetest.net/development/#reporting-issues).
|
[elsewhere](http://www.luanti.org/development/#reporting-issues).
|
||||||
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe
|
3. [Open an issue](https://github.com/luanti-org/luanti/issues/new) and describe
|
||||||
the issue you are having - you could include:
|
the issue you are having - you could include:
|
||||||
- Error logs (check the bottom of the `debug.txt` file).
|
- Error logs (check the bottom of the `debug.txt` file).
|
||||||
- Screenshots.
|
- Screenshots.
|
||||||
|
@ -106,21 +106,21 @@ the project page with a list of current languages
|
||||||
Builtin (the component which contains things like server messages, chat command
|
Builtin (the component which contains things like server messages, chat command
|
||||||
descriptions, privilege descriptions) is translated separately; it needs to be
|
descriptions, privilege descriptions) is translated separately; it needs to be
|
||||||
translated by editing a `.tr` text file. See
|
translated by editing a `.tr` text file. See
|
||||||
[Translation](https://dev.minetest.net/Translation) for more information.
|
[Translation](https://dev.luanti.org/Translation) for more information.
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
If you'd like to monetarily support Luanti development, you can find donation
|
If you'd like to monetarily support Luanti development, you can find donation
|
||||||
methods on [our website](http://www.minetest.net/development/#donate).
|
methods on [our website](http://www.luanti.org/development/#donate).
|
||||||
|
|
||||||
# Maintaining
|
# Maintaining
|
||||||
|
|
||||||
* This is a concise version of the
|
* This is a concise version of the
|
||||||
[Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
|
[Rules & Guidelines](https://dev.luanti.org/engine-dev-process/) on the developer wiki.*
|
||||||
|
|
||||||
These notes are for those who have push access Luanti (core developers / maintainers).
|
These notes are for those who have push access Luanti (core developers / maintainers).
|
||||||
|
|
||||||
- See the [project organisation](http://dev.minetest.net/Organisation) for the people involved.
|
- See the [project organisation](https://dev.luanti.org/Organisation) for the people involved.
|
||||||
|
|
||||||
## Concept approvals and roadmaps
|
## Concept approvals and roadmaps
|
||||||
|
|
||||||
|
@ -159,14 +159,14 @@ Submit a :+1: (+1) or "Looks good" comment to show you believe the pull-request
|
||||||
- The title should follow the commit guidelines (title starts with a capital letter, present tense, descriptive).
|
- The title should follow the commit guidelines (title starts with a capital letter, present tense, descriptive).
|
||||||
- Don't modify history older than 10 minutes.
|
- Don't modify history older than 10 minutes.
|
||||||
- Use rebase, not merge to get linear history:
|
- Use rebase, not merge to get linear history:
|
||||||
- `curl https://github.com/minetest/minetest/pull/1.patch | git am`
|
- `curl -Ls https://github.com/luanti-org/luanti/pull/1.patch | git am`
|
||||||
|
|
||||||
## Reviewing issues and feature requests
|
## Reviewing issues and feature requests
|
||||||
|
|
||||||
- If an issue does not get a response from its author within 1 month (when requiring more details), it can be closed.
|
- If an issue does not get a response from its author within 1 month (when requiring more details), it can be closed.
|
||||||
- When an issue is a duplicate, refer to the first ones and close the later ones.
|
- When an issue is a duplicate, refer to the first ones and close the later ones.
|
||||||
- Tag issues with the appropriate [labels](https://github.com/minetest/minetest/labels) for devices, platforms etc.
|
- Tag issues with the appropriate [labels](https://github.com/luanti-org/luanti/labels) for devices, platforms etc.
|
||||||
|
|
||||||
## Releasing a new version
|
## Releasing a new version
|
||||||
|
|
||||||
*Refer to [dev.minetest.net/Releasing_Luanti](https://dev.minetest.net/Releasing_Luanti)*
|
*Refer to [dev.luanti.org/Releasing_Luanti](https://dev.luanti.org/Releasing_Luanti)*
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
|
@ -9,7 +9,7 @@ body:
|
||||||
|
|
||||||
1. **Please update Luanti to the latest stable or dev version** before submitting bug reports. Make sure the bug is still reproducible on the latest version.
|
1. **Please update Luanti to the latest stable or dev version** before submitting bug reports. Make sure the bug is still reproducible on the latest version.
|
||||||
2. This page is for reporting the bugs of **the engine itself**. For bugs in a particular game, please [search for the game in the ContentDB](https://content.luanti.org/packages/?type=game) and submit a bug report in their issue trackers.
|
2. This page is for reporting the bugs of **the engine itself**. For bugs in a particular game, please [search for the game in the ContentDB](https://content.luanti.org/packages/?type=game) and submit a bug report in their issue trackers.
|
||||||
* For example, you can submit issues about the Minetest Game [in its own repository](https://github.com/minetest/minetest_game/issues).
|
* For example, you can submit issues about the Minetest Game [in its own repository](https://github.com/luanti-org/minetest_game/issues).
|
||||||
3. Please provide as many details as possible for us to spot the problem quicker.
|
3. Please provide as many details as possible for us to spot the problem quicker.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1,7 +1,7 @@
|
||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Submit issues about Minetest Game
|
- name: Submit issues about Minetest Game
|
||||||
url: https://github.com/minetest/minetest_game/issues/new/choose
|
url: https://github.com/luanti-org/minetest_game/issues/new
|
||||||
about: Only submit issues of the engine in this repository's issue tracker. Submit those of Minetest Game in its own issue tracker.
|
about: Only submit issues of the engine in this repository's issue tracker. Submit those of Minetest Game in its own issue tracker.
|
||||||
- name: Search for issue trackers of third-party games
|
- name: Search for issue trackers of third-party games
|
||||||
url: https://content.luanti.org/packages/?type=game
|
url: https://content.luanti.org/packages/?type=game
|
||||||
|
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -3,7 +3,7 @@ Add compact, short information about your PR for easier understanding:
|
||||||
- Goal of the PR
|
- Goal of the PR
|
||||||
- How does the PR work?
|
- How does the PR work?
|
||||||
- Does it resolve any reported issue?
|
- Does it resolve any reported issue?
|
||||||
- Does this relate to a goal in [the roadmap](https://github.com/minetest/minetest/blob/master/doc/direction.md)?
|
- Does this relate to a goal in [the roadmap](https://github.com/luanti-org/luanti/blob/master/doc/direction.md)?
|
||||||
- If not a bug fix, why is this PR needed? What usecases does it solve?
|
- If not a bug fix, why is this PR needed? What usecases does it solve?
|
||||||
|
|
||||||
## To do
|
## To do
|
||||||
|
|
4
.github/SECURITY.md
vendored
4
.github/SECURITY.md
vendored
|
@ -3,7 +3,7 @@
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
We only support the latest stable version for security issues.
|
We only support the latest stable version for security issues.
|
||||||
See the [releases page](https://github.com/minetest/minetest/releases).
|
See the [releases page](https://github.com/luanti-org/luanti/releases).
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ We ask that you report vulnerabilities privately, by contacting a core developer
|
||||||
to give us time to fix them. You can do that by emailing one of the following addresses:
|
to give us time to fix them. You can do that by emailing one of the following addresses:
|
||||||
|
|
||||||
* celeron55@gmail.com
|
* celeron55@gmail.com
|
||||||
* rubenwardy@minetest.net
|
* rw@rubenwardy.com
|
||||||
|
|
||||||
Depending on severity, we will either create a private issue for the vulnerability
|
Depending on severity, we will either create a private issue for the vulnerability
|
||||||
and release a patch version of Luanti, or give you permission to file the issue publicly.
|
and release a patch version of Luanti, or give you permission to file the issue publicly.
|
||||||
|
|
40
.github/workflows/linux.yml
vendored
40
.github/workflows/linux.yml
vendored
|
@ -34,28 +34,32 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Older gcc version (should be close to our minimum supported version)
|
# Older gcc version (should be close to our minimum supported version)
|
||||||
gcc_7:
|
gcc_9:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps g++-7
|
install_linux_deps g++-9
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./util/ci/build.sh
|
./util/ci/build.sh
|
||||||
env:
|
env:
|
||||||
CC: gcc-7
|
CC: gcc-9
|
||||||
CXX: g++-7
|
CXX: g++-9
|
||||||
# Test fallback SHA implementations
|
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
|
||||||
CMAKE_FLAGS: '-DENABLE_OPENSSL=0'
|
|
||||||
|
|
||||||
- name: Test
|
- name: Unittest
|
||||||
run: |
|
run: |
|
||||||
./bin/luanti --run-unittests
|
./bin/luanti --run-unittests
|
||||||
|
|
||||||
|
# Do this here because we have ASan and error paths are sensitive to dangling pointers
|
||||||
|
- name: Test error cases
|
||||||
|
run: |
|
||||||
|
./util/test_error_cases.sh
|
||||||
|
|
||||||
# Current gcc version
|
# Current gcc version
|
||||||
gcc_14:
|
gcc_14:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
@ -81,32 +85,28 @@ jobs:
|
||||||
../bin/luanti --run-unittests
|
../bin/luanti --run-unittests
|
||||||
|
|
||||||
# Older clang version (should be close to our minimum supported version)
|
# Older clang version (should be close to our minimum supported version)
|
||||||
clang_7:
|
clang_11:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps clang-7 llvm-7
|
install_linux_deps clang-11
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./util/ci/build.sh
|
./util/ci/build.sh
|
||||||
env:
|
env:
|
||||||
CC: clang-7
|
CC: clang-11
|
||||||
CXX: clang++-7
|
CXX: clang++-11
|
||||||
CMAKE_FLAGS: '-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"'
|
# Test fallback SHA implementations
|
||||||
|
CMAKE_FLAGS: '-DENABLE_OPENSSL=0'
|
||||||
|
|
||||||
- name: Unittest
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
./bin/luanti --run-unittests
|
./bin/luanti --run-unittests
|
||||||
|
|
||||||
# Do this here because we have ASan and error paths are sensitive to dangling pointers
|
|
||||||
- name: Test error cases
|
|
||||||
run: |
|
|
||||||
./util/test_error_cases.sh
|
|
||||||
|
|
||||||
# Current clang version
|
# Current clang version
|
||||||
clang_18:
|
clang_18:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
2
.github/workflows/lua_api_deploy.yml
vendored
2
.github/workflows/lua_api_deploy.yml
vendored
|
@ -16,7 +16,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.repository == 'minetest/minetest'
|
if: github.repository == 'luanti-org/luanti'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=13 \
|
||||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||||
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||||
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE \
|
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE \
|
||||||
|
|
26
.github/workflows/png_file_checks.yml
vendored
Normal file
26
.github/workflows/png_file_checks.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: png_file_checks
|
||||||
|
|
||||||
|
# Check whether all png files are in a valid format
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '**.png'
|
||||||
|
- '.github/workflows/**.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '**.png'
|
||||||
|
- '.github/workflows/**.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
png_optimized:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt install -y optipng
|
||||||
|
|
||||||
|
- name: Check whether all png files are optimized
|
||||||
|
run: |
|
||||||
|
./util/ci/check_png_optimized.sh
|
79
.github/workflows/whitespace_checks.yml
vendored
79
.github/workflows/whitespace_checks.yml
vendored
|
@ -14,6 +14,7 @@ on:
|
||||||
- '**.sh'
|
- '**.sh'
|
||||||
- '**.cmake'
|
- '**.cmake'
|
||||||
- '**.glsl'
|
- '**.glsl'
|
||||||
|
- '**.lua'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- '**.txt'
|
- '**.txt'
|
||||||
|
@ -24,6 +25,7 @@ on:
|
||||||
- '**.sh'
|
- '**.sh'
|
||||||
- '**.cmake'
|
- '**.cmake'
|
||||||
- '**.glsl'
|
- '**.glsl'
|
||||||
|
- '**.lua'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
trailing_whitespaces:
|
trailing_whitespaces:
|
||||||
|
@ -32,7 +34,72 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# Line endings are already ensured by .gitattributes
|
# Line endings are already ensured by .gitattributes
|
||||||
- name: Check trailing whitespaces
|
- name: Check trailing whitespaces
|
||||||
run: if git ls-files | grep -E '\.txt$|\.md$|\.[ch]$|\.cpp$|\.hpp$|\.sh$|\.cmake$|\.glsl$' | xargs grep -n '\s$'; then echo -e "\033[0;31mFound trailing whitespace"; (exit 1); else (exit 0); fi
|
run: |
|
||||||
|
if git ls-files |\
|
||||||
|
grep -E '\.txt$|\.md$|\.[ch]$|\.cpp$|\.hpp$|\.sh$|\.cmake$|\.glsl$' |\
|
||||||
|
xargs grep -n '\s$';\
|
||||||
|
then\
|
||||||
|
echo -e "\033[0;31mFound trailing whitespace";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
|
||||||
|
indent_spaces:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# Line endings are already ensured by .gitattributes
|
||||||
|
# Multiple multline comments in one line is not supported by this check
|
||||||
|
# and there is no reason to use them
|
||||||
|
# So lines like: "/* */ /*" or "*/ a = 5; /*" will result in error
|
||||||
|
- name: Check for unsupported multiline comments
|
||||||
|
run: |
|
||||||
|
if git ls-files |\
|
||||||
|
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$' |\
|
||||||
|
xargs grep -n '\*/.*/\*';\
|
||||||
|
then
|
||||||
|
echo -e "\033[0;31mUnsupported combination of multiline comments. New multiline comment should begin on new line.";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
if git ls-files |\
|
||||||
|
grep -E '\.lua$' |\
|
||||||
|
xargs grep -n -e '\]\].*--\[\[';\
|
||||||
|
then
|
||||||
|
echo -e "\033[0;31mUnsupported combination of multiline comments. New multiline comment should begin on new line.";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
# This prepare files for final check
|
||||||
|
# See python script ./util/ci/indent_tab_preprocess.py for details.
|
||||||
|
- name: Preprocess files
|
||||||
|
run: |
|
||||||
|
git ls-files |\
|
||||||
|
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$' |\
|
||||||
|
xargs -L 1 -P $(($(nproc) + 1)) \
|
||||||
|
python3 ./util/ci/indent_tab_preprocess.py "/*" "*/"
|
||||||
|
git ls-files |\
|
||||||
|
grep -E '\.lua$' |\
|
||||||
|
xargs -L 1 -P $(($(nproc) + 1)) \
|
||||||
|
python3 ./util/ci/indent_tab_preprocess.py "--[[" "]]"
|
||||||
|
# Check for bad indent.
|
||||||
|
# This runs over preprocessed files.
|
||||||
|
# If there is any remaining space on line beginning or after tab,
|
||||||
|
# error is generated
|
||||||
|
- name: Check indent spaces
|
||||||
|
run: |
|
||||||
|
if git ls-files |\
|
||||||
|
grep -E '^src/.*\.cpp$|^src/.*\.[ch]$|\.lua$' |\
|
||||||
|
xargs grep -n -P '^\t*[ ]';\
|
||||||
|
then\
|
||||||
|
echo -e "\033[0;31mFound incorrect indent whitespaces";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
|
||||||
tabs_lua_api_files:
|
tabs_lua_api_files:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -40,6 +107,12 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# Some files should not contain tabs
|
# Some files should not contain tabs
|
||||||
- name: Check tabs in Lua API files
|
- name: Check tabs in Lua API files
|
||||||
run: if grep -n $'\t' doc/lua_api.md doc/client_lua_api.md; then echo -e "\033[0;31mFound tab in markdown file"; (exit 1); else (exit 0); fi
|
run: |
|
||||||
|
if grep -n $'\t' doc/lua_api.md doc/client_lua_api.md;\
|
||||||
|
then\
|
||||||
|
echo -e "\033[0;31mFound tab in markdown file";\
|
||||||
|
(exit 1);\
|
||||||
|
else\
|
||||||
|
(exit 0);\
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
12
.github/workflows/windows.yml
vendored
12
.github/workflows/windows.yml
vendored
|
@ -71,9 +71,7 @@ jobs:
|
||||||
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
env:
|
env:
|
||||||
VCPKG_VERSION: 01f602195983451bc83e72f4214af2cbc495aa94
|
VCPKG_DEFAULT_TRIPLET: ${{matrix.config.vcpkg_triplet}}
|
||||||
# 2024.05.24
|
|
||||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -97,19 +95,17 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Restore from cache and run vcpkg
|
- name: Restore from cache and run vcpkg
|
||||||
uses: lukka/run-vcpkg@v7
|
uses: lukka/run-vcpkg@v11
|
||||||
with:
|
with:
|
||||||
vcpkgArguments: ${{env.vcpkg_packages}}
|
|
||||||
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
||||||
appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
|
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
|
||||||
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
|
||||||
|
|
||||||
- name: CMake
|
- name: CMake
|
||||||
|
# Note: See #15976 for why CMAKE_POLICY_VERSION_MINIMUM=3.5 is set.
|
||||||
run: |
|
run: |
|
||||||
cmake ${{matrix.config.generator}} `
|
cmake ${{matrix.config.generator}} `
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
||||||
-DCMAKE_BUILD_TYPE=Release `
|
-DCMAKE_BUILD_TYPE=Release `
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 `
|
||||||
-DENABLE_POSTGRESQL=OFF `
|
-DENABLE_POSTGRESQL=OFF `
|
||||||
-DENABLE_LUAJIT=TRUE `
|
-DENABLE_LUAJIT=TRUE `
|
||||||
-DREQUIRE_LUAJIT=TRUE `
|
-DREQUIRE_LUAJIT=TRUE `
|
||||||
|
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -49,13 +49,13 @@ AppDir
|
||||||
# Nix
|
# Nix
|
||||||
/result
|
/result
|
||||||
|
|
||||||
## Files related to Minetest development cycle
|
## Files related to Luanti development cycle
|
||||||
/*.patch
|
/*.patch
|
||||||
*.diff
|
*.diff
|
||||||
# GNU Patch reject file
|
# GNU Patch reject file
|
||||||
*.rej
|
*.rej
|
||||||
|
|
||||||
## Non-static Minetest directories or symlinks to these
|
## Non-static Luanti directories or symlinks to these
|
||||||
/bin/
|
/bin/
|
||||||
/games/*
|
/games/*
|
||||||
!/games/devtest/
|
!/games/devtest/
|
||||||
|
@ -80,7 +80,7 @@ minetest.conf
|
||||||
debug.txt
|
debug.txt
|
||||||
debug.txt.1
|
debug.txt.1
|
||||||
|
|
||||||
## Other files generated by Minetest
|
## Other files generated by Luanti
|
||||||
screenshot_*.png
|
screenshot_*.png
|
||||||
testbm.txt
|
testbm.txt
|
||||||
|
|
||||||
|
|
19
.luacheckrc
19
.luacheckrc
|
@ -10,6 +10,7 @@ ignore = {
|
||||||
read_globals = {
|
read_globals = {
|
||||||
"ItemStack",
|
"ItemStack",
|
||||||
"INIT",
|
"INIT",
|
||||||
|
"PLATFORM",
|
||||||
"DIR_DELIM",
|
"DIR_DELIM",
|
||||||
"dump", "dump2",
|
"dump", "dump2",
|
||||||
"fgettext", "fgettext_ne",
|
"fgettext", "fgettext_ne",
|
||||||
|
@ -18,10 +19,10 @@ read_globals = {
|
||||||
"VoxelManip",
|
"VoxelManip",
|
||||||
"profiler",
|
"profiler",
|
||||||
"Settings",
|
"Settings",
|
||||||
"PerlinNoise", "PerlinNoiseMap",
|
"ValueNoise", "ValueNoiseMap",
|
||||||
|
|
||||||
string = {fields = {"split", "trim"}},
|
string = {fields = {"split", "trim"}},
|
||||||
table = {fields = {"copy", "getn", "indexof", "keyof", "insert_all"}},
|
table = {fields = {"copy", "copy_with_metatables", "getn", "indexof", "keyof", "insert_all"}},
|
||||||
math = {fields = {"hypot", "round"}},
|
math = {fields = {"hypot", "round"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +33,13 @@ globals = {
|
||||||
"_",
|
"_",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stds.menu_common = {
|
||||||
|
globals = {
|
||||||
|
"mt_color_grey", "mt_color_blue", "mt_color_lightblue", "mt_color_green",
|
||||||
|
"mt_color_dark_green", "mt_color_orange", "mt_color_red",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
files["builtin/client/register.lua"] = {
|
files["builtin/client/register.lua"] = {
|
||||||
globals = {
|
globals = {
|
||||||
debug = {fields={"getinfo"}},
|
debug = {fields={"getinfo"}},
|
||||||
|
@ -72,13 +80,14 @@ files["builtin/common/filterlist.lua"] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
files["builtin/mainmenu"] = {
|
files["builtin/mainmenu"] = {
|
||||||
|
std = "+menu_common",
|
||||||
globals = {
|
globals = {
|
||||||
"gamedata",
|
"gamedata",
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
read_globals = {
|
files["builtin/common/settings"] = {
|
||||||
"PLATFORM",
|
std = "+menu_common",
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
files["builtin/common/tests"] = {
|
files["builtin/common/tests"] = {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
cmake_minimum_required(VERSION 3.12)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
if(POLICY CMP0177)
|
||||||
|
cmake_policy(SET CMP0177 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
# This can be read from ${PROJECT_NAME} after project() is called
|
# This can be read from ${PROJECT_NAME} after project() is called
|
||||||
project(luanti)
|
project(luanti)
|
||||||
|
@ -11,7 +14,7 @@ set(CLANG_MINIMUM_VERSION "7.0.1")
|
||||||
|
|
||||||
# You should not need to edit these manually, use util/bump_version.sh
|
# You should not need to edit these manually, use util/bump_version.sh
|
||||||
set(VERSION_MAJOR 5)
|
set(VERSION_MAJOR 5)
|
||||||
set(VERSION_MINOR 11)
|
set(VERSION_MINOR 13)
|
||||||
set(VERSION_PATCH 0)
|
set(VERSION_PATCH 0)
|
||||||
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
||||||
|
|
||||||
|
@ -48,7 +51,7 @@ endif()
|
||||||
# - win32/gcc: fails to link
|
# - win32/gcc: fails to link
|
||||||
# - win32/clang: works
|
# - win32/clang: works
|
||||||
# - macOS on x86: seems to be fine
|
# - macOS on x86: seems to be fine
|
||||||
# - macOS on ARM: crashes, see <https://github.com/minetest/minetest/issues/14397>
|
# - macOS on ARM: crashes, see <https://github.com/luanti-org/luanti/issues/14397>
|
||||||
# Note: since CMake has no easy architecture detection disabling for Mac entirely
|
# Note: since CMake has no easy architecture detection disabling for Mac entirely
|
||||||
#### ####
|
#### ####
|
||||||
if((WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR APPLE)
|
if((WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR APPLE)
|
||||||
|
@ -262,8 +265,8 @@ install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
install(FILES "doc/luanti.6" "doc/luantiserver.6" DESTINATION "${MANDIR}/man6")
|
install(FILES "doc/luanti.6" "doc/luantiserver.6" DESTINATION "${MANDIR}/man6")
|
||||||
install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}")
|
install(FILES "misc/org.luanti.luanti.desktop" DESTINATION "${XDG_APPS_DIR}")
|
||||||
install(FILES "misc/net.minetest.minetest.metainfo.xml" DESTINATION "${METAINFODIR}")
|
install(FILES "misc/org.luanti.luanti.metainfo.xml" DESTINATION "${METAINFODIR}")
|
||||||
install(FILES "misc/luanti.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
|
install(FILES "misc/luanti.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
|
||||||
install(FILES "misc/luanti-xorg-icon-128.png"
|
install(FILES "misc/luanti-xorg-icon-128.png"
|
||||||
DESTINATION "${ICONDIR}/hicolor/128x128/apps"
|
DESTINATION "${ICONDIR}/hicolor/128x128/apps"
|
||||||
|
|
17
LICENSE.txt
17
LICENSE.txt
|
@ -14,6 +14,9 @@ https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
Textures by Zughy are under CC BY-SA 4.0
|
Textures by Zughy are under CC BY-SA 4.0
|
||||||
https://creativecommons.org/licenses/by-sa/4.0/
|
https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
|
||||||
|
Textures by cx384 are under CC BY-SA 4.0
|
||||||
|
https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
|
||||||
Media files by DS are under CC BY-SA 4.0
|
Media files by DS are under CC BY-SA 4.0
|
||||||
https://creativecommons.org/licenses/by-sa/4.0/
|
https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
|
||||||
|
@ -64,7 +67,13 @@ Zughy:
|
||||||
textures/base/pack/settings_info.png
|
textures/base/pack/settings_info.png
|
||||||
textures/base/pack/settings_reset.png
|
textures/base/pack/settings_reset.png
|
||||||
textures/base/pack/server_url.png
|
textures/base/pack/server_url.png
|
||||||
|
textures/base/pack/server_url_unavailable.png
|
||||||
textures/base/pack/server_view_clients.png
|
textures/base/pack/server_view_clients.png
|
||||||
|
textures/base/pack/server_view_clients_unavailable.png
|
||||||
|
|
||||||
|
cx384:
|
||||||
|
textures/base/pack/server_view_mods.png
|
||||||
|
textures/base/pack/server_view_mods_unavailable.png
|
||||||
|
|
||||||
appgurueu:
|
appgurueu:
|
||||||
textures/base/pack/server_incompatible.png
|
textures/base/pack/server_incompatible.png
|
||||||
|
@ -93,6 +102,14 @@ grorp:
|
||||||
using the font "undefined medium" (https://undefined-medium.com/),
|
using the font "undefined medium" (https://undefined-medium.com/),
|
||||||
which is licensed under the SIL Open Font License, Version 1.1
|
which is licensed under the SIL Open Font License, Version 1.1
|
||||||
modified by DS
|
modified by DS
|
||||||
|
textures/base/pack/dig_btn.png
|
||||||
|
textures/base/pack/place_btn.png
|
||||||
|
derived by editing the text in aux1_btn.svg
|
||||||
|
|
||||||
|
Material Design, Google (Apache license v2.0):
|
||||||
|
textures/base/pack/contentdb_thumb_up.png
|
||||||
|
textures/base/pack/contentdb_thumb_down.png
|
||||||
|
textures/base/pack/contentdb_neutral.png
|
||||||
|
|
||||||
License of Luanti source code
|
License of Luanti source code
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
19
README.md
19
README.md
|
@ -1,13 +1,15 @@
|
||||||
Luanti (formerly Minetest)
|
<div align="center">
|
||||||
==========================
|
<img src="textures/base/pack/logo.png" width="32%">
|
||||||
|
<h1>Luanti (formerly Minetest)</h1>
|
||||||

|
<img src="https://github.com/luanti-org/luanti/workflows/build/badge.svg" alt="Build Status">
|
||||||
[](https://hosted.weblate.org/engage/minetest/?utm_source=widget)
|
<a href="https://hosted.weblate.org/engage/minetest/?utm_source=widget"><img src="https://hosted.weblate.org/widgets/minetest/-/svg-badge.svg" alt="Translation status"></a>
|
||||||
[](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)
|
<a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html"><img src="https://img.shields.io/badge/license-LGPLv2.1%2B-blue.svg" alt="License"></a>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
|
||||||
Luanti is a free open-source voxel game engine with easy modding and game creation.
|
Luanti is a free open-source voxel game engine with easy modding and game creation.
|
||||||
|
|
||||||
Copyright (C) 2010-2024 Perttu Ahola <celeron55@gmail.com>
|
Copyright (C) 2010-2025 Perttu Ahola <celeron55@gmail.com>
|
||||||
and contributors (see source file comments and the version control log)
|
and contributors (see source file comments and the version control log)
|
||||||
|
|
||||||
Table of Contents
|
Table of Contents
|
||||||
|
@ -28,7 +30,7 @@ Further documentation
|
||||||
- Website: https://www.luanti.org/
|
- Website: https://www.luanti.org/
|
||||||
- Wiki: https://wiki.luanti.org/
|
- Wiki: https://wiki.luanti.org/
|
||||||
- Forum: https://forum.luanti.org/
|
- Forum: https://forum.luanti.org/
|
||||||
- GitHub: https://github.com/minetest/minetest/
|
- GitHub: https://github.com/luanti-org/luanti/
|
||||||
- [Developer documentation](doc/developing/)
|
- [Developer documentation](doc/developing/)
|
||||||
- [doc/](doc/) directory of source distribution
|
- [doc/](doc/) directory of source distribution
|
||||||
|
|
||||||
|
@ -55,6 +57,7 @@ Some can be changed in the key config dialog in the settings tab.
|
||||||
| T | Chat |
|
| T | Chat |
|
||||||
| / | Command |
|
| / | Command |
|
||||||
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
|
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
|
||||||
|
| Shift + Esc | Exit directly to main menu from anywhere, bypassing pause menu |
|
||||||
| + | Increase view range |
|
| + | Increase view range |
|
||||||
| - | Decrease view range |
|
| - | Decrease view range |
|
||||||
| K | Enable/disable fly mode (needs fly privilege) |
|
| K | Enable/disable fly mode (needs fly privilege) |
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
diff --git a/android/app/src/main/java/org/libsdl/app/SDLActivity.java b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
diff --git a/android/app/src/main/java/org/libsdl/app/SDLActivity.java b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
||||||
index fd5a056e3..83e3cf657 100644
|
|
||||||
--- a/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
--- a/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
||||||
+++ b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
+++ b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
|
||||||
@@ -1345,7 +1345,12 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
@@ -1345,7 +1345,12 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||||
|
@ -9,7 +8,7 @@ index fd5a056e3..83e3cf657 100644
|
||||||
- if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
|
- if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
|
||||||
+ if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ||
|
+ if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ||
|
||||||
+ /*
|
+ /*
|
||||||
+ * CUSTOM ADDITION FOR MINETEST
|
+ * CUSTOM ADDITION FOR LUANTI
|
||||||
+ * should be upstreamed
|
+ * should be upstreamed
|
||||||
+ */
|
+ */
|
||||||
+ (source & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE) {
|
+ (source & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE) {
|
||||||
|
|
|
@ -22,14 +22,19 @@ package net.minetest.minetest;
|
||||||
|
|
||||||
import org.libsdl.app.SDLActivity;
|
import org.libsdl.app.SDLActivity;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
@ -91,6 +96,9 @@ public class GameActivity extends SDLActivity {
|
||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NotificationManager mNotifyManager;
|
||||||
|
private boolean gameNotificationShown = false;
|
||||||
|
|
||||||
public void showTextInputDialog(String hint, String current, int editType) {
|
public void showTextInputDialog(String hint, String current, int editType) {
|
||||||
runOnUiThread(() -> showTextInputDialogUI(hint, current, editType));
|
runOnUiThread(() -> showTextInputDialogUI(hint, current, editType));
|
||||||
}
|
}
|
||||||
|
@ -263,4 +271,67 @@ public class GameActivity extends SDLActivity {
|
||||||
public boolean hasPhysicalKeyboard() {
|
public boolean hasPhysicalKeyboard() {
|
||||||
return getContext().getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
|
return getContext().getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: share code with UnzipService.createNotification
|
||||||
|
private void updateGameNotification() {
|
||||||
|
if (mNotifyManager == null) {
|
||||||
|
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameNotificationShown) {
|
||||||
|
mNotifyManager.cancel(MainActivity.NOTIFICATION_ID_GAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification.Builder builder;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
|
||||||
|
} else {
|
||||||
|
builder = new Notification.Builder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent notificationIntent = new Intent(this, GameActivity.class);
|
||||||
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
|
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
|
int pendingIntentFlag = 0;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
pendingIntentFlag = PendingIntent.FLAG_MUTABLE;
|
||||||
|
}
|
||||||
|
PendingIntent intent = PendingIntent.getActivity(this, 0,
|
||||||
|
notificationIntent, pendingIntentFlag);
|
||||||
|
|
||||||
|
builder.setContentTitle(getString(R.string.game_notification_title))
|
||||||
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
|
.setContentIntent(intent)
|
||||||
|
.setOngoing(true);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
// This avoids a stuck notification if the app is killed while
|
||||||
|
// in-game: (1) if the user closes the app from the "Recents" screen
|
||||||
|
// or (2) if the system kills the app while it is in background.
|
||||||
|
// onStop is called too early to remove the notification and
|
||||||
|
// onDestroy is often not called at all, so there's this hack instead.
|
||||||
|
builder.setTimeoutAfter(16000);
|
||||||
|
|
||||||
|
// Replace the notification just before it expires as long as the app is
|
||||||
|
// running (and we're still in-game).
|
||||||
|
final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (gameNotificationShown) {
|
||||||
|
updateGameNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 15000);
|
||||||
|
}
|
||||||
|
|
||||||
|
mNotifyManager.notify(MainActivity.NOTIFICATION_ID_GAME, builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setPlayingNowNotification(boolean show) {
|
||||||
|
gameNotificationShown = show;
|
||||||
|
updateGameNotification();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,8 @@ import static net.minetest.minetest.UnzipService.*;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
public static final String NOTIFICATION_CHANNEL_ID = "Minetest channel";
|
public static final String NOTIFICATION_CHANNEL_ID = "Minetest channel";
|
||||||
|
public static final int NOTIFICATION_ID_UNZIP = 1;
|
||||||
|
public static final int NOTIFICATION_ID_GAME = 2;
|
||||||
|
|
||||||
private final static int versionCode = BuildConfig.VERSION_CODE;
|
private final static int versionCode = BuildConfig.VERSION_CODE;
|
||||||
private static final String SETTINGS = "MinetestSettings";
|
private static final String SETTINGS = "MinetestSettings";
|
||||||
|
|
|
@ -51,7 +51,6 @@ public class UnzipService extends IntentService {
|
||||||
public static final int SUCCESS = -1;
|
public static final int SUCCESS = -1;
|
||||||
public static final int FAILURE = -2;
|
public static final int FAILURE = -2;
|
||||||
public static final int INDETERMINATE = -3;
|
public static final int INDETERMINATE = -3;
|
||||||
private final int id = 1;
|
|
||||||
private NotificationManager mNotifyManager;
|
private NotificationManager mNotifyManager;
|
||||||
private boolean isSuccess = true;
|
private boolean isSuccess = true;
|
||||||
private String failureMessage;
|
private String failureMessage;
|
||||||
|
@ -100,11 +99,14 @@ public class UnzipService extends IntentService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: share code with GameActivity.updateGameNotification
|
||||||
@NonNull
|
@NonNull
|
||||||
private Notification.Builder createNotification() {
|
private Notification.Builder createNotification() {
|
||||||
Notification.Builder builder;
|
if (mNotifyManager == null) {
|
||||||
if (mNotifyManager == null)
|
|
||||||
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification.Builder builder;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
|
builder = new Notification.Builder(this, MainActivity.NOTIFICATION_CHANNEL_ID);
|
||||||
} else {
|
} else {
|
||||||
|
@ -128,7 +130,7 @@ public class UnzipService extends IntentService {
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setProgress(0, 0, true);
|
.setProgress(0, 0, true);
|
||||||
|
|
||||||
mNotifyManager.notify(id, builder.build());
|
mNotifyManager.notify(MainActivity.NOTIFICATION_ID_UNZIP, builder.build());
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,14 +202,14 @@ public class UnzipService extends IntentService {
|
||||||
} else {
|
} else {
|
||||||
notificationBuilder.setProgress(100, progress, false);
|
notificationBuilder.setProgress(100, progress, false);
|
||||||
}
|
}
|
||||||
mNotifyManager.notify(id, notificationBuilder.build());
|
mNotifyManager.notify(MainActivity.NOTIFICATION_ID_UNZIP, notificationBuilder.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
mNotifyManager.cancel(id);
|
mNotifyManager.cancel(MainActivity.NOTIFICATION_ID_UNZIP);
|
||||||
publishProgress(null, R.string.loading, isSuccess ? SUCCESS : FAILURE);
|
publishProgress(null, R.string.loading, isSuccess ? SUCCESS : FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,8 @@ import java.util.Locale;
|
||||||
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
|
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
|
||||||
private static final String TAG = "SDL";
|
private static final String TAG = "SDL";
|
||||||
private static final int SDL_MAJOR_VERSION = 2;
|
private static final int SDL_MAJOR_VERSION = 2;
|
||||||
private static final int SDL_MINOR_VERSION = 30;
|
private static final int SDL_MINOR_VERSION = 32;
|
||||||
private static final int SDL_MICRO_VERSION = 8;
|
private static final int SDL_MICRO_VERSION = 0;
|
||||||
/*
|
/*
|
||||||
// Display InputType.SOURCE/CLASS of events and devices
|
// Display InputType.SOURCE/CLASS of events and devices
|
||||||
//
|
//
|
||||||
|
@ -790,6 +790,9 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
SDLActivity.mFullscreenModeActive = false;
|
SDLActivity.mFullscreenModeActive = false;
|
||||||
}
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= 28 /* Android 9 (Pie) */) {
|
||||||
|
window.getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "error handling message, getContext() returned no Activity");
|
Log.e(TAG, "error handling message, getContext() returned no Activity");
|
||||||
|
@ -1347,7 +1350,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||||
|
|
||||||
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ||
|
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE ||
|
||||||
/*
|
/*
|
||||||
* CUSTOM ADDITION FOR MINETEST
|
* CUSTOM ADDITION FOR LUANTI
|
||||||
* should be upstreamed
|
* should be upstreamed
|
||||||
*/
|
*/
|
||||||
(source & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE) {
|
(source & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE) {
|
||||||
|
|
11
android/app/src/main/res/values-br/strings.xml
Normal file
11
android/app/src/main/res/values-br/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="unzip_notification_title">O kargañ Luanti</string>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">O kargañ…</string>
|
||||||
|
<string name="notification_channel_description">Evezhiadennoù gant Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Nebeutoc\'h eget ur vunutenn…</string>
|
||||||
|
<string name="ime_dialog_done">Graet</string>
|
||||||
|
<string name="no_web_browser">Merdeer web ebet bet kavet</string>
|
||||||
|
<string name="notification_channel_name">Evezhiadennoù hollek</string>
|
||||||
|
</resources>
|
|
@ -1,11 +1,12 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="label">Luanti</string>
|
<string name="label">Luanti</string>
|
||||||
<string name="loading">Lädt…</string>
|
<string name="loading">Laden …</string>
|
||||||
<string name="unzip_notification_title">Luanti lädt</string>
|
<string name="unzip_notification_title">Luanti lädt</string>
|
||||||
<string name="unzip_notification_description">Weniger als 1 Minute…</string>
|
<string name="unzip_notification_description">Weniger als 1 Minute …</string>
|
||||||
<string name="ime_dialog_done">Fertig</string>
|
<string name="ime_dialog_done">Fertig</string>
|
||||||
<string name="no_web_browser">Kein Web-Browser gefunden</string>
|
<string name="no_web_browser">Keinen Web-Browser gefunden</string>
|
||||||
<string name="notification_channel_name">Allgemeine Benachrichtigung</string>
|
<string name="notification_channel_name">Allgemeine Benachrichtigung</string>
|
||||||
<string name="notification_channel_description">Benachrichtigungen von Luanti</string>
|
<string name="notification_channel_description">Benachrichtigungen von Luanti</string>
|
||||||
</resources>
|
<string name="game_notification_title">Luanti läuft</string>
|
||||||
|
</resources>
|
||||||
|
|
11
android/app/src/main/res/values-fa/strings.xml
Normal file
11
android/app/src/main/res/values-fa/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="unzip_notification_description">کمتر از 1 دقیقه…</string>
|
||||||
|
<string name="loading">در حال بارگذاری…</string>
|
||||||
|
<string name="ime_dialog_done">انجام شد</string>
|
||||||
|
<string name="label">لوآنتی</string>
|
||||||
|
<string name="unzip_notification_title">در حال بارگذاری لوآنتی</string>
|
||||||
|
<string name="notification_channel_name">نوتیفیکیشن عمومی</string>
|
||||||
|
<string name="notification_channel_description">نوتیفیکیشن از لوآنتی</string>
|
||||||
|
<string name="no_web_browser">هیچ مرورگری یافت نشد</string>
|
||||||
|
</resources>
|
12
android/app/src/main/res/values-fr/strings.xml
Normal file
12
android/app/src/main/res/values-fr/strings.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Chargement…</string>
|
||||||
|
<string name="notification_channel_name">Notification générale</string>
|
||||||
|
<string name="notification_channel_description">Notifications de Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Chargement de Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Moins d\'une minute…</string>
|
||||||
|
<string name="ime_dialog_done">Terminé</string>
|
||||||
|
<string name="no_web_browser">Aucun navigateur web trouvé</string>
|
||||||
|
<string name="game_notification_title">Luanti est en cours d\'exécution</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-gl/strings.xml
Normal file
11
android/app/src/main/res/values-gl/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Cargando…</string>
|
||||||
|
<string name="notification_channel_name">Notificación xeral</string>
|
||||||
|
<string name="notification_channel_description">Notificacións de Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Cargando Luanti</string>
|
||||||
|
<string name="unzip_notification_description">Menos de 1 minuto…</string>
|
||||||
|
<string name="ime_dialog_done">Feito</string>
|
||||||
|
<string name="no_web_browser">Non se atopou ningún navegador web</string>
|
||||||
|
</resources>
|
|
@ -8,4 +8,5 @@
|
||||||
<string name="unzip_notification_description">Kurang dari 1 menit…</string>
|
<string name="unzip_notification_description">Kurang dari 1 menit…</string>
|
||||||
<string name="notification_channel_description">Pemberitahuan dari Luanti</string>
|
<string name="notification_channel_description">Pemberitahuan dari Luanti</string>
|
||||||
<string name="unzip_notification_title">Memuat Luanti…</string>
|
<string name="unzip_notification_title">Memuat Luanti…</string>
|
||||||
</resources>
|
<string name="game_notification_title">Luanti sedang berjalan</string>
|
||||||
|
</resources>
|
||||||
|
|
11
android/app/src/main/res/values-ro/strings.xml
Normal file
11
android/app/src/main/res/values-ro/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Se încarcă…</string>
|
||||||
|
<string name="notification_channel_name">Notificare generală</string>
|
||||||
|
<string name="notification_channel_description">Notificări de la Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Luanti pornește</string>
|
||||||
|
<string name="unzip_notification_description">Sub 1 minut…</string>
|
||||||
|
<string name="ime_dialog_done">Gata</string>
|
||||||
|
<string name="no_web_browser">Niciun navigator web găsit</string>
|
||||||
|
</resources>
|
|
@ -1,11 +1,12 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="unzip_notification_title">Загрузка Luanti</string>
|
<string name="unzip_notification_title">Загрузка Luanti</string>
|
||||||
<string name="unzip_notification_description">Меньше чам за 1 минуту…</string>
|
<string name="unzip_notification_description">Менее 1 минуты…</string>
|
||||||
<string name="ime_dialog_done">Готово</string>
|
<string name="ime_dialog_done">Готово</string>
|
||||||
<string name="label">Luаnti</string>
|
<string name="label">Luаnti</string>
|
||||||
<string name="notification_channel_description">Уведомления от Luanti</string>
|
<string name="notification_channel_description">Уведомления от Luanti</string>
|
||||||
<string name="notification_channel_name">Основные уведомления</string>
|
<string name="notification_channel_name">Основные уведомления</string>
|
||||||
<string name="loading">Загрузка…</string>
|
<string name="loading">Загрузка…</string>
|
||||||
<string name="no_web_browser">Не найдено веб-браузера</string>
|
<string name="no_web_browser">Не найдено веб-браузера</string>
|
||||||
</resources>
|
<string name="game_notification_title">Luanti запущено</string>
|
||||||
|
</resources>
|
||||||
|
|
11
android/app/src/main/res/values-sl/strings.xml
Normal file
11
android/app/src/main/res/values-sl/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="no_web_browser">Ni bil najden spletni brskalnik</string>
|
||||||
|
<string name="notification_channel_name">Glavno obvestilo</string>
|
||||||
|
<string name="loading">Nalaganje …</string>
|
||||||
|
<string name="unzip_notification_description">Manj kot 1 minuta …</string>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="notification_channel_description">Obvestilo od Luantia</string>
|
||||||
|
<string name="ime_dialog_done">Končano!l</string>
|
||||||
|
<string name="unzip_notification_title">Nalaganje Luantia</string>
|
||||||
|
</resources>
|
12
android/app/src/main/res/values-sv/strings.xml
Normal file
12
android/app/src/main/res/values-sv/strings.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">Laddar…</string>
|
||||||
|
<string name="unzip_notification_description">Mindre än 1 minut…</string>
|
||||||
|
<string name="ime_dialog_done">Färdig</string>
|
||||||
|
<string name="no_web_browser">Ingen webbläsare hittades</string>
|
||||||
|
<string name="notification_channel_name">Allmän notifikation</string>
|
||||||
|
<string name="notification_channel_description">Notifikationer från Luanti</string>
|
||||||
|
<string name="unzip_notification_title">Laddar Luanti</string>
|
||||||
|
<string name="game_notification_title">Luanti är igång</string>
|
||||||
|
</resources>
|
2
android/app/src/main/res/values-ta/strings.xml
Normal file
2
android/app/src/main/res/values-ta/strings.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
11
android/app/src/main/res/values-uk/strings.xml
Normal file
11
android/app/src/main/res/values-uk/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="no_web_browser">Браузерів не знайдено</string>
|
||||||
|
<string name="loading">Завантаження…</string>
|
||||||
|
<string name="notification_channel_name">Загальні сповіщення</string>
|
||||||
|
<string name="unzip_notification_title">Luanti завантажується</string>
|
||||||
|
<string name="unzip_notification_description">Менше за 1 хвилину…</string>
|
||||||
|
<string name="ime_dialog_done">Готово</string>
|
||||||
|
<string name="notification_channel_description">Сповіщення від Luanti</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-zh-rCN/strings.xml
Normal file
11
android/app/src/main/res/values-zh-rCN/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="loading">加载中…</string>
|
||||||
|
<string name="notification_channel_name">一般通知</string>
|
||||||
|
<string name="notification_channel_description">Luanti 的通知</string>
|
||||||
|
<string name="unzip_notification_title">加载 Luanti 中</string>
|
||||||
|
<string name="unzip_notification_description">不到1分钟…</string>
|
||||||
|
<string name="ime_dialog_done">完成</string>
|
||||||
|
<string name="no_web_browser">未找到网页浏览器</string>
|
||||||
|
</resources>
|
11
android/app/src/main/res/values-zh-rTW/strings.xml
Normal file
11
android/app/src/main/res/values-zh-rTW/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="loading">載入中…</string>
|
||||||
|
<string name="notification_channel_name">一般通知</string>
|
||||||
|
<string name="notification_channel_description">Luanti 的通知</string>
|
||||||
|
<string name="unzip_notification_description">不到1分鐘…</string>
|
||||||
|
<string name="ime_dialog_done">完畢</string>
|
||||||
|
<string name="no_web_browser">未找到任何網頁瀏覽器</string>
|
||||||
|
<string name="label">Luanti</string>
|
||||||
|
<string name="unzip_notification_title">載入Luanti中</string>
|
||||||
|
</resources>
|
|
@ -6,6 +6,7 @@
|
||||||
<string name="notification_channel_description">Notifications from Luanti</string>
|
<string name="notification_channel_description">Notifications from Luanti</string>
|
||||||
<string name="unzip_notification_title">Loading Luanti</string>
|
<string name="unzip_notification_title">Loading Luanti</string>
|
||||||
<string name="unzip_notification_description">Less than 1 minute…</string>
|
<string name="unzip_notification_description">Less than 1 minute…</string>
|
||||||
|
<string name="game_notification_title">Luanti is running</string>
|
||||||
<string name="ime_dialog_done">Done</string>
|
<string name="ime_dialog_done">Done</string>
|
||||||
<string name="no_web_browser">No web browser found</string>
|
<string name="no_web_browser">No web browser found</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
project.ext.set("versionMajor", 5) // Version Major
|
project.ext.set("versionMajor", 5) // Version Major
|
||||||
project.ext.set("versionMinor", 11) // Version Minor
|
project.ext.set("versionMinor", 13) // Version Minor
|
||||||
project.ext.set("versionPatch", 0) // Version Patch
|
project.ext.set("versionPatch", 0) // Version Patch
|
||||||
// ^ keep in sync with cmake
|
// ^ keep in sync with cmake
|
||||||
|
|
||||||
|
|
148
android/icons/dig_btn.svg
Normal file
148
android/icons/dig_btn.svg
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
inkscape:export-ydpi="24.000002"
|
||||||
|
inkscape:export-xdpi="24.000002"
|
||||||
|
inkscape:export-filename="../../textures/base/pack/dig_btn.png"
|
||||||
|
sodipodi:docname="dig_btn.svg"
|
||||||
|
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||||
|
id="svg8"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 135.46666 135.46667"
|
||||||
|
height="512"
|
||||||
|
width="512"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:snap-bbox-midpoints="true"
|
||||||
|
inkscape:snap-others="true"
|
||||||
|
inkscape:snap-object-midpoints="false"
|
||||||
|
inkscape:snap-to-guides="true"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:snap-page="true"
|
||||||
|
inkscape:snap-grids="false"
|
||||||
|
inkscape:pagecheckerboard="false"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:window-y="32"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-height="1011"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
units="px"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:current-layer="layer2"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:cy="266.84627"
|
||||||
|
inkscape:cx="201.24514"
|
||||||
|
inkscape:zoom="1.4633894"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
borderopacity="1.0"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#404040"
|
||||||
|
id="base"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:grid
|
||||||
|
empopacity="0.25098039"
|
||||||
|
empcolor="#40ff40"
|
||||||
|
opacity="0.1254902"
|
||||||
|
color="#40ff40"
|
||||||
|
empspacing="4"
|
||||||
|
spacingy="0.26458333"
|
||||||
|
spacingx="0.26458333"
|
||||||
|
id="grid16"
|
||||||
|
type="xygrid"
|
||||||
|
originx="0"
|
||||||
|
originy="0"
|
||||||
|
units="px"
|
||||||
|
visible="true" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
style="display:inline"
|
||||||
|
inkscape:label="Layer 2"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:groupmode="layer">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path7055"
|
||||||
|
d=""
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path7035"
|
||||||
|
d=""
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path7005"
|
||||||
|
d=""
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path5127"
|
||||||
|
d=""
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<text
|
||||||
|
transform="scale(1.0078883,0.99217343)"
|
||||||
|
id="text4716"
|
||||||
|
y="85.59491"
|
||||||
|
x="67.78315"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:48.4785px;line-height:1.25;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
style="fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
y="85.59491"
|
||||||
|
x="67.78315"
|
||||||
|
id="tspan4714"
|
||||||
|
sodipodi:role="line">LMB</tspan></text>
|
||||||
|
<flowRoot
|
||||||
|
transform="scale(0.26458333)"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||||
|
id="flowRoot4718"
|
||||||
|
xml:space="preserve"><flowRegion
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||||
|
id="flowRegion4720"><rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||||
|
y="124.10143"
|
||||||
|
x="264.65997"
|
||||||
|
height="136.37059"
|
||||||
|
width="157.5838"
|
||||||
|
id="rect4722" /></flowRegion><flowPara
|
||||||
|
id="flowPara4724" /></flowRoot>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
148
android/icons/place_btn.svg
Normal file
148
android/icons/place_btn.svg
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
inkscape:export-ydpi="24.000002"
|
||||||
|
inkscape:export-xdpi="24.000002"
|
||||||
|
inkscape:export-filename="../../textures/base/pack/place_btn.png"
|
||||||
|
sodipodi:docname="place_btn.svg"
|
||||||
|
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||||
|
id="svg8"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 135.46666 135.46667"
|
||||||
|
height="512"
|
||||||
|
width="512"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:snap-bbox-midpoints="true"
|
||||||
|
inkscape:snap-others="true"
|
||||||
|
inkscape:snap-object-midpoints="false"
|
||||||
|
inkscape:snap-to-guides="true"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:snap-page="true"
|
||||||
|
inkscape:snap-grids="false"
|
||||||
|
inkscape:pagecheckerboard="false"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:window-y="32"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-height="1011"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
units="px"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:current-layer="layer2"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:cy="266.84627"
|
||||||
|
inkscape:cx="201.24514"
|
||||||
|
inkscape:zoom="1.4633894"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
borderopacity="1.0"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#404040"
|
||||||
|
id="base"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:grid
|
||||||
|
empopacity="0.25098039"
|
||||||
|
empcolor="#40ff40"
|
||||||
|
opacity="0.1254902"
|
||||||
|
color="#40ff40"
|
||||||
|
empspacing="4"
|
||||||
|
spacingy="0.26458333"
|
||||||
|
spacingx="0.26458333"
|
||||||
|
id="grid16"
|
||||||
|
type="xygrid"
|
||||||
|
originx="0"
|
||||||
|
originy="0"
|
||||||
|
units="px"
|
||||||
|
visible="true" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
style="display:inline"
|
||||||
|
inkscape:label="Layer 2"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:groupmode="layer">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path7055"
|
||||||
|
d=""
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path7035"
|
||||||
|
d=""
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path7005"
|
||||||
|
d=""
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path5127"
|
||||||
|
d=""
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<text
|
||||||
|
transform="scale(1.0078883,0.99217343)"
|
||||||
|
id="text4716"
|
||||||
|
y="85.59491"
|
||||||
|
x="67.78315"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:48.4785px;line-height:1.25;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
style="fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
y="85.59491"
|
||||||
|
x="67.78315"
|
||||||
|
id="tspan4714"
|
||||||
|
sodipodi:role="line">RMB</tspan></text>
|
||||||
|
<flowRoot
|
||||||
|
transform="scale(0.26458333)"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||||
|
id="flowRoot4718"
|
||||||
|
xml:space="preserve"><flowRegion
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||||
|
id="flowRegion4720"><rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||||
|
y="124.10143"
|
||||||
|
x="264.65997"
|
||||||
|
height="136.37059"
|
||||||
|
width="157.5838"
|
||||||
|
id="rect4722" /></flowRegion><flowPara
|
||||||
|
id="flowPara4724" /></flowRoot>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
|
@ -52,7 +52,7 @@ if (new File(depsDir, 'armeabi-v7a').exists()) {
|
||||||
task downloadDeps(type: Download) {
|
task downloadDeps(type: Download) {
|
||||||
def depsZip = new File(buildDir, 'deps.zip')
|
def depsZip = new File(buildDir, 'deps.zip')
|
||||||
|
|
||||||
src 'https://github.com/minetest/minetest_android_deps/releases/download/latest/deps-lite.zip'
|
src 'https://github.com/luanti-org/luanti_android_deps/releases/download/latest/deps-lite.zip'
|
||||||
dest depsZip
|
dest depsZip
|
||||||
overwrite false
|
overwrite false
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,8 @@ do
|
||||||
all.registered_craftitems = {}
|
all.registered_craftitems = {}
|
||||||
all.registered_tools = {}
|
all.registered_tools = {}
|
||||||
for k, v in pairs(all.registered_items) do
|
for k, v in pairs(all.registered_items) do
|
||||||
-- Disable further modification
|
-- Ignore new keys
|
||||||
setmetatable(v, {__newindex = {}})
|
setmetatable(v, {__newindex = function() end})
|
||||||
-- Reassemble the other tables
|
-- Reassemble the other tables
|
||||||
if v.type == "node" then
|
if v.type == "node" then
|
||||||
getmetatable(v).__index = all.nodedef_default
|
getmetatable(v).__index = all.nodedef_default
|
||||||
|
@ -59,6 +59,9 @@ end
|
||||||
local alias_metatable = {
|
local alias_metatable = {
|
||||||
__index = function(t, name)
|
__index = function(t, name)
|
||||||
return rawget(t, core.registered_aliases[name])
|
return rawget(t, core.registered_aliases[name])
|
||||||
|
end,
|
||||||
|
__newindex = function()
|
||||||
|
error("table is read-only")
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
setmetatable(core.registered_items, alias_metatable)
|
setmetatable(core.registered_items, alias_metatable)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
core.log("info", "Initializing asynchronous environment")
|
core.log("info", "Initializing asynchronous environment")
|
||||||
|
|
||||||
|
|
||||||
function core.job_processor(func, serialized_param)
|
function core.job_processor(func, serialized_param)
|
||||||
local param = core.deserialize(serialized_param)
|
local param = core.deserialize(serialized_param)
|
||||||
|
|
||||||
|
@ -7,3 +8,15 @@ function core.job_processor(func, serialized_param)
|
||||||
|
|
||||||
return retval or core.serialize(nil)
|
return retval or core.serialize(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function core.get_http_accept_languages()
|
||||||
|
local languages
|
||||||
|
local current_language = core.get_language()
|
||||||
|
if current_language ~= "" then
|
||||||
|
languages = { current_language, "en;q=0.8" }
|
||||||
|
else
|
||||||
|
languages = { "en" }
|
||||||
|
end
|
||||||
|
return "Accept-Language: " .. table.concat(languages, ", ")
|
||||||
|
end
|
||||||
|
|
|
@ -2,7 +2,10 @@ local scriptpath = core.get_builtin_path()
|
||||||
local clientpath = scriptpath.."client"..DIR_DELIM
|
local clientpath = scriptpath.."client"..DIR_DELIM
|
||||||
local commonpath = scriptpath.."common"..DIR_DELIM
|
local commonpath = scriptpath.."common"..DIR_DELIM
|
||||||
|
|
||||||
dofile(clientpath .. "register.lua")
|
local builtin_shared = {}
|
||||||
|
|
||||||
|
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
|
||||||
|
assert(loadfile(clientpath .. "register.lua"))(builtin_shared)
|
||||||
dofile(commonpath .. "after.lua")
|
dofile(commonpath .. "after.lua")
|
||||||
dofile(commonpath .. "mod_storage.lua")
|
dofile(commonpath .. "mod_storage.lua")
|
||||||
dofile(commonpath .. "chatcommands.lua")
|
dofile(commonpath .. "chatcommands.lua")
|
||||||
|
|
|
@ -1,68 +1,6 @@
|
||||||
core.callback_origins = {}
|
local builtin_shared = ...
|
||||||
|
|
||||||
local getinfo = debug.getinfo
|
local make_registration = builtin_shared.make_registration
|
||||||
debug.getinfo = nil
|
|
||||||
|
|
||||||
--- Runs given callbacks.
|
|
||||||
--
|
|
||||||
-- Note: this function is also called from C++
|
|
||||||
-- @tparam table callbacks a table with registered callbacks, like `core.registered_on_*`
|
|
||||||
-- @tparam number mode a RunCallbacksMode, as defined in src/script/common/c_internal.h
|
|
||||||
-- @param ... arguments for the callback
|
|
||||||
-- @return depends on mode
|
|
||||||
function core.run_callbacks(callbacks, mode, ...)
|
|
||||||
assert(type(callbacks) == "table")
|
|
||||||
local cb_len = #callbacks
|
|
||||||
if cb_len == 0 then
|
|
||||||
if mode == 2 or mode == 3 then
|
|
||||||
return true
|
|
||||||
elseif mode == 4 or mode == 5 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local ret
|
|
||||||
for i = 1, cb_len do
|
|
||||||
local cb_ret = callbacks[i](...)
|
|
||||||
|
|
||||||
if mode == 0 and i == 1 or mode == 1 and i == cb_len then
|
|
||||||
ret = cb_ret
|
|
||||||
elseif mode == 2 then
|
|
||||||
if not cb_ret or i == 1 then
|
|
||||||
ret = cb_ret
|
|
||||||
end
|
|
||||||
elseif mode == 3 then
|
|
||||||
if cb_ret then
|
|
||||||
return cb_ret
|
|
||||||
end
|
|
||||||
ret = cb_ret
|
|
||||||
elseif mode == 4 then
|
|
||||||
if (cb_ret and not ret) or i == 1 then
|
|
||||||
ret = cb_ret
|
|
||||||
end
|
|
||||||
elseif mode == 5 and cb_ret then
|
|
||||||
return cb_ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Callback registration
|
|
||||||
--
|
|
||||||
|
|
||||||
local function make_registration()
|
|
||||||
local t = {}
|
|
||||||
local registerfunc = function(func)
|
|
||||||
t[#t + 1] = func
|
|
||||||
core.callback_origins[func] = {
|
|
||||||
mod = core.get_current_modname() or "??",
|
|
||||||
name = getinfo(1, "n").name or "??"
|
|
||||||
}
|
|
||||||
--local origin = core.callback_origins[func]
|
|
||||||
--print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
|
|
||||||
end
|
|
||||||
return t, registerfunc
|
|
||||||
end
|
|
||||||
|
|
||||||
core.registered_globalsteps, core.register_globalstep = make_registration()
|
core.registered_globalsteps, core.register_globalstep = make_registration()
|
||||||
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
|
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2013 sapier
|
-- Copyright (C) 2013 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- TODO improve doc --
|
-- TODO improve doc --
|
||||||
|
|
|
@ -90,7 +90,7 @@ local facedir_to_dir_map = {
|
||||||
1, 4, 3, 2,
|
1, 4, 3, 2,
|
||||||
}
|
}
|
||||||
function core.facedir_to_dir(facedir)
|
function core.facedir_to_dir(facedir)
|
||||||
return facedir_to_dir[facedir_to_dir_map[facedir % 32]]
|
return vector.copy(facedir_to_dir[facedir_to_dir_map[facedir % 32]])
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.dir_to_fourdir(dir)
|
function core.dir_to_fourdir(dir)
|
||||||
|
@ -110,7 +110,7 @@ function core.dir_to_fourdir(dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.fourdir_to_dir(fourdir)
|
function core.fourdir_to_dir(fourdir)
|
||||||
return facedir_to_dir[facedir_to_dir_map[fourdir % 4]]
|
return vector.copy(facedir_to_dir[facedir_to_dir_map[fourdir % 4]])
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.dir_to_wallmounted(dir)
|
function core.dir_to_wallmounted(dir)
|
||||||
|
@ -147,7 +147,7 @@ local wallmounted_to_dir = {
|
||||||
vector.new( 0, -1, 0),
|
vector.new( 0, -1, 0),
|
||||||
}
|
}
|
||||||
function core.wallmounted_to_dir(wallmounted)
|
function core.wallmounted_to_dir(wallmounted)
|
||||||
return wallmounted_to_dir[wallmounted % 8]
|
return vector.copy(wallmounted_to_dir[wallmounted % 8])
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.dir_to_yaw(dir)
|
function core.dir_to_yaw(dir)
|
||||||
|
|
15
builtin/common/menu.lua
Normal file
15
builtin/common/menu.lua
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
-- Luanti
|
||||||
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
-- These colors are used by the main menu and the settings menu
|
||||||
|
mt_color_grey = "#AAAAAA"
|
||||||
|
mt_color_blue = "#6389FF"
|
||||||
|
mt_color_lightblue = "#99CCFF"
|
||||||
|
mt_color_green = "#72FF63"
|
||||||
|
mt_color_dark_green = "#25C191"
|
||||||
|
mt_color_orange = "#FF8800"
|
||||||
|
mt_color_red = "#FF3300"
|
||||||
|
|
||||||
|
function core.are_keycodes_equal(k1, k2)
|
||||||
|
return core.normalize_keycode(k1) == core.normalize_keycode(k2)
|
||||||
|
end
|
|
@ -7,18 +7,21 @@ local math = math
|
||||||
local function basic_dump(o)
|
local function basic_dump(o)
|
||||||
local tp = type(o)
|
local tp = type(o)
|
||||||
if tp == "number" then
|
if tp == "number" then
|
||||||
return tostring(o)
|
local s = tostring(o)
|
||||||
|
if tonumber(s) == o then
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
-- Prefer an exact representation over a compact representation.
|
||||||
|
-- e.g. basic_dump(0.3) == "0.3",
|
||||||
|
-- but basic_dump(0.1 + 0.2) == "0.30000000000000004"
|
||||||
|
-- so the user can see that 0.1 + 0.2 ~= 0.3
|
||||||
|
return string.format("%.17g", o)
|
||||||
elseif tp == "string" then
|
elseif tp == "string" then
|
||||||
return string.format("%q", o)
|
return string.format("%q", o)
|
||||||
elseif tp == "boolean" then
|
elseif tp == "boolean" then
|
||||||
return tostring(o)
|
return tostring(o)
|
||||||
elseif tp == "nil" then
|
elseif tp == "nil" then
|
||||||
return "nil"
|
return "nil"
|
||||||
-- Uncomment for full function dumping support.
|
|
||||||
-- Not currently enabled because bytecode isn't very human-readable and
|
|
||||||
-- dump's output is intended for humans.
|
|
||||||
--elseif tp == "function" then
|
|
||||||
-- return string.format("loadstring(%q)", string.dump(o))
|
|
||||||
elseif tp == "userdata" then
|
elseif tp == "userdata" then
|
||||||
return tostring(o)
|
return tostring(o)
|
||||||
else
|
else
|
||||||
|
@ -105,65 +108,141 @@ function dump2(o, name, dumped)
|
||||||
return string.format("%s = {}\n%s", name, table.concat(t))
|
return string.format("%s = {}\n%s", name, table.concat(t))
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
-- This dumps values in a one-statement format.
|
-- This dumps values in a human-readable expression format.
|
||||||
|
-- If possible, the resulting string should evaluate to an equivalent value if loaded and executed.
|
||||||
-- For example, {test = {"Testing..."}} becomes:
|
-- For example, {test = {"Testing..."}} becomes:
|
||||||
-- [[{
|
-- [[{
|
||||||
-- test = {
|
-- test = {
|
||||||
-- "Testing..."
|
-- "Testing..."
|
||||||
-- }
|
-- }
|
||||||
-- }]]
|
-- }]]
|
||||||
-- This supports tables as keys, but not circular references.
|
function dump(value, indent)
|
||||||
-- It performs poorly with multiple references as it writes out the full
|
|
||||||
-- table each time.
|
|
||||||
-- The indent field specifies a indentation string, it defaults to a tab.
|
|
||||||
-- Use the empty string to disable indentation.
|
|
||||||
-- The dumped and level arguments are internal-only.
|
|
||||||
|
|
||||||
function dump(o, indent, nested, level)
|
|
||||||
local t = type(o)
|
|
||||||
if not level and t == "userdata" then
|
|
||||||
-- when userdata (e.g. player) is passed directly, print its metatable:
|
|
||||||
return "userdata metatable: " .. dump(getmetatable(o))
|
|
||||||
end
|
|
||||||
if t ~= "table" then
|
|
||||||
return basic_dump(o)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Contains table -> true/nil of currently nested tables
|
|
||||||
nested = nested or {}
|
|
||||||
if nested[o] then
|
|
||||||
return "<circular reference>"
|
|
||||||
end
|
|
||||||
nested[o] = true
|
|
||||||
indent = indent or "\t"
|
indent = indent or "\t"
|
||||||
level = level or 1
|
local newline = indent == "" and "" or "\n"
|
||||||
|
|
||||||
local ret = {}
|
local rope = {}
|
||||||
local dumped_indexes = {}
|
local write
|
||||||
for i, v in ipairs(o) do
|
do
|
||||||
ret[#ret + 1] = dump(v, indent, nested, level + 1)
|
-- Keeping the length of the table as a local variable is *much*
|
||||||
dumped_indexes[i] = true
|
-- faster than invoking the length operator.
|
||||||
end
|
-- See https://gitspartv.github.io/LuaJIT-Benchmarks/#test12.
|
||||||
for k, v in pairs(o) do
|
local i = 0
|
||||||
if not dumped_indexes[k] then
|
function write(str)
|
||||||
if type(k) ~= "string" or not is_valid_identifier(k) then
|
i = i + 1
|
||||||
k = "["..dump(k, indent, nested, level + 1).."]"
|
rope[i] = str
|
||||||
end
|
|
||||||
v = dump(v, indent, nested, level + 1)
|
|
||||||
ret[#ret + 1] = k.." = "..v
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
nested[o] = nil
|
|
||||||
if indent ~= "" then
|
local n_refs = {}
|
||||||
local indent_str = "\n"..string.rep(indent, level)
|
local function count_refs(val)
|
||||||
local end_indent_str = "\n"..string.rep(indent, level - 1)
|
if type(val) ~= "table" then
|
||||||
return string.format("{%s%s%s}",
|
return
|
||||||
indent_str,
|
end
|
||||||
table.concat(ret, ","..indent_str),
|
local tbl = val
|
||||||
end_indent_str)
|
if n_refs[tbl] then
|
||||||
|
n_refs[tbl] = n_refs[tbl] + 1
|
||||||
|
return
|
||||||
|
end
|
||||||
|
n_refs[tbl] = 1
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
count_refs(k)
|
||||||
|
count_refs(v)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return "{"..table.concat(ret, ", ").."}"
|
count_refs(value)
|
||||||
|
|
||||||
|
local refs = {}
|
||||||
|
local cur_ref = 1
|
||||||
|
local function write_value(val, level)
|
||||||
|
if type(val) ~= "table" then
|
||||||
|
write(basic_dump(val))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local tbl = val
|
||||||
|
if refs[tbl] then
|
||||||
|
write(refs[tbl])
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if n_refs[val] > 1 then
|
||||||
|
refs[val] = ("getref(%d)"):format(cur_ref)
|
||||||
|
write(("setref(%d)"):format(cur_ref))
|
||||||
|
cur_ref = cur_ref + 1
|
||||||
|
end
|
||||||
|
write("{")
|
||||||
|
if next(tbl) == nil then
|
||||||
|
write("}")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
write(newline)
|
||||||
|
|
||||||
|
local function write_entry(k, v)
|
||||||
|
write(indent:rep(level))
|
||||||
|
write("[")
|
||||||
|
write_value(k, level + 1)
|
||||||
|
write("] = ")
|
||||||
|
write_value(v, level + 1)
|
||||||
|
write(",")
|
||||||
|
write(newline)
|
||||||
|
end
|
||||||
|
|
||||||
|
local keys = {string = {}, number = {}}
|
||||||
|
for k in pairs(tbl) do
|
||||||
|
local t = type(k)
|
||||||
|
if keys[t] then
|
||||||
|
table.insert(keys[t], k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Write string-keyed entries
|
||||||
|
table.sort(keys.string)
|
||||||
|
for _, k in ipairs(keys.string) do
|
||||||
|
local v = val[k]
|
||||||
|
if is_valid_identifier(k) then
|
||||||
|
write(indent:rep(level))
|
||||||
|
write(k)
|
||||||
|
write(" = ")
|
||||||
|
write_value(v, level + 1)
|
||||||
|
write(",")
|
||||||
|
write(newline)
|
||||||
|
else
|
||||||
|
write_entry(k, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Write number-keyed entries
|
||||||
|
local len = 0
|
||||||
|
for i in ipairs(tbl) do
|
||||||
|
len = i
|
||||||
|
end
|
||||||
|
if #keys.number == len then -- table is a list
|
||||||
|
for _, v in ipairs(tbl) do
|
||||||
|
write(indent:rep(level))
|
||||||
|
write_value(v, level + 1)
|
||||||
|
write(",")
|
||||||
|
write(newline)
|
||||||
|
end
|
||||||
|
else -- table harbors arbitrary number keys
|
||||||
|
table.sort(keys.number)
|
||||||
|
for _, k in ipairs(keys.number) do
|
||||||
|
write_entry(k, tbl[k])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Write all remaining entries
|
||||||
|
for k, v in pairs(val) do
|
||||||
|
if not keys[type(k)] then
|
||||||
|
write_entry(k, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
write(indent:rep(level - 1))
|
||||||
|
write("}")
|
||||||
|
end
|
||||||
|
write_value(value, 1)
|
||||||
|
return table.concat(rope)
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
@ -457,18 +536,37 @@ do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function table.copy(t, seen)
|
local function table_copy(value, preserve_metatables)
|
||||||
local n = {}
|
local seen = {}
|
||||||
seen = seen or {}
|
local function copy(val)
|
||||||
seen[t] = n
|
if type(val) ~= "table" then
|
||||||
for k, v in pairs(t) do
|
return val
|
||||||
n[(type(k) == "table" and (seen[k] or table.copy(k, seen))) or k] =
|
end
|
||||||
(type(v) == "table" and (seen[v] or table.copy(v, seen))) or v
|
local t = val
|
||||||
|
if seen[t] then
|
||||||
|
return seen[t]
|
||||||
|
end
|
||||||
|
local res = {}
|
||||||
|
seen[t] = res
|
||||||
|
for k, v in pairs(t) do
|
||||||
|
res[copy(k)] = copy(v)
|
||||||
|
end
|
||||||
|
if preserve_metatables then
|
||||||
|
setmetatable(res, getmetatable(t))
|
||||||
|
end
|
||||||
|
return res
|
||||||
end
|
end
|
||||||
return n
|
return copy(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function table.copy(value)
|
||||||
|
return table_copy(value, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.copy_with_metatables(value)
|
||||||
|
return table_copy(value, true)
|
||||||
|
end
|
||||||
|
|
||||||
function table.insert_all(t, other)
|
function table.insert_all(t, other)
|
||||||
if table.move then -- LuaJIT
|
if table.move then -- LuaJIT
|
||||||
|
@ -536,6 +634,10 @@ if core.gettext then -- for client and mainmenu
|
||||||
function fgettext(text, ...)
|
function fgettext(text, ...)
|
||||||
return core.formspec_escape(fgettext_ne(text, ...))
|
return core.formspec_escape(fgettext_ne(text, ...))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function hgettext(text, ...)
|
||||||
|
return core.hypertext_escape(fgettext_ne(text, ...))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ESCAPE_CHAR = string.char(0x1b)
|
local ESCAPE_CHAR = string.char(0x1b)
|
||||||
|
|
|
@ -54,7 +54,8 @@ function builtin_shared.make_registration()
|
||||||
local registerfunc = function(func)
|
local registerfunc = function(func)
|
||||||
t[#t + 1] = func
|
t[#t + 1] = func
|
||||||
core.callback_origins[func] = {
|
core.callback_origins[func] = {
|
||||||
mod = core.get_current_modname() or "??",
|
-- may be nil or return nil
|
||||||
|
mod = core.get_current_modname and core.get_current_modname() or "??",
|
||||||
name = debug.getinfo(1, "n").name or "??"
|
name = debug.getinfo(1, "n").name or "??"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -66,7 +67,8 @@ function builtin_shared.make_registration_reverse()
|
||||||
local registerfunc = function(func)
|
local registerfunc = function(func)
|
||||||
table.insert(t, 1, func)
|
table.insert(t, 1, func)
|
||||||
core.callback_origins[func] = {
|
core.callback_origins[func] = {
|
||||||
mod = core.get_current_modname() or "??",
|
-- may be nil or return nil
|
||||||
|
mod = core.get_current_modname and core.get_current_modname() or "??",
|
||||||
name = debug.getinfo(1, "n").name or "??"
|
name = debug.getinfo(1, "n").name or "??"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -190,11 +190,41 @@ local function serialize(value, write)
|
||||||
dump(value)
|
dump(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Whether `value` recursively contains a function
|
||||||
|
local function contains_function(value)
|
||||||
|
local seen = {}
|
||||||
|
local function check(val)
|
||||||
|
if type(val) == "function" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if type(val) == "table" then
|
||||||
|
if seen[val] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
seen[val] = true
|
||||||
|
for k, v in pairs(val) do
|
||||||
|
if check(k) or check(v) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return check(value)
|
||||||
|
end
|
||||||
|
|
||||||
function core.serialize(value)
|
function core.serialize(value)
|
||||||
|
if contains_function(value) then
|
||||||
|
core.log("deprecated", "Support for dumping functions in `core.serialize` is deprecated.")
|
||||||
|
end
|
||||||
local rope = {}
|
local rope = {}
|
||||||
|
-- Keeping the length of the table as a local variable is *much*
|
||||||
|
-- faster than invoking the length operator.
|
||||||
|
-- See https://gitspartv.github.io/LuaJIT-Benchmarks/#test12.
|
||||||
|
local i = 0
|
||||||
serialize(value, function(text)
|
serialize(value, function(text)
|
||||||
-- Faster than table.insert(rope, text) on PUC Lua 5.1
|
i = i + 1
|
||||||
rope[#rope + 1] = text
|
rope[i] = text
|
||||||
end)
|
end)
|
||||||
return table_concat(rope)
|
return table_concat(rope)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2022 rubenwardy
|
-- Copyright (C) 2022 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
local make = {}
|
local make = {}
|
||||||
|
|
||||||
|
@ -37,6 +24,7 @@ local make = {}
|
||||||
-- * `fs` is a string for the formspec.
|
-- * `fs` is a string for the formspec.
|
||||||
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
|
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
|
||||||
-- * `used_height` is the space used by components in `fs`.
|
-- * `used_height` is the space used by components in `fs`.
|
||||||
|
-- * `spacing`: (Optional) the vertical margin to be added before the component (default 0.25)
|
||||||
-- * `on_submit = function(self, fields, parent)`:
|
-- * `on_submit = function(self, fields, parent)`:
|
||||||
-- * `fields`: submitted formspec fields
|
-- * `fields`: submitted formspec fields
|
||||||
-- * `parent`: the fstk element for the settings UI, use to show dialogs
|
-- * `parent`: the fstk element for the settings UI, use to show dialogs
|
||||||
|
@ -98,6 +86,7 @@ local function make_field(converter, validator, stringifier)
|
||||||
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
|
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
|
||||||
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
|
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
|
||||||
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
|
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
|
||||||
|
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name) -- for pause menu env
|
||||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
|
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
|
||||||
|
|
||||||
return fs, 1.1
|
return fs, 1.1
|
||||||
|
@ -217,6 +206,8 @@ local function make_path(setting)
|
||||||
|
|
||||||
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
|
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
|
||||||
avail_w - 3, setting.name, get_label(setting), core.formspec_escape(value))
|
avail_w - 3, setting.name, get_label(setting), core.formspec_escape(value))
|
||||||
|
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
|
||||||
|
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name) -- for pause menu env
|
||||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
|
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
|
||||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
|
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
|
||||||
|
|
||||||
|
@ -249,8 +240,11 @@ local function make_path(setting)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
if PLATFORM == "Android" then
|
if PLATFORM == "Android" or INIT == "pause_menu" then
|
||||||
-- The Irrlicht file picker doesn't work on Android.
|
-- The Irrlicht file picker doesn't work on Android.
|
||||||
|
-- Access to the Irrlicht file picker isn't implemented in the pause menu.
|
||||||
|
-- We want to delete the Irrlicht file picker anyway, so any time spent on
|
||||||
|
-- that would be wasted.
|
||||||
make.path = make.string
|
make.path = make.string
|
||||||
make.filepath = make.string
|
make.filepath = make.string
|
||||||
else
|
else
|
||||||
|
@ -282,6 +276,14 @@ function make.v3f(setting)
|
||||||
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
|
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
|
||||||
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)
|
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)
|
||||||
|
|
||||||
|
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_x")
|
||||||
|
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_y")
|
||||||
|
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_z")
|
||||||
|
-- for pause menu env
|
||||||
|
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_x")
|
||||||
|
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_y")
|
||||||
|
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_z")
|
||||||
|
|
||||||
fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))
|
fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))
|
||||||
|
|
||||||
return fs, 1.4
|
return fs, 1.4
|
||||||
|
@ -428,8 +430,75 @@ local function make_noise_params(setting)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
make.noise_params_2d = make_noise_params
|
function make.key(setting)
|
||||||
make.noise_params_3d = make_noise_params
|
local btn_bind = "bind_" .. setting.name
|
||||||
|
local btn_clear = "unbind_" .. setting.name
|
||||||
|
local function add_conflict_warnings(fs, height)
|
||||||
|
local value = core.settings:get(setting.name)
|
||||||
|
if value == "" then
|
||||||
|
return height
|
||||||
|
end
|
||||||
|
for _, o in ipairs(core.full_settingtypes) do
|
||||||
|
if o.type == "key" and o.name ~= setting.name and core.are_keycodes_equal(core.settings:get(o.name), value) then
|
||||||
|
table.insert(fs, ("label[0,%f;%s]"):format(height + 0.3,
|
||||||
|
core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext(o.readable_name)))))
|
||||||
|
height = height + 0.6
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return height
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
info_text = setting.comment,
|
||||||
|
setting = setting,
|
||||||
|
spacing = 0.1,
|
||||||
|
|
||||||
|
get_formspec = function(self, avail_w)
|
||||||
|
self.resettable = core.settings:has(setting.name)
|
||||||
|
local btn_bind_width = math.max(2.5, avail_w/2)
|
||||||
|
local value = core.settings:get(setting.name)
|
||||||
|
local fs = {
|
||||||
|
("label[0,0.4;%s]"):format(get_label(setting)),
|
||||||
|
("button_key[%f,0;%f,0.8;%s;%s]"):format(
|
||||||
|
btn_bind_width, btn_bind_width-0.8,
|
||||||
|
btn_bind, core.formspec_escape(value)),
|
||||||
|
("image_button[%f,0;0.8,0.8;%s;%s;]"):format(avail_w - 0.8,
|
||||||
|
core.formspec_escape(defaulttexturedir .. "clear.png"),
|
||||||
|
btn_clear),
|
||||||
|
("tooltip[%s;%s]"):format(btn_clear, fgettext("Remove keybinding")),
|
||||||
|
}
|
||||||
|
local height = 0.8
|
||||||
|
height = add_conflict_warnings(fs, height)
|
||||||
|
return table.concat(fs), height
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_submit = function(self, fields)
|
||||||
|
if fields[btn_bind] then
|
||||||
|
core.settings:set(setting.name, fields[btn_bind])
|
||||||
|
return true
|
||||||
|
elseif fields[btn_clear] then
|
||||||
|
core.settings:set(setting.name, "")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
if INIT == "pause_menu" then
|
||||||
|
-- Making the noise parameter dialog work in the pause menu settings would
|
||||||
|
-- require porting "FSTK" (at least the dialog API) from the mainmenu formspec
|
||||||
|
-- API to the in-game formspec API.
|
||||||
|
-- There's no reason you'd want to adjust mapgen noise parameter settings
|
||||||
|
-- in-game (they only apply to new worlds, hidden as [world_creation]),
|
||||||
|
-- so there's no reason to implement this.
|
||||||
|
local empty = function()
|
||||||
|
return { get_formspec = function() return "", 0 end }
|
||||||
|
end
|
||||||
|
make.noise_params_2d = empty
|
||||||
|
make.noise_params_3d = empty
|
||||||
|
else
|
||||||
|
make.noise_params_2d = make_noise_params
|
||||||
|
make.noise_params_3d = make_noise_params
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
return make
|
return make
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2015 PilzAdam
|
-- Copyright (C) 2015 PilzAdam
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
local checkboxes = {}
|
local checkboxes = {}
|
|
@ -1,29 +1,14 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2022 rubenwardy
|
-- Copyright (C) 2022 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
|
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM
|
||||||
"settings" .. DIR_DELIM .. "components.lua")
|
|
||||||
|
|
||||||
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
|
local component_funcs = dofile(path .. "components.lua")
|
||||||
"settings" .. DIR_DELIM .. "shadows_component.lua")
|
local shadows_component = dofile(path .. "shadows_component.lua")
|
||||||
|
|
||||||
local loaded = false
|
local loaded = false
|
||||||
local full_settings
|
|
||||||
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
|
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
|
||||||
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
|
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
|
||||||
local all_pages = {}
|
local all_pages = {}
|
||||||
|
@ -33,7 +18,7 @@ local filtered_page_by_id = page_by_id
|
||||||
|
|
||||||
|
|
||||||
local function get_setting_info(name)
|
local function get_setting_info(name)
|
||||||
for _, entry in ipairs(full_settings) do
|
for _, entry in ipairs(core.full_settingtypes) do
|
||||||
if entry.type ~= "category" and entry.name == name then
|
if entry.type ~= "category" and entry.name == name then
|
||||||
return entry
|
return entry
|
||||||
end
|
end
|
||||||
|
@ -71,7 +56,7 @@ local function load_settingtypes()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, entry in ipairs(full_settings) do
|
for _, entry in ipairs(core.full_settingtypes) do
|
||||||
if entry.type == "category" then
|
if entry.type == "category" then
|
||||||
if entry.level == 0 then
|
if entry.level == 0 then
|
||||||
section = entry.name
|
section = entry.name
|
||||||
|
@ -105,29 +90,14 @@ local function load()
|
||||||
end
|
end
|
||||||
loaded = true
|
loaded = true
|
||||||
|
|
||||||
full_settings = settingtypes.parse_config_file(false, true)
|
core.full_settingtypes = settingtypes.parse_config_file(false, true)
|
||||||
|
|
||||||
local change_keys = {
|
|
||||||
query_text = "Controls",
|
|
||||||
requires = {
|
|
||||||
touch_controls = false,
|
|
||||||
},
|
|
||||||
get_formspec = function(self, avail_w)
|
|
||||||
local btn_w = math.min(avail_w, 3)
|
|
||||||
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
|
|
||||||
end,
|
|
||||||
on_submit = function(self, fields)
|
|
||||||
if fields.btn_change_keys then
|
|
||||||
core.show_keys_menu()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
local touchscreen_layout = {
|
local touchscreen_layout = {
|
||||||
query_text = "Touchscreen layout",
|
query_text = "Touchscreen layout",
|
||||||
requires = {
|
requires = {
|
||||||
touchscreen = true,
|
touchscreen = true,
|
||||||
},
|
},
|
||||||
|
context = "client",
|
||||||
get_formspec = function(self, avail_w)
|
get_formspec = function(self, avail_w)
|
||||||
local btn_w = math.min(avail_w, 6)
|
local btn_w = math.min(avail_w, 6)
|
||||||
return ("button[0,0;%f,0.8;btn_touch_layout;%s]"):format(btn_w, fgettext("Touchscreen layout")), 0.8
|
return ("button[0,0;%f,0.8;btn_touch_layout;%s]"):format(btn_w, fgettext("Touchscreen layout")), 0.8
|
||||||
|
@ -160,13 +130,11 @@ local function load()
|
||||||
{ heading = fgettext_ne("Movement") },
|
{ heading = fgettext_ne("Movement") },
|
||||||
"arm_inertia",
|
"arm_inertia",
|
||||||
"view_bobbing_amount",
|
"view_bobbing_amount",
|
||||||
"fall_bobbing_amount",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
load_settingtypes()
|
load_settingtypes()
|
||||||
|
|
||||||
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
|
|
||||||
-- insert after "touch_controls"
|
-- insert after "touch_controls"
|
||||||
table.insert(page_by_id.controls_touchscreen.content, 2, touchscreen_layout)
|
table.insert(page_by_id.controls_touchscreen.content, 2, touchscreen_layout)
|
||||||
do
|
do
|
||||||
|
@ -175,18 +143,24 @@ local function load()
|
||||||
table.insert(content, idx, shadows_component)
|
table.insert(content, idx, shadows_component)
|
||||||
|
|
||||||
idx = table.indexof(content, "enable_auto_exposure") + 1
|
idx = table.indexof(content, "enable_auto_exposure") + 1
|
||||||
|
local setting_info = get_setting_info("enable_auto_exposure")
|
||||||
local note = component_funcs.note(fgettext_ne("(The game will need to enable automatic exposure as well)"))
|
local note = component_funcs.note(fgettext_ne("(The game will need to enable automatic exposure as well)"))
|
||||||
note.requires = get_setting_info("enable_auto_exposure").requires
|
note.requires = setting_info.requires
|
||||||
|
note.context = setting_info.context
|
||||||
table.insert(content, idx, note)
|
table.insert(content, idx, note)
|
||||||
|
|
||||||
idx = table.indexof(content, "enable_bloom") + 1
|
idx = table.indexof(content, "enable_bloom") + 1
|
||||||
|
setting_info = get_setting_info("enable_bloom")
|
||||||
note = component_funcs.note(fgettext_ne("(The game will need to enable bloom as well)"))
|
note = component_funcs.note(fgettext_ne("(The game will need to enable bloom as well)"))
|
||||||
note.requires = get_setting_info("enable_bloom").requires
|
note.requires = setting_info.requires
|
||||||
|
note.context = setting_info.context
|
||||||
table.insert(content, idx, note)
|
table.insert(content, idx, note)
|
||||||
|
|
||||||
idx = table.indexof(content, "enable_volumetric_lighting") + 1
|
idx = table.indexof(content, "enable_volumetric_lighting") + 1
|
||||||
|
setting_info = get_setting_info("enable_volumetric_lighting")
|
||||||
note = component_funcs.note(fgettext_ne("(The game will need to enable volumetric lighting as well)"))
|
note = component_funcs.note(fgettext_ne("(The game will need to enable volumetric lighting as well)"))
|
||||||
note.requires = get_setting_info("enable_volumetric_lighting").requires
|
note.requires = setting_info.requires
|
||||||
|
note.context = setting_info.context
|
||||||
table.insert(content, idx, note)
|
table.insert(content, idx, note)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -261,6 +235,17 @@ local function load()
|
||||||
["true"] = fgettext_ne("Enabled"),
|
["true"] = fgettext_ne("Enabled"),
|
||||||
["false"] = fgettext_ne("Disabled"),
|
["false"] = fgettext_ne("Disabled"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_setting_info("touch_interaction_style").option_labels = {
|
||||||
|
["tap"] = fgettext_ne("Tap"),
|
||||||
|
["tap_crosshair"] = fgettext_ne("Tap with crosshair"),
|
||||||
|
["buttons_crosshair"] = fgettext("Buttons with crosshair"),
|
||||||
|
}
|
||||||
|
|
||||||
|
get_setting_info("touch_punch_gesture").option_labels = {
|
||||||
|
["short_tap"] = fgettext_ne("Short tap"),
|
||||||
|
["long_tap"] = fgettext_ne("Long tap"),
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -353,7 +338,18 @@ local function update_filtered_pages(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function check_requirements(name, requires)
|
local shown_contexts = {
|
||||||
|
common = true,
|
||||||
|
client = true,
|
||||||
|
server = INIT ~= "pause_menu" or core.is_internal_server(),
|
||||||
|
world_creation = INIT ~= "pause_menu",
|
||||||
|
}
|
||||||
|
|
||||||
|
local function check_requirements(name, requires, context)
|
||||||
|
if context and not shown_contexts[context] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
if requires == nil then
|
if requires == nil then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -361,6 +357,7 @@ local function check_requirements(name, requires)
|
||||||
local video_driver = core.get_active_driver()
|
local video_driver = core.get_active_driver()
|
||||||
local touch_support = core.irrlicht_device_supports_touch()
|
local touch_support = core.irrlicht_device_supports_touch()
|
||||||
local touch_controls = core.settings:get("touch_controls")
|
local touch_controls = core.settings:get("touch_controls")
|
||||||
|
local touch_interaction_style = core.settings:get("touch_interaction_style")
|
||||||
local special = {
|
local special = {
|
||||||
android = PLATFORM == "Android",
|
android = PLATFORM == "Android",
|
||||||
desktop = PLATFORM ~= "Android",
|
desktop = PLATFORM ~= "Android",
|
||||||
|
@ -371,6 +368,7 @@ local function check_requirements(name, requires)
|
||||||
keyboard_mouse = not touch_support or (touch_controls == "auto" or not core.is_yes(touch_controls)),
|
keyboard_mouse = not touch_support or (touch_controls == "auto" or not core.is_yes(touch_controls)),
|
||||||
opengl = (video_driver == "opengl" or video_driver == "opengl3"),
|
opengl = (video_driver == "opengl" or video_driver == "opengl3"),
|
||||||
gles = video_driver:sub(1, 5) == "ogles",
|
gles = video_driver:sub(1, 5) == "ogles",
|
||||||
|
touch_interaction_style_tap = touch_interaction_style ~= "buttons_crosshair",
|
||||||
}
|
}
|
||||||
|
|
||||||
for req_key, req_value in pairs(requires) do
|
for req_key, req_value in pairs(requires) do
|
||||||
|
@ -378,6 +376,9 @@ local function check_requirements(name, requires)
|
||||||
local required_setting = get_setting_info(req_key)
|
local required_setting = get_setting_info(req_key)
|
||||||
if required_setting == nil then
|
if required_setting == nil then
|
||||||
core.log("warning", "Unknown setting " .. req_key .. " required by " .. (name or "???"))
|
core.log("warning", "Unknown setting " .. req_key .. " required by " .. (name or "???"))
|
||||||
|
elseif required_setting.type ~= "bool" then
|
||||||
|
core.log("warning", "Setting " .. req_key .. " of type " .. required_setting.type ..
|
||||||
|
" used as requirement by " .. (name or "???") .. ", only bool is allowed")
|
||||||
end
|
end
|
||||||
local actual_value = core.settings:get_bool(req_key,
|
local actual_value = core.settings:get_bool(req_key,
|
||||||
required_setting and core.is_yes(required_setting.default))
|
required_setting and core.is_yes(required_setting.default))
|
||||||
|
@ -409,11 +410,11 @@ function page_has_contents(page, actual_content)
|
||||||
elseif type(item) == "string" then
|
elseif type(item) == "string" then
|
||||||
local setting = get_setting_info(item)
|
local setting = get_setting_info(item)
|
||||||
assert(setting, "Unknown setting: " .. item)
|
assert(setting, "Unknown setting: " .. item)
|
||||||
if check_requirements(setting.name, setting.requires) then
|
if check_requirements(setting.name, setting.requires, setting.context) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
elseif item.get_formspec then
|
elseif item.get_formspec then
|
||||||
if check_requirements(item.id, item.requires) then
|
if check_requirements(item.id, item.requires, item.context) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -435,20 +436,22 @@ local function build_page_components(page)
|
||||||
elseif item.heading then
|
elseif item.heading then
|
||||||
last_heading = item
|
last_heading = item
|
||||||
else
|
else
|
||||||
local name, requires
|
local name, requires, context
|
||||||
if type(item) == "string" then
|
if type(item) == "string" then
|
||||||
local setting = get_setting_info(item)
|
local setting = get_setting_info(item)
|
||||||
assert(setting, "Unknown setting: " .. item)
|
assert(setting, "Unknown setting: " .. item)
|
||||||
name = setting.name
|
name = setting.name
|
||||||
requires = setting.requires
|
requires = setting.requires
|
||||||
|
context = setting.context
|
||||||
elseif item.get_formspec then
|
elseif item.get_formspec then
|
||||||
name = item.id
|
name = item.id
|
||||||
requires = item.requires
|
requires = item.requires
|
||||||
|
context = item.context
|
||||||
else
|
else
|
||||||
error("Unknown content in page: " .. dump(item))
|
error("Unknown content in page: " .. dump(item))
|
||||||
end
|
end
|
||||||
|
|
||||||
if check_requirements(name, requires) then
|
if check_requirements(name, requires, context) then
|
||||||
if last_heading then
|
if last_heading then
|
||||||
content[#content + 1] = last_heading
|
content[#content + 1] = last_heading
|
||||||
last_heading = nil
|
last_heading = nil
|
||||||
|
@ -514,7 +517,8 @@ local function get_formspec(dialogdata)
|
||||||
"box[0,0;", tostring(tabsize.width), ",", tostring(tabsize.height), ";#0000008C]",
|
"box[0,0;", tostring(tabsize.width), ",", tostring(tabsize.height), ";#0000008C]",
|
||||||
|
|
||||||
("button[0,%f;%f,0.8;back;%s]"):format(
|
("button[0,%f;%f,0.8;back;%s]"):format(
|
||||||
tabsize.height + 0.2, back_w, fgettext("Back")),
|
tabsize.height + 0.2, back_w,
|
||||||
|
fgettext("Back")),
|
||||||
|
|
||||||
("box[%f,%f;%f,0.8;#0000008C]"):format(
|
("box[%f,%f;%f,0.8;#0000008C]"):format(
|
||||||
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
|
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
|
||||||
|
@ -531,6 +535,7 @@ local function get_formspec(dialogdata)
|
||||||
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
|
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
|
||||||
core.formspec_escape(dialogdata.query or ""), "]",
|
core.formspec_escape(dialogdata.query or ""), "]",
|
||||||
"field_enter_after_edit[search_query;true]",
|
"field_enter_after_edit[search_query;true]",
|
||||||
|
"field_close_on_enter[search_query;false]", -- for pause menu env
|
||||||
"container[", tostring(search_width + 0.25), ", 0.25]",
|
"container[", tostring(search_width + 0.25), ", 0.25]",
|
||||||
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
|
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
|
||||||
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
|
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
|
||||||
|
@ -628,7 +633,13 @@ local function get_formspec(dialogdata)
|
||||||
fs[#fs + 1] = "container_end[]"
|
fs[#fs + 1] = "container_end[]"
|
||||||
|
|
||||||
if used_h > 0 then
|
if used_h > 0 then
|
||||||
y = y + used_h + 0.25
|
local spacing = 0.25
|
||||||
|
local next_comp = dialogdata.components[i + 1]
|
||||||
|
if next_comp and next_comp.spacing then
|
||||||
|
spacing = next_comp.spacing
|
||||||
|
end
|
||||||
|
|
||||||
|
y = y + used_h + spacing
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -671,7 +682,8 @@ local function buttonhandler(this, fields)
|
||||||
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
|
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
|
||||||
dialogdata.query = fields.search_query
|
dialogdata.query = fields.search_query
|
||||||
|
|
||||||
if fields.back then
|
-- "fields.quit" is for the pause menu env
|
||||||
|
if fields.back or fields.quit then
|
||||||
this:delete()
|
this:delete()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -765,11 +777,44 @@ local function eventhandler(event)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function create_settings_dlg()
|
if INIT == "mainmenu" then
|
||||||
load()
|
function create_settings_dlg(page_id)
|
||||||
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
|
load()
|
||||||
|
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
|
||||||
|
|
||||||
dlg.data.page_id = update_filtered_pages("")
|
dlg.data.page_id = page_id or update_filtered_pages("")
|
||||||
|
|
||||||
return dlg
|
return dlg
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
assert(INIT == "pause_menu")
|
||||||
|
|
||||||
|
local dialog
|
||||||
|
|
||||||
|
core.register_on_formspec_input(function(formname, fields)
|
||||||
|
if dialog and formname == "__builtin:settings" then
|
||||||
|
-- buttonhandler returning true means we should update the formspec.
|
||||||
|
-- dialog is re-checked since the buttonhandler may have closed it.
|
||||||
|
if buttonhandler(dialog, fields) and dialog then
|
||||||
|
core.show_formspec("__builtin:settings", get_formspec(dialog.data))
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
core.open_settings = function()
|
||||||
|
load()
|
||||||
|
dialog = {}
|
||||||
|
dialog.data = {}
|
||||||
|
dialog.data.page_id = update_filtered_pages("")
|
||||||
|
dialog.delete = function()
|
||||||
|
dialog = nil
|
||||||
|
-- only needed for the "fields.back" case, in the "fields.quit"
|
||||||
|
-- case it's a no-op
|
||||||
|
core.show_formspec("__builtin:settings", "")
|
||||||
|
end
|
||||||
|
|
||||||
|
core.show_formspec("__builtin:settings", get_formspec(dialog.data))
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -61,7 +61,7 @@ local function create_minetest_conf_example(settings)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if entry.type == "key" then
|
if entry.type == "key" then
|
||||||
local line = "See https://github.com/minetest/irrlicht/blob/master/include/Keycodes.h"
|
local line = "See https://docs.luanti.org/for-players/controls/"
|
||||||
insert(result, "# " .. line .. "\n")
|
insert(result, "# " .. line .. "\n")
|
||||||
end
|
end
|
||||||
insert(result, "# type: " .. entry.type)
|
insert(result, "# type: " .. entry.type)
|
15
builtin/common/settings/init.lua
Normal file
15
builtin/common/settings/init.lua
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
-- Luanti
|
||||||
|
-- Copyright (C) 2022 rubenwardy
|
||||||
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM
|
||||||
|
|
||||||
|
dofile(path .. "settingtypes.lua")
|
||||||
|
dofile(path .. "dlg_change_mapgen_flags.lua")
|
||||||
|
dofile(path .. "dlg_settings.lua")
|
||||||
|
|
||||||
|
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
|
||||||
|
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
|
||||||
|
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
|
||||||
|
|
||||||
|
-- dofile(path .. DIR_DELIM .. "generate_from_settingtypes.lua")
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2015 PilzAdam
|
-- Copyright (C) 2015 PilzAdam
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
settingtypes = {}
|
settingtypes = {}
|
||||||
|
|
||||||
|
@ -40,12 +27,24 @@ local CHAR_CLASSES = {
|
||||||
FLAGS = "[%w_%-%.,]",
|
FLAGS = "[%w_%-%.,]",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local valid_contexts = {common = true, client = true, server = true, world_creation = true}
|
||||||
|
|
||||||
|
local function check_context_annotation(context, force_context)
|
||||||
|
if force_context then
|
||||||
|
return "Context annotations are not allowed, context is always " .. force_context
|
||||||
|
end
|
||||||
|
if not valid_contexts[context] then
|
||||||
|
return "Unknown context"
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
local function flags_to_table(flags)
|
local function flags_to_table(flags)
|
||||||
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
|
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
|
||||||
end
|
end
|
||||||
|
|
||||||
-- returns error message, or nil
|
-- returns error message, or nil
|
||||||
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
|
local function parse_setting_line(settings, line, read_all, base_level, allow_secure, force_context)
|
||||||
|
|
||||||
-- strip carriage returns (CR, /r)
|
-- strip carriage returns (CR, /r)
|
||||||
line = line:gsub("\r", "")
|
line = line:gsub("\r", "")
|
||||||
|
@ -69,9 +68,32 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
|
|
||||||
-- category
|
-- category
|
||||||
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
|
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
|
||||||
|
local category_context
|
||||||
|
if not category then
|
||||||
|
stars, category, category_context = line:match("^%[([%*]*)([^%]]+)%] %[([^%]]+)%]$")
|
||||||
|
end
|
||||||
if category then
|
if category then
|
||||||
local category_level = stars:len() + base_level
|
local category_level = stars:len() + base_level
|
||||||
|
|
||||||
|
if settings.current_context_level and
|
||||||
|
category_level <= settings.current_context_level then
|
||||||
|
-- The start of this category marks the end of the context annotation's scope.
|
||||||
|
settings.current_context_level = nil
|
||||||
|
settings.current_context = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if category_context then
|
||||||
|
local err = check_context_annotation(category_context, force_context)
|
||||||
|
if err then
|
||||||
|
return err
|
||||||
|
end
|
||||||
|
if settings.current_context_level then
|
||||||
|
return "Category context annotations cannot be nested"
|
||||||
|
end
|
||||||
|
settings.current_context_level = category_level
|
||||||
|
settings.current_context = category_context
|
||||||
|
end
|
||||||
|
|
||||||
if settings.current_hide_level then
|
if settings.current_hide_level then
|
||||||
if settings.current_hide_level < category_level then
|
if settings.current_hide_level < category_level then
|
||||||
-- Skip this category, it's inside a hidden category.
|
-- Skip this category, it's inside a hidden category.
|
||||||
|
@ -102,7 +124,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
end
|
end
|
||||||
|
|
||||||
-- settings
|
-- settings
|
||||||
local first_part, name, readable_name, setting_type = line:match("^"
|
local function make_pattern(include_context)
|
||||||
|
return "^"
|
||||||
-- this first capture group matches the whole first part,
|
-- this first capture group matches the whole first part,
|
||||||
-- so we can later strip it from the rest of the line
|
-- so we can later strip it from the rest of the line
|
||||||
.. "("
|
.. "("
|
||||||
|
@ -110,9 +133,19 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
.. CHAR_CLASSES.SPACE .. "*"
|
.. CHAR_CLASSES.SPACE .. "*"
|
||||||
.. "%(([^%)]*)%)" -- readable name
|
.. "%(([^%)]*)%)" -- readable name
|
||||||
.. CHAR_CLASSES.SPACE .. "*"
|
.. CHAR_CLASSES.SPACE .. "*"
|
||||||
|
.. (include_context and (
|
||||||
|
"%[([^%]]+)%]" -- context annotation
|
||||||
|
.. CHAR_CLASSES.SPACE .. "*"
|
||||||
|
) or "")
|
||||||
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
|
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
|
||||||
.. CHAR_CLASSES.SPACE .. "*"
|
.. CHAR_CLASSES.SPACE .. "*"
|
||||||
.. ")")
|
.. ")"
|
||||||
|
end
|
||||||
|
local first_part, name, readable_name, setting_type = line:match(make_pattern(false))
|
||||||
|
local setting_context
|
||||||
|
if not first_part then
|
||||||
|
first_part, name, readable_name, setting_context, setting_type = line:match(make_pattern(true))
|
||||||
|
end
|
||||||
|
|
||||||
if not first_part then
|
if not first_part then
|
||||||
return "Invalid line"
|
return "Invalid line"
|
||||||
|
@ -122,6 +155,26 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
return "Tried to add \"secure.\" setting"
|
return "Tried to add \"secure.\" setting"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if setting_context then
|
||||||
|
local err = check_context_annotation(setting_context, force_context)
|
||||||
|
if err then
|
||||||
|
return err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local context
|
||||||
|
if force_context then
|
||||||
|
context = force_context
|
||||||
|
else
|
||||||
|
if setting_context then
|
||||||
|
context = setting_context
|
||||||
|
elseif settings.current_context_level then
|
||||||
|
context = settings.current_context
|
||||||
|
else
|
||||||
|
return "Missing context annotation"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local requires = {}
|
local requires = {}
|
||||||
local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
|
local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
|
||||||
if last_line and last_line:lower():sub(1, 9) == "requires:" then
|
if last_line and last_line:lower():sub(1, 9) == "requires:" then
|
||||||
|
@ -170,6 +223,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
min = min,
|
min = min,
|
||||||
max = max,
|
max = max,
|
||||||
requires = requires,
|
requires = requires,
|
||||||
|
context = context,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -182,9 +236,9 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
if not default then
|
if not default then
|
||||||
return "Invalid string setting"
|
return "Invalid string setting"
|
||||||
end
|
end
|
||||||
if setting_type == "key" and not read_all then
|
|
||||||
-- ignore key type if read_all is false
|
if setting_type == "key" then
|
||||||
return
|
requires.keyboard_mouse = true
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(settings, {
|
table.insert(settings, {
|
||||||
|
@ -193,6 +247,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
type = setting_type,
|
type = setting_type,
|
||||||
default = default,
|
default = default,
|
||||||
requires = requires,
|
requires = requires,
|
||||||
|
context = context,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -245,6 +300,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
},
|
},
|
||||||
values = values,
|
values = values,
|
||||||
requires = requires,
|
requires = requires,
|
||||||
|
context = context,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
noise_params = true,
|
noise_params = true,
|
||||||
flags = flags_to_table("defaults,eased,absvalue")
|
flags = flags_to_table("defaults,eased,absvalue")
|
||||||
|
@ -263,6 +319,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
type = "bool",
|
type = "bool",
|
||||||
default = remaining_line,
|
default = remaining_line,
|
||||||
requires = requires,
|
requires = requires,
|
||||||
|
context = context,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -290,6 +347,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
min = min,
|
min = min,
|
||||||
max = max,
|
max = max,
|
||||||
requires = requires,
|
requires = requires,
|
||||||
|
context = context,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -313,6 +371,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
default = default,
|
default = default,
|
||||||
values = values:split(",", true),
|
values = values:split(",", true),
|
||||||
requires = requires,
|
requires = requires,
|
||||||
|
context = context,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -331,6 +390,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
type = setting_type,
|
type = setting_type,
|
||||||
default = default,
|
default = default,
|
||||||
requires = requires,
|
requires = requires,
|
||||||
|
context = context,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -361,6 +421,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
default = default,
|
default = default,
|
||||||
possible = flags_to_table(possible),
|
possible = flags_to_table(possible),
|
||||||
requires = requires,
|
requires = requires,
|
||||||
|
context = context,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -369,14 +430,14 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
|
||||||
return "Invalid setting type \"" .. setting_type .. "\""
|
return "Invalid setting type \"" .. setting_type .. "\""
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
|
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure, force_context)
|
||||||
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
|
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
|
||||||
result.current_comment = {}
|
result.current_comment = {}
|
||||||
result.current_hide_level = nil
|
result.current_hide_level = nil
|
||||||
|
|
||||||
local line = file:read("*line")
|
local line = file:read("*line")
|
||||||
while line do
|
while line do
|
||||||
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
|
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure, force_context)
|
||||||
if error_msg then
|
if error_msg then
|
||||||
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
|
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
|
||||||
end
|
end
|
||||||
|
@ -408,7 +469,15 @@ function settingtypes.parse_config_file(read_all, parse_mods)
|
||||||
file:close()
|
file:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
if parse_mods then
|
-- TODO: Support game/mod settings in the pause menu too
|
||||||
|
-- Note that this will need to work different from how it's done in the
|
||||||
|
-- mainmenu:
|
||||||
|
-- * Only show settings for the active game and mods
|
||||||
|
-- (add API function to get them, can return nil if on a remote server)
|
||||||
|
-- (names are probably not enough, will need paths for uniqueness)
|
||||||
|
-- This means just making "pkgmgr.lua" work won't get you very far.
|
||||||
|
|
||||||
|
if INIT == "mainmenu" and parse_mods then
|
||||||
-- Parse games
|
-- Parse games
|
||||||
local games_category_initialized = false
|
local games_category_initialized = false
|
||||||
for _, game in ipairs(pkgmgr.games) do
|
for _, game in ipairs(pkgmgr.games) do
|
||||||
|
@ -432,7 +501,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
|
||||||
type = "category",
|
type = "category",
|
||||||
})
|
})
|
||||||
|
|
||||||
parse_single_file(file, path, read_all, settings, 2, false)
|
parse_single_file(file, path, read_all, settings, 2, false, "server")
|
||||||
|
|
||||||
file:close()
|
file:close()
|
||||||
end
|
end
|
||||||
|
@ -465,7 +534,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
|
||||||
type = "category",
|
type = "category",
|
||||||
})
|
})
|
||||||
|
|
||||||
parse_single_file(file, path, read_all, settings, 2, false)
|
parse_single_file(file, path, read_all, settings, 2, false, "server")
|
||||||
|
|
||||||
file:close()
|
file:close()
|
||||||
end
|
end
|
||||||
|
@ -496,7 +565,7 @@ function settingtypes.parse_config_file(read_all, parse_mods)
|
||||||
type = "category",
|
type = "category",
|
||||||
})
|
})
|
||||||
|
|
||||||
parse_single_file(file, path, read_all, settings, 2, false)
|
parse_single_file(file, path, read_all, settings, 2, false, "client")
|
||||||
|
|
||||||
file:close()
|
file:close()
|
||||||
end
|
end
|
|
@ -1,20 +1,7 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2021-2 x2048
|
-- Copyright (C) 2021-2 x2048
|
||||||
--Copyright (C) 2022-3 rubenwardy
|
-- Copyright (C) 2022-3 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
local shadow_levels_labels = {
|
local shadow_levels_labels = {
|
||||||
|
@ -84,6 +71,7 @@ return {
|
||||||
requires = {
|
requires = {
|
||||||
opengl = true,
|
opengl = true,
|
||||||
},
|
},
|
||||||
|
context = "client",
|
||||||
get_formspec = function(self, avail_w)
|
get_formspec = function(self, avail_w)
|
||||||
local labels = table.copy(shadow_levels_labels)
|
local labels = table.copy(shadow_levels_labels)
|
||||||
local idx = detect_mapping_idx()
|
local idx = detect_mapping_idx()
|
|
@ -178,6 +178,35 @@ describe("table", function()
|
||||||
assert.equal(2, table.keyof({[2] = "foo", [3] = "bar"}, "foo"))
|
assert.equal(2, table.keyof({[2] = "foo", [3] = "bar"}, "foo"))
|
||||||
assert.equal(3, table.keyof({[1] = "foo", [3] = "bar"}, "bar"))
|
assert.equal(3, table.keyof({[1] = "foo", [3] = "bar"}, "bar"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("copy()", function()
|
||||||
|
it("strips metatables", function()
|
||||||
|
local v = vector.new(1, 2, 3)
|
||||||
|
local w = table.copy(v)
|
||||||
|
assert.are_not.equal(v, w)
|
||||||
|
assert.same(v, w)
|
||||||
|
assert.equal(nil, getmetatable(w))
|
||||||
|
end)
|
||||||
|
it("preserves referential structure", function()
|
||||||
|
local t = {{}, {}}
|
||||||
|
t[1][1] = t[2]
|
||||||
|
t[2][1] = t[1]
|
||||||
|
local copy = table.copy(t)
|
||||||
|
assert.same(t, copy)
|
||||||
|
assert.equal(copy[1][1], copy[2])
|
||||||
|
assert.equal(copy[2][1], copy[1])
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("copy_with_metatables()", function()
|
||||||
|
it("preserves metatables", function()
|
||||||
|
local v = vector.new(1, 2, 3)
|
||||||
|
local w = table.copy_with_metatables(v)
|
||||||
|
assert.equal(getmetatable(v), getmetatable(w))
|
||||||
|
assert(vector.check(w))
|
||||||
|
assert.equal(v, w) -- vector overrides ==
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe("formspec_escape", function()
|
describe("formspec_escape", function()
|
||||||
|
@ -201,3 +230,124 @@ describe("math", function()
|
||||||
assert.equal(0, math.round(-0.49999999999999994))
|
assert.equal(0, math.round(-0.49999999999999994))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("dump", function()
|
||||||
|
local function test_expression(expr)
|
||||||
|
local chunk = assert(loadstring("return " .. expr))
|
||||||
|
local refs = {}
|
||||||
|
setfenv(chunk, {
|
||||||
|
setref = function(id)
|
||||||
|
refs[id] = {}
|
||||||
|
return function(fields)
|
||||||
|
for k, v in pairs(fields) do
|
||||||
|
refs[id][k] = v
|
||||||
|
end
|
||||||
|
return refs[id]
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
getref = function(id)
|
||||||
|
return assert(refs[id])
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
assert.equal(expr, dump(chunk()))
|
||||||
|
end
|
||||||
|
|
||||||
|
it("nil", function()
|
||||||
|
test_expression("nil")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("booleans", function()
|
||||||
|
test_expression("false")
|
||||||
|
test_expression("true")
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("numbers", function()
|
||||||
|
it("formats integers nicely", function()
|
||||||
|
test_expression("42")
|
||||||
|
end)
|
||||||
|
it("avoids misleading rounding", function()
|
||||||
|
test_expression("0.3")
|
||||||
|
assert.equal("0.30000000000000004", dump(0.1 + 0.2))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("strings", function()
|
||||||
|
test_expression('"hello world"')
|
||||||
|
test_expression([["hello \"world\""]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("tables", function()
|
||||||
|
it("empty", function()
|
||||||
|
test_expression("{}")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("lists", function()
|
||||||
|
test_expression([[
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
"foo",
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
}]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("number keys", function()
|
||||||
|
test_expression([[
|
||||||
|
{
|
||||||
|
[0.5] = false,
|
||||||
|
[1.5] = true,
|
||||||
|
[2.5] = "foo",
|
||||||
|
}]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("dicts", function()
|
||||||
|
test_expression([[{
|
||||||
|
a = 1,
|
||||||
|
b = 2,
|
||||||
|
c = 3,
|
||||||
|
}]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("mixed", function()
|
||||||
|
test_expression([[{
|
||||||
|
a = 1,
|
||||||
|
b = 2,
|
||||||
|
c = 3,
|
||||||
|
["d e"] = true,
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
}]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("nested", function()
|
||||||
|
test_expression([[{
|
||||||
|
a = {
|
||||||
|
1,
|
||||||
|
{},
|
||||||
|
},
|
||||||
|
b = "foo",
|
||||||
|
c = {
|
||||||
|
[0.5] = 0.1,
|
||||||
|
[1.5] = 0.2,
|
||||||
|
},
|
||||||
|
}]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("circular references", function()
|
||||||
|
test_expression([[setref(1){
|
||||||
|
child = {
|
||||||
|
parent = getref(1),
|
||||||
|
},
|
||||||
|
other_child = {
|
||||||
|
parent = getref(1),
|
||||||
|
},
|
||||||
|
}]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("supports variable indent", function()
|
||||||
|
assert.equal('{1,2,3,{foo = "bar",},}', dump({1, 2, 3, {foo = "bar"}}, ""))
|
||||||
|
assert.equal('{\n "x",\n "y",\n}', dump({"x", "y"}, " "))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
|
@ -93,21 +93,49 @@ describe("serialize", function()
|
||||||
assert_preserves(test_in)
|
assert_preserves(test_in)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("strips functions in safe mode", function()
|
describe("safe mode", function()
|
||||||
local test_in = {
|
setup(function()
|
||||||
func = function(a, b)
|
assert(not core.log)
|
||||||
error("test")
|
-- logging a deprecation warning will be attempted
|
||||||
end,
|
function core.log() end
|
||||||
foo = "bar"
|
end)
|
||||||
}
|
teardown(function()
|
||||||
setfenv(test_in.func, _G)
|
core.log = nil
|
||||||
|
end)
|
||||||
|
it("functions are stripped", function()
|
||||||
|
local test_in = {
|
||||||
|
func = function(a, b)
|
||||||
|
error("test")
|
||||||
|
end,
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
setfenv(test_in.func, _G)
|
||||||
|
|
||||||
local str = core.serialize(test_in)
|
local str = core.serialize(test_in)
|
||||||
assert.not_nil(str:find("loadstring"))
|
assert.not_nil(str:find("loadstring"))
|
||||||
|
|
||||||
local test_out = core.deserialize(str, true)
|
local test_out = core.deserialize(str, true)
|
||||||
assert.is_nil(test_out.func)
|
assert.is_nil(test_out.func)
|
||||||
assert.equals(test_out.foo, "bar")
|
assert.equals(test_out.foo, "bar")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("deprecation warnings", function()
|
||||||
|
before_each(function()
|
||||||
|
assert(not core.log)
|
||||||
|
core.log = spy.new(function(level)
|
||||||
|
assert(level == "deprecated")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
after_each(function()
|
||||||
|
core.log = nil
|
||||||
|
end)
|
||||||
|
it("dumping functions", function()
|
||||||
|
local t = {f = function() end, g = function() end}
|
||||||
|
t.t = t
|
||||||
|
core.serialize(t)
|
||||||
|
assert.spy(core.log).was.called(1) -- should have been called exactly *once*
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("vectors work", function()
|
it("vectors work", function()
|
||||||
|
|
|
@ -389,7 +389,7 @@ function vector.random_direction()
|
||||||
local x, y, z, l2
|
local x, y, z, l2
|
||||||
repeat -- expected less than two attempts on average (volume sphere vs. cube)
|
repeat -- expected less than two attempts on average (volume sphere vs. cube)
|
||||||
x, y, z = math.random() * 2 - 1, math.random() * 2 - 1, math.random() * 2 - 1
|
x, y, z = math.random() * 2 - 1, math.random() * 2 - 1, math.random() * 2 - 1
|
||||||
l2 = x*x + y*y + z*z
|
l2 = x*x + y*y + z*z
|
||||||
until l2 <= 1 and l2 >= 1e-6
|
until l2 <= 1 and l2 >= 1e-6
|
||||||
-- normalize
|
-- normalize
|
||||||
local l = math.sqrt(l2)
|
local l = math.sqrt(l2)
|
||||||
|
|
|
@ -33,7 +33,7 @@ function core.get_node(pos)
|
||||||
return core.vmanip:get_node_at(pos)
|
return core.vmanip:get_node_at(pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.get_perlin(seed, octaves, persist, spread)
|
function core.get_value_noise(seed, octaves, persist, spread)
|
||||||
local params
|
local params
|
||||||
if type(seed) == "table" then
|
if type(seed) == "table" then
|
||||||
params = table.copy(seed)
|
params = table.copy(seed)
|
||||||
|
@ -47,12 +47,18 @@ function core.get_perlin(seed, octaves, persist, spread)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
params.seed = core.get_seed(params.seed) -- add mapgen seed
|
params.seed = core.get_seed(params.seed) -- add mapgen seed
|
||||||
return PerlinNoise(params)
|
return ValueNoise(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function core.get_value_noise_map(params, size)
|
||||||
function core.get_perlin_map(params, size)
|
|
||||||
local params2 = table.copy(params)
|
local params2 = table.copy(params)
|
||||||
params2.seed = core.get_seed(params.seed) -- add mapgen seed
|
params2.seed = core.get_seed(params.seed) -- add mapgen seed
|
||||||
return PerlinNoiseMap(params2, size)
|
return ValueNoiseMap(params2, size)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- deprecated as of 5.12, as it was not Perlin noise
|
||||||
|
-- but with no warnings (yet) for compatibility
|
||||||
|
core.get_perlin = core.get_value_noise
|
||||||
|
core.get_perlin_map = core.get_value_noise_map
|
||||||
|
PerlinNoise = ValueNoise
|
||||||
|
PerlinNoiseMap = ValueNoiseMap
|
||||||
|
|
|
@ -9,8 +9,8 @@ do
|
||||||
all.registered_craftitems = {}
|
all.registered_craftitems = {}
|
||||||
all.registered_tools = {}
|
all.registered_tools = {}
|
||||||
for k, v in pairs(all.registered_items) do
|
for k, v in pairs(all.registered_items) do
|
||||||
-- Disable further modification
|
-- Ignore new keys
|
||||||
setmetatable(v, {__newindex = {}})
|
setmetatable(v, {__newindex = function() end})
|
||||||
-- Reassemble the other tables
|
-- Reassemble the other tables
|
||||||
if v.type == "node" then
|
if v.type == "node" then
|
||||||
getmetatable(v).__index = all.nodedef_default
|
getmetatable(v).__index = all.nodedef_default
|
||||||
|
@ -36,6 +36,9 @@ end
|
||||||
local alias_metatable = {
|
local alias_metatable = {
|
||||||
__index = function(t, name)
|
__index = function(t, name)
|
||||||
return rawget(t, core.registered_aliases[name])
|
return rawget(t, core.registered_aliases[name])
|
||||||
|
end,
|
||||||
|
__newindex = function()
|
||||||
|
error("table is read-only")
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
setmetatable(core.registered_items, alias_metatable)
|
setmetatable(core.registered_items, alias_metatable)
|
||||||
|
|
|
@ -1,20 +1,7 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--Copyright (C) 2023 Gregor Parzefall
|
-- Copyright (C) 2023 Gregor Parzefall
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
local BASE_SPACING = 0.1
|
local BASE_SPACING = 0.1
|
||||||
|
@ -38,7 +25,7 @@ local function buttonbar_formspec(self)
|
||||||
-- `BASE_SPACING` is used as the minimum spacing, like `gap` in CSS Flexbox.
|
-- `BASE_SPACING` is used as the minimum spacing, like `gap` in CSS Flexbox.
|
||||||
|
|
||||||
-- The number of buttons per page is always calculated as if the scroll
|
-- The number of buttons per page is always calculated as if the scroll
|
||||||
-- buttons were visible.
|
-- buttons were visible.
|
||||||
local avail_space = self.size.x - 2*BASE_SPACING - 2*get_scroll_btn_width()
|
local avail_space = self.size.x - 2*BASE_SPACING - 2*get_scroll_btn_width()
|
||||||
local btns_per_page = math.floor((avail_space - BASE_SPACING) / (btn_size + BASE_SPACING))
|
local btns_per_page = math.floor((avail_space - BASE_SPACING) / (btn_size + BASE_SPACING))
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--this program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
local function dialog_event_handler(self,event)
|
local function dialog_event_handler(self,event)
|
||||||
if self.user_eventhandler == nil or
|
if self.user_eventhandler == nil or
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
ui = {}
|
ui = {}
|
||||||
ui.childlist = {}
|
ui.childlist = {}
|
||||||
|
@ -179,6 +166,10 @@ end
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
core.button_handler = function(fields)
|
core.button_handler = function(fields)
|
||||||
|
if fields["try_quit"] and not fields["key_enter"] then
|
||||||
|
core.event_handler("MenuQuit")
|
||||||
|
return
|
||||||
|
end
|
||||||
if fields["btn_reconnect_yes"] then
|
if fields["btn_reconnect_yes"] then
|
||||||
gamedata.reconnect_requested = false
|
gamedata.reconnect_requested = false
|
||||||
gamedata.errormessage = nil
|
gamedata.errormessage = nil
|
||||||
|
|
|
@ -60,6 +60,8 @@ core.register_on_chat_message(function(name, message)
|
||||||
|
|
||||||
param = param or ""
|
param = param or ""
|
||||||
|
|
||||||
|
core.log("verbose", string.format("Handling chat command %q with params %q", cmd, param))
|
||||||
|
|
||||||
-- Run core.registered_on_chatcommands callbacks.
|
-- Run core.registered_on_chatcommands callbacks.
|
||||||
if core.run_callbacks(core.registered_on_chatcommands, 5, name, cmd, param) then
|
if core.run_callbacks(core.registered_on_chatcommands, 5, name, cmd, param) then
|
||||||
return true
|
return true
|
||||||
|
@ -1275,7 +1277,7 @@ core.register_chatcommand("msg", {
|
||||||
core.log("action", "DM from " .. name .. " to " .. sendto
|
core.log("action", "DM from " .. name .. " to " .. sendto
|
||||||
.. ": " .. message)
|
.. ": " .. message)
|
||||||
core.chat_send_player(sendto, S("DM from @1: @2", name, message))
|
core.chat_send_player(sendto, S("DM from @1: @2", name, message))
|
||||||
return true, S("Message sent.")
|
return true, S("DM sent to @1: @2", sendto, message)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -61,3 +61,10 @@ function core.register_on_auth_fail(func)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- deprecated as of 5.12, as it was not Perlin noise
|
||||||
|
-- but with no warnings (yet) for compatibility
|
||||||
|
core.get_perlin = core.get_value_noise
|
||||||
|
core.get_perlin_map = core.get_value_noise_map
|
||||||
|
PerlinNoise = ValueNoise
|
||||||
|
PerlinNoiseMap = ValueNoiseMap
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
local builtin_shared = ...
|
local builtin_shared = ...
|
||||||
local SCALE = 0.667
|
|
||||||
|
|
||||||
local facedir_to_euler = {
|
local facedir_to_euler = {
|
||||||
{y = 0, x = 0, z = 0},
|
{y = 0, x = 0, z = 0},
|
||||||
|
@ -36,9 +35,7 @@ local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
|
||||||
|
|
||||||
core.register_entity(":__builtin:falling_node", {
|
core.register_entity(":__builtin:falling_node", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
visual = "item",
|
visual = "node",
|
||||||
visual_size = vector.new(SCALE, SCALE, SCALE),
|
|
||||||
textures = {},
|
|
||||||
physical = true,
|
physical = true,
|
||||||
is_visible = false,
|
is_visible = false,
|
||||||
collide_with_objects = true,
|
collide_with_objects = true,
|
||||||
|
@ -78,43 +75,17 @@ core.register_entity(":__builtin:falling_node", {
|
||||||
self.floats = core.get_item_group(node.name, "float") ~= 0
|
self.floats = core.get_item_group(node.name, "float") ~= 0
|
||||||
|
|
||||||
-- Save liquidtype for falling water
|
-- Save liquidtype for falling water
|
||||||
self.liquidtype = def.liquidtype
|
self.liquidtype = def.liquidtype
|
||||||
|
|
||||||
-- Set entity visuals
|
-- Set up entity visuals
|
||||||
if def.drawtype == "torchlike" or def.drawtype == "signlike" then
|
-- For compatibility with older clients we continue to use "item" visual
|
||||||
local textures
|
-- for simple situations.
|
||||||
if def.tiles and def.tiles[1] then
|
local drawtypes = {normal=true, glasslike=true, allfaces=true, nodebox=true}
|
||||||
local tile = def.tiles[1]
|
local p2types = {none=true, facedir=true, ["4dir"]=true}
|
||||||
if type(tile) == "table" then
|
if drawtypes[def.drawtype] and p2types[def.paramtype2] and def.use_texture_alpha ~= "blend" then
|
||||||
tile = tile.name
|
|
||||||
end
|
|
||||||
if def.drawtype == "torchlike" then
|
|
||||||
textures = { "("..tile..")^[transformFX", tile }
|
|
||||||
else
|
|
||||||
textures = { tile, "("..tile..")^[transformFX" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local vsize
|
|
||||||
if def.visual_scale then
|
|
||||||
local s = def.visual_scale
|
|
||||||
vsize = vector.new(s, s, s)
|
|
||||||
end
|
|
||||||
self.object:set_properties({
|
|
||||||
is_visible = true,
|
|
||||||
visual = "upright_sprite",
|
|
||||||
visual_size = vsize,
|
|
||||||
textures = textures,
|
|
||||||
glow = def.light_source,
|
|
||||||
})
|
|
||||||
elseif def.drawtype ~= "airlike" then
|
|
||||||
local itemstring = node.name
|
|
||||||
if core.is_colored_paramtype(def.paramtype2) then
|
|
||||||
itemstring = core.itemstring_with_palette(itemstring, node.param2)
|
|
||||||
end
|
|
||||||
-- FIXME: solution needed for paramtype2 == "leveled"
|
|
||||||
-- Calculate size of falling node
|
-- Calculate size of falling node
|
||||||
local s = {}
|
local s = vector.zero()
|
||||||
s.x = (def.visual_scale or 1) * SCALE
|
s.x = (def.visual_scale or 1) * 0.667
|
||||||
s.y = s.x
|
s.y = s.x
|
||||||
s.z = s.x
|
s.z = s.x
|
||||||
-- Compensate for wield_scale
|
-- Compensate for wield_scale
|
||||||
|
@ -125,10 +96,31 @@ core.register_entity(":__builtin:falling_node", {
|
||||||
end
|
end
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
is_visible = true,
|
is_visible = true,
|
||||||
wield_item = itemstring,
|
visual = "item",
|
||||||
|
wield_item = node.name,
|
||||||
visual_size = s,
|
visual_size = s,
|
||||||
glow = def.light_source,
|
glow = def.light_source,
|
||||||
})
|
})
|
||||||
|
-- Rotate as needed
|
||||||
|
if def.paramtype2 == "facedir" then
|
||||||
|
local fdir = node.param2 % 32 % 24
|
||||||
|
local euler = facedir_to_euler[fdir + 1]
|
||||||
|
if euler then
|
||||||
|
self.object:set_rotation(euler)
|
||||||
|
end
|
||||||
|
elseif def.paramtype2 == "4dir" then
|
||||||
|
local fdir = node.param2 % 4
|
||||||
|
local euler = facedir_to_euler[fdir + 1]
|
||||||
|
if euler then
|
||||||
|
self.object:set_rotation(euler)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif def.drawtype ~= "airlike" then
|
||||||
|
self.object:set_properties({
|
||||||
|
is_visible = true,
|
||||||
|
node = node,
|
||||||
|
glow = def.light_source,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set collision box (certain nodeboxes only for now)
|
-- Set collision box (certain nodeboxes only for now)
|
||||||
|
@ -148,111 +140,6 @@ core.register_entity(":__builtin:falling_node", {
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Rotate entity
|
|
||||||
if def.drawtype == "torchlike" then
|
|
||||||
if (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted")
|
|
||||||
and node.param2 % 8 == 7 then
|
|
||||||
self.object:set_yaw(-math.pi*0.25)
|
|
||||||
else
|
|
||||||
self.object:set_yaw(math.pi*0.25)
|
|
||||||
end
|
|
||||||
elseif ((node.param2 ~= 0 or def.drawtype == "nodebox" or def.drawtype == "mesh")
|
|
||||||
and (def.wield_image == "" or def.wield_image == nil))
|
|
||||||
or def.drawtype == "signlike"
|
|
||||||
or def.drawtype == "mesh"
|
|
||||||
or def.drawtype == "normal"
|
|
||||||
or def.drawtype == "nodebox" then
|
|
||||||
if (def.paramtype2 == "facedir" or def.paramtype2 == "colorfacedir") then
|
|
||||||
local fdir = node.param2 % 32 % 24
|
|
||||||
-- Get rotation from a precalculated lookup table
|
|
||||||
local euler = facedir_to_euler[fdir + 1]
|
|
||||||
if euler then
|
|
||||||
self.object:set_rotation(euler)
|
|
||||||
end
|
|
||||||
elseif (def.paramtype2 == "4dir" or def.paramtype2 == "color4dir") then
|
|
||||||
local fdir = node.param2 % 4
|
|
||||||
-- Get rotation from a precalculated lookup table
|
|
||||||
local euler = facedir_to_euler[fdir + 1]
|
|
||||||
if euler then
|
|
||||||
self.object:set_rotation(euler)
|
|
||||||
end
|
|
||||||
elseif (def.drawtype ~= "plantlike" and def.drawtype ~= "plantlike_rooted" and
|
|
||||||
(def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" or def.drawtype == "signlike")) then
|
|
||||||
local rot = node.param2 % 8
|
|
||||||
if (def.drawtype == "signlike" and def.paramtype2 ~= "wallmounted" and def.paramtype2 ~= "colorwallmounted") then
|
|
||||||
-- Change rotation to "floor" by default for non-wallmounted paramtype2
|
|
||||||
rot = 1
|
|
||||||
end
|
|
||||||
local pitch, yaw, roll = 0, 0, 0
|
|
||||||
if def.drawtype == "nodebox" or def.drawtype == "mesh" then
|
|
||||||
if rot == 0 then
|
|
||||||
pitch, yaw = math.pi/2, 0
|
|
||||||
elseif rot == 1 then
|
|
||||||
pitch, yaw = -math.pi/2, math.pi
|
|
||||||
elseif rot == 2 then
|
|
||||||
pitch, yaw = 0, math.pi/2
|
|
||||||
elseif rot == 3 then
|
|
||||||
pitch, yaw = 0, -math.pi/2
|
|
||||||
elseif rot == 4 then
|
|
||||||
pitch, yaw = 0, math.pi
|
|
||||||
elseif rot == 6 then
|
|
||||||
pitch, yaw = math.pi/2, 0
|
|
||||||
elseif rot == 7 then
|
|
||||||
pitch, yaw = -math.pi/2, math.pi
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if rot == 1 then
|
|
||||||
pitch, yaw = math.pi, math.pi
|
|
||||||
elseif rot == 2 then
|
|
||||||
pitch, yaw = math.pi/2, math.pi/2
|
|
||||||
elseif rot == 3 then
|
|
||||||
pitch, yaw = math.pi/2, -math.pi/2
|
|
||||||
elseif rot == 4 then
|
|
||||||
pitch, yaw = math.pi/2, math.pi
|
|
||||||
elseif rot == 5 then
|
|
||||||
pitch, yaw = math.pi/2, 0
|
|
||||||
elseif rot == 6 then
|
|
||||||
pitch, yaw = math.pi, -math.pi/2
|
|
||||||
elseif rot == 7 then
|
|
||||||
pitch, yaw = 0, -math.pi/2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if def.drawtype == "signlike" then
|
|
||||||
pitch = pitch - math.pi/2
|
|
||||||
if rot == 0 then
|
|
||||||
yaw = yaw + math.pi/2
|
|
||||||
elseif rot == 1 then
|
|
||||||
yaw = yaw - math.pi/2
|
|
||||||
elseif rot == 6 then
|
|
||||||
yaw = yaw - math.pi/2
|
|
||||||
pitch = pitch + math.pi
|
|
||||||
elseif rot == 7 then
|
|
||||||
yaw = yaw + math.pi/2
|
|
||||||
pitch = pitch + math.pi
|
|
||||||
end
|
|
||||||
elseif def.drawtype == "mesh" or def.drawtype == "normal" or def.drawtype == "nodebox" then
|
|
||||||
if rot == 0 or rot == 1 then
|
|
||||||
roll = roll + math.pi
|
|
||||||
elseif rot == 6 or rot == 7 then
|
|
||||||
if def.drawtype ~= "normal" then
|
|
||||||
roll = roll - math.pi/2
|
|
||||||
end
|
|
||||||
else
|
|
||||||
yaw = yaw + math.pi
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.object:set_rotation({x=pitch, y=yaw, z=roll})
|
|
||||||
elseif (def.drawtype == "mesh" and def.paramtype2 == "degrotate") then
|
|
||||||
local p2 = (node.param2 - (def.place_param2 or 0)) % 240
|
|
||||||
local yaw = (p2 / 240) * (math.pi * 2)
|
|
||||||
self.object:set_yaw(yaw)
|
|
||||||
elseif (def.drawtype == "mesh" and def.paramtype2 == "colordegrotate") then
|
|
||||||
local p2 = (node.param2 % 32 - (def.place_param2 or 0) % 32) % 24
|
|
||||||
local yaw = (p2 / 24) * (math.pi * 2)
|
|
||||||
self.object:set_yaw(yaw)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
|
@ -308,23 +195,26 @@ core.register_entity(":__builtin:falling_node", {
|
||||||
|
|
||||||
core.remove_node(bcp)
|
core.remove_node(bcp)
|
||||||
else
|
else
|
||||||
|
-- We are placing on top so check what's there
|
||||||
np.y = np.y + 1
|
np.y = np.y + 1
|
||||||
end
|
|
||||||
|
|
||||||
-- Check what's here
|
local n2 = core.get_node(np)
|
||||||
local n2 = core.get_node(np)
|
local nd = core.registered_nodes[n2.name]
|
||||||
local nd = core.registered_nodes[n2.name]
|
if not nd or nd.buildable_to then
|
||||||
-- If it's not air or liquid, remove node and replace it with
|
core.remove_node(np)
|
||||||
-- it's drops
|
else
|
||||||
if n2.name ~= "air" and (not nd or nd.liquidtype ~= "source") then
|
-- 'walkable' is used to mean "falling nodes can't replace this"
|
||||||
if nd and nd.buildable_to == false then
|
-- here. Normally we would collide with the walkable node itself
|
||||||
|
-- and place our node on top (so `n2.name == "air"`), but we
|
||||||
|
-- re-check this in case we ended up inside a node.
|
||||||
|
if not nd.diggable or nd.walkable then
|
||||||
|
return false
|
||||||
|
end
|
||||||
nd.on_dig(np, n2, nil)
|
nd.on_dig(np, n2, nil)
|
||||||
-- If it's still there, it might be protected
|
-- If it's still there, it might be protected
|
||||||
if core.get_node(np).name == n2.name then
|
if core.get_node(np).name == n2.name then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
else
|
|
||||||
core.remove_node(np)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@ core.features = {
|
||||||
abm_without_neighbors = true,
|
abm_without_neighbors = true,
|
||||||
biome_weights = true,
|
biome_weights = true,
|
||||||
particle_blend_clip = true,
|
particle_blend_clip = true,
|
||||||
|
remove_item_match_meta = true,
|
||||||
|
httpfetch_additional_methods = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function core.has_feature(arg)
|
function core.has_feature(arg)
|
||||||
|
|
|
@ -360,13 +360,12 @@ end
|
||||||
function core.item_drop(itemstack, dropper, pos)
|
function core.item_drop(itemstack, dropper, pos)
|
||||||
local dropper_is_player = dropper and dropper:is_player()
|
local dropper_is_player = dropper and dropper:is_player()
|
||||||
local p = table.copy(pos)
|
local p = table.copy(pos)
|
||||||
local cnt = itemstack:get_count()
|
|
||||||
if dropper_is_player then
|
if dropper_is_player then
|
||||||
p.y = p.y + 1.2
|
p.y = p.y + 1.2
|
||||||
end
|
end
|
||||||
local item = itemstack:take_item(cnt)
|
local obj = core.add_item(p, ItemStack(itemstack))
|
||||||
local obj = core.add_item(p, item)
|
|
||||||
if obj then
|
if obj then
|
||||||
|
itemstack:clear()
|
||||||
if dropper_is_player then
|
if dropper_is_player then
|
||||||
local dir = dropper:get_look_dir()
|
local dir = dropper:get_look_dir()
|
||||||
dir.x = dir.x * 2.9
|
dir.x = dir.x * 2.9
|
||||||
|
@ -375,7 +374,7 @@ function core.item_drop(itemstack, dropper, pos)
|
||||||
obj:set_velocity(dir)
|
obj:set_velocity(dir)
|
||||||
obj:get_luaentity().dropped_by = dropper:get_player_name()
|
obj:get_luaentity().dropped_by = dropper:get_player_name()
|
||||||
end
|
end
|
||||||
return itemstack
|
return itemstack, obj
|
||||||
end
|
end
|
||||||
-- If we reach this, adding the object to the
|
-- If we reach this, adding the object to the
|
||||||
-- environment failed
|
-- environment failed
|
||||||
|
@ -514,7 +513,8 @@ function core.node_dig(pos, node, digger)
|
||||||
.. node.name .. " at " .. core.pos_to_string(pos))
|
.. node.name .. " at " .. core.pos_to_string(pos))
|
||||||
|
|
||||||
local wielded = digger and digger:get_wielded_item()
|
local wielded = digger and digger:get_wielded_item()
|
||||||
local drops = core.get_node_drops(node, wielded and wielded:get_name())
|
local drops = core.get_node_drops(node, wielded and wielded:get_name(),
|
||||||
|
wielded and ItemStack(wielded), digger, vector.copy(pos))
|
||||||
|
|
||||||
if wielded then
|
if wielded then
|
||||||
local wdef = wielded:get_definition()
|
local wdef = wielded:get_definition()
|
||||||
|
|
|
@ -36,7 +36,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
function core.setting_get_pos(name)
|
function core.setting_get_pos(name)
|
||||||
return core.settings:get_pos(name)
|
return core.settings:get_pos(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,3 +112,26 @@ if core.set_push_moveresult1 then
|
||||||
end)
|
end)
|
||||||
core.set_push_moveresult1 = nil
|
core.set_push_moveresult1 = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Protocol version table
|
||||||
|
-- see also src/network/networkprotocol.cpp
|
||||||
|
core.protocol_versions = {
|
||||||
|
["5.0.0"] = 37,
|
||||||
|
["5.1.0"] = 38,
|
||||||
|
["5.2.0"] = 39,
|
||||||
|
["5.3.0"] = 39,
|
||||||
|
["5.4.0"] = 39,
|
||||||
|
["5.5.0"] = 40,
|
||||||
|
["5.6.0"] = 41,
|
||||||
|
["5.7.0"] = 42,
|
||||||
|
["5.8.0"] = 43,
|
||||||
|
["5.9.0"] = 44,
|
||||||
|
["5.9.1"] = 45,
|
||||||
|
["5.10.0"] = 46,
|
||||||
|
["5.11.0"] = 47,
|
||||||
|
["5.12.0"] = 48,
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(core.protocol_versions, {__newindex = function()
|
||||||
|
error("core.protocol_versions is read-only")
|
||||||
|
end})
|
||||||
|
|
|
@ -61,7 +61,7 @@ local function check_modname_prefix(name)
|
||||||
return name:sub(2)
|
return name:sub(2)
|
||||||
else
|
else
|
||||||
-- Enforce that the name starts with the correct mod name.
|
-- Enforce that the name starts with the correct mod name.
|
||||||
local expected_prefix = core.get_current_modname() .. ":"
|
local expected_prefix = (core.get_current_modname() or "") .. ":"
|
||||||
if name:sub(1, #expected_prefix) ~= expected_prefix then
|
if name:sub(1, #expected_prefix) ~= expected_prefix then
|
||||||
error("Name " .. name .. " does not follow naming conventions: " ..
|
error("Name " .. name .. " does not follow naming conventions: " ..
|
||||||
"\"" .. expected_prefix .. "\" or \":\" prefix required")
|
"\"" .. expected_prefix .. "\" or \":\" prefix required")
|
||||||
|
@ -95,6 +95,7 @@ function core.register_abm(spec)
|
||||||
check_node_list(spec.nodenames, "nodenames")
|
check_node_list(spec.nodenames, "nodenames")
|
||||||
check_node_list(spec.neighbors, "neighbors")
|
check_node_list(spec.neighbors, "neighbors")
|
||||||
assert(type(spec.action) == "function", "Required field 'action' of type function")
|
assert(type(spec.action) == "function", "Required field 'action' of type function")
|
||||||
|
|
||||||
core.registered_abms[#core.registered_abms + 1] = spec
|
core.registered_abms[#core.registered_abms + 1] = spec
|
||||||
spec.mod_origin = core.get_current_modname() or "??"
|
spec.mod_origin = core.get_current_modname() or "??"
|
||||||
end
|
end
|
||||||
|
@ -128,127 +129,51 @@ function core.register_entity(name, prototype)
|
||||||
prototype.mod_origin = core.get_current_modname() or "??"
|
prototype.mod_origin = core.get_current_modname() or "??"
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.register_item(name, itemdef)
|
local function preprocess_node(nodedef)
|
||||||
-- Check name
|
-- Use the nodebox as selection box if it's not set manually
|
||||||
if name == nil then
|
if nodedef.drawtype == "nodebox" and not nodedef.selection_box then
|
||||||
error("Unable to register item: Name is nil")
|
nodedef.selection_box = nodedef.node_box
|
||||||
|
elseif nodedef.drawtype == "fencelike" and not nodedef.selection_box then
|
||||||
|
nodedef.selection_box = {
|
||||||
|
type = "fixed",
|
||||||
|
fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
|
||||||
|
}
|
||||||
end
|
end
|
||||||
name = check_modname_prefix(tostring(name))
|
|
||||||
if forbidden_item_names[name] then
|
|
||||||
error("Unable to register item: Name is forbidden: " .. name)
|
|
||||||
end
|
|
||||||
itemdef.name = name
|
|
||||||
|
|
||||||
-- Apply defaults and add to registered_* table
|
if nodedef.light_source and nodedef.light_source > core.LIGHT_MAX then
|
||||||
if itemdef.type == "node" then
|
nodedef.light_source = core.LIGHT_MAX
|
||||||
-- Use the nodebox as selection box if it's not set manually
|
core.log("warning", "Node 'light_source' value exceeds maximum," ..
|
||||||
if itemdef.drawtype == "nodebox" and not itemdef.selection_box then
|
" limiting it: " .. nodedef.name)
|
||||||
itemdef.selection_box = itemdef.node_box
|
|
||||||
elseif itemdef.drawtype == "fencelike" and not itemdef.selection_box then
|
|
||||||
itemdef.selection_box = {
|
|
||||||
type = "fixed",
|
|
||||||
fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
if itemdef.light_source and itemdef.light_source > core.LIGHT_MAX then
|
|
||||||
itemdef.light_source = core.LIGHT_MAX
|
|
||||||
core.log("warning", "Node 'light_source' value exceeds maximum," ..
|
|
||||||
" limiting to maximum: " ..name)
|
|
||||||
end
|
|
||||||
setmetatable(itemdef, {__index = core.nodedef_default})
|
|
||||||
core.registered_nodes[itemdef.name] = itemdef
|
|
||||||
elseif itemdef.type == "craft" then
|
|
||||||
setmetatable(itemdef, {__index = core.craftitemdef_default})
|
|
||||||
core.registered_craftitems[itemdef.name] = itemdef
|
|
||||||
elseif itemdef.type == "tool" then
|
|
||||||
setmetatable(itemdef, {__index = core.tooldef_default})
|
|
||||||
core.registered_tools[itemdef.name] = itemdef
|
|
||||||
elseif itemdef.type == "none" then
|
|
||||||
setmetatable(itemdef, {__index = core.noneitemdef_default})
|
|
||||||
else
|
|
||||||
error("Unable to register item: Type is invalid: " .. dump(itemdef))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Flowing liquid uses param2
|
-- Flowing liquid uses param2
|
||||||
if itemdef.type == "node" and itemdef.liquidtype == "flowing" then
|
if nodedef.liquidtype == "flowing" then
|
||||||
itemdef.paramtype2 = "flowingliquid"
|
nodedef.paramtype2 = "flowingliquid"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function preprocess_craft(itemdef)
|
||||||
-- BEGIN Legacy stuff
|
-- BEGIN Legacy stuff
|
||||||
if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
|
if itemdef.inventory_image == nil and itemdef.image ~= nil then
|
||||||
core.register_craft({
|
core.log("deprecated", "The `image` field in craftitem definitions " ..
|
||||||
type="cooking",
|
"is deprecated. Use `inventory_image` instead. " ..
|
||||||
output=itemdef.cookresult_itemstring,
|
"Craftitem name: " .. itemdef.name, 3)
|
||||||
recipe=itemdef.name,
|
itemdef.inventory_image = itemdef.image
|
||||||
cooktime=itemdef.furnace_cooktime
|
|
||||||
})
|
|
||||||
end
|
|
||||||
if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
|
|
||||||
core.register_craft({
|
|
||||||
type="fuel",
|
|
||||||
recipe=itemdef.name,
|
|
||||||
burntime=itemdef.furnace_burntime
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
-- END Legacy stuff
|
-- END Legacy stuff
|
||||||
|
|
||||||
itemdef.mod_origin = core.get_current_modname() or "??"
|
|
||||||
|
|
||||||
-- Disable all further modifications
|
|
||||||
getmetatable(itemdef).__newindex = {}
|
|
||||||
|
|
||||||
--core.log("Registering item: " .. itemdef.name)
|
|
||||||
core.registered_items[itemdef.name] = itemdef
|
|
||||||
core.registered_aliases[itemdef.name] = nil
|
|
||||||
register_item_raw(itemdef)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.unregister_item(name)
|
local function preprocess_tool(tooldef)
|
||||||
if not core.registered_items[name] then
|
|
||||||
core.log("warning", "Not unregistering item " ..name..
|
|
||||||
" because it doesn't exist.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
-- Erase from registered_* table
|
|
||||||
local type = core.registered_items[name].type
|
|
||||||
if type == "node" then
|
|
||||||
core.registered_nodes[name] = nil
|
|
||||||
elseif type == "craft" then
|
|
||||||
core.registered_craftitems[name] = nil
|
|
||||||
elseif type == "tool" then
|
|
||||||
core.registered_tools[name] = nil
|
|
||||||
end
|
|
||||||
core.registered_items[name] = nil
|
|
||||||
|
|
||||||
|
|
||||||
unregister_item_raw(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.register_node(name, nodedef)
|
|
||||||
nodedef.type = "node"
|
|
||||||
core.register_item(name, nodedef)
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.register_craftitem(name, craftitemdef)
|
|
||||||
craftitemdef.type = "craft"
|
|
||||||
|
|
||||||
-- BEGIN Legacy stuff
|
|
||||||
if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then
|
|
||||||
craftitemdef.inventory_image = craftitemdef.image
|
|
||||||
end
|
|
||||||
-- END Legacy stuff
|
|
||||||
|
|
||||||
core.register_item(name, craftitemdef)
|
|
||||||
end
|
|
||||||
|
|
||||||
function core.register_tool(name, tooldef)
|
|
||||||
tooldef.type = "tool"
|
|
||||||
tooldef.stack_max = 1
|
tooldef.stack_max = 1
|
||||||
|
|
||||||
-- BEGIN Legacy stuff
|
-- BEGIN Legacy stuff
|
||||||
if tooldef.inventory_image == nil and tooldef.image ~= nil then
|
if tooldef.inventory_image == nil and tooldef.image ~= nil then
|
||||||
|
core.log("deprecated", "The `image` field in tool definitions " ..
|
||||||
|
"is deprecated. Use `inventory_image` instead. " ..
|
||||||
|
"Tool name: " .. tooldef.name, 3)
|
||||||
tooldef.inventory_image = tooldef.image
|
tooldef.inventory_image = tooldef.image
|
||||||
end
|
end
|
||||||
|
|
||||||
if tooldef.tool_capabilities == nil and
|
if tooldef.tool_capabilities == nil and
|
||||||
(tooldef.full_punch_interval ~= nil or
|
(tooldef.full_punch_interval ~= nil or
|
||||||
tooldef.basetime ~= nil or
|
tooldef.basetime ~= nil or
|
||||||
|
@ -261,6 +186,9 @@ function core.register_tool(name, tooldef)
|
||||||
tooldef.dd_crackiness ~= nil or
|
tooldef.dd_crackiness ~= nil or
|
||||||
tooldef.dd_crumbliness ~= nil or
|
tooldef.dd_crumbliness ~= nil or
|
||||||
tooldef.dd_cuttability ~= nil) then
|
tooldef.dd_cuttability ~= nil) then
|
||||||
|
core.log("deprecated", "Specifying tool capabilities directly in the tool " ..
|
||||||
|
"definition is deprecated. Use the `tool_capabilities` field instead. " ..
|
||||||
|
"Tool name: " .. tooldef.name, 3)
|
||||||
tooldef.tool_capabilities = {
|
tooldef.tool_capabilities = {
|
||||||
full_punch_interval = tooldef.full_punch_interval,
|
full_punch_interval = tooldef.full_punch_interval,
|
||||||
basetime = tooldef.basetime,
|
basetime = tooldef.basetime,
|
||||||
|
@ -277,7 +205,7 @@ function core.register_tool(name, tooldef)
|
||||||
end
|
end
|
||||||
-- END Legacy stuff
|
-- END Legacy stuff
|
||||||
|
|
||||||
-- This isn't just legacy, but more of a convenience feature
|
-- Automatically set punch_attack_uses as a convenience feature
|
||||||
local toolcaps = tooldef.tool_capabilities
|
local toolcaps = tooldef.tool_capabilities
|
||||||
if toolcaps and toolcaps.punch_attack_uses == nil then
|
if toolcaps and toolcaps.punch_attack_uses == nil then
|
||||||
for _, cap in pairs(toolcaps.groupcaps or {}) do
|
for _, cap in pairs(toolcaps.groupcaps or {}) do
|
||||||
|
@ -288,8 +216,126 @@ function core.register_tool(name, tooldef)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
core.register_item(name, tooldef)
|
local default_tables = {
|
||||||
|
node = core.nodedef_default,
|
||||||
|
craft = core.craftitemdef_default,
|
||||||
|
tool = core.tooldef_default,
|
||||||
|
none = core.noneitemdef_default,
|
||||||
|
}
|
||||||
|
|
||||||
|
local preprocess_fns = {
|
||||||
|
node = preprocess_node,
|
||||||
|
craft = preprocess_craft,
|
||||||
|
tool = preprocess_tool,
|
||||||
|
}
|
||||||
|
|
||||||
|
function core.register_item(name, itemdef)
|
||||||
|
-- Check name
|
||||||
|
if name == nil then
|
||||||
|
error("Unable to register item: Name is nil")
|
||||||
|
end
|
||||||
|
name = check_modname_prefix(tostring(name))
|
||||||
|
if forbidden_item_names[name] then
|
||||||
|
error("Unable to register item: Name is forbidden: " .. name)
|
||||||
|
end
|
||||||
|
|
||||||
|
itemdef.name = name
|
||||||
|
|
||||||
|
-- Compatibility stuff depending on type
|
||||||
|
local fn = preprocess_fns[itemdef.type]
|
||||||
|
if fn then
|
||||||
|
fn(itemdef)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Apply defaults
|
||||||
|
local defaults = default_tables[itemdef.type]
|
||||||
|
if defaults == nil then
|
||||||
|
error("Unable to register item: Type is invalid: " .. dump(itemdef))
|
||||||
|
end
|
||||||
|
local old_mt = getmetatable(itemdef)
|
||||||
|
-- TODO most of these checks should become an error after a while (maybe in 2026?)
|
||||||
|
if old_mt ~= nil and next(old_mt) ~= nil then
|
||||||
|
-- Note that even registering multiple identical items with the same table
|
||||||
|
-- is not allowed, due to the 'name' property.
|
||||||
|
if old_mt.__index == defaults then
|
||||||
|
core.log("warning", "Item definition table was reused between registrations. "..
|
||||||
|
"This is unsupported and broken: " .. name)
|
||||||
|
else
|
||||||
|
core.log("warning", "Item definition has a metatable, this is "..
|
||||||
|
"unsupported and it will be overwritten: " .. name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
setmetatable(itemdef, {__index = defaults})
|
||||||
|
|
||||||
|
-- BEGIN Legacy stuff
|
||||||
|
if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
|
||||||
|
core.log("deprecated", "The `cookresult_itemstring` item definition " ..
|
||||||
|
"field is deprecated. Use `core.register_craft` instead. " ..
|
||||||
|
"Item name: " .. itemdef.name, 2)
|
||||||
|
core.register_craft({
|
||||||
|
type="cooking",
|
||||||
|
output=itemdef.cookresult_itemstring,
|
||||||
|
recipe=itemdef.name,
|
||||||
|
cooktime=itemdef.furnace_cooktime
|
||||||
|
})
|
||||||
|
end
|
||||||
|
if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
|
||||||
|
core.log("deprecated", "The `furnace_burntime` item definition " ..
|
||||||
|
"field is deprecated. Use `core.register_craft` instead. " ..
|
||||||
|
"Item name: " .. itemdef.name, 2)
|
||||||
|
core.register_craft({
|
||||||
|
type="fuel",
|
||||||
|
recipe=itemdef.name,
|
||||||
|
burntime=itemdef.furnace_burntime
|
||||||
|
})
|
||||||
|
end
|
||||||
|
-- END Legacy stuff
|
||||||
|
|
||||||
|
itemdef.mod_origin = core.get_current_modname() or "??"
|
||||||
|
|
||||||
|
-- Ignore new keys as a failsafe to prevent mistakes
|
||||||
|
getmetatable(itemdef).__newindex = function() end
|
||||||
|
|
||||||
|
-- Add to registered_* tables
|
||||||
|
if itemdef.type == "node" then
|
||||||
|
core.registered_nodes[itemdef.name] = itemdef
|
||||||
|
elseif itemdef.type == "craft" then
|
||||||
|
core.registered_craftitems[itemdef.name] = itemdef
|
||||||
|
elseif itemdef.type == "tool" then
|
||||||
|
core.registered_tools[itemdef.name] = itemdef
|
||||||
|
end
|
||||||
|
core.registered_items[itemdef.name] = itemdef
|
||||||
|
core.registered_aliases[itemdef.name] = nil
|
||||||
|
|
||||||
|
register_item_raw(itemdef)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_register_item_wrapper(the_type)
|
||||||
|
return function(name, itemdef)
|
||||||
|
itemdef.type = the_type
|
||||||
|
return core.register_item(name, itemdef)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
core.register_node = make_register_item_wrapper("node")
|
||||||
|
core.register_craftitem = make_register_item_wrapper("craft")
|
||||||
|
core.register_tool = make_register_item_wrapper("tool")
|
||||||
|
|
||||||
|
function core.unregister_item(name)
|
||||||
|
if not core.registered_items[name] then
|
||||||
|
core.log("warning", "Not unregistering item " ..name..
|
||||||
|
" because it doesn't exist.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- Erase from registered_* table
|
||||||
|
core.registered_nodes[name] = nil
|
||||||
|
core.registered_craftitems[name] = nil
|
||||||
|
core.registered_tools[name] = nil
|
||||||
|
core.registered_items[name] = nil
|
||||||
|
|
||||||
|
unregister_item_raw(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.register_alias(name, convert_to)
|
function core.register_alias(name, convert_to)
|
||||||
|
@ -300,7 +346,6 @@ function core.register_alias(name, convert_to)
|
||||||
core.log("warning", "Not registering alias, item with same name" ..
|
core.log("warning", "Not registering alias, item with same name" ..
|
||||||
" is already defined: " .. name .. " -> " .. convert_to)
|
" is already defined: " .. name .. " -> " .. convert_to)
|
||||||
else
|
else
|
||||||
--core.log("Registering alias: " .. name .. " -> " .. convert_to)
|
|
||||||
core.registered_aliases[name] = convert_to
|
core.registered_aliases[name] = convert_to
|
||||||
register_alias_raw(name, convert_to)
|
register_alias_raw(name, convert_to)
|
||||||
end
|
end
|
||||||
|
@ -315,7 +360,6 @@ function core.register_alias_force(name, convert_to)
|
||||||
core.log("info", "Removed item " ..name..
|
core.log("info", "Removed item " ..name..
|
||||||
" while attempting to force add an alias")
|
" while attempting to force add an alias")
|
||||||
end
|
end
|
||||||
--core.log("Registering alias: " .. name .. " -> " .. convert_to)
|
|
||||||
core.registered_aliases[name] = convert_to
|
core.registered_aliases[name] = convert_to
|
||||||
register_alias_raw(name, convert_to)
|
register_alias_raw(name, convert_to)
|
||||||
end
|
end
|
||||||
|
@ -406,6 +450,7 @@ core.register_item(":", {
|
||||||
groups = {not_in_creative_inventory=1},
|
groups = {not_in_creative_inventory=1},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
local itemdefs_finalized = false
|
||||||
|
|
||||||
function core.override_item(name, redefinition, del_fields)
|
function core.override_item(name, redefinition, del_fields)
|
||||||
if redefinition.name ~= nil then
|
if redefinition.name ~= nil then
|
||||||
|
@ -418,10 +463,16 @@ function core.override_item(name, redefinition, del_fields)
|
||||||
if not item then
|
if not item then
|
||||||
error("Attempt to override non-existent item "..name, 2)
|
error("Attempt to override non-existent item "..name, 2)
|
||||||
end
|
end
|
||||||
|
if itemdefs_finalized then
|
||||||
|
-- TODO: it's not clear if this needs to be allowed at all?
|
||||||
|
core.log("warning", "Overriding item " .. name .. " after server startup. " ..
|
||||||
|
"This is unsupported and can cause problems related to data inconsistency.")
|
||||||
|
end
|
||||||
for k, v in pairs(redefinition) do
|
for k, v in pairs(redefinition) do
|
||||||
rawset(item, k, v)
|
rawset(item, k, v)
|
||||||
end
|
end
|
||||||
for _, field in ipairs(del_fields or {}) do
|
for _, field in ipairs(del_fields or {}) do
|
||||||
|
assert(field ~= "name" and field ~= "type")
|
||||||
rawset(item, field, nil)
|
rawset(item, field, nil)
|
||||||
end
|
end
|
||||||
register_item_raw(item)
|
register_item_raw(item)
|
||||||
|
@ -568,13 +619,57 @@ core.registered_on_rightclickplayers, core.register_on_rightclickplayer = make_r
|
||||||
core.registered_on_liquid_transformed, core.register_on_liquid_transformed = make_registration()
|
core.registered_on_liquid_transformed, core.register_on_liquid_transformed = make_registration()
|
||||||
core.registered_on_mapblocks_changed, core.register_on_mapblocks_changed = make_registration()
|
core.registered_on_mapblocks_changed, core.register_on_mapblocks_changed = make_registration()
|
||||||
|
|
||||||
|
-- A bunch of registrations are read by the C++ side once on env init, so we cannot
|
||||||
|
-- allow them to change afterwards (see s_env.cpp).
|
||||||
|
-- Nodes and items do not have this problem but there are obvious consistency
|
||||||
|
-- problems if this would be allowed.
|
||||||
|
|
||||||
|
local function freeze_table(t)
|
||||||
|
-- Freezing a Lua table is not actually possible without some very intrusive
|
||||||
|
-- metatable hackery, but we can trivially prevent new additions.
|
||||||
|
local mt = table.copy(getmetatable(t) or {})
|
||||||
|
mt.__newindex = function()
|
||||||
|
error("modification forbidden")
|
||||||
|
end
|
||||||
|
setmetatable(t, mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function generic_reg_error(what)
|
||||||
|
return function(something)
|
||||||
|
local described = what
|
||||||
|
if type(something) == "table" and type(something.name) == "string" then
|
||||||
|
described = what .. " " .. something.name
|
||||||
|
elseif type(something) == "string" then
|
||||||
|
described = what .. " " .. something
|
||||||
|
end
|
||||||
|
error("Tried to register " .. described .. " after load time!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
core.register_on_mods_loaded(function()
|
core.register_on_mods_loaded(function()
|
||||||
core.after(0, function()
|
core.after(0, function()
|
||||||
setmetatable(core.registered_on_mapblocks_changed, {
|
itemdefs_finalized = true
|
||||||
__newindex = function()
|
|
||||||
error("on_mapblocks_changed callbacks must be registered at load time")
|
-- prevent direct modification
|
||||||
end,
|
freeze_table(core.registered_abms)
|
||||||
})
|
freeze_table(core.registered_lbms)
|
||||||
|
freeze_table(core.registered_items)
|
||||||
|
freeze_table(core.registered_nodes)
|
||||||
|
freeze_table(core.registered_craftitems)
|
||||||
|
freeze_table(core.registered_tools)
|
||||||
|
freeze_table(core.registered_aliases)
|
||||||
|
freeze_table(core.registered_on_mapblocks_changed)
|
||||||
|
|
||||||
|
-- neutralize registration functions
|
||||||
|
core.register_abm = generic_reg_error("ABM")
|
||||||
|
core.register_lbm = generic_reg_error("LBM")
|
||||||
|
core.register_item = generic_reg_error("item")
|
||||||
|
core.unregister_item = function(name)
|
||||||
|
error("Refusing to unregister item " .. name .. " after load time")
|
||||||
|
end
|
||||||
|
core.register_alias = generic_reg_error("alias")
|
||||||
|
core.register_alias_force = generic_reg_error("alias")
|
||||||
|
core.register_on_mapblocks_changed = generic_reg_error("on_mapblocks_changed callback")
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,8 @@ elseif INIT == "client" then
|
||||||
dofile(scriptdir .. "client" .. DIR_DELIM .. "init.lua")
|
dofile(scriptdir .. "client" .. DIR_DELIM .. "init.lua")
|
||||||
elseif INIT == "emerge" then
|
elseif INIT == "emerge" then
|
||||||
dofile(scriptdir .. "emerge" .. DIR_DELIM .. "init.lua")
|
dofile(scriptdir .. "emerge" .. DIR_DELIM .. "init.lua")
|
||||||
|
elseif INIT == "pause_menu" then
|
||||||
|
dofile(scriptdir .. "pause_menu" .. DIR_DELIM .. "init.lua")
|
||||||
else
|
else
|
||||||
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
|
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
-- Global menu data
|
-- Global menu data
|
||||||
menudata = {}
|
menudata = {}
|
||||||
|
@ -34,7 +21,6 @@ function check_cache_age(key, max_age)
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.on_before_close()
|
function core.on_before_close()
|
||||||
-- called before the menu is closed, either exit or to join a game
|
|
||||||
cache_settings:write()
|
cache_settings:write()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2018-24 rubenwardy
|
-- Copyright (C) 2018-24 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
if not core.get_http_api then
|
if not core.get_http_api then
|
||||||
return
|
return
|
||||||
|
@ -41,6 +28,7 @@ contentdb = {
|
||||||
REASON_DEPENDENCY = "dependency",
|
REASON_DEPENDENCY = "dependency",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- API documentation: https://content.luanti.org/help/api/
|
||||||
|
|
||||||
local function get_download_url(package, reason)
|
local function get_download_url(package, reason)
|
||||||
local base_url = core.settings:get("contentdb_url")
|
local base_url = core.settings:get("contentdb_url")
|
||||||
|
@ -182,14 +170,16 @@ function contentdb.get_package_by_id(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function contentdb.calculate_package_id(type, author, name)
|
local function strip_game_suffix(type, name)
|
||||||
local id = author:lower() .. "/"
|
|
||||||
if (type == nil or type == "game") and #name > 5 and name:sub(#name - 4) == "_game" then
|
if (type == nil or type == "game") and #name > 5 and name:sub(#name - 4) == "_game" then
|
||||||
id = id .. name:sub(1, #name - 5)
|
return name:sub(1, #name - 5)
|
||||||
else
|
else
|
||||||
id = id .. name
|
return name
|
||||||
end
|
end
|
||||||
return id
|
end
|
||||||
|
|
||||||
|
function contentdb.calculate_package_id(type, author, name)
|
||||||
|
return author:lower() .. "/" .. strip_game_suffix(type, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -398,7 +388,6 @@ local function fetch_pkgs()
|
||||||
local url = base_url ..
|
local url = base_url ..
|
||||||
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
||||||
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
|
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
|
||||||
|
|
||||||
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
|
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
|
||||||
item = item:trim()
|
item = item:trim()
|
||||||
if item ~= "" then
|
if item ~= "" then
|
||||||
|
@ -406,19 +395,11 @@ local function fetch_pkgs()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local languages
|
|
||||||
local current_language = core.get_language()
|
|
||||||
if current_language ~= "" then
|
|
||||||
languages = { current_language, "en;q=0.8" }
|
|
||||||
else
|
|
||||||
languages = { "en" }
|
|
||||||
end
|
|
||||||
|
|
||||||
local http = core.get_http_api()
|
local http = core.get_http_api()
|
||||||
local response = http.fetch_sync({
|
local response = http.fetch_sync({
|
||||||
url = url,
|
url = url,
|
||||||
extra_headers = {
|
extra_headers = {
|
||||||
"Accept-Language: " .. table.concat(languages, ", ")
|
core.get_http_accept_languages()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if not response.succeeded then
|
if not response.succeeded then
|
||||||
|
@ -448,7 +429,7 @@ function contentdb.set_packages_from_api(packages)
|
||||||
-- We currently don't support name changing
|
-- We currently don't support name changing
|
||||||
local suffix = "/" .. package.name
|
local suffix = "/" .. package.name
|
||||||
if alias:sub(-#suffix) == suffix then
|
if alias:sub(-#suffix) == suffix then
|
||||||
contentdb.aliases[alias:lower()] = package.id
|
contentdb.aliases[strip_game_suffix(packages.type, alias:lower())] = package.id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -596,57 +577,54 @@ function contentdb.filter_packages(query, by_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function contentdb.get_full_package_info(package, callback)
|
local function get_package_info(key, path)
|
||||||
assert(package)
|
return function(package, callback)
|
||||||
if package.full_info then
|
assert(package)
|
||||||
callback(package.full_info)
|
if package[key] then
|
||||||
return
|
callback(package[key])
|
||||||
end
|
return
|
||||||
|
|
||||||
local function fetch(params)
|
|
||||||
local version = core.get_version()
|
|
||||||
local base_url = core.settings:get("contentdb_url")
|
|
||||||
|
|
||||||
local languages
|
|
||||||
local current_language = core.get_language()
|
|
||||||
if current_language ~= "" then
|
|
||||||
languages = { current_language, "en;q=0.8" }
|
|
||||||
else
|
|
||||||
languages = { "en" }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local url = base_url ..
|
local function fetch(params)
|
||||||
"/api/packages/" .. params.package.url_part .. "/for-client/?" ..
|
local version = core.get_version()
|
||||||
"protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
|
local base_url = core.settings:get("contentdb_url")
|
||||||
"&engine_version=" .. core.urlencode(version.string) ..
|
local url = base_url ..
|
||||||
"&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
|
"/api/packages/" .. params.package.url_part .. params.path .. "?" ..
|
||||||
"&include_images=false"
|
"protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
|
||||||
local http = core.get_http_api()
|
"&engine_version=" .. core.urlencode(version.string) ..
|
||||||
local response = http.fetch_sync({
|
"&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
|
||||||
url = url,
|
"&include_images=false"
|
||||||
extra_headers = {
|
local http = core.get_http_api()
|
||||||
"Accept-Language: " .. table.concat(languages, ", ")
|
local response = http.fetch_sync({
|
||||||
},
|
url = url,
|
||||||
})
|
extra_headers = {
|
||||||
if not response.succeeded then
|
core.get_http_accept_languages()
|
||||||
return nil
|
},
|
||||||
|
})
|
||||||
|
if not response.succeeded then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return core.parse_json(response.data)
|
||||||
end
|
end
|
||||||
|
|
||||||
return core.parse_json(response.data)
|
local function my_callback(value)
|
||||||
end
|
package[key] = value
|
||||||
|
callback(value)
|
||||||
|
end
|
||||||
|
|
||||||
local function my_callback(value)
|
if not core.handle_async(fetch, { package = package, path = path }, my_callback) then
|
||||||
package.full_info = value
|
core.log("error", "ERROR: async event failed")
|
||||||
callback(value)
|
callback(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not core.handle_async(fetch, { package = package }, my_callback) then
|
|
||||||
core.log("error", "ERROR: async event failed")
|
|
||||||
callback(nil)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
contentdb.get_full_package_info = get_package_info("full_info", "/for-client/")
|
||||||
|
contentdb.get_package_reviews = get_package_info("reviews", "/for-client/reviews/")
|
||||||
|
|
||||||
|
|
||||||
function contentdb.get_formspec_padding()
|
function contentdb.get_formspec_padding()
|
||||||
-- Padding is increased on Android to account for notches
|
-- Padding is increased on Android to account for notches
|
||||||
-- TODO: use Android API to determine size of cut outs
|
-- TODO: use Android API to determine size of cut outs
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2018-20 rubenwardy
|
-- Copyright (C) 2018-20 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
if not core.get_http_api then
|
if not core.get_http_api then
|
||||||
function create_contentdb_dlg()
|
function create_contentdb_dlg()
|
||||||
|
@ -323,9 +310,17 @@ local function get_formspec(dlgdata)
|
||||||
})
|
})
|
||||||
local img_w = cell_h * 3 / 2
|
local img_w = cell_h * 3 / 2
|
||||||
|
|
||||||
|
-- Use as much of the available space as possible (so no padding on the
|
||||||
|
-- right/bottom), but don't quite allow the text to touch the border.
|
||||||
|
local text_w = cell_w - img_w - 0.25 - 0.025
|
||||||
|
local text_h = cell_h - 0.25 - 0.025
|
||||||
|
|
||||||
local start_idx = (cur_page - 1) * num_per_page + 1
|
local start_idx = (cur_page - 1) * num_per_page + 1
|
||||||
for i=start_idx, math.min(#contentdb.packages, start_idx+num_per_page-1) do
|
for i=start_idx, math.min(#contentdb.packages, start_idx+num_per_page-1) do
|
||||||
local package = contentdb.packages[i]
|
local package = contentdb.packages[i]
|
||||||
|
local text = core.colorize(mt_color_green, package.title) ..
|
||||||
|
core.colorize("#BFBFBF", " by " .. package.author) .. "\n" ..
|
||||||
|
package.short_description
|
||||||
|
|
||||||
table.insert_all(formspec, {
|
table.insert_all(formspec, {
|
||||||
"container[",
|
"container[",
|
||||||
|
@ -340,13 +335,14 @@ local function get_formspec(dlgdata)
|
||||||
"image[0,0;", img_w, ",", cell_h, ";",
|
"image[0,0;", img_w, ",", cell_h, ";",
|
||||||
core.formspec_escape(get_screenshot(package, package.thumbnail, 2)), "]",
|
core.formspec_escape(get_screenshot(package, package.thumbnail, 2)), "]",
|
||||||
|
|
||||||
"label[", img_w + 0.25 + 0.05, ",0.5;",
|
"label[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
|
||||||
core.formspec_escape(
|
core.formspec_escape(text), "]",
|
||||||
core.colorize(mt_color_green, package.title) ..
|
|
||||||
core.colorize("#BFBFBF", " by " .. package.author)), "]",
|
|
||||||
|
|
||||||
"textarea[", img_w + 0.25, ",0.75;", cell_w - img_w - 0.25, ",", cell_h - 0.75, ";;;",
|
-- Add a tooltip in case the label overflows and the short description is cut off.
|
||||||
core.formspec_escape(package.short_description), "]",
|
"tooltip[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
|
||||||
|
-- Text in tooltips doesn't wrap automatically, so we do it manually to
|
||||||
|
-- avoid everything being one long line.
|
||||||
|
core.formspec_escape(core.wrap_text(package.short_description, 80)), "]",
|
||||||
|
|
||||||
"style[view_", i, ";border=false]",
|
"style[view_", i, ";border=false]",
|
||||||
"style[view_", i, ":hovered;bgimg=", core.formspec_escape(defaulttexturedir .. "button_hover_semitrans.png"), "]",
|
"style[view_", i, ":hovered;bgimg=", core.formspec_escape(defaulttexturedir .. "button_hover_semitrans.png"), "]",
|
||||||
|
@ -362,7 +358,7 @@ local function get_formspec(dlgdata)
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert_all(formspec, {
|
table.insert_all(formspec, {
|
||||||
"container[", cell_w - 0.625,",", 0.25, "]",
|
"container[", cell_w - 0.625,",", 0.125, "]",
|
||||||
})
|
})
|
||||||
|
|
||||||
if package.downloading then
|
if package.downloading then
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2018-24 rubenwardy
|
-- Copyright (C) 2018-24 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
local function is_still_visible(dlg)
|
local function is_still_visible(dlg)
|
||||||
local this = ui.find_by_name("install_dialog")
|
local this = ui.find_by_name("install_dialog")
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2018-24 rubenwardy
|
-- Copyright (C) 2018-24 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
function get_formspec(data)
|
function get_formspec(data)
|
||||||
local package = data.package
|
local package = data.package
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2018-24 rubenwardy
|
-- Copyright (C) 2018-24 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
local function get_info_formspec(size, padding, text)
|
local function get_info_formspec(size, padding, text)
|
||||||
|
@ -32,6 +19,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
local function get_formspec(data)
|
local function get_formspec(data)
|
||||||
|
local package = data.package
|
||||||
local window_padding = contentdb.get_formspec_padding()
|
local window_padding = contentdb.get_formspec_padding()
|
||||||
local size = contentdb.get_formspec_size()
|
local size = contentdb.get_formspec_size()
|
||||||
size.x = math.min(size.x, 20)
|
size.x = math.min(size.x, 20)
|
||||||
|
@ -42,7 +30,7 @@ local function get_formspec(data)
|
||||||
if not data.loading and not data.loading_error then
|
if not data.loading and not data.loading_error then
|
||||||
data.loading = true
|
data.loading = true
|
||||||
|
|
||||||
contentdb.get_full_package_info(data.package, function(info)
|
contentdb.get_full_package_info(package, function(info)
|
||||||
data.loading = false
|
data.loading = false
|
||||||
|
|
||||||
if info == nil then
|
if info == nil then
|
||||||
|
@ -61,7 +49,7 @@ local function get_formspec(data)
|
||||||
-- check to see if that happened
|
-- check to see if that happened
|
||||||
if not data.info then
|
if not data.info then
|
||||||
if data.loading_error then
|
if data.loading_error then
|
||||||
return get_info_formspec(size, window_padding, fgettext("No packages could be retrieved"))
|
return get_info_formspec(size, window_padding, fgettext("Error loading package information"))
|
||||||
end
|
end
|
||||||
return get_info_formspec(size, window_padding, fgettext("Loading..."))
|
return get_info_formspec(size, window_padding, fgettext("Loading..."))
|
||||||
end
|
end
|
||||||
|
@ -103,15 +91,15 @@ local function get_formspec(data)
|
||||||
|
|
||||||
local left_button_rect = "0,0;2.875,1"
|
local left_button_rect = "0,0;2.875,1"
|
||||||
local right_button_rect = "3.125,0;2.875,1"
|
local right_button_rect = "3.125,0;2.875,1"
|
||||||
if data.package.downloading then
|
if package.downloading then
|
||||||
formspec[#formspec + 1] = "animated_image[5,0;1,1;downloading;"
|
formspec[#formspec + 1] = "animated_image[5,0;1,1;downloading;"
|
||||||
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
|
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
|
||||||
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
|
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
|
||||||
elseif data.package.queued then
|
elseif package.queued then
|
||||||
formspec[#formspec + 1] = "style[queued;border=false]"
|
formspec[#formspec + 1] = "style[queued;border=false]"
|
||||||
formspec[#formspec + 1] = "image_button[5,0;1,1;" .. core.formspec_escape(defaulttexturedir)
|
formspec[#formspec + 1] = "image_button[5,0;1,1;" .. core.formspec_escape(defaulttexturedir)
|
||||||
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
|
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
|
||||||
elseif not data.package.path then
|
elseif not package.path then
|
||||||
formspec[#formspec + 1] = "style[install;bgcolor=green]"
|
formspec[#formspec + 1] = "style[install;bgcolor=green]"
|
||||||
formspec[#formspec + 1] = "button["
|
formspec[#formspec + 1] = "button["
|
||||||
formspec[#formspec + 1] = right_button_rect
|
formspec[#formspec + 1] = right_button_rect
|
||||||
|
@ -119,7 +107,7 @@ local function get_formspec(data)
|
||||||
formspec[#formspec + 1] = fgettext("Install [$1]", info.download_size)
|
formspec[#formspec + 1] = fgettext("Install [$1]", info.download_size)
|
||||||
formspec[#formspec + 1] = "]"
|
formspec[#formspec + 1] = "]"
|
||||||
else
|
else
|
||||||
if data.package.installed_release < data.package.release then
|
if package.installed_release < package.release then
|
||||||
-- The install_ action also handles updating
|
-- The install_ action also handles updating
|
||||||
formspec[#formspec + 1] = "style[install;bgcolor=#28ccdf]"
|
formspec[#formspec + 1] = "style[install;bgcolor=#28ccdf]"
|
||||||
formspec[#formspec + 1] = "button["
|
formspec[#formspec + 1] = "button["
|
||||||
|
@ -137,10 +125,12 @@ local function get_formspec(data)
|
||||||
formspec[#formspec + 1] = "]"
|
formspec[#formspec + 1] = "]"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local review_count = info.reviews.positive + info.reviews.neutral + info.reviews.negative
|
||||||
local current_tab = data.current_tab or 1
|
local current_tab = data.current_tab or 1
|
||||||
local tab_titles = {
|
local tab_titles = {
|
||||||
fgettext("Description"),
|
fgettext("Description"),
|
||||||
fgettext("Information"),
|
fgettext("Information"),
|
||||||
|
fgettext("Reviews") .. core.formspec_escape(" [" .. review_count .. "]"),
|
||||||
}
|
}
|
||||||
|
|
||||||
local tab_body_height = bottom_buttons_y - 2.8
|
local tab_body_height = bottom_buttons_y - 2.8
|
||||||
|
@ -162,8 +152,8 @@ local function get_formspec(data)
|
||||||
local winfo = core.get_window_info()
|
local winfo = core.get_window_info()
|
||||||
local fs_to_px = winfo.size.x / winfo.max_formspec_size.x
|
local fs_to_px = winfo.size.x / winfo.max_formspec_size.x
|
||||||
for i, ss in ipairs(info.screenshots) do
|
for i, ss in ipairs(info.screenshots) do
|
||||||
local path = get_screenshot(data.package, ss.url, 2)
|
local path = get_screenshot(package, ss.url, 2)
|
||||||
hypertext = hypertext .. "<action name=\"ss_" .. i .. "\"><img name=\"" ..
|
hypertext = hypertext .. "<action name=\"ss_".. i .. "\"><img name=\"" ..
|
||||||
core.hypertext_escape(path) .. "\" width=" .. (3 * fs_to_px) ..
|
core.hypertext_escape(path) .. "\" width=" .. (3 * fs_to_px) ..
|
||||||
" height=" .. (2 * fs_to_px) .. "></action>"
|
" height=" .. (2 * fs_to_px) .. "></action>"
|
||||||
if i ~= #info.screenshots then
|
if i ~= #info.screenshots then
|
||||||
|
@ -194,22 +184,54 @@ local function get_formspec(data)
|
||||||
|
|
||||||
hypertext = hypertext .. "\n\n" .. info.long_description.body
|
hypertext = hypertext .. "\n\n" .. info.long_description.body
|
||||||
|
|
||||||
|
-- Fix the path to blank.png. This is needed for bullet indentation.
|
||||||
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
|
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
|
||||||
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
|
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
|
||||||
|
|
||||||
table.insert_all(formspec, {
|
table.insert_all(formspec, {
|
||||||
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
|
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
|
||||||
";desc;", core.formspec_escape(hypertext), "]",
|
";desc;", core.formspec_escape(hypertext), "]",
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
elseif current_tab == 2 then
|
elseif current_tab == 2 then
|
||||||
local hypertext = info.info_hypertext.head .. info.info_hypertext.body
|
local hypertext = info.info_hypertext.head .. info.info_hypertext.body
|
||||||
|
|
||||||
table.insert_all(formspec, {
|
table.insert_all(formspec, {
|
||||||
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
|
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
|
||||||
";info;", core.formspec_escape(hypertext), "]",
|
";info;", core.formspec_escape(hypertext), "]",
|
||||||
})
|
})
|
||||||
|
elseif current_tab == 3 then
|
||||||
|
if not package.reviews and not data.reviews_error and not data.reviews_loading then
|
||||||
|
data.reviews_loading = true
|
||||||
|
|
||||||
|
contentdb.get_package_reviews(package, function(reviews)
|
||||||
|
if not reviews then
|
||||||
|
data.reviews_error = true
|
||||||
|
end
|
||||||
|
ui.update()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
if package.reviews then
|
||||||
|
local hypertext = package.reviews.head .. package.reviews.body
|
||||||
|
-- Provide correct path to blank.png image. This is needed for bullet indentation.
|
||||||
|
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
|
||||||
|
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
|
||||||
|
-- Placeholders in reviews hypertext for icons
|
||||||
|
hypertext = hypertext:gsub("<thumbsup>",
|
||||||
|
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_thumb_up.png\" width=24>")
|
||||||
|
hypertext = hypertext:gsub("<thumbsdown>",
|
||||||
|
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_thumb_down.png\" width=24>")
|
||||||
|
hypertext = hypertext:gsub("<neutral>",
|
||||||
|
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_neutral.png\" width=24>")
|
||||||
|
table.insert_all(formspec, {
|
||||||
|
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
|
||||||
|
";reviews;", core.formspec_escape(hypertext), "]",
|
||||||
|
})
|
||||||
|
elseif data.reviews_error then
|
||||||
|
table.insert_all(formspec, {"label[2,2;", fgettext("Error loading reviews"), "]"} )
|
||||||
|
else
|
||||||
|
table.insert_all(formspec, {"label[2,2;", fgettext("Loading..."), "]"} )
|
||||||
|
end
|
||||||
else
|
else
|
||||||
error("Unknown tab " .. current_tab)
|
error("Unknown tab " .. current_tab)
|
||||||
end
|
end
|
||||||
|
@ -269,9 +291,10 @@ local function handle_submit(this, fields)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fields.open_contentdb then
|
if fields.open_contentdb then
|
||||||
local url = ("%s/packages/%s/?protocol_version=%d"):format(
|
local version = core.get_version()
|
||||||
core.settings:get("contentdb_url"), package.url_part,
|
local url = core.settings:get("contentdb_url") .. "/packages/" .. package.url_part ..
|
||||||
core.get_max_supp_proto())
|
"/?protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
|
||||||
|
"&engine_version=" .. core.urlencode(version.string)
|
||||||
core.open_url(url)
|
core.open_url(url)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -295,7 +318,8 @@ local function handle_submit(this, fields)
|
||||||
end
|
end
|
||||||
|
|
||||||
if handle_hypertext_event(this, fields.desc, info.long_description) or
|
if handle_hypertext_event(this, fields.desc, info.long_description) or
|
||||||
handle_hypertext_event(this, fields.info, info.info_hypertext) then
|
handle_hypertext_event(this, fields.info, info.info_hypertext) or
|
||||||
|
(package.reviews and handle_hypertext_event(this, fields.reviews, package.reviews)) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2023 rubenwardy
|
-- Copyright (C) 2023 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"
|
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2013 sapier
|
-- Copyright (C) 2013 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
local function get_last_folder(text,count)
|
local function get_last_folder(text,count)
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2023-24 rubenwardy
|
-- Copyright (C) 2023-24 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
-- Screenshot
|
-- Screenshot
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2022 rubenwardy
|
-- Copyright (C) 2022 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
local mods_dir = "/tmp/.minetest/mods"
|
local mods_dir = "/tmp/.minetest/mods"
|
||||||
local games_dir = "/tmp/.minetest/games"
|
local games_dir = "/tmp/.minetest/games"
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2023 rubenwardy
|
-- Copyright (C) 2023 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
update_detector = {}
|
update_detector = {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"#": "https://github.com/orgs/minetest/teams/engine/members",
|
"#": "https://github.com/orgs/luanti-org/teams/engine/members",
|
||||||
"core_developers": [
|
"core_developers": [
|
||||||
"Perttu Ahola (celeron55) <celeron55@gmail.com> [Project founder]",
|
"Perttu Ahola (celeron55) <celeron55@gmail.com> [Project founder]",
|
||||||
"sfan5 <sfan5@live.de>",
|
"sfan5 <sfan5@live.de>",
|
||||||
|
@ -15,7 +15,8 @@
|
||||||
"Gregor Parzefall (grorp)",
|
"Gregor Parzefall (grorp)",
|
||||||
"Lars Müller (luatic)",
|
"Lars Müller (luatic)",
|
||||||
"cx384",
|
"cx384",
|
||||||
"sfence"
|
"sfence",
|
||||||
|
"y5nw"
|
||||||
],
|
],
|
||||||
"previous_core_developers": [
|
"previous_core_developers": [
|
||||||
"BlockMen",
|
"BlockMen",
|
||||||
|
@ -38,7 +39,7 @@
|
||||||
"Hugues Ross <hugues.ross@gmail.com>",
|
"Hugues Ross <hugues.ross@gmail.com>",
|
||||||
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>"
|
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>"
|
||||||
],
|
],
|
||||||
"#": "Currently only https://github.com/orgs/minetest/teams/triagers/members",
|
"#": "Currently only https://github.com/orgs/luanti-org/teams/triagers/members",
|
||||||
"core_team": [
|
"core_team": [
|
||||||
"Zughy [Issue triager]",
|
"Zughy [Issue triager]",
|
||||||
"wsor [Issue triager]",
|
"wsor [Issue triager]",
|
||||||
|
@ -46,25 +47,20 @@
|
||||||
],
|
],
|
||||||
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
|
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"JosiahWI",
|
|
||||||
"1F616EMO",
|
|
||||||
"y5nw",
|
|
||||||
"Erich Schubert",
|
"Erich Schubert",
|
||||||
"numzero",
|
"wrrrzr",
|
||||||
"red-001 <red-001@outlook.ie>",
|
"siliconsniffer",
|
||||||
"David Heidelberg",
|
"JosiahWI",
|
||||||
"Wuzzy",
|
"veprogames",
|
||||||
"paradust7",
|
"Miguel P.L",
|
||||||
"HybridDog",
|
"AFCMS"
|
||||||
"Zemtzov7",
|
|
||||||
"kromka-chleba",
|
|
||||||
"AFCMS",
|
|
||||||
"chmodsayshello",
|
|
||||||
"OgelGames"
|
|
||||||
],
|
],
|
||||||
"previous_contributors": [
|
"previous_contributors": [
|
||||||
"Ælla Chiana Moskopp (erle) <erle@dieweltistgarnichtso.net> [Logo]",
|
"Ælla Chiana Moskopp (erle) <erle@dieweltistgarnichtso.net> [Logo]",
|
||||||
|
"numzero",
|
||||||
|
"red-001 <red-001@outlook.ie>",
|
||||||
"Giuseppe Bilotta",
|
"Giuseppe Bilotta",
|
||||||
|
"HybridDog",
|
||||||
"ClobberXD",
|
"ClobberXD",
|
||||||
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
|
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
|
||||||
"MirceaKitsune <mirceakitsune@gmail.com>",
|
"MirceaKitsune <mirceakitsune@gmail.com>",
|
||||||
|
@ -77,6 +73,7 @@
|
||||||
"stujones11",
|
"stujones11",
|
||||||
"Rogier <rogier777@gmail.com>",
|
"Rogier <rogier777@gmail.com>",
|
||||||
"Gregory Currie (gregorycu)",
|
"Gregory Currie (gregorycu)",
|
||||||
|
"paradust7",
|
||||||
"JacobF",
|
"JacobF",
|
||||||
"Jeija <jeija@mesecons.net>"
|
"Jeija <jeija@mesecons.net>"
|
||||||
]
|
]
|
||||||
|
|
|
@ -21,7 +21,7 @@ local function clients_list_formspec(dialogdata)
|
||||||
"size[6,9.5]",
|
"size[6,9.5]",
|
||||||
TOUCH_GUI and "padding[0.01,0.01]" or "",
|
TOUCH_GUI and "padding[0.01,0.01]" or "",
|
||||||
"hypertext[0,0;6,1.5;;<global margin=5 halign=center valign=middle>",
|
"hypertext[0,0;6,1.5;;<global margin=5 halign=center valign=middle>",
|
||||||
fgettext("This is the list of clients connected to\n$1",
|
fgettext("Players connected to\n$1",
|
||||||
"<b>" .. core.hypertext_escape(servername) .. "</b>") .. "]",
|
"<b>" .. core.hypertext_escape(servername) .. "</b>") .. "]",
|
||||||
"textlist[0.5,1.5;5,6.8;;" .. fmt_formspec_list(clients_list) .. "]",
|
"textlist[0.5,1.5;5,6.8;;" .. fmt_formspec_list(clients_list) .. "]",
|
||||||
"button[1.5,8.5;3,0.8;quit;OK]"
|
"button[1.5,8.5;3,0.8;quit;OK]"
|
||||||
|
@ -32,7 +32,7 @@ end
|
||||||
|
|
||||||
local function clients_list_buttonhandler(this, fields)
|
local function clients_list_buttonhandler(this, fields)
|
||||||
if fields.quit then
|
if fields.quit then
|
||||||
this:delete()
|
this:delete()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2013 sapier
|
-- Copyright (C) 2013 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -299,7 +286,7 @@ local function handle_buttons(this, fields)
|
||||||
worldfile:set("load_mod_" .. mod.name, mod.virtual_path)
|
worldfile:set("load_mod_" .. mod.name, mod.virtual_path)
|
||||||
was_set[mod.name] = true
|
was_set[mod.name] = true
|
||||||
elseif not was_set[mod.name] then
|
elseif not was_set[mod.name] then
|
||||||
worldfile:set("load_mod_" .. mod.name, "false")
|
worldfile:remove("load_mod_" .. mod.name)
|
||||||
end
|
end
|
||||||
elseif mod.enabled then
|
elseif mod.enabled then
|
||||||
gamedata.errormessage = fgettext_ne("Failed to enable mo" ..
|
gamedata.errormessage = fgettext_ne("Failed to enable mo" ..
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
local function table_to_flags(ftable)
|
local function table_to_flags(ftable)
|
||||||
-- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"
|
-- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
local function delete_world_formspec(dialogdata)
|
local function delete_world_formspec(dialogdata)
|
||||||
|
|
108
builtin/mainmenu/dlg_rebind_keys.lua
Normal file
108
builtin/mainmenu/dlg_rebind_keys.lua
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
-- Luanti
|
||||||
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
-- Modified based on dlg_reinstall_mtg.lua
|
||||||
|
-- Note that this is only needed for migrating from <5.11 to 5.12.
|
||||||
|
|
||||||
|
local doc_url = "https://docs.luanti.org/for-players/controls/"
|
||||||
|
local SETTING_NAME = "no_keycode_migration_warning"
|
||||||
|
|
||||||
|
local function get_formspec(dialogdata)
|
||||||
|
local markup = table.concat({
|
||||||
|
"<big>" .. hgettext("Keybindings changed") .. "</big>",
|
||||||
|
hgettext("The input handling system was reworked in Luanti 5.12.0."),
|
||||||
|
hgettext("As a result, your keybindings may have been changed."),
|
||||||
|
hgettext("Check out the key settings or refer to the documentation:"),
|
||||||
|
("<action name='doc_url'><style color='cyan' hovercolor='orangered'>%s</style></action>"):format(doc_url),
|
||||||
|
}, "\n")
|
||||||
|
|
||||||
|
return table.concat({
|
||||||
|
"formspec_version[6]",
|
||||||
|
"size[12,7]",
|
||||||
|
"hypertext[0.5,0.5;11,4.7;text;", core.formspec_escape(markup), "]",
|
||||||
|
"container[0.5,5.7]",
|
||||||
|
"button[0,0;4,0.8;dismiss;", fgettext("Close"), "]",
|
||||||
|
"button[4.5,0;6.5,0.8;reconfigure;", fgettext("Open settings"), "]",
|
||||||
|
"container_end[]",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function close_dialog(this)
|
||||||
|
cache_settings:set_bool(SETTING_NAME, true)
|
||||||
|
this:delete()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function buttonhandler(this, fields)
|
||||||
|
if fields.reconfigure then
|
||||||
|
close_dialog(this)
|
||||||
|
|
||||||
|
local maintab = ui.find_by_name("maintab")
|
||||||
|
|
||||||
|
local dlg = create_settings_dlg("controls_keyboard_and_mouse")
|
||||||
|
dlg:set_parent(maintab)
|
||||||
|
maintab:hide()
|
||||||
|
dlg:show()
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if fields.dismiss then
|
||||||
|
close_dialog(this)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if fields.text == "action:doc_url" then
|
||||||
|
core.open_url(doc_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function eventhandler(event)
|
||||||
|
if event == "DialogShow" then
|
||||||
|
mm_game_theme.set_engine()
|
||||||
|
return true
|
||||||
|
elseif event == "MenuQuit" then
|
||||||
|
-- Don't allow closing the dialog with ESC, but still allow exiting
|
||||||
|
-- Luanti
|
||||||
|
core.close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_rebind_keys_dlg()
|
||||||
|
local dlg = dialog_create("dlg_rebind_keys", get_formspec,
|
||||||
|
buttonhandler, eventhandler)
|
||||||
|
return dlg
|
||||||
|
end
|
||||||
|
|
||||||
|
function migrate_keybindings()
|
||||||
|
-- Show migration dialog if the user upgraded from an earlier version
|
||||||
|
-- and this has not yet been shown before, *or* if keys settings had to be changed
|
||||||
|
if core.is_first_run then
|
||||||
|
cache_settings:set_bool(SETTING_NAME, true)
|
||||||
|
end
|
||||||
|
local has_migration = not cache_settings:get_bool(SETTING_NAME)
|
||||||
|
|
||||||
|
-- normalize all existing key settings, this converts them from KEY_KEY_C to SYSTEM_SCANCODE_6
|
||||||
|
local settings = core.settings:to_table()
|
||||||
|
for name, value in pairs(settings) do
|
||||||
|
if name:match("^keymap_") then
|
||||||
|
local normalized = core.normalize_keycode(value)
|
||||||
|
if value ~= normalized then
|
||||||
|
has_migration = true
|
||||||
|
core.settings:set(name, normalized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not has_migration then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local maintab = ui.find_by_name("maintab")
|
||||||
|
|
||||||
|
local dlg = create_rebind_keys_dlg()
|
||||||
|
dlg:set_parent(maintab)
|
||||||
|
maintab:hide()
|
||||||
|
dlg:show()
|
||||||
|
ui.update()
|
||||||
|
end
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2022 rubenwardy
|
-- Copyright (C) 2022 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,12 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2023 Gregor Parzefall
|
-- Copyright (C) 2023 Gregor Parzefall
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
---- IMPORTANT ----
|
---- IMPORTANT ----
|
||||||
-- This whole file can be removed after a while.
|
-- This whole file can be removed after a while.
|
||||||
-- It was only directly useful for upgrades from 5.7.0 to 5.8.0, but
|
-- It was only directly useful for upgrades from 5.7.0 to 5.8.0, but
|
||||||
-- maybe some odd fellow directly upgrades from 5.6.1 to 5.9.0 in the future...
|
-- maybe some odd fellow directly upgrades from 5.6.1 to 5.9.0 in the future...
|
||||||
-- see <https://github.com/minetest/minetest/pull/13850> in case it's not obvious
|
-- see <https://github.com/luanti-org/luanti/pull/13850> in case it's not obvious
|
||||||
---- ----
|
---- ----
|
||||||
|
|
||||||
local SETTING_NAME = "no_mtg_notification"
|
local SETTING_NAME = "no_mtg_notification"
|
||||||
|
@ -67,10 +54,10 @@ end
|
||||||
|
|
||||||
local function get_formspec(dialogdata)
|
local function get_formspec(dialogdata)
|
||||||
local markup = table.concat({
|
local markup = table.concat({
|
||||||
"<big>", fgettext("Minetest Game is no longer installed by default"), "</big>\n",
|
"<big>", hgettext("Minetest Game is no longer installed by default"), "</big>\n",
|
||||||
fgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
|
hgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
|
||||||
"Since version 5.8.0, Luanti ships without a default game."), "\n",
|
"Since version 5.8.0, Luanti ships without a default game."), "\n",
|
||||||
fgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
|
hgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
|
||||||
})
|
})
|
||||||
|
|
||||||
return table.concat({
|
return table.concat({
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
105
builtin/mainmenu/dlg_server_list_mods.lua
Normal file
105
builtin/mainmenu/dlg_server_list_mods.lua
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
-- Luanti
|
||||||
|
-- Copyright (C) 2024 cx384
|
||||||
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
local function get_formspec(dialogdata)
|
||||||
|
local TOUCH_GUI = core.settings:get_bool("touch_gui")
|
||||||
|
local server = dialogdata.server
|
||||||
|
local group_by_prefix = dialogdata.group_by_prefix
|
||||||
|
local expand_all = dialogdata.expand_all
|
||||||
|
|
||||||
|
-- A wrongly behaving server may send ill formed mod names
|
||||||
|
table.sort(server.mods)
|
||||||
|
|
||||||
|
local cells = {}
|
||||||
|
if group_by_prefix then
|
||||||
|
local function get_prefix(mod)
|
||||||
|
return mod:match("[^_]*")
|
||||||
|
end
|
||||||
|
local count = {}
|
||||||
|
for _, mod in ipairs(server.mods) do
|
||||||
|
local prefix = get_prefix(mod)
|
||||||
|
count[prefix] = (count[prefix] or 0) + 1
|
||||||
|
end
|
||||||
|
local last_prefix
|
||||||
|
local function add_row(depth, mod)
|
||||||
|
table.insert(cells, ("%d"):format(depth))
|
||||||
|
table.insert(cells, mod)
|
||||||
|
end
|
||||||
|
for i, mod in ipairs(server.mods) do
|
||||||
|
local prefix = get_prefix(mod)
|
||||||
|
if last_prefix == prefix then
|
||||||
|
add_row(1, mod)
|
||||||
|
elseif count[prefix] > 1 then
|
||||||
|
add_row(0, prefix)
|
||||||
|
add_row(1, mod)
|
||||||
|
else
|
||||||
|
add_row(0, mod)
|
||||||
|
end
|
||||||
|
last_prefix = prefix
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cells = table.copy(server.mods)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i, cell in ipairs(cells) do
|
||||||
|
cells[i] = core.formspec_escape(cell)
|
||||||
|
end
|
||||||
|
cells = table.concat(cells, ",")
|
||||||
|
|
||||||
|
local heading
|
||||||
|
if server.gameid then
|
||||||
|
heading = fgettext("The $1 server uses a game called $2 and the following mods:",
|
||||||
|
"<b>" .. core.hypertext_escape(server.name) .. "</b>",
|
||||||
|
"<style font=mono>" .. core.hypertext_escape(server.gameid) .. "</style>")
|
||||||
|
else
|
||||||
|
heading = fgettext("The $1 server uses the following mods:",
|
||||||
|
"<b>" .. core.hypertext_escape(server.name) .. "</b>")
|
||||||
|
end
|
||||||
|
|
||||||
|
local formspec = {
|
||||||
|
"formspec_version[8]",
|
||||||
|
"size[8,9.5]",
|
||||||
|
TOUCH_GUI and "padding[0.01,0.01]" or "",
|
||||||
|
"hypertext[0,0;8,1.5;;<global margin=5 halign=center valign=middle>", heading, "]",
|
||||||
|
"tablecolumns[", group_by_prefix and
|
||||||
|
(expand_all and "indent;text" or "tree;text") or "text", "]",
|
||||||
|
"table[0.5,1.5;7,6.8;mods;", cells, "]",
|
||||||
|
"checkbox[0.5,8.7;group_by_prefix;", fgettext("Group by prefix"), ";",
|
||||||
|
group_by_prefix and "true" or "false", "]",
|
||||||
|
group_by_prefix and ("checkbox[0.5,9.15;expand_all;" .. fgettext("Expand all") .. ";" ..
|
||||||
|
(expand_all and "true" or "false") .. "]") or "",
|
||||||
|
"button[5.5,8.5;2,0.8;quit;OK]"
|
||||||
|
}
|
||||||
|
return table.concat(formspec, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function buttonhandler(this, fields)
|
||||||
|
if fields.quit then
|
||||||
|
this:delete()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if fields.group_by_prefix then
|
||||||
|
this.data.group_by_prefix = core.is_yes(fields.group_by_prefix)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if fields.expand_all then
|
||||||
|
this.data.expand_all = core.is_yes(fields.expand_all)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function create_server_list_mods_dialog(server)
|
||||||
|
local retval = dialog_create("dlg_server_list_mods",
|
||||||
|
get_formspec,
|
||||||
|
buttonhandler,
|
||||||
|
nil)
|
||||||
|
retval.data.group_by_prefix = false
|
||||||
|
retval.data.expand_all = false
|
||||||
|
retval.data.server = server
|
||||||
|
return retval
|
||||||
|
end
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2013 sapier
|
-- Copyright (C) 2013 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
|
|
||||||
mm_game_theme = {}
|
mm_game_theme = {}
|
||||||
|
|
|
@ -1,27 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2014 sapier
|
-- Copyright (C) 2014 sapier
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
mt_color_grey = "#AAAAAA"
|
|
||||||
mt_color_blue = "#6389FF"
|
|
||||||
mt_color_lightblue = "#99CCFF"
|
|
||||||
mt_color_green = "#72FF63"
|
|
||||||
mt_color_dark_green = "#25C191"
|
|
||||||
mt_color_orange = "#FF8800"
|
|
||||||
mt_color_red = "#FF3300"
|
|
||||||
|
|
||||||
MAIN_TAB_W = 15.5
|
MAIN_TAB_W = 15.5
|
||||||
MAIN_TAB_H = 7.1
|
MAIN_TAB_H = 7.1
|
||||||
|
@ -35,6 +14,7 @@ local basepath = core.get_builtin_path()
|
||||||
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
|
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
|
||||||
DIR_DELIM .. "pack" .. DIR_DELIM
|
DIR_DELIM .. "pack" .. DIR_DELIM
|
||||||
|
|
||||||
|
dofile(basepath .. "common" .. DIR_DELIM .. "menu.lua")
|
||||||
dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua")
|
dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua")
|
||||||
dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
|
dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
|
||||||
dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua")
|
dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua")
|
||||||
|
@ -47,7 +27,7 @@ dofile(menupath .. DIR_DELIM .. "game_theme.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
|
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
|
||||||
|
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
|
dofile(basepath .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
|
||||||
|
@ -55,7 +35,9 @@ dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
|
||||||
|
dofile(menupath .. DIR_DELIM .. "dlg_rebind_keys.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua")
|
||||||
|
dofile(menupath .. DIR_DELIM .. "dlg_server_list_mods.lua")
|
||||||
|
|
||||||
local tabs = {
|
local tabs = {
|
||||||
content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"),
|
content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"),
|
||||||
|
@ -131,6 +113,7 @@ local function init_globals()
|
||||||
ui.update()
|
ui.update()
|
||||||
|
|
||||||
check_reinstall_mtg()
|
check_reinstall_mtg()
|
||||||
|
migrate_keybindings()
|
||||||
check_new_version()
|
check_new_version()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
--Luanti
|
-- Luanti
|
||||||
--Copyright (C) 2020 rubenwardy
|
-- Copyright (C) 2020 rubenwardy
|
||||||
--
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
--This program is free software; you can redistribute it and/or modify
|
|
||||||
--it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
--the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
--(at your option) any later version.
|
|
||||||
--
|
|
||||||
--This program is distributed in the hope that it will be useful,
|
|
||||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
--GNU Lesser General Public License for more details.
|
|
||||||
--
|
|
||||||
--You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
serverlistmgr = {
|
serverlistmgr = {
|
||||||
-- continent code we detected for ourselves
|
-- continent code we detected for ourselves
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue