merge upstream
@ -1,6 +1,7 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 8
|
||||
IndentWidth: 4
|
||||
UseTab: Always
|
||||
TabWidth: 4
|
||||
BreakBeforeBraces: Custom
|
||||
Standard: Cpp11
|
||||
BraceWrapping:
|
||||
@ -16,7 +17,7 @@ BraceWrapping:
|
||||
FixNamespaceComments: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
IndentCaseLabels: false
|
||||
AccessModifierOffset: -8
|
||||
AccessModifierOffset: -4
|
||||
ColumnLimit: 90
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
SortIncludes: false
|
||||
@ -26,6 +27,7 @@ IncludeCategories:
|
||||
- Regex: '^<.*'
|
||||
Priority: 1
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
ContinuationIndentWidth: 16
|
||||
ConstructorInitializerIndentWidth: 16
|
||||
ContinuationIndentWidth: 8
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
BreakConstructorInitializers: AfterColon
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
4
.dockerignore
Normal file
@ -0,0 +1,4 @@
|
||||
./cmake-build-*
|
||||
./build/*
|
||||
./cache/*
|
||||
Dockerfile
|
9
.editorconfig
Executable file
@ -0,0 +1,9 @@
|
||||
[*]
|
||||
end_of_line = lf
|
||||
|
||||
[*.{cpp,h,lua,txt,glsl,md,c,cmake,java,gradle}]
|
||||
charset = utf8
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
112
.github/CONTRIBUTING.md
vendored
@ -10,13 +10,31 @@ Contributions are welcome! Here's how you can help:
|
||||
|
||||
## Code
|
||||
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository and [clone](https://help.github.com/articles/cloning-a-repository/) your fork.
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository and
|
||||
[clone](https://help.github.com/articles/cloning-a-repository/) your fork.
|
||||
|
||||
2. Before you start coding, consider opening an [issue at Github](https://github.com/minetest/minetest/issues) to discuss the suitability and implementation of your intended contribution with the core developers. If you are planning to start some very significant coding, you would benefit from first discussing on our IRC development channel [#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client is required to speak on this channel.
|
||||
2. Before you start coding, consider opening an
|
||||
[issue at Github](https://github.com/minetest/minetest/issues) to discuss the
|
||||
suitability and implementation of your intended contribution with the core
|
||||
developers.
|
||||
|
||||
Any Pull Request that isn't a bug fix and isn't covered by
|
||||
[the roadmap](../doc/direction.md) will be closed within a week unless it
|
||||
receives a concept approval from a Core Developer. For this reason, it is
|
||||
recommended that you open an issue for any such pull requests before doing
|
||||
the work, to avoid disappointment.
|
||||
|
||||
You may also benefit from discussing on our IRC development channel
|
||||
[#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client
|
||||
is required to speak on this channel.
|
||||
|
||||
3. Start coding!
|
||||
- Refer to the [Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt), [Developer Wiki](http://dev.minetest.net/Main_Page) and other [documentation](https://github.com/minetest/minetest/tree/master/doc).
|
||||
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and [Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
|
||||
- Refer to the
|
||||
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt),
|
||||
[Developer Wiki](http://dev.minetest.net/Main_Page) and other
|
||||
[documentation](https://github.com/minetest/minetest/tree/master/doc).
|
||||
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and
|
||||
[Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
|
||||
- Check your code works as expected and document any changes to the Lua API.
|
||||
|
||||
4. Commit & [push](https://help.github.com/articles/pushing-to-a-remote/) your changes to a new branch (not `master`, one change per branch)
|
||||
@ -33,61 +51,115 @@ Contributions are welcome! Here's how you can help:
|
||||
|
||||
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).
|
||||
- Add a description explaining what you've done (or if it's a work-in-progress - what you need to do).
|
||||
- Add a description explaining what you've done (or if it's a
|
||||
work-in-progress - what you need to do).
|
||||
- Make sure to fill out the pull request template.
|
||||
|
||||
### A pull-request is considered merge-able when:
|
||||
|
||||
1. It follows the roadmap in some way and fits the whole picture of the project: [roadmap introduction](http://c55.me/blog/?p=1491), [roadmap continued](https://forum.minetest.net/viewtopic.php?t=9177)
|
||||
1. It follows [the roadmap](../doc/direction.md) in some way and fits the whole
|
||||
picture of the project.
|
||||
2. It works.
|
||||
3. It follows the code style for [C/C++](http://dev.minetest.net/Code_style_guidelines) or [Lua](http://dev.minetest.net/Lua_code_style_guidelines).
|
||||
4. The code's interfaces are well designed, regardless of other aspects that might need more work in the future.
|
||||
3. It follows the code style for
|
||||
[C/C++](http://dev.minetest.net/Code_style_guidelines) or
|
||||
[Lua](http://dev.minetest.net/Lua_code_style_guidelines).
|
||||
4. The code's interfaces are well designed, regardless of other aspects that
|
||||
might need more work in the future.
|
||||
5. It uses protocols and formats which include the required compatibility.
|
||||
|
||||
### Important note about automated GitHub checks
|
||||
|
||||
When you submit a pull request, GitHub automatically runs checks on the Minetest
|
||||
Engine combined with your changes. One of these checks is called 'cpp lint /
|
||||
clang format', which checks code formatting. Because formatting for readability
|
||||
requires human judgement this check often fails and often makes unsuitable
|
||||
formatting requests which make code readability worse.
|
||||
|
||||
If this check fails, look at the details to check for any clear mistakes and
|
||||
correct those. However, you should not apply everything ClangFormat requests.
|
||||
Ignore requests that make code readability worse and any other clearly
|
||||
unsuitable requests. Discuss in the pull request with a core developer about how
|
||||
to progress.
|
||||
|
||||
## Issues
|
||||
|
||||
If you experience an issue, we would like to know the details - especially when a stable release is on the way.
|
||||
If you experience an issue, we would like to know the details - especially when
|
||||
a stable release is on the way.
|
||||
|
||||
1. Do a quick search on GitHub to check if the issue has already been reported.
|
||||
2. Is it an issue with the Minetest *engine*? If not, report it [elsewhere](http://www.minetest.net/development/#reporting-issues).
|
||||
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe the issue you are having - you could include:
|
||||
2. Is it an issue with the Minetest *engine*? If not, report it
|
||||
[elsewhere](http://www.minetest.net/development/#reporting-issues).
|
||||
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe
|
||||
the issue you are having - you could include:
|
||||
- Error logs (check the bottom of the `debug.txt` file).
|
||||
- Screenshots.
|
||||
- Ways you have tried to solve the issue, and whether they worked or not.
|
||||
- Your Minetest version and the content (games, mods or texture packs) you have installed.
|
||||
- Your platform (e.g. Windows 10 or Ubuntu 15.04 x64).
|
||||
|
||||
After reporting you should aim to answer questions or clarifications as this helps pinpoint the cause of the issue (if you don't do this your issue may be closed after 1 month).
|
||||
After reporting you should aim to answer questions or clarifications as this
|
||||
helps pinpoint the cause of the issue (if you don't do this your issue may be
|
||||
closed after 1 month).
|
||||
|
||||
## Feature requests
|
||||
|
||||
Feature requests are welcome but take a moment to see if your idea follows the roadmap in some way and fits the whole picture of the project: [roadmap introduction](http://c55.me/blog/?p=1491), [roadmap continued](https://forum.minetest.net/viewtopic.php?t=9177). You should provide a clear explanation with as much detail as possible.
|
||||
Feature requests are welcome but take a moment to see if your idea follows
|
||||
[the roadmap](../doc/direction.md) in some way and fits the whole picture of
|
||||
the project. You should provide a clear explanation with as much detail as
|
||||
possible.
|
||||
|
||||
## Translations
|
||||
|
||||
Translations of Minetest are performed using Weblate. You can access the project page with a list of current languages [here](https://hosted.weblate.org/projects/minetest/minetest/).
|
||||
The core translations of Minetest are performed using Weblate. You can access
|
||||
the project page with a list of current languages
|
||||
[here](https://hosted.weblate.org/projects/minetest/minetest/).
|
||||
|
||||
Builtin (the component which contains things like server messages, chat command
|
||||
descriptions, privilege descriptions) is translated separately; it needs to be
|
||||
translated by editing a `.tr` text file. See
|
||||
[Translation](https://dev.minetest.net/Translation) for more information.
|
||||
|
||||
## Donations
|
||||
|
||||
If you'd like to monetarily support Minetest development, you can find donation methods on [our website](http://www.minetest.net/development/#donate).
|
||||
If you'd like to monetarily support Minetest development, you can find donation
|
||||
methods on [our website](http://www.minetest.net/development/#donate).
|
||||
|
||||
# Maintaining
|
||||
|
||||
*This is a concise version of the [Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
|
||||
* This is a concise version of the
|
||||
[Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
|
||||
|
||||
These notes are for those who have push access Minetest (core developers / maintainers).
|
||||
|
||||
- See the [project organisation](http://dev.minetest.net/Organisation) for the people involved.
|
||||
|
||||
## Concept approvals and roadmaps
|
||||
|
||||
If a Pull Request is not a bug fix:
|
||||
|
||||
* If it matches a goal in [the roadmap](../doc/direction.md), then the PR should
|
||||
be labelled as "Roadmap" and the goal stated by number in the description.
|
||||
* If it doesn't match a goal, then it needs to receive a concept approval within
|
||||
a week of being opened to remain open. This 1 week deadline does not apply to
|
||||
PRs opened before the roadmap was adopted; instead, they may remain open or be
|
||||
closed as needed. Use the "Concept Approved" label. Issues can be marked as
|
||||
"Concept Approved" to give preapproval to future PRs.
|
||||
|
||||
## Reviewing pull requests
|
||||
|
||||
Pull requests should be reviewed and, if appropriate, checked if they achieve their intended purpose. You can show that you are in the process of, or will review the pull request by commenting *"Looks good"* or something similar.
|
||||
Pull requests should be reviewed and, if appropriate, checked if they achieve
|
||||
their intended purpose. You can show that you are in the process of, or will
|
||||
review the pull request by commenting *"Looks good"* or something similar.
|
||||
|
||||
**If the pull-request is not [merge-able](#a-pull-request-is-considered-merge-able-when):**
|
||||
|
||||
Submit a comment explaining to the author what they need to change to make the pull-request merge-able.
|
||||
Submit a comment explaining to the author what they need to change to make the
|
||||
pull-request merge-able.
|
||||
|
||||
- If the author comments or makes changes to the pull-request, it can be reviewed again.
|
||||
- If no response is made from the author within 1 month (when improvements are suggested or a question is asked), it can be closed.
|
||||
- If the author comments or makes changes to the pull-request, it can be
|
||||
reviewed again.
|
||||
- If no response is made from the author within 1 month (when improvements are
|
||||
suggested or a question is asked), it can be closed.
|
||||
|
||||
**If the pull-request is [merge-able](#a-pull-request-is-considered-merge-able-when):**
|
||||
|
||||
|
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -8,8 +8,8 @@ assignees: ''
|
||||
|
||||
##### Minetest version
|
||||
<!--
|
||||
Paste Minetest version between quotes below
|
||||
If you are on a devel version, please add git commit hash
|
||||
Paste Minetest version between quotes below.
|
||||
If you are on a devel version, please add git commit hash.
|
||||
You can use `minetest --version` to find it.
|
||||
-->
|
||||
```
|
||||
@ -29,4 +29,4 @@ OpenGL version:
|
||||
<!-- Describe your problem here -->
|
||||
|
||||
##### Steps to reproduce
|
||||
<!-- For bug reports or build issues, explain how the problem happened -->
|
||||
<!-- Explain how the problem has happened, providing a minimal test (i.e. a code snippet reduced to the bone) where possible -->
|
||||
|
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -3,6 +3,7 @@ Add compact, short information about your PR for easier understanding:
|
||||
- Goal of the PR
|
||||
- How does the PR work?
|
||||
- Does it resolve any reported issue?
|
||||
- Does this relate to a goal in [the roadmap](../doc/direction.md)?
|
||||
- If not a bug fix, why is this PR needed? What usecases does it solve?
|
||||
|
||||
## To do
|
||||
|
42
.github/workflows/android.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
name: android
|
||||
|
||||
# build on c/cpp changes or workflow changes
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'lib/**.[ch]'
|
||||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- 'android/**'
|
||||
- '.github/workflows/android.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'lib/**.[ch]'
|
||||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- 'android/**'
|
||||
- '.github/workflows/android.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gettext openjdk-11-jdk-headless
|
||||
- name: Build with Gradle
|
||||
run: cd android; ./gradlew assemblerelease
|
||||
- name: Save armeabi artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Minetest-armeabi-v7a.apk
|
||||
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
|
||||
- name: Save arm64 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Minetest-arm64-v8a.apk
|
||||
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
166
.github/workflows/build.yml
vendored
@ -13,6 +13,8 @@ on:
|
||||
- 'util/buildbot/**'
|
||||
- 'util/ci/**'
|
||||
- '.github/workflows/**.yml'
|
||||
- 'Dockerfile'
|
||||
- '.dockerignore'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'lib/**.[ch]'
|
||||
@ -24,62 +26,61 @@ on:
|
||||
- 'util/buildbot/**'
|
||||
- 'util/ci/**'
|
||||
- '.github/workflows/**.yml'
|
||||
- 'Dockerfile'
|
||||
- '.dockerignore'
|
||||
|
||||
jobs:
|
||||
# This is our minor gcc compiler
|
||||
gcc_6:
|
||||
# Older gcc version (should be close to our minimum supported version)
|
||||
gcc_5:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install g++-6 gcc-6 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
install_linux_deps g++-5
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: gcc-6
|
||||
CXX: g++-6
|
||||
CC: gcc-5
|
||||
CXX: g++-5
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# This is the current gcc compiler (available in bionic)
|
||||
gcc_8:
|
||||
runs-on: ubuntu-18.04
|
||||
# Current gcc version
|
||||
gcc_12:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install g++-8 gcc-8 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
install_linux_deps g++-12 libluajit-5.1-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: gcc-8
|
||||
CXX: g++-8
|
||||
CC: gcc-12
|
||||
CXX: g++-12
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# This is our minor clang compiler
|
||||
# Older clang version (should be close to our minimum supported version)
|
||||
clang_3_9:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install clang-3.9 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
install_linux_deps clang-3.9 valgrind
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@ -88,31 +89,7 @@ jobs:
|
||||
CC: clang-3.9
|
||||
CXX: clang++-3.9
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
# This is the current clang version
|
||||
clang_9:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install clang-9 valgrind -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
env:
|
||||
WITH_LUAJIT: 1
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-9
|
||||
CXX: clang++-9
|
||||
|
||||
- name: Test
|
||||
- name: Unittest
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
@ -120,17 +97,41 @@ jobs:
|
||||
run: |
|
||||
valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ./bin/minetest --run-unittests
|
||||
|
||||
# Current clang version
|
||||
clang_14:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-14 gdb
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-14
|
||||
CXX: clang++-14
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
- name: Integration test + devtest
|
||||
run: |
|
||||
./util/test_multiplayer.sh
|
||||
|
||||
# Build with prometheus-cpp (server-only)
|
||||
clang_9_prometheus:
|
||||
name: "clang_9 (PROMETHEUS=1)"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install clang-9 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
install_linux_deps clang-9
|
||||
|
||||
- name: Build prometheus-cpp
|
||||
run: |
|
||||
@ -148,48 +149,25 @@ jobs:
|
||||
run: |
|
||||
./bin/minetestserver --run-unittests
|
||||
|
||||
# Build without freetype (client-only)
|
||||
clang_9_no_freetype:
|
||||
name: "clang_9 (FREETYPE=0)"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install clang-9 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-9
|
||||
CXX: clang++-9
|
||||
CMAKE_FLAGS: "-DENABLE_FREETYPE=0 -DBUILD_SERVER=0"
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./bin/minetest --run-unittests
|
||||
|
||||
docker:
|
||||
name: "Docker image"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build docker image
|
||||
run: |
|
||||
docker build .
|
||||
docker build . -t minetest:latest
|
||||
docker run --rm minetest:latest /usr/local/bin/minetestserver --version
|
||||
|
||||
win32:
|
||||
name: "MinGW cross-compiler (32-bit)"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get install gettext -qyy
|
||||
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
sudo apt-get update && sudo apt-get install -y gettext
|
||||
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
||||
sudo tar -xaf mingw.tar.xz -C /usr
|
||||
|
||||
- name: Build
|
||||
@ -201,13 +179,13 @@ jobs:
|
||||
|
||||
win64:
|
||||
name: "MinGW cross-compiler (64-bit)"
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get install gettext -qyy
|
||||
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
sudo apt-get update && sudo apt-get install -y gettext
|
||||
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
||||
sudo tar -xaf mingw.tar.xz -C /usr
|
||||
|
||||
- name: Build
|
||||
@ -221,9 +199,9 @@ jobs:
|
||||
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
||||
runs-on: windows-2019
|
||||
env:
|
||||
VCPKG_VERSION: c7ab9d3110813979a873b2dbac630a9ab79850dc
|
||||
# 2020.04
|
||||
vcpkg_packages: irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit
|
||||
VCPKG_VERSION: 5cf60186a241e84e8232641ee973395d4fde90e1
|
||||
# 2022.02
|
||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -244,11 +222,15 @@ jobs:
|
||||
# Enable it, when working on the installer.
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Checkout IrrlichtMt
|
||||
run: |
|
||||
$ref = @(Get-Content misc\irrlichtmt_tag.txt)
|
||||
git clone https://github.com/minetest/irrlicht lib\irrlichtmt --depth 1 -b $ref[0]
|
||||
|
||||
- name: Restore from cache and run vcpkg
|
||||
uses: lukka/run-vcpkg@v2
|
||||
uses: lukka/run-vcpkg@v7
|
||||
with:
|
||||
vcpkgArguments: ${{env.vcpkg_packages}}
|
||||
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
||||
@ -256,7 +238,7 @@ jobs:
|
||||
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
||||
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
||||
|
||||
- name: CMake
|
||||
- name: Minetest CMake
|
||||
run: |
|
||||
cmake ${{matrix.config.generator}} `
|
||||
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
||||
@ -264,7 +246,7 @@ jobs:
|
||||
-DENABLE_POSTGRESQL=OFF `
|
||||
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
||||
|
||||
- name: Build
|
||||
- name: Build Minetest
|
||||
run: cmake --build . --config Release
|
||||
|
||||
- name: CPack
|
||||
@ -283,7 +265,7 @@ jobs:
|
||||
- name: Package Clean
|
||||
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
||||
path: .\Package\
|
||||
|
35
.github/workflows/cpp_lint.yml
vendored
@ -24,30 +24,31 @@ on:
|
||||
- '.github/workflows/**.yml'
|
||||
|
||||
jobs:
|
||||
clang_format:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install clang-format
|
||||
run: |
|
||||
sudo apt-get install clang-format-9 -qyy
|
||||
|
||||
- name: Run clang-format
|
||||
run: |
|
||||
source ./util/ci/lint.sh
|
||||
perform_lint
|
||||
env:
|
||||
CLANG_FORMAT: clang-format-9
|
||||
# clang_format:
|
||||
# runs-on: ubuntu-20.04
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - name: Install clang-format
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y clang-format-9
|
||||
#
|
||||
# - name: Run clang-format
|
||||
# run: |
|
||||
# source ./util/ci/clang-format.sh
|
||||
# check_format
|
||||
# env:
|
||||
# CLANG_FORMAT: clang-format-9
|
||||
|
||||
clang_tidy:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get install clang-tidy-9 -qyy
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps
|
||||
install_linux_deps clang-tidy-9
|
||||
|
||||
- name: Run clang-tidy
|
||||
run: |
|
||||
|
72
.github/workflows/lua.yml
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
name: lua_lint
|
||||
|
||||
# Lint on lua changes on builtin or if workflow changed
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'builtin/**.lua'
|
||||
- 'games/devtest/**.lua'
|
||||
- '.github/workflows/**.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'builtin/**.lua'
|
||||
- 'games/devtest/**.lua'
|
||||
- '.github/workflows/**.yml'
|
||||
|
||||
jobs:
|
||||
# Note that the integration tests are also run build.yml, but only when C++ code is changed.
|
||||
integration_tests:
|
||||
name: "Compile and run multiplayer tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-10 gdb libluajit-5.1-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./util/ci/build.sh
|
||||
env:
|
||||
CC: clang-10
|
||||
CXX: clang++-10
|
||||
CMAKE_FLAGS: "-DENABLE_GETTEXT=0 -DBUILD_SERVER=0"
|
||||
|
||||
- name: Integration test + devtest
|
||||
run: |
|
||||
./util/test_multiplayer.sh
|
||||
|
||||
luacheck:
|
||||
name: "Builtin Luacheck and Unit Tests"
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: leafo/gh-actions-lua@v9
|
||||
with:
|
||||
luaVersion: "5.1.5"
|
||||
- uses: leafo/gh-actions-luarocks@v4
|
||||
|
||||
- name: Install LuaJIT
|
||||
run: |
|
||||
cd $HOME
|
||||
git clone https://github.com/LuaJIT/LuaJIT/
|
||||
cd LuaJIT
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Install luarocks tools
|
||||
run: |
|
||||
luarocks install --local luacheck
|
||||
luarocks install --local busted
|
||||
|
||||
- name: Run checks (builtin)
|
||||
run: |
|
||||
$HOME/.luarocks/bin/luacheck builtin
|
||||
$HOME/.luarocks/bin/busted builtin
|
||||
$HOME/.luarocks/bin/busted builtin --lua=$HOME/LuaJIT/src/luajit
|
||||
|
||||
- name: Run checks (devtest)
|
||||
run: |
|
||||
$HOME/.luarocks/bin/luacheck --config=games/devtest/.luacheckrc games/devtest
|
32
.github/workflows/lua_lint.yml
vendored
@ -1,32 +0,0 @@
|
||||
name: lua_lint
|
||||
|
||||
# Lint on lua changes on builtin or if workflow changed
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'builtin/**.lua'
|
||||
- '.github/workflows/**.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'builtin/**.lua'
|
||||
- '.github/workflows/**.yml'
|
||||
|
||||
jobs:
|
||||
luacheck:
|
||||
name: "Builtin Luacheck and Unit Tests"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install luarocks
|
||||
run: |
|
||||
sudo apt-get install luarocks -qyy
|
||||
|
||||
- name: Install luarocks tools
|
||||
run: |
|
||||
luarocks install --local luacheck
|
||||
luarocks install --local busted
|
||||
|
||||
- name: Run checks
|
||||
run: |
|
||||
$HOME/.luarocks/bin/luacheck builtin
|
||||
$HOME/.luarocks/bin/busted builtin
|
67
.github/workflows/macos.yml
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
name: macos
|
||||
|
||||
# build on c/cpp changes or workflow changes
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'lib/**.[ch]'
|
||||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- '**/CMakeLists.txt'
|
||||
- 'cmake/Modules/**'
|
||||
- '.github/workflows/macos.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'lib/**.[ch]'
|
||||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- '**/CMakeLists.txt'
|
||||
- 'cmake/Modules/**'
|
||||
- '.github/workflows/macos.yml'
|
||||
|
||||
env:
|
||||
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
|
||||
MINETEST_GAME_BRANCH: master
|
||||
MINETEST_GAME_NAME: minetest_game
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_macos_deps
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
git clone -b $MINETEST_GAME_BRANCH $MINETEST_GAME_REPO games/$MINETEST_GAME_NAME
|
||||
git clone https://github.com/minetest/irrlicht lib/irrlichtmt --depth 1 -b $(cat misc/irrlichtmt_tag.txt)
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE
|
||||
make -j2
|
||||
make install
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
./build/macos/minetest.app/Contents/MacOS/minetest --run-unittests
|
||||
|
||||
# Zipping the built .app preserves permissions on the contained files,
|
||||
# which the GitHub artifact pipeline would otherwise strip away.
|
||||
- name: CPack
|
||||
run: |
|
||||
cd build
|
||||
cpack -G ZIP -B macos
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: minetest-macos
|
||||
path: ./build/macos/*.zip
|
15
.gitignore
vendored
@ -76,6 +76,7 @@ doc/mkdocs/docs/*.md
|
||||
doc/mkdocs/mkdocs.yml
|
||||
|
||||
## Build files
|
||||
build/
|
||||
CMakeFiles
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
@ -86,8 +87,8 @@ src/test_config.h
|
||||
src/cmake_config.h
|
||||
src/cmake_config_githash.h
|
||||
src/unittest/test_world/world.mt
|
||||
src/lua/build/
|
||||
locale/
|
||||
games/devtest/mods/testnodes/textures/testnodes_generated_*.png
|
||||
/locale/
|
||||
.directory
|
||||
*.cbp
|
||||
*.layout
|
||||
@ -106,3 +107,13 @@ CMakeDoxy*
|
||||
compile_commands.json
|
||||
*.apk
|
||||
*.zip
|
||||
# Visual Studio
|
||||
*.vcxproj*
|
||||
*.sln
|
||||
.vs/
|
||||
|
||||
# Optional user provided library folder
|
||||
lib/irrlichtmt
|
||||
|
||||
# Generated mod storage database
|
||||
client/mod_storage.sqlite
|
||||
|
291
.gitlab-ci.yml
@ -14,271 +14,68 @@ variables:
|
||||
|
||||
.build_template:
|
||||
stage: build
|
||||
before_script:
|
||||
- apt-get update
|
||||
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential gettext git cmake libpng-dev libjpeg-dev libxi-dev libgl1-mesa-dev libsqlite3-dev libleveldb-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev
|
||||
script:
|
||||
- mkdir cmakebuild
|
||||
- mkdir -p artifact/minetest/usr/
|
||||
- cd cmakebuild
|
||||
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
|
||||
- make -j2
|
||||
- git clone https://github.com/minetest/irrlicht lib/irrlichtmt --depth 1 -b $(cat misc/irrlichtmt_tag.txt)
|
||||
- mkdir build && cd build
|
||||
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE ..
|
||||
- make -j $(($(nproc) + 1))
|
||||
- make install
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 2h
|
||||
expire_in: 1h
|
||||
paths:
|
||||
- artifact/*
|
||||
|
||||
.debpkg_template:
|
||||
stage: package
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get install -y git
|
||||
- mkdir -p build/deb/minetest/DEBIAN/
|
||||
- cp misc/debpkg-control build/deb/minetest/DEBIAN/control
|
||||
- cp -Rp artifact/minetest/usr build/deb/minetest/
|
||||
script:
|
||||
- git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest
|
||||
- rm -Rf build/deb/minetest/usr/share/minetest/games/minetest/.git
|
||||
- sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control
|
||||
- sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control
|
||||
- cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 90 day
|
||||
paths:
|
||||
- ./*.deb
|
||||
|
||||
.debpkg_install:
|
||||
stage: deploy
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get install -y libc6 libcurl3-gnutls libfreetype6 libirrlicht1.8 $LEVELDB_PKG liblua5.1-0 libluajit-5.1-2 libopenal1 libstdc++6 libvorbisfile3 libx11-6 zlib1g
|
||||
script:
|
||||
- dpkg -i ./*.deb
|
||||
|
||||
##
|
||||
## Debian
|
||||
## Ubuntu (prerequisite for AppImage build)
|
||||
##
|
||||
|
||||
# Jessie
|
||||
|
||||
build:debian-8:
|
||||
build:ubuntu-20.04:
|
||||
extends: .build_template
|
||||
image: debian:8
|
||||
before_script:
|
||||
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list
|
||||
- apt-key adv --keyserver keyserver.ubuntu.com --recv BA9EF27F
|
||||
- apt-get update -y
|
||||
- apt-get -y install build-essential gcc-6 g++-6 libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
|
||||
variables:
|
||||
CC: gcc-6
|
||||
CXX: g++-6
|
||||
|
||||
package:debian-8:
|
||||
extends: .debpkg_template
|
||||
image: debian:8
|
||||
dependencies:
|
||||
- build:debian-8
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1
|
||||
|
||||
deploy:debian-8:
|
||||
extends: .debpkg_install
|
||||
image: debian:8
|
||||
dependencies:
|
||||
- package:debian-8
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1
|
||||
|
||||
# Stretch
|
||||
|
||||
build:debian-9:
|
||||
extends: .build_template
|
||||
image: debian:9
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
|
||||
|
||||
package:debian-9:
|
||||
extends: .debpkg_template
|
||||
image: debian:9
|
||||
dependencies:
|
||||
- build:debian-9
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
|
||||
deploy:debian-9:
|
||||
extends: .debpkg_install
|
||||
image: debian:9
|
||||
dependencies:
|
||||
- package:debian-9
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
|
||||
# Stretch
|
||||
|
||||
build:debian-10:
|
||||
extends: .build_template
|
||||
image: debian:10
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
|
||||
|
||||
package:debian-10:
|
||||
extends: .debpkg_template
|
||||
image: debian:10
|
||||
dependencies:
|
||||
- build:debian-10
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1d
|
||||
|
||||
deploy:debian-10:
|
||||
extends: .debpkg_install
|
||||
image: debian:10
|
||||
dependencies:
|
||||
- package:debian-10
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1d
|
||||
##
|
||||
## Ubuntu
|
||||
##
|
||||
|
||||
# Trusty
|
||||
|
||||
build:ubuntu-14.04:
|
||||
extends: .build_template
|
||||
image: ubuntu:trusty
|
||||
before_script:
|
||||
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list
|
||||
- apt-key adv --keyserver keyserver.ubuntu.com --recv BA9EF27F
|
||||
- apt-get update -y
|
||||
- apt-get -y install build-essential gcc-6 g++-6 libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
|
||||
variables:
|
||||
CC: gcc-6
|
||||
CXX: g++-6
|
||||
|
||||
package:ubuntu-14.04:
|
||||
extends: .debpkg_template
|
||||
image: ubuntu:trusty
|
||||
dependencies:
|
||||
- build:ubuntu-14.04
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1
|
||||
|
||||
deploy:ubuntu-14.04:
|
||||
extends: .debpkg_install
|
||||
image: ubuntu:trusty
|
||||
dependencies:
|
||||
- package:ubuntu-14.04
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1
|
||||
|
||||
# Xenial
|
||||
|
||||
build:ubuntu-16.04:
|
||||
extends: .build_template
|
||||
image: ubuntu:xenial
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
|
||||
|
||||
package:ubuntu-16.04:
|
||||
extends: .debpkg_template
|
||||
image: ubuntu:xenial
|
||||
dependencies:
|
||||
- build:ubuntu-16.04
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
|
||||
deploy:ubuntu-16.04:
|
||||
extends: .debpkg_install
|
||||
image: ubuntu:xenial
|
||||
dependencies:
|
||||
- package:ubuntu-16.04
|
||||
variables:
|
||||
LEVELDB_PKG: libleveldb1v5
|
||||
image: ubuntu:focal
|
||||
|
||||
##
|
||||
## Fedora
|
||||
##
|
||||
|
||||
# Do we need to support this old version ?
|
||||
build:fedora-24:
|
||||
extends: .build_template
|
||||
image: fedora:24
|
||||
before_script:
|
||||
- dnf -y install make automake gcc gcc-c++ kernel-devel cmake libcurl* openal* libvorbis* libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel bzip2-libs gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel doxygen spatialindex-devel bzip2-devel
|
||||
|
||||
|
||||
##
|
||||
## Mingw for Windows
|
||||
## MinGW for Windows
|
||||
##
|
||||
|
||||
.generic_win_template:
|
||||
image: ubuntu:bionic
|
||||
image: ubuntu:focal
|
||||
before_script:
|
||||
- apt-get update -y
|
||||
- apt-get install -y wget xz-utils unzip git cmake gettext
|
||||
- wget -q http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
|
||||
- apt-get update
|
||||
- DEBIAN_FRONTEND=noninteractive apt-get install -y wget xz-utils unzip git cmake gettext
|
||||
- wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_11.2.0_ubuntu20.04.tar.xz -O mingw.tar.xz
|
||||
- tar -xaf mingw.tar.xz -C /usr
|
||||
|
||||
.build_win_template:
|
||||
extends: .generic_win_template
|
||||
stage: build
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 2h
|
||||
paths:
|
||||
- build/*
|
||||
|
||||
.package_win_template:
|
||||
extends: .generic_win_template
|
||||
stage: package
|
||||
script:
|
||||
- cd build/minetest/_build
|
||||
- make package
|
||||
- cd ../../../
|
||||
- mkdir minetest-win-${WIN_ARCH}
|
||||
- unzip build/minetest/_build/minetest-*-win*.zip -d minetest-win-${WIN_ARCH}
|
||||
- cp /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
|
||||
- cp /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
|
||||
- cp /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
|
||||
artifacts:
|
||||
when: on_success
|
||||
expire_in: 90 day
|
||||
paths:
|
||||
- minetest-win-*/*
|
||||
- minetest-*-win*/*
|
||||
|
||||
build:win32:
|
||||
extends: .build_win_template
|
||||
script:
|
||||
- ./util/buildbot/buildwin32.sh build
|
||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh build
|
||||
- unzip -q build/build/*.zip
|
||||
variables:
|
||||
NO_PACKAGE: "1"
|
||||
WIN_ARCH: "i686"
|
||||
|
||||
package:win32:
|
||||
extends: .package_win_template
|
||||
dependencies:
|
||||
- build:win32
|
||||
variables:
|
||||
NO_PACKAGE: "1"
|
||||
WIN_ARCH: "i686"
|
||||
|
||||
build:win64:
|
||||
extends: .build_win_template
|
||||
script:
|
||||
- ./util/buildbot/buildwin64.sh build
|
||||
- EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh build
|
||||
- unzip -q build/build/*.zip
|
||||
variables:
|
||||
NO_PACKAGE: "1"
|
||||
WIN_ARCH: "x86_64"
|
||||
|
||||
package:win64:
|
||||
extends: .package_win_template
|
||||
dependencies:
|
||||
- build:win64
|
||||
variables:
|
||||
NO_PACKAGE: "1"
|
||||
WIN_ARCH: "x86_64"
|
||||
##
|
||||
## Docker
|
||||
##
|
||||
|
||||
package:docker:
|
||||
stage: package
|
||||
@ -288,18 +85,17 @@ package:docker:
|
||||
before_script:
|
||||
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
|
||||
script:
|
||||
- docker build . -t ${CONTAINER_IMAGE}/server:$CI_COMMIT_SHA -t ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME -t ${CONTAINER_IMAGE}/server:latest
|
||||
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_SHA
|
||||
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME
|
||||
- docker push ${CONTAINER_IMAGE}/server:latest
|
||||
- ./util/ci/docker.sh
|
||||
|
||||
##
|
||||
## Gitlab Pages (Lua API documentation)
|
||||
##
|
||||
|
||||
pages:
|
||||
stage: deploy
|
||||
image: python:3.8
|
||||
before_script:
|
||||
- pip install git+https://github.com/Python-Markdown/markdown.git
|
||||
- pip install git+https://github.com/mkdocs/mkdocs.git
|
||||
- pip install pygments
|
||||
- pip install -U -r doc/mkdocs/requirements.txt
|
||||
script:
|
||||
- cd doc/mkdocs && ./build.sh
|
||||
artifacts:
|
||||
@ -308,3 +104,30 @@ pages:
|
||||
only:
|
||||
- master
|
||||
|
||||
##
|
||||
## AppImage
|
||||
##
|
||||
|
||||
package:appimage-client:
|
||||
stage: package
|
||||
image: appimagecrafters/appimage-builder
|
||||
needs:
|
||||
- build:ubuntu-20.04
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get install -y git
|
||||
# Collect files
|
||||
- mkdir AppDir
|
||||
- cp -a artifact/minetest/usr/ AppDir/usr/
|
||||
- cp -a clientmods AppDir/usr/share/minetest
|
||||
- git clone $MINETEST_GAME_REPO AppDir/usr/share/minetest/games/minetest_game
|
||||
- rm -rf AppDir/usr/share/minetest/games/minetest_game/.git
|
||||
# Remove PrefersNonDefaultGPU property due to validation errors
|
||||
- sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/net.minetest.minetest.desktop
|
||||
script:
|
||||
- export VERSION=$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA
|
||||
- appimage-builder --skip-test --recipe misc/AppImageBuilder.yml
|
||||
artifacts:
|
||||
expire_in: 90 day
|
||||
paths:
|
||||
- ./*.AppImage
|
||||
|
@ -20,7 +20,7 @@ read_globals = {
|
||||
|
||||
string = {fields = {"split", "trim"}},
|
||||
table = {fields = {"copy", "getn", "indexof", "insert_all"}},
|
||||
math = {fields = {"hypot"}},
|
||||
math = {fields = {"hypot", "round"}},
|
||||
}
|
||||
|
||||
globals = {
|
||||
|
76
.mailmap
@ -1,33 +1,69 @@
|
||||
# Documentation: https://git-scm.com/docs/git-check-mailmap#_mapping_authors
|
||||
|
||||
0gb.us <0gb.us@0gb.us> <us_0gb@laptop-0gb-us.0gb.us>
|
||||
Calinou <calinou9999@gmail.com> <calinou9999spam@gmail.com>
|
||||
Perttu Ahola <celeron55@gmail.com> celeron55 <celeron55@gmail.com>
|
||||
Calinou <calinou@opmbx.org> <calinou9999@gmail.com>
|
||||
Calinou <calinou@opmbx.org> <calinou9999spam@gmail.com>
|
||||
Perttu Ahola <celeron55@gmail.com>
|
||||
Perttu Ahola <celeron55@gmail.com> celeron55 <celeron55@armada.(none)>
|
||||
Craig Robbins <kde.psych@gmail.com> <crobbins@localhost.localdomain>
|
||||
Zeno- <kde.psych@gmail.com>
|
||||
Zeno- <kde.psych@gmail.com> <crobbins@localhost.localdomain>
|
||||
Diego Martínez <kaeza@users.sf.net>
|
||||
Diego Martínez <kaeza@users.sf.net> <lkaezadl3@gmail.com>
|
||||
Ilya Zhuravlev <zhuravlevilya@ya.ru>
|
||||
Ilya Zhuravlev <zhuravlevilya@ya.ru> <whatever@xyz.is>
|
||||
kwolekr <kwolekr@minetest.net> <mirrorisim@gmail.com>
|
||||
PilzAdam <pilzadam@minetest.net> PilzAdam <adam-k@outlook.com>
|
||||
PilzAdam <pilzadam@minetest.net> Pilz Adam <PilzAdam@gmx.de>
|
||||
PilzAdam <pilzadam@minetest.net> PilzAdam <PilzAdam@gmx.de>
|
||||
PilzAdam <pilzadam@minetest.net> <adam-k@outlook.com>
|
||||
PilzAdam <pilzadam@minetest.net> <PilzAdam@gmx.de>
|
||||
proller <proller@github.com> <proler@github.com>
|
||||
proller <proller@github.com> <proler@gmail.com>
|
||||
RealBadAngel <maciej.kasatkin@o2.pl> <mk@realbadangel.pl>
|
||||
RealBadAngel <maciej.kasatkin@o2.pl> <maciej.kasatkin@yahoo.com>
|
||||
Selat <LongExampleTestName@gmail.com> <LongExampletestName@gmail.com>
|
||||
ShadowNinja <shadowninja@minetest.net> ShadowNinja <noreply@gmail.com>
|
||||
Shen Zheyu <arsdragonfly@gmail.com> arsdragonfly <arsdragonfly@gmail.com>
|
||||
Pavel Elagin <elagin.pasha@gmail.com> elagin <elagin.pasha@gmail.com>
|
||||
Esteban I. Ruiz Moreno <exio4.com@gmail.com> Esteban I. RM <exio4.com@gmail.com>
|
||||
manuel duarte <ffrogger0@yahoo.com> manuel joaquim <ffrogger0@yahoo.com>
|
||||
manuel duarte <ffrogger0@yahoo.com> sweetbomber <ffrogger _zero_ at yahoo dot com>
|
||||
Diego Martínez <kaeza@users.sf.net> kaeza <kaeza@users.sf.net>
|
||||
Diego Martínez <kaeza@users.sf.net> Diego Martinez <kaeza@users.sf.net>
|
||||
Lord James <neftali_dtctv@hotmail.com> Lord89James <neftali_dtctv@hotmail.com>
|
||||
BlockMen <nmuelll@web.de> Block Men <nmuelll@web.de>
|
||||
sfan5 <sfan5@live.de> Sfan5 <sfan5@live.de>
|
||||
DannyDark <the_skeleton_of_a_child@yahoo.co.uk> dannydark <the_skeleton_of_a_child@yahoo.co.uk>
|
||||
Ilya Pavlov <TTChangeTheWorld@gmail.com> Ilya <TTChangeTheWorld@gmail.com>
|
||||
Ilya Zhuravlev <zhuravlevilya@ya.ru> xyzz <zhuravlevilya@ya.ru>
|
||||
Esteban I. Ruiz Moreno <exio4.com@gmail.com>
|
||||
Esteban I. Ruiz Moreno <exio4.com@gmail.com> <me@exio4.xyz>
|
||||
Lord James <neftali_dtctv@hotmail.com>
|
||||
BlockMen <nmuelll@web.de>
|
||||
sfan5 <sfan5@live.de>
|
||||
DannyDark <the_skeleton_of_a_child@yahoo.co.uk>
|
||||
Ilya Pavlov <TTChangeTheWorld@gmail.com>
|
||||
sapier <Sapier at GMX dot net> sapier <sapier AT gmx DOT net>
|
||||
sapier <Sapier at GMX dot net> sapier <sapier at gmx dot net>
|
||||
|
||||
SmallJoker <SmallJoker@users.noreply.github.com> <mk939@ymail.com>
|
||||
Loïc Blot <nerzhul@users.noreply.github.com>
|
||||
Loïc Blot <nerzhul@users.noreply.github.com> <loic.blot@unix-experience.fr>
|
||||
numzero <numzer0@yandex.ru> Vitaliy <numzer0@yandex.ru>
|
||||
numzero <numzer0@yandex.ru> <silverunicorn2011@yandex.ru>
|
||||
Jean-Patrick Guerrero <kilbith@users.noreply.github.com>
|
||||
Jean-Patrick Guerrero <kilbith@users.noreply.github.com> <jeanpatrick.guerrero@gmail.com>
|
||||
HybridDog <3192173+HybridDog@users.noreply.github.com> <ovvv@web.de>
|
||||
srfqi <muhammadrifqipriyosusanto@gmail.com>
|
||||
Dániel Juhász <juhdanad@gmail.com>
|
||||
rubenwardy <rw@rubenwardy.com>
|
||||
rubenwardy <rw@rubenwardy.com> <rubenwardy@gmail.com>
|
||||
Paul Ouellette <oue.paul18@gmail.com>
|
||||
Vanessa Dannenberg <vanessa.e.dannenberg@gmail.com> <vanessaezekowitz@gmail.com>
|
||||
ClobberXD <ClobberXD@gmail.com>
|
||||
ClobberXD <ClobberXD@gmail.com> <ClobberXD@protonmail.com>
|
||||
ClobberXD <ClobberXD@gmail.com> <36130650+ClobberXD@users.noreply.github.com>
|
||||
Auke Kok <sofar+github@foo-projects.org>
|
||||
Auke Kok <sofar+github@foo-projects.org> <sofar@foo-projects.org>
|
||||
Desour <vorunbekannt75@web.de>
|
||||
Nathanaëlle Courant <Ekdohibs@users.noreply.github.com> <nathanael.courant@laposte.net>
|
||||
Ezhh <owlecho@live.com>
|
||||
paramat <paramat@users.noreply.github.com>
|
||||
paramat <paramat@users.noreply.github.com> <mat.gregory@virginmedia.com>
|
||||
lhofhansl <lhofhansl@yahoo.com> <larsh@apache.org>
|
||||
red-001 <red-001@outlook.ie> <red-001@openmailbox.org>
|
||||
Wuzzy <Wuzzy@disroot.org> <wuzzy2@mail.ru>
|
||||
Wuzzy <Wuzzy@disroot.org> <Wuzzy2@mail.ru>
|
||||
Wuzzy <Wuzzy@disroot.org> <almikes@aol.com>
|
||||
Jordach <jordach.snelling@gmail.com>
|
||||
MoNTE48 <MoNTE48@mail.ua>
|
||||
v-rob <robinsonvincent89@gmail.com>
|
||||
v-rob <robinsonvincent89@gmail.com> <31123645+v-rob@users.noreply.github.com>
|
||||
EvidenceB <49488517+EvidenceBKidscode@users.noreply.github.com>
|
||||
gregorycu <gregory.currie@gmail.com>
|
||||
Rogier <rogier777@gmail.com>
|
||||
Rogier <rogier777@gmail.com> <Rogier-5@users.noreply.github.com>
|
||||
x2048 <codeforsmile@gmail.com>
|
||||
|
142
CMakeLists.txt
@ -1,22 +1,24 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
if(${CMAKE_VERSION} STREQUAL "2.8.2")
|
||||
# Bug http://vtk.org/Bug/view.php?id=11020
|
||||
message(WARNING "CMake/CPack version 2.8.2 will not create working .deb packages!")
|
||||
# Set policies up to 3.9 since we want to enable the IPO option
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.9)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.9)
|
||||
endif()
|
||||
|
||||
# This can be read from ${PROJECT_NAME} after project() is called
|
||||
project(minetest)
|
||||
set(PROJECT_NAME_CAPITALIZED "Minetest")
|
||||
|
||||
# Works only for cmake 3.1 and greater
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(GCC_MINIMUM_VERSION "4.8")
|
||||
set(CLANG_MINIMUM_VERSION "3.4")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(GCC_MINIMUM_VERSION "5.1")
|
||||
set(CLANG_MINIMUM_VERSION "3.5")
|
||||
|
||||
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
|
||||
set(VERSION_MAJOR 5)
|
||||
set(VERSION_MINOR 4)
|
||||
set(VERSION_MINOR 7)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
||||
|
||||
@ -25,7 +27,7 @@ set(DEVELOPMENT_BUILD TRUE)
|
||||
|
||||
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||
if(VERSION_EXTRA)
|
||||
set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
|
||||
set(VERSION_STRING "${VERSION_STRING}-${VERSION_EXTRA}")
|
||||
elseif(DEVELOPMENT_BUILD)
|
||||
set(VERSION_STRING "${VERSION_STRING}-dev")
|
||||
endif()
|
||||
@ -50,7 +52,7 @@ set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL
|
||||
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
||||
set(BUILD_SERVER FALSE CACHE BOOL "Build server")
|
||||
set(BUILD_UNITTESTS TRUE CACHE BOOL "Build unittests")
|
||||
|
||||
set(BUILD_BENCHMARKS FALSE CACHE BOOL "Build benchmarks")
|
||||
|
||||
set(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build")
|
||||
|
||||
@ -59,12 +61,80 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug or Release" FORCE)
|
||||
endif()
|
||||
|
||||
set(ENABLE_UPDATE_CHECKER (NOT ${DEVELOPMENT_BUILD}) CACHE BOOL
|
||||
"Whether to enable update checks by default")
|
||||
|
||||
# Included stuff
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||
|
||||
|
||||
# This is done here so that relative search paths are more reasonable
|
||||
find_package(Irrlicht)
|
||||
set(IRRLICHTMT_BUILD_DIR "" CACHE PATH "Path to IrrlichtMt build directory.")
|
||||
if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
|
||||
find_package(IrrlichtMt QUIET
|
||||
PATHS "${IRRLICHTMT_BUILD_DIR}"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
if(NOT TARGET IrrlichtMt::IrrlichtMt)
|
||||
# find_package() searches certain subdirectories. ${PATH}/cmake is not
|
||||
# the only one, but it is the one where IrrlichtMt is supposed to export
|
||||
# IrrlichtMtConfig.cmake
|
||||
message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.")
|
||||
endif()
|
||||
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
|
||||
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
|
||||
if(BUILD_CLIENT)
|
||||
# tell IrrlichtMt to create a static library
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared library" FORCE)
|
||||
add_subdirectory(lib/irrlichtmt EXCLUDE_FROM_ALL)
|
||||
unset(BUILD_SHARED_LIBS CACHE)
|
||||
|
||||
if(NOT TARGET IrrlichtMt)
|
||||
message(FATAL_ERROR "IrrlichtMt project is missing a CMake target?!")
|
||||
endif()
|
||||
else()
|
||||
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
|
||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt/include")
|
||||
endif()
|
||||
else()
|
||||
find_package(IrrlichtMt QUIET)
|
||||
if(NOT TARGET IrrlichtMt::IrrlichtMt)
|
||||
string(CONCAT explanation_msg
|
||||
"The Minetest team has forked Irrlicht to make their own customizations. "
|
||||
"It can be found here: https://github.com/minetest/irrlicht\n"
|
||||
"For example use: git clone --depth=1 https://github.com/minetest/irrlicht lib/irrlichtmt\n")
|
||||
if(BUILD_CLIENT)
|
||||
message(FATAL_ERROR "IrrlichtMt is required to build the client, but it was not found.\n${explanation_msg}")
|
||||
endif()
|
||||
|
||||
include(MinetestFindIrrlichtHeaders)
|
||||
if(NOT IRRLICHT_INCLUDE_DIR)
|
||||
message(FATAL_ERROR "IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}")
|
||||
endif()
|
||||
message(STATUS "Found IrrlichtMt headers: ${IRRLICHT_INCLUDE_DIR}")
|
||||
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
|
||||
# Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11
|
||||
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${IRRLICHT_INCLUDE_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
|
||||
# retrieve version somehow
|
||||
if(NOT IrrlichtMt_VERSION)
|
||||
get_target_property(IrrlichtMt_VERSION IrrlichtMt VERSION)
|
||||
endif()
|
||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
||||
|
||||
set(TARGET_VER_S 1.9.0mt7)
|
||||
string(REPLACE "mt" "." TARGET_VER ${TARGET_VER_S})
|
||||
if(IrrlichtMt_VERSION VERSION_LESS ${TARGET_VER})
|
||||
message(FATAL_ERROR "At least IrrlichtMt ${TARGET_VER_S} is required to build")
|
||||
elseif(NOT DEVELOPMENT_BUILD AND IrrlichtMt_VERSION VERSION_GREATER ${TARGET_VER})
|
||||
message(FATAL_ERROR "IrrlichtMt ${TARGET_VER_S} is required to build")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Installation
|
||||
@ -95,15 +165,16 @@ elseif(UNIX) # Linux, BSD etc
|
||||
set(ICONDIR "unix/icons")
|
||||
set(LOCALEDIR "locale")
|
||||
else()
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
|
||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}")
|
||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man")
|
||||
include(GNUInstallDirs)
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}")
|
||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
|
||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DOCDIR}")
|
||||
set(MANDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_MANDIR}")
|
||||
set(EXAMPLE_CONF_DIR ${DOCDIR})
|
||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/metainfo")
|
||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons")
|
||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/locale")
|
||||
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications")
|
||||
set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/metainfo")
|
||||
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons")
|
||||
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -173,7 +244,6 @@ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/devtest" DESTINATION "${SHA
|
||||
if(BUILD_CLIENT)
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client/shaders" DESTINATION "${SHAREDIR}/client")
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/textures/base/pack" DESTINATION "${SHAREDIR}/textures/base")
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/fonts" DESTINATION "${SHAREDIR}")
|
||||
if(RUN_IN_PLACE)
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/clientmods" DESTINATION "${SHAREDIR}")
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client/serverlist" DESTINATION "${SHAREDIR}/client")
|
||||
@ -207,10 +277,10 @@ endif()
|
||||
find_package(GMP REQUIRED)
|
||||
find_package(Json REQUIRED)
|
||||
find_package(Lua REQUIRED)
|
||||
|
||||
# JsonCPP doesn't compile well on GCC 4.8
|
||||
if(NOT ENABLE_SYSTEM_JSONCPP)
|
||||
set(GCC_MINIMUM_VERSION "4.9")
|
||||
if(NOT USE_LUAJIT)
|
||||
set(LUA_BIT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/bitop)
|
||||
set(LUA_BIT_LIBRARY bitop)
|
||||
add_subdirectory(lib/bitop)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
@ -218,16 +288,19 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
message(FATAL_ERROR "Insufficient gcc version, found ${CMAKE_CXX_COMPILER_VERSION}. "
|
||||
"Version ${GCC_MINIMUM_VERSION} or higher is required.")
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${CLANG_MINIMUM_VERSION}")
|
||||
message(FATAL_ERROR "Insufficient clang version, found ${CMAKE_CXX_COMPILER_VERSION}. "
|
||||
"Version ${CLANG_MINIMUM_VERSION} or higher is required.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_BENCHMARKS)
|
||||
add_subdirectory(lib/catch2)
|
||||
endif()
|
||||
|
||||
# Subdirectories
|
||||
# Be sure to add all relevant definitions above this
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
||||
@ -249,15 +322,15 @@ cpack_add_component(Docs
|
||||
|
||||
cpack_add_component(SUBGAME_MINETEST_GAME
|
||||
DISPLAY_NAME "Minetest Game"
|
||||
DESCRIPTION "The official subgame for the Minetest engine, that can easily extended by mods."
|
||||
GROUP "Subgames"
|
||||
DESCRIPTION "The default game bundled in the Minetest engine. Mainly used as a modding base."
|
||||
GROUP "Games"
|
||||
)
|
||||
|
||||
cpack_add_component(SUBGAME_MINIMAL
|
||||
DISPLAY_NAME "Development Test"
|
||||
DESCRIPTION "A minimal test game helping to develop the engine."
|
||||
DESCRIPTION "A basic testing environment used for engine development and sometimes for testing mods."
|
||||
DISABLED #DISABLED does not mean it is disabled, and is just not selected by default.
|
||||
GROUP "Subgames"
|
||||
GROUP "Games"
|
||||
)
|
||||
|
||||
cpack_add_component_group(Subgames
|
||||
@ -278,11 +351,12 @@ if(WIN32)
|
||||
set(CPACK_GENERATOR ZIP)
|
||||
|
||||
else()
|
||||
set(CPACK_GENERATOR WIX ZIP)
|
||||
set(CPACK_GENERATOR WIX)
|
||||
set(CPACK_PACKAGE_NAME "${PROJECT_NAME_CAPITALIZED}")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME_CAPITALIZED}")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY ".")
|
||||
set(CPACK_PACKAGE_EXECUTABLES ${PROJECT_NAME} "${PROJECT_NAME_CAPITALIZED}")
|
||||
set(CPACK_CREATE_DESKTOP_LINKS ${PROJECT_NAME})
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX "/${PROJECT_NAME_CAPITALIZED}")
|
||||
|
||||
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/misc/minetest-icon.ico")
|
||||
# Supported languages can be found at
|
||||
|
47
Dockerfile
@ -1,6 +1,8 @@
|
||||
FROM alpine:3.11
|
||||
ARG DOCKER_IMAGE=alpine:3.14
|
||||
FROM $DOCKER_IMAGE AS builder
|
||||
|
||||
ENV MINETEST_GAME_VERSION master
|
||||
ENV IRRLICHT_VERSION master
|
||||
|
||||
COPY .git /usr/src/minetest/.git
|
||||
COPY CMakeLists.txt /usr/src/minetest/CMakeLists.txt
|
||||
@ -18,48 +20,49 @@ COPY textures /usr/src/minetest/textures
|
||||
|
||||
WORKDIR /usr/src/minetest
|
||||
|
||||
RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \
|
||||
jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \
|
||||
libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \
|
||||
gmp-dev jsoncpp-dev postgresql-dev ca-certificates && \
|
||||
RUN apk add --no-cache git build-base cmake sqlite-dev curl-dev zlib-dev zstd-dev \
|
||||
gmp-dev jsoncpp-dev postgresql-dev ninja luajit-dev ca-certificates && \
|
||||
git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
|
||||
rm -fr ./games/minetest_game/.git
|
||||
|
||||
WORKDIR /usr/src/
|
||||
RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
|
||||
mkdir prometheus-cpp/build && \
|
||||
cd prometheus-cpp/build && \
|
||||
cmake .. \
|
||||
cd prometheus-cpp && \
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DENABLE_TESTING=0 && \
|
||||
make -j2 && \
|
||||
make install
|
||||
-DENABLE_TESTING=0 \
|
||||
-GNinja && \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
RUN git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
|
||||
cp -r irrlicht/include /usr/include/irrlichtmt
|
||||
|
||||
WORKDIR /usr/src/minetest
|
||||
RUN mkdir build && \
|
||||
cd build && \
|
||||
cmake .. \
|
||||
RUN cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SERVER=TRUE \
|
||||
-DENABLE_PROMETHEUS=TRUE \
|
||||
-DBUILD_UNITTESTS=FALSE \
|
||||
-DBUILD_CLIENT=FALSE && \
|
||||
make -j2 && \
|
||||
make install
|
||||
-DBUILD_CLIENT=FALSE \
|
||||
-GNinja && \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
FROM alpine:3.11
|
||||
ARG DOCKER_IMAGE=alpine:3.14
|
||||
FROM $DOCKER_IMAGE AS runtime
|
||||
|
||||
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq && \
|
||||
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit jsoncpp zstd-libs && \
|
||||
adduser -D minetest --uid 30000 -h /var/lib/minetest && \
|
||||
chown -R minetest:minetest /var/lib/minetest
|
||||
|
||||
WORKDIR /var/lib/minetest
|
||||
|
||||
COPY --from=0 /usr/local/share/minetest /usr/local/share/minetest
|
||||
COPY --from=0 /usr/local/bin/minetestserver /usr/local/bin/minetestserver
|
||||
COPY --from=0 /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/minetest.conf
|
||||
COPY --from=builder /usr/local/share/minetest /usr/local/share/minetest
|
||||
COPY --from=builder /usr/local/bin/minetestserver /usr/local/bin/minetestserver
|
||||
COPY --from=builder /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/minetest.conf
|
||||
|
||||
USER minetest:minetest
|
||||
|
||||
|
35
LICENSE.txt
@ -11,6 +11,12 @@ http://creativecommons.org/licenses/by-sa/3.0/
|
||||
textures/base/pack/refresh.png is under the Apache 2 license
|
||||
https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
Textures by Zughy are under CC BY-SA 4.0
|
||||
https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
textures/base/pack/server_public.png is under CC-BY 4.0, taken from Twitter's Twemoji set
|
||||
https://creativecommons.org/licenses/by/4.0/
|
||||
|
||||
Authors of media files
|
||||
-----------------------
|
||||
Everything not listed in here:
|
||||
@ -23,6 +29,8 @@ paramat:
|
||||
textures/base/pack/menu_header.png
|
||||
textures/base/pack/next_icon.png
|
||||
textures/base/pack/prev_icon.png
|
||||
textures/base/pack/clear.png
|
||||
textures/base/pack/search.png
|
||||
|
||||
rubenwardy, paramat:
|
||||
textures/base/pack/start_icon.png
|
||||
@ -34,10 +42,10 @@ erlehmann:
|
||||
misc/minetest.svg
|
||||
textures/base/pack/logo.png
|
||||
|
||||
JRottm
|
||||
JRottm:
|
||||
textures/base/pack/player_marker.png
|
||||
|
||||
srifqi
|
||||
srifqi:
|
||||
textures/base/pack/chat_hide_btn.png
|
||||
textures/base/pack/chat_show_btn.png
|
||||
textures/base/pack/joystick_bg.png
|
||||
@ -45,6 +53,26 @@ srifqi
|
||||
textures/base/pack/joystick_off.png
|
||||
textures/base/pack/minimap_btn.png
|
||||
|
||||
Zughy:
|
||||
textures/base/pack/cdb_add.png
|
||||
textures/base/pack/cdb_clear.png
|
||||
textures/base/pack/cdb_downloading.png
|
||||
textures/base/pack/cdb_queued.png
|
||||
textures/base/pack/cdb_update.png
|
||||
textures/base/pack/cdb_viewonline.png
|
||||
|
||||
appgurueu:
|
||||
textures/base/pack/server_incompatible.png
|
||||
|
||||
erlehmann, Warr1024, rollerozxa:
|
||||
textures/base/pack/no_screenshot.png
|
||||
|
||||
kilbith:
|
||||
textures/base/pack/server_favorite.png
|
||||
|
||||
SmallJoker
|
||||
textures/base/pack/server_favorite_delete.png (based on server_favorite.png)
|
||||
|
||||
License of Minetest source code
|
||||
-------------------------------
|
||||
|
||||
@ -68,7 +96,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Irrlicht
|
||||
---------------
|
||||
|
||||
This program uses the Irrlicht Engine. http://irrlicht.sourceforge.net/
|
||||
This program uses IrrlichtMt, Minetest's fork of
|
||||
the Irrlicht Engine. http://irrlicht.sourceforge.net/
|
||||
|
||||
The Irrlicht Engine License
|
||||
|
||||
|
482
README.md
Normal file
@ -0,0 +1,482 @@
|
||||
# Slightly modified version of:
|
||||
|
||||
Minetest
|
||||
========
|
||||
|
||||
![Build Status](https://github.com/minetest/minetest/workflows/build/badge.svg)
|
||||
[![Translation status](https://hosted.weblate.org/widgets/minetest/-/svg-badge.svg)](https://hosted.weblate.org/engage/minetest/?utm_source=widget)
|
||||
[![License](https://img.shields.io/badge/license-LGPLv2.1%2B-blue.svg)](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)
|
||||
|
||||
Minetest is a free open-source voxel game engine with easy modding and game creation.
|
||||
|
||||
Copyright (C) 2010-2022 Perttu Ahola <celeron55@gmail.com>
|
||||
and contributors (see source file comments and the version control log)
|
||||
|
||||
In case you downloaded the source code
|
||||
--------------------------------------
|
||||
If you downloaded the Minetest Engine source code in which this file is
|
||||
contained, you probably want to download the [Minetest Game](https://github.com/minetest/minetest_game/)
|
||||
project too. See its README.txt for more information.
|
||||
|
||||
Table of Contents
|
||||
------------------
|
||||
|
||||
1. [Further Documentation](#further-documentation)
|
||||
2. [Default Controls](#default-controls)
|
||||
3. [Paths](#paths)
|
||||
4. [Configuration File](#configuration-file)
|
||||
5. [Command-line Options](#command-line-options)
|
||||
6. [Compiling](#compiling)
|
||||
7. [Docker](#docker)
|
||||
8. [Version Scheme](#version-scheme)
|
||||
|
||||
|
||||
Further documentation
|
||||
----------------------
|
||||
- Website: https://minetest.net/
|
||||
- Wiki: https://wiki.minetest.net/
|
||||
- Developer wiki: https://dev.minetest.net/
|
||||
- Forum: https://forum.minetest.net/
|
||||
- GitHub: https://github.com/minetest/minetest/
|
||||
- [doc/](doc/) directory of source distribution
|
||||
|
||||
Default controls
|
||||
----------------
|
||||
All controls are re-bindable using settings.
|
||||
Some can be changed in the key config dialog in the settings tab.
|
||||
|
||||
| Button | Action |
|
||||
|-------------------------------|----------------------------------------------------------------|
|
||||
| Move mouse | Look around |
|
||||
| W, A, S, D | Move |
|
||||
| Space | Jump/move up |
|
||||
| Shift | Sneak/move down |
|
||||
| Q | Drop itemstack |
|
||||
| Shift + Q | Drop single item |
|
||||
| Left mouse button | Dig/punch/take item |
|
||||
| Right mouse button | Place/use |
|
||||
| Shift + right mouse button | Build (without using) |
|
||||
| I | Inventory menu |
|
||||
| Mouse wheel | Select item |
|
||||
| 0-9 | Select item |
|
||||
| Z | Zoom (needs zoom privilege) |
|
||||
| T | Chat |
|
||||
| / | Command |
|
||||
| Esc | Pause menu/abort/exit (pauses only singleplayer game) |
|
||||
| R | Enable/disable full range view |
|
||||
| + | Increase view range |
|
||||
| - | Decrease view range |
|
||||
| K | Enable/disable fly mode (needs fly privilege) |
|
||||
| P | Enable/disable pitch move mode |
|
||||
| J | Enable/disable fast mode (needs fast privilege) |
|
||||
| H | Enable/disable noclip mode (needs noclip privilege) |
|
||||
| E | Aux1 (Move fast in fast mode. Games may add special features) |
|
||||
| C | Cycle through camera modes |
|
||||
| V | Cycle through minimap modes |
|
||||
| Shift + V | Change minimap orientation |
|
||||
| F1 | Hide/show HUD |
|
||||
| F2 | Hide/show chat |
|
||||
| F3 | Disable/enable fog |
|
||||
| F4 | Disable/enable camera update (Mapblocks are not updated anymore when disabled, disabled in release builds) |
|
||||
| F5 | Cycle through debug information screens |
|
||||
| F6 | Cycle through profiler info screens |
|
||||
| F10 | Show/hide console |
|
||||
| F12 | Take screenshot |
|
||||
|
||||
Paths
|
||||
-----
|
||||
Locations:
|
||||
|
||||
* `bin` - Compiled binaries
|
||||
* `share` - Distributed read-only data
|
||||
* `user` - User-created modifiable data
|
||||
|
||||
Where each location is on each platform:
|
||||
|
||||
* Windows .zip / RUN_IN_PLACE source:
|
||||
* `bin` = `bin`
|
||||
* `share` = `.`
|
||||
* `user` = `.`
|
||||
* Windows installed:
|
||||
* `bin` = `C:\Program Files\Minetest\bin (Depends on the install location)`
|
||||
* `share` = `C:\Program Files\Minetest (Depends on the install location)`
|
||||
* `user` = `%APPDATA%\Minetest`
|
||||
* Linux installed:
|
||||
* `bin` = `/usr/bin`
|
||||
* `share` = `/usr/share/minetest`
|
||||
* `user` = `~/.minetest`
|
||||
* macOS:
|
||||
* `bin` = `Contents/MacOS`
|
||||
* `share` = `Contents/Resources`
|
||||
* `user` = `Contents/User OR ~/Library/Application Support/minetest`
|
||||
|
||||
Worlds can be found as separate folders in: `user/worlds/`
|
||||
|
||||
Configuration file
|
||||
------------------
|
||||
- Default location:
|
||||
`user/minetest.conf`
|
||||
- This file is created by closing Minetest for the first time.
|
||||
- A specific file can be specified on the command line:
|
||||
`--config <path-to-file>`
|
||||
- A run-in-place build will look for the configuration file in
|
||||
`location_of_exe/../minetest.conf` and also `location_of_exe/../../minetest.conf`
|
||||
|
||||
Command-line options
|
||||
--------------------
|
||||
- Use `--help`
|
||||
|
||||
Compiling
|
||||
---------
|
||||
### Compiling on GNU/Linux
|
||||
|
||||
#### Dependencies
|
||||
|
||||
| Dependency | Version | Commentary |
|
||||
|------------|---------|------------|
|
||||
| GCC | 5.1+ | or Clang 3.5+ |
|
||||
| CMake | 3.5+ | |
|
||||
| IrrlichtMt | - | Custom version of Irrlicht, see https://github.com/minetest/irrlicht |
|
||||
| Freetype | 2.0+ | |
|
||||
| SQLite3 | 3+ | |
|
||||
| Zstd | 1.0+ | |
|
||||
| LuaJIT | 2.0+ | Bundled Lua 5.1 is used if not present |
|
||||
| GMP | 5.0.0+ | Bundled mini-GMP is used if not present |
|
||||
| JsonCPP | 1.0.0+ | Bundled JsonCPP is used if not present |
|
||||
|
||||
For Debian/Ubuntu users:
|
||||
|
||||
sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxi-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev
|
||||
|
||||
For Fedora users:
|
||||
|
||||
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXi-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel
|
||||
|
||||
For Arch users:
|
||||
|
||||
sudo pacman -S base-devel libcurl-gnutls cmake libxi libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd
|
||||
|
||||
For Alpine users:
|
||||
|
||||
sudo apk add build-base cmake libpng-dev jpeg-dev libxi-dev mesa-dev sqlite-dev libogg-dev libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev gmp-dev jsoncpp-dev luajit-dev zstd-dev
|
||||
|
||||
#### Download
|
||||
|
||||
You can install Git for easily keeping your copy up to date.
|
||||
If you don’t want Git, read below on how to get the source without Git.
|
||||
This is an example for installing Git on Debian/Ubuntu:
|
||||
|
||||
sudo apt install git
|
||||
|
||||
For Fedora users:
|
||||
|
||||
sudo dnf install git
|
||||
|
||||
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
|
||||
|
||||
git clone --depth 1 https://github.com/minetest/minetest.git
|
||||
cd minetest
|
||||
|
||||
Download minetest_game (otherwise only the "Development Test" game is available) using Git:
|
||||
|
||||
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
|
||||
|
||||
Download IrrlichtMt to `lib/irrlichtmt`, it will be used to satisfy the IrrlichtMt dependency that way:
|
||||
|
||||
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
|
||||
|
||||
Download source, without using Git:
|
||||
|
||||
wget https://github.com/minetest/minetest/archive/master.tar.gz
|
||||
tar xf master.tar.gz
|
||||
cd minetest-master
|
||||
|
||||
Download minetest_game, without using Git:
|
||||
|
||||
cd games/
|
||||
wget https://github.com/minetest/minetest_game/archive/master.tar.gz
|
||||
tar xf master.tar.gz
|
||||
mv minetest_game-master minetest_game
|
||||
cd ..
|
||||
|
||||
Download IrrlichtMt, without using Git:
|
||||
|
||||
cd lib/
|
||||
wget https://github.com/minetest/irrlicht/archive/master.tar.gz
|
||||
tar xf master.tar.gz
|
||||
mv irrlicht-master irrlichtmt
|
||||
cd ..
|
||||
|
||||
#### Build
|
||||
|
||||
Build a version that runs directly from the source directory:
|
||||
|
||||
cmake . -DRUN_IN_PLACE=TRUE
|
||||
make -j$(nproc)
|
||||
|
||||
Run it:
|
||||
|
||||
./bin/minetest
|
||||
|
||||
- Use `cmake . -LH` to see all CMake options and their current state.
|
||||
- If you want to install it system-wide (or are making a distribution package),
|
||||
you will want to use `-DRUN_IN_PLACE=FALSE`.
|
||||
- You can build a bare server by specifying `-DBUILD_SERVER=TRUE`.
|
||||
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
|
||||
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
|
||||
- Debug build is slower, but gives much more useful output in a debugger.
|
||||
- If you build a bare server you don't need to compile IrrlichtMt, just the headers suffice.
|
||||
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlichtmt/include`.
|
||||
|
||||
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
|
||||
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
|
||||
2. `${PROJECT_SOURCE_DIR}/lib/irrlichtmt` (if existent)
|
||||
3. Installation of IrrlichtMt in the system-specific library paths
|
||||
4. For server builds with disabled `BUILD_CLIENT` variable, the headers from `IRRLICHT_INCLUDE_DIR` will be used.
|
||||
- NOTE: Changing the IrrlichtMt build directory (includes system installs) requires regenerating the CMake cache (`rm CMakeCache.txt`)
|
||||
|
||||
### CMake options
|
||||
|
||||
General options and their default values:
|
||||
|
||||
BUILD_CLIENT=TRUE - Build Minetest client
|
||||
BUILD_SERVER=FALSE - Build Minetest server
|
||||
BUILD_UNITTESTS=TRUE - Build unittest sources
|
||||
BUILD_BENCHMARKS=FALSE - Build benchmark sources
|
||||
CMAKE_BUILD_TYPE=Release - Type of build (Release vs. Debug)
|
||||
Release - Release build
|
||||
Debug - Debug build
|
||||
SemiDebug - Partially optimized debug build
|
||||
RelWithDebInfo - Release build with debug information
|
||||
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
|
||||
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
|
||||
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
|
||||
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations
|
||||
ENABLE_GLES=OFF - Enable extra support code for OpenGL ES (requires support by IrrlichtMt)
|
||||
ENABLE_LEVELDB=ON - Build with LevelDB; Enables use of LevelDB map backend
|
||||
ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended)
|
||||
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
|
||||
ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores
|
||||
ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds
|
||||
ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua)
|
||||
ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
|
||||
ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
|
||||
ENABLE_SYSTEM_JSONCPP=ON - Use JsonCPP from system
|
||||
RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory)
|
||||
ENABLE_UPDATE_CHECKER=TRUE - Whether to enable update checks by default
|
||||
USE_GPROF=FALSE - Enable profiling using GProf
|
||||
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
|
||||
ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt)
|
||||
|
||||
Library specific options:
|
||||
|
||||
CURL_DLL - Only if building with cURL on Windows; path to libcurl.dll
|
||||
CURL_INCLUDE_DIR - Only if building with cURL; directory where curl.h is located
|
||||
CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib
|
||||
EGL_INCLUDE_DIR - Only if building with GLES; directory that contains egl.h
|
||||
EGL_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so
|
||||
EXTRA_DLL - Only on Windows; optional paths to additional DLLs that should be packaged
|
||||
FREETYPE_INCLUDE_DIR_freetype2 - Directory that contains files such as ftimage.h
|
||||
FREETYPE_INCLUDE_DIR_ft2build - Directory that contains ft2build.h
|
||||
FREETYPE_LIBRARY - Path to libfreetype.a/libfreetype.so/freetype.lib
|
||||
FREETYPE_DLL - Only on Windows; path to libfreetype-6.dll
|
||||
GETTEXT_DLL - Only when building with gettext on Windows; paths to libintl + libiconv DLLs
|
||||
GETTEXT_INCLUDE_DIR - Only when building with gettext; directory that contains iconv.h
|
||||
GETTEXT_LIBRARY - Only when building with gettext on Windows; path to libintl.dll.a
|
||||
GETTEXT_MSGFMT - Only when building with gettext; path to msgfmt/msgfmt.exe
|
||||
IRRLICHT_DLL - Only on Windows; path to IrrlichtMt.dll
|
||||
IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h (usable for server build only)
|
||||
LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h
|
||||
LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a
|
||||
LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll
|
||||
PostgreSQL_INCLUDE_DIR - Only when building with PostgreSQL; directory that contains libpq-fe.h
|
||||
PostgreSQL_LIBRARY - Only when building with PostgreSQL; path to libpq.a/libpq.so/libpq.lib
|
||||
REDIS_INCLUDE_DIR - Only when building with Redis; directory that contains hiredis.h
|
||||
REDIS_LIBRARY - Only when building with Redis; path to libhiredis.a/libhiredis.so
|
||||
SPATIAL_INCLUDE_DIR - Only when building with LibSpatial; directory that contains spatialindex/SpatialIndex.h
|
||||
SPATIAL_LIBRARY - Only when building with LibSpatial; path to libspatialindex_c.so/spatialindex-32.lib
|
||||
LUA_INCLUDE_DIR - Only if you want to use LuaJIT; directory where luajit.h is located
|
||||
LUA_LIBRARY - Only if you want to use LuaJIT; path to libluajit.a/libluajit.so
|
||||
OGG_DLL - Only if building with sound on Windows; path to libogg.dll
|
||||
OGG_INCLUDE_DIR - Only if building with sound; directory that contains an ogg directory which contains ogg.h
|
||||
OGG_LIBRARY - Only if building with sound; path to libogg.a/libogg.so/libogg.dll.a
|
||||
OPENAL_DLL - Only if building with sound on Windows; path to OpenAL32.dll
|
||||
OPENAL_INCLUDE_DIR - Only if building with sound; directory where al.h is located
|
||||
OPENAL_LIBRARY - Only if building with sound; path to libopenal.a/libopenal.so/OpenAL32.lib
|
||||
SQLITE3_INCLUDE_DIR - Directory that contains sqlite3.h
|
||||
SQLITE3_LIBRARY - Path to libsqlite3.a/libsqlite3.so/sqlite3.lib
|
||||
VORBISFILE_LIBRARY - Only if building with sound; path to libvorbisfile.a/libvorbisfile.so/libvorbisfile.dll.a
|
||||
VORBIS_DLL - Only if building with sound on Windows; paths to vorbis DLLs
|
||||
VORBIS_INCLUDE_DIR - Only if building with sound; directory that contains a directory vorbis with vorbisenc.h inside
|
||||
VORBIS_LIBRARY - Only if building with sound; path to libvorbis.a/libvorbis.so/libvorbis.dll.a
|
||||
ZLIB_DLL - Only on Windows; path to zlib1.dll
|
||||
ZLIB_INCLUDE_DIR - Directory that contains zlib.h
|
||||
ZLIB_LIBRARY - Path to libz.a/libz.so/zlib.lib
|
||||
ZSTD_DLL - Only on Windows; path to libzstd.dll
|
||||
ZSTD_INCLUDE_DIR - Directory that contains zstd.h
|
||||
ZSTD_LIBRARY - Path to libzstd.a/libzstd.so/ztd.lib
|
||||
|
||||
### Compiling on Windows using MSVC
|
||||
|
||||
### Requirements
|
||||
|
||||
- [Visual Studio 2015 or newer](https://visualstudio.microsoft.com)
|
||||
- [CMake](https://cmake.org/download/)
|
||||
- [vcpkg](https://github.com/Microsoft/vcpkg)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
### Compiling and installing the dependencies
|
||||
|
||||
It is highly recommended to use vcpkg as package manager.
|
||||
|
||||
After you successfully built vcpkg you can easily install the required libraries:
|
||||
```powershell
|
||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry --triplet x64-windows
|
||||
```
|
||||
|
||||
- **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt` as described in the Linux section.
|
||||
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
|
||||
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
|
||||
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
|
||||
- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
|
||||
|
||||
There are other optional libraries, but they are not tested if they can build and link correctly.
|
||||
|
||||
Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-windows`.
|
||||
|
||||
### Compile Minetest
|
||||
|
||||
#### a) Using the vcpkg toolchain and CMake GUI
|
||||
1. Start up the CMake GUI
|
||||
2. Select **Browse Source...** and select DIR/minetest
|
||||
3. Select **Browse Build...** and select DIR/minetest-build
|
||||
4. Select **Configure**
|
||||
5. Choose the right visual Studio version and target platform. It has to match the version of the installed dependencies
|
||||
6. Choose **Specify toolchain file for cross-compiling**
|
||||
7. Click **Next**
|
||||
8. Select the vcpkg toolchain file e.g. `D:/vcpkg/scripts/buildsystems/vcpkg.cmake`
|
||||
9. Click Finish
|
||||
10. Wait until cmake have generated the cash file
|
||||
11. If there are any errors, solve them and hit **Configure**
|
||||
12. Click **Generate**
|
||||
13. Click **Open Project**
|
||||
14. Compile Minetest inside Visual studio.
|
||||
|
||||
#### b) Using the vcpkg toolchain and the commandline
|
||||
|
||||
Run the following script in PowerShell:
|
||||
|
||||
```powershell
|
||||
cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=OFF -DENABLE_CURSES=OFF
|
||||
cmake --build . --config Release
|
||||
```
|
||||
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
|
||||
|
||||
### Windows Installer using WiX Toolset
|
||||
|
||||
Requirements:
|
||||
* [Visual Studio 2017](https://visualstudio.microsoft.com/)
|
||||
* [WiX Toolset](https://wixtoolset.org/)
|
||||
|
||||
In the Visual Studio 2017 Installer select **Optional Features -> WiX Toolset**.
|
||||
|
||||
Build the binaries as described above, but make sure you unselect `RUN_IN_PLACE`.
|
||||
|
||||
Open the generated project file with Visual Studio. Right-click **Package** and choose **Generate**.
|
||||
It may take some minutes to generate the installer.
|
||||
|
||||
### Compiling on MacOS
|
||||
|
||||
#### Requirements
|
||||
- [Homebrew](https://brew.sh/)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
Install dependencies with homebrew:
|
||||
|
||||
```
|
||||
brew install cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd
|
||||
```
|
||||
|
||||
#### Download
|
||||
|
||||
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
|
||||
|
||||
```bash
|
||||
git clone --depth 1 https://github.com/minetest/minetest.git
|
||||
cd minetest
|
||||
```
|
||||
|
||||
Download minetest_game (otherwise only the "Development Test" game is available) using Git:
|
||||
|
||||
```
|
||||
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
|
||||
```
|
||||
|
||||
Download Minetest's fork of Irrlicht:
|
||||
|
||||
```
|
||||
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
|
||||
```
|
||||
|
||||
#### Build
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake .. \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE
|
||||
|
||||
make -j$(sysctl -n hw.logicalcpu)
|
||||
make install
|
||||
```
|
||||
|
||||
#### Run
|
||||
|
||||
```
|
||||
open ./build/macos/minetest.app
|
||||
```
|
||||
|
||||
Docker
|
||||
------
|
||||
We provide Minetest server Docker images using the GitLab mirror registry.
|
||||
|
||||
Images are built on each commit and available using the following tag scheme:
|
||||
|
||||
* `registry.gitlab.com/minetest/minetest/server:latest` (latest build)
|
||||
* `registry.gitlab.com/minetest/minetest/server:<branch/tag>` (current branch or current tag)
|
||||
* `registry.gitlab.com/minetest/minetest/server:<commit-id>` (current commit id)
|
||||
|
||||
If you want to test it on a Docker server you can easily run:
|
||||
|
||||
sudo docker run registry.gitlab.com/minetest/minetest/server:<docker tag>
|
||||
|
||||
If you want to use it in a production environment you should use volumes bound to the Docker host
|
||||
to persist data and modify the configuration:
|
||||
|
||||
sudo docker create -v /home/minetest/data/:/var/lib/minetest/ -v /home/minetest/conf/:/etc/minetest/ registry.gitlab.com/minetest/minetest/server:master
|
||||
|
||||
Data will be written to `/home/minetest/data` on the host, and configuration will be read from `/home/minetest/conf/minetest.conf`.
|
||||
|
||||
**Note:** If you don't understand the previous commands please read the official Docker documentation before use.
|
||||
|
||||
You can also host your Minetest server inside a Kubernetes cluster. See our example implementation in [`misc/kubernetes.yml`](misc/kubernetes.yml).
|
||||
|
||||
|
||||
Version scheme
|
||||
--------------
|
||||
We use `major.minor.patch` since 5.0.0-dev. Prior to that we used `0.major.minor`.
|
||||
|
||||
- Major is incremented when the release contains breaking changes, all other
|
||||
numbers are set to 0.
|
||||
- Minor is incremented when the release contains new non-breaking features,
|
||||
patch is set to 0.
|
||||
- Patch is incremented when the release only contains bugfixes and very
|
||||
minor/trivial features considered necessary.
|
||||
|
||||
Since 5.0.0-dev and 0.4.17-dev, the dev notation refers to the next release,
|
||||
i.e.: 5.0.0-dev is the development version leading to 5.0.0.
|
||||
Prior to that we used `previous_version-dev`.
|
@ -1,18 +1,21 @@
|
||||
apply plugin: 'com.android.application'
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.3'
|
||||
ndkVersion '21.1.6352462'
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion "$ndk_version"
|
||||
defaultConfig {
|
||||
applicationId 'net.minetest.minetest'
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||
versionCode project.versionCode
|
||||
}
|
||||
|
||||
// load properties
|
||||
Properties props = new Properties()
|
||||
props.load(new FileInputStream(file('../local.properties')))
|
||||
def propfile = file('../local.properties')
|
||||
if (propfile.exists())
|
||||
props.load(new FileInputStream(propfile))
|
||||
|
||||
if (props.getProperty('keystore') != null) {
|
||||
signingConfigs {
|
||||
@ -49,7 +52,7 @@ android {
|
||||
|
||||
task prepareAssets() {
|
||||
def assetsFolder = "build/assets"
|
||||
def projRoot = "../../.."
|
||||
def projRoot = "../.."
|
||||
def gameToCopy = "minetest_game"
|
||||
|
||||
copy {
|
||||
@ -61,12 +64,11 @@ task prepareAssets() {
|
||||
copy {
|
||||
from "${projRoot}/builtin" into "${assetsFolder}/builtin"
|
||||
}
|
||||
/*copy {
|
||||
// ToDo: fix Minetest shaders that currently don't work with OpenGL ES
|
||||
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
|
||||
}*/
|
||||
copy {
|
||||
from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht"
|
||||
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
|
||||
}
|
||||
copy {
|
||||
from "../native/deps/armeabi-v7a/Irrlicht/Shaders" into "${assetsFolder}/client/shaders/Irrlicht"
|
||||
}
|
||||
copy {
|
||||
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
|
||||
@ -74,10 +76,13 @@ task prepareAssets() {
|
||||
copy {
|
||||
from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}"
|
||||
}
|
||||
/*copy {
|
||||
// ToDo: fix broken locales
|
||||
from "${projRoot}/po" into "${assetsFolder}/po"
|
||||
}*/
|
||||
fileTree("${projRoot}/po").include("**/*.po").forEach { poFile ->
|
||||
def moPath = "${assetsFolder}/locale/${poFile.parentFile.name}/LC_MESSAGES/"
|
||||
file(moPath).mkdirs()
|
||||
exec {
|
||||
commandLine 'msgfmt', '-o', "${moPath}/minetest.mo", poFile
|
||||
}
|
||||
}
|
||||
copy {
|
||||
from "${projRoot}/textures" into "${assetsFolder}/textures"
|
||||
}
|
||||
@ -107,5 +112,5 @@ android.applicationVariants.all { variant ->
|
||||
|
||||
dependencies {
|
||||
implementation project(':native')
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
}
|
@ -17,8 +17,8 @@
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/label"
|
||||
android:resizeableActivity="false"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:resizeableActivity="false"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<meta-data
|
||||
@ -30,7 +30,8 @@
|
||||
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
|
||||
android:maxAspectRatio="3.0"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -44,7 +45,8 @@
|
||||
android:launchMode="singleTask"
|
||||
android:maxAspectRatio="3.0"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
@ -53,15 +55,21 @@
|
||||
android:value="Minetest" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".InputDialogActivity"
|
||||
android:maxAspectRatio="3.0"
|
||||
android:theme="@style/InputTheme" />
|
||||
|
||||
<service
|
||||
android:name=".UnzipService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="net.minetest.minetest.fileprovider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class CustomEditText extends AppCompatEditText {
|
||||
public CustomEditText(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
InputMethodManager mgr = (InputMethodManager)
|
||||
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.app.NativeActivity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
|
||||
// Native code finds these methods by name (see porting_android.cpp).
|
||||
// This annotation prevents the minifier/Proguard from mangling them.
|
||||
@Keep
|
||||
public class GameActivity extends NativeActivity {
|
||||
static {
|
||||
System.loadLibrary("c++_shared");
|
||||
System.loadLibrary("Minetest");
|
||||
}
|
||||
|
||||
private int messageReturnCode = -1;
|
||||
private String messageReturnValue = "";
|
||||
|
||||
public static native void putMessageBoxResult(String text);
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
private void makeFullScreen() {
|
||||
if (Build.VERSION.SDK_INT >= 19)
|
||||
this.getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus)
|
||||
makeFullScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
makeFullScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Ignore the back press so Minetest can handle it
|
||||
}
|
||||
|
||||
public void showDialog(String acceptButton, String hint, String current, int editType) {
|
||||
runOnUiThread(() -> showDialogUI(hint, current, editType));
|
||||
}
|
||||
|
||||
private void showDialogUI(String hint, String current, int editType) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
LinearLayout container = new LinearLayout(this);
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
builder.setView(container);
|
||||
AlertDialog alertDialog = builder.create();
|
||||
EditText editText;
|
||||
// For multi-line, do not close the dialog after pressing back button
|
||||
if (editType == 1) {
|
||||
editText = new EditText(this);
|
||||
} else {
|
||||
editText = new CustomEditText(this);
|
||||
}
|
||||
container.addView(editText);
|
||||
editText.setMaxLines(8);
|
||||
editText.requestFocus();
|
||||
editText.setHint(hint);
|
||||
editText.setText(current);
|
||||
final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED,
|
||||
InputMethodManager.HIDE_IMPLICIT_ONLY);
|
||||
if (editType == 1)
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
|
||||
else if (editType == 3)
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
else
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
editText.setSelection(editText.getText().length());
|
||||
editText.setOnKeyListener((view, keyCode, event) -> {
|
||||
// For multi-line, do not submit the text after pressing Enter key
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
messageReturnCode = 0;
|
||||
messageReturnValue = editText.getText().toString();
|
||||
alertDialog.dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// For multi-line, add Done button since Enter key does not submit text
|
||||
if (editType == 1) {
|
||||
Button doneButton = new Button(this);
|
||||
container.addView(doneButton);
|
||||
doneButton.setText(R.string.ime_dialog_done);
|
||||
doneButton.setOnClickListener((view -> {
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
messageReturnCode = 0;
|
||||
messageReturnValue = editText.getText().toString();
|
||||
alertDialog.dismiss();
|
||||
}));
|
||||
}
|
||||
alertDialog.show();
|
||||
alertDialog.setOnCancelListener(dialog -> {
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
messageReturnValue = current;
|
||||
messageReturnCode = -1;
|
||||
});
|
||||
}
|
||||
|
||||
public int getDialogState() {
|
||||
return messageReturnCode;
|
||||
}
|
||||
|
||||
public String getDialogValue() {
|
||||
messageReturnCode = -1;
|
||||
return messageReturnValue;
|
||||
}
|
||||
|
||||
public float getDensity() {
|
||||
return getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
public int getDisplayHeight() {
|
||||
return getResources().getDisplayMetrics().heightPixels;
|
||||
}
|
||||
|
||||
public int getDisplayWidth() {
|
||||
return getResources().getDisplayMetrics().widthPixels;
|
||||
}
|
||||
|
||||
public void openURI(String uri) {
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
|
||||
public String getUserDataPath() {
|
||||
return Utils.getUserDataDirectory(this).getAbsolutePath();
|
||||
}
|
||||
|
||||
public String getCachePath() {
|
||||
return Utils.getCacheDirectory(this).getAbsolutePath();
|
||||
}
|
||||
|
||||
public void shareFile(String path) {
|
||||
File file = new File(path);
|
||||
if (!file.exists()) {
|
||||
Log.e("GameActivity", "File " + file.getAbsolutePath() + " doesn't exist");
|
||||
return;
|
||||
}
|
||||
|
||||
Uri fileUri = FileProvider.getUriForFile(this, "net.minetest.minetest.fileprovider", file);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_SEND, fileUri);
|
||||
intent.setDataAndType(fileUri, getContentResolver().getType(fileUri));
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
|
||||
Intent shareIntent = Intent.createChooser(intent, null);
|
||||
startActivity(shareIntent);
|
||||
}
|
||||
}
|
@ -29,12 +29,14 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
@ -43,11 +45,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minetest.minetest.UnzipService.ACTION_FAILURE;
|
||||
import static net.minetest.minetest.UnzipService.ACTION_PROGRESS;
|
||||
import static net.minetest.minetest.UnzipService.ACTION_UPDATE;
|
||||
import static net.minetest.minetest.UnzipService.FAILURE;
|
||||
import static net.minetest.minetest.UnzipService.SUCCESS;
|
||||
import static net.minetest.minetest.UnzipService.*;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private final static int versionCode = BuildConfig.VERSION_CODE;
|
||||
@ -56,26 +54,40 @@ public class MainActivity extends AppCompatActivity {
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
private static final String SETTINGS = "MinetestSettings";
|
||||
private static final String TAG_VERSION_CODE = "versionCode";
|
||||
|
||||
private ProgressBar mProgressBar;
|
||||
private TextView mTextView;
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
int progress = 0;
|
||||
if (intent != null)
|
||||
@StringRes int message = 0;
|
||||
if (intent != null) {
|
||||
progress = intent.getIntExtra(ACTION_PROGRESS, 0);
|
||||
if (progress >= 0) {
|
||||
if (mProgressBar != null) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setProgress(progress);
|
||||
message = intent.getIntExtra(ACTION_PROGRESS_MESSAGE, 0);
|
||||
}
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
} else if (progress == FAILURE) {
|
||||
|
||||
if (progress == FAILURE) {
|
||||
Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
} else if (progress == SUCCESS)
|
||||
} else if (progress == SUCCESS) {
|
||||
startNative();
|
||||
} else {
|
||||
if (mProgressBar != null) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
if (progress == INDETERMINATE) {
|
||||
mProgressBar.setIndeterminate(true);
|
||||
} else {
|
||||
mProgressBar.setIndeterminate(false);
|
||||
mProgressBar.setProgress(progress);
|
||||
}
|
||||
}
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
if (message != 0)
|
||||
mTextView.setText(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -88,7 +100,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
mProgressBar = findViewById(R.id.progressBar);
|
||||
mTextView = findViewById(R.id.textView);
|
||||
sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||
checkPermission();
|
||||
else
|
||||
checkAppVersion();
|
||||
@ -120,6 +134,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
checkAppVersion();
|
||||
@ -127,10 +142,27 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void checkAppVersion() {
|
||||
if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode)
|
||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
Toast.makeText(this, R.string.no_external_storage, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (UnzipService.getIsRunning()) {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setIndeterminate(true);
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
} else if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode &&
|
||||
Utils.isInstallValid(this)) {
|
||||
startNative();
|
||||
else
|
||||
new CopyZipTask(this).execute(getCacheDir() + "/Minetest.zip");
|
||||
} else {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBar.setIndeterminate(true);
|
||||
mTextView.setVisibility(View.VISIBLE);
|
||||
|
||||
Intent intent = new Intent(this, UnzipService.class);
|
||||
startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void startNative() {
|
@ -0,0 +1,259 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class UnzipService extends IntentService {
|
||||
public static final String ACTION_UPDATE = "net.minetest.minetest.UPDATE";
|
||||
public static final String ACTION_PROGRESS = "net.minetest.minetest.PROGRESS";
|
||||
public static final String ACTION_PROGRESS_MESSAGE = "net.minetest.minetest.PROGRESS_MESSAGE";
|
||||
public static final String ACTION_FAILURE = "net.minetest.minetest.FAILURE";
|
||||
public static final int SUCCESS = -1;
|
||||
public static final int FAILURE = -2;
|
||||
public static final int INDETERMINATE = -3;
|
||||
private final int id = 1;
|
||||
private NotificationManager mNotifyManager;
|
||||
private boolean isSuccess = true;
|
||||
private String failureMessage;
|
||||
|
||||
private static boolean isRunning = false;
|
||||
public static synchronized boolean getIsRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
private static synchronized void setIsRunning(boolean v) {
|
||||
isRunning = v;
|
||||
}
|
||||
|
||||
public UnzipService() {
|
||||
super("net.minetest.minetest.UnzipService");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
Notification.Builder notificationBuilder = createNotification();
|
||||
final File zipFile = new File(getCacheDir(), "Minetest.zip");
|
||||
try {
|
||||
setIsRunning(true);
|
||||
File userDataDirectory = Utils.getUserDataDirectory(this);
|
||||
if (userDataDirectory == null) {
|
||||
throw new IOException("Unable to find user data directory");
|
||||
}
|
||||
|
||||
try (InputStream in = this.getAssets().open(zipFile.getName())) {
|
||||
try (OutputStream out = new FileOutputStream(zipFile)) {
|
||||
int readLen;
|
||||
byte[] readBuffer = new byte[16384];
|
||||
while ((readLen = in.read(readBuffer)) != -1) {
|
||||
out.write(readBuffer, 0, readLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
migrate(notificationBuilder, userDataDirectory);
|
||||
unzip(notificationBuilder, zipFile, userDataDirectory);
|
||||
} catch (IOException e) {
|
||||
isSuccess = false;
|
||||
failureMessage = e.getLocalizedMessage();
|
||||
} finally {
|
||||
setIsRunning(false);
|
||||
zipFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private Notification.Builder createNotification() {
|
||||
String name = "net.minetest.minetest";
|
||||
String channelId = "Minetest channel";
|
||||
String description = "notifications from Minetest";
|
||||
Notification.Builder builder;
|
||||
if (mNotifyManager == null)
|
||||
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||
NotificationChannel mChannel = null;
|
||||
if (mNotifyManager != null)
|
||||
mChannel = mNotifyManager.getNotificationChannel(channelId);
|
||||
if (mChannel == null) {
|
||||
mChannel = new NotificationChannel(channelId, name, importance);
|
||||
mChannel.setDescription(description);
|
||||
// Configure the notification channel, NO SOUND
|
||||
mChannel.setSound(null, null);
|
||||
mChannel.enableLights(false);
|
||||
mChannel.enableVibration(false);
|
||||
mNotifyManager.createNotificationChannel(mChannel);
|
||||
}
|
||||
builder = new Notification.Builder(this, channelId);
|
||||
} else {
|
||||
builder = new Notification.Builder(this);
|
||||
}
|
||||
|
||||
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
PendingIntent intent = PendingIntent.getActivity(this, 0,
|
||||
notificationIntent, 0);
|
||||
|
||||
builder.setContentTitle(getString(R.string.notification_title))
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentText(getString(R.string.notification_description))
|
||||
.setContentIntent(intent)
|
||||
.setOngoing(true)
|
||||
.setProgress(0, 0, true);
|
||||
|
||||
mNotifyManager.notify(id, builder.build());
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void unzip(Notification.Builder notificationBuilder, File zipFile, File userDataDirectory) throws IOException {
|
||||
int per = 0;
|
||||
|
||||
int size;
|
||||
try (ZipFile zipSize = new ZipFile(zipFile)) {
|
||||
size = zipSize.size();
|
||||
}
|
||||
|
||||
int readLen;
|
||||
byte[] readBuffer = new byte[16384];
|
||||
try (FileInputStream fileInputStream = new FileInputStream(zipFile);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
|
||||
ZipEntry ze;
|
||||
while ((ze = zipInputStream.getNextEntry()) != null) {
|
||||
if (ze.isDirectory()) {
|
||||
++per;
|
||||
Utils.createDirs(userDataDirectory, ze.getName());
|
||||
continue;
|
||||
}
|
||||
publishProgress(notificationBuilder, R.string.loading, 100 * ++per / size);
|
||||
try (OutputStream outputStream = new FileOutputStream(
|
||||
new File(userDataDirectory, ze.getName()))) {
|
||||
while ((readLen = zipInputStream.read(readBuffer)) != -1) {
|
||||
outputStream.write(readBuffer, 0, readLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void moveFileOrDir(@NonNull File src, @NonNull File dst) throws IOException {
|
||||
try {
|
||||
Process p = new ProcessBuilder("/system/bin/mv",
|
||||
src.getAbsolutePath(), dst.getAbsolutePath()).start();
|
||||
int exitcode = p.waitFor();
|
||||
if (exitcode != 0)
|
||||
throw new IOException("Move failed with exit code " + exitcode);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("Move operation interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
boolean recursivelyDeleteDirectory(@NonNull File loc) {
|
||||
try {
|
||||
Process p = new ProcessBuilder("/system/bin/rm", "-rf",
|
||||
loc.getAbsolutePath()).start();
|
||||
return p.waitFor() == 0;
|
||||
} catch (IOException | InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates user data from deprecated external storage to app scoped storage
|
||||
*/
|
||||
private void migrate(Notification.Builder notificationBuilder, File newLocation) throws IOException {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
return;
|
||||
}
|
||||
|
||||
File oldLocation = new File(Environment.getExternalStorageDirectory(), "Minetest");
|
||||
if (!oldLocation.isDirectory())
|
||||
return;
|
||||
|
||||
publishProgress(notificationBuilder, R.string.migrating, 0);
|
||||
newLocation.mkdir();
|
||||
|
||||
String[] dirs = new String[] { "worlds", "games", "mods", "textures", "client" };
|
||||
for (int i = 0; i < dirs.length; i++) {
|
||||
publishProgress(notificationBuilder, R.string.migrating, 100 * i / dirs.length);
|
||||
File dir = new File(oldLocation, dirs[i]), dir2 = new File(newLocation, dirs[i]);
|
||||
if (dir.isDirectory() && !dir2.isDirectory()) {
|
||||
moveFileOrDir(dir, dir2);
|
||||
}
|
||||
}
|
||||
|
||||
for (String filename : new String[] { "minetest.conf" }) {
|
||||
File file = new File(oldLocation, filename), file2 = new File(newLocation, filename);
|
||||
if (file.isFile() && !file2.isFile()) {
|
||||
moveFileOrDir(file, file2);
|
||||
}
|
||||
}
|
||||
|
||||
recursivelyDeleteDirectory(oldLocation);
|
||||
}
|
||||
|
||||
private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {
|
||||
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
||||
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
||||
intentUpdate.putExtra(ACTION_PROGRESS_MESSAGE, message);
|
||||
if (!isSuccess)
|
||||
intentUpdate.putExtra(ACTION_FAILURE, failureMessage);
|
||||
sendBroadcast(intentUpdate);
|
||||
|
||||
if (notificationBuilder != null) {
|
||||
notificationBuilder.setContentText(getString(message));
|
||||
if (progress == INDETERMINATE) {
|
||||
notificationBuilder.setProgress(100, 50, true);
|
||||
} else {
|
||||
notificationBuilder.setProgress(100, progress, false);
|
||||
}
|
||||
mNotifyManager.notify(id, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mNotifyManager.cancel(id);
|
||||
publishProgress(null, R.string.loading, isSuccess ? SUCCESS : FAILURE);
|
||||
}
|
||||
}
|
39
android/app/src/main/java/net/minetest/minetest/Utils.java
Normal file
@ -0,0 +1,39 @@
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import java.io.File;
|
||||
|
||||
public class Utils {
|
||||
public static @NonNull File createDirs(File root, String dir) {
|
||||
File f = new File(root, dir);
|
||||
if (!f.isDirectory())
|
||||
f.mkdirs();
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public static @Nullable File getUserDataDirectory(Context context) {
|
||||
File extDir = context.getExternalFilesDir(null);
|
||||
if (extDir == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createDirs(extDir, "Minetest");
|
||||
}
|
||||
|
||||
public static @Nullable File getCacheDirectory(Context context) {
|
||||
return context.getCacheDir();
|
||||
}
|
||||
|
||||
public static boolean isInstallValid(Context context) {
|
||||
File userDataDirectory = getUserDataDirectory(context);
|
||||
return userDataDirectory != null && userDataDirectory.isDirectory() &&
|
||||
new File(userDataDirectory, "games").isDirectory() &&
|
||||
new File(userDataDirectory, "builtin").isDirectory() &&
|
||||
new File(userDataDirectory, "client").isDirectory() &&
|
||||
new File(userDataDirectory, "textures").isDirectory();
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 83 B After Width: | Height: | Size: 83 B |
@ -1,4 +1,5 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -14,7 +15,8 @@
|
||||
android:layout_marginRight="90dp"
|
||||
android:indeterminate="false"
|
||||
android:max="100"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
@ -25,6 +27,7 @@
|
||||
android:background="@android:color/transparent"
|
||||
android:text="@string/loading"
|
||||
android:textColor="#FEFEFE"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</RelativeLayout>
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
@ -3,8 +3,11 @@
|
||||
|
||||
<string name="label">Minetest</string>
|
||||
<string name="loading">Loading…</string>
|
||||
<string name="migrating">Migrating save data from old install… (this may take a while)</string>
|
||||
<string name="not_granted">Required permission wasn\'t granted, Minetest can\'t run without it</string>
|
||||
<string name="notification_title">Loading Minetest</string>
|
||||
<string name="notification_description">Less than 1 minute…</string>
|
||||
<string name="ime_dialog_done">Done</string>
|
||||
<string name="no_external_storage">External storage isn\'t available. If you use an SDCard, please reinsert it. Otherwise, try restarting your phone or contacting the Minetest developers</string>
|
||||
|
||||
</resources>
|
@ -8,15 +8,8 @@
|
||||
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="p">shortEdges</item>
|
||||
</style>
|
||||
|
||||
<style name="InputTheme" parent="Theme.AppCompat.DayNight.Dialog">
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="CustomProgressBar" parent="@style/Widget.AppCompat.ProgressBar.Horizontal">
|
||||
<style name="CustomProgressBar" parent="Widget.AppCompat.ProgressBar.Horizontal">
|
||||
<item name="android:indeterminateOnly">false</item>
|
||||
<item name="android:minHeight">10dip</item>
|
||||
<item name="android:maxHeight">20dip</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
3
android/app/src/main/res/xml/filepaths.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<paths>
|
||||
<external-files-path path="Minetest/" name="minetest" />
|
||||
</paths>
|
@ -1,22 +1,24 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
project.ext.set("versionMajor", 5) // Version Major
|
||||
project.ext.set("versionMinor", 3) // Version Minor
|
||||
project.ext.set("versionMinor", 7) // Version Minor
|
||||
project.ext.set("versionPatch", 0) // Version Patch
|
||||
project.ext.set("versionExtra", "-dev") // Version Extra
|
||||
project.ext.set("versionCode", 30) // Android Version Code
|
||||
project.ext.set("versionCode", 40) // Android Version Code
|
||||
project.ext.set("developmentBuild", 1) // Whether it is a development build, or a release
|
||||
// NOTE: +2 after each release!
|
||||
// +1 for ARM and +1 for ARM64 APK's, because
|
||||
// each APK must have a larger `versionCode` than the previous
|
||||
|
||||
buildscript {
|
||||
ext.ndk_version = '23.2.8568313'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||
classpath 'org.ajoberstar.grgit:grgit-gradle:4.0.2'
|
||||
classpath 'com.android.tools.build:gradle:7.0.3'
|
||||
classpath 'de.undercouch:gradle-download-task:4.1.1'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
@ -31,4 +33,5 @@ allprojects {
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
delete 'native/deps'
|
||||
}
|
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
2
build/android/gradlew → android/gradlew
vendored
@ -98,7 +98,7 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
command -v java >/dev/null || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
143
android/icons/aux1_btn.svg
Normal file
@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="24.000002"
|
||||
inkscape:export-xdpi="24.000002"
|
||||
inkscape:export-filename="/home/stu/Desktop/icons/png/aux_btn.png"
|
||||
sodipodi:docname="aux_btn.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 135.46666 135.46667"
|
||||
height="512"
|
||||
width="512">
|
||||
<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="31"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-height="1024"
|
||||
inkscape:window-width="1920"
|
||||
units="px"
|
||||
showgrid="true"
|
||||
inkscape:current-layer="layer2"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:cy="212.91276"
|
||||
inkscape:cx="201.43176"
|
||||
inkscape:zoom="1.4633894"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#404040"
|
||||
id="base">
|
||||
<inkscape:grid
|
||||
empopacity="0.25098039"
|
||||
empcolor="#40ff40"
|
||||
opacity="0.1254902"
|
||||
color="#40ff40"
|
||||
empspacing="4"
|
||||
spacingy="0.26458333"
|
||||
spacingx="0.26458333"
|
||||
id="grid16"
|
||||
type="xygrid" />
|
||||
</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" />
|
||||
<dc:title />
|
||||
<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">Aux1</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.5 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
69
android/native/build.gradle
Normal file
@ -0,0 +1,69 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'de.undercouch.download'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion "$ndk_version"
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments '-j' + Runtime.getRuntime().availableProcessors(),
|
||||
"versionMajor=${versionMajor}",
|
||||
"versionMinor=${versionMinor}",
|
||||
"versionPatch=${versionPatch}",
|
||||
"versionExtra=${versionExtra}",
|
||||
"developmentBuild=${developmentBuild}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path file('jni/Android.mk')
|
||||
}
|
||||
}
|
||||
|
||||
// supported architectures
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'armeabi-v7a', 'arm64-v8a'//, 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments 'NDEBUG=1'
|
||||
}
|
||||
}
|
||||
|
||||
ndk {
|
||||
debugSymbolLevel 'SYMBOL_TABLE'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get precompiled deps
|
||||
task downloadDeps(type: Download) {
|
||||
src 'https://github.com/minetest/minetest_android_deps/releases/download/latest/deps.zip'
|
||||
dest new File(buildDir, 'deps.zip')
|
||||
overwrite false
|
||||
}
|
||||
|
||||
task getDeps(dependsOn: downloadDeps, type: Copy) {
|
||||
def deps = new File(buildDir.parent, 'deps')
|
||||
if (!deps.exists()) {
|
||||
deps.mkdir()
|
||||
from zipTree(downloadDeps.dest)
|
||||
into deps
|
||||
}
|
||||
}
|
||||
|
||||
preBuild.dependsOn getDeps
|
291
android/native/jni/Android.mk
Normal file
@ -0,0 +1,291 @@
|
||||
LOCAL_PATH := $(call my-dir)/..
|
||||
|
||||
#LOCAL_ADDRESS_SANITIZER:=true
|
||||
#USE_BUILTIN_LUA:=true
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Curl
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libcurl.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libmbedcrypto
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedcrypto.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libmbedtls
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedtls.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libmbedx509
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Curl/libmbedx509.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Freetype
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Freetype/libfreetype.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Iconv
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Iconv/libiconv.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libcharset
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Iconv/libcharset.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Irrlicht
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libIrrlichtMt.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
ifndef USE_BUILTIN_LUA
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := LuaJIT
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/libluajit.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := OpenAL
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Gettext
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Gettext/libintl.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := SQLite3
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/SQLite/libsqlite3.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Vorbis
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libvorbis.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libvorbisfile
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libvorbisfile.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libogg
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Vorbis/libogg.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Zstd
|
||||
LOCAL_SRC_FILES := deps/$(APP_ABI)/Zstd/libzstd.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Minetest
|
||||
|
||||
LOCAL_CFLAGS += \
|
||||
-DJSONCPP_NO_LOCALE_SUPPORT \
|
||||
-DHAVE_TOUCHSCREENGUI \
|
||||
-DENABLE_GLES=1 \
|
||||
-DUSE_CURL=1 \
|
||||
-DUSE_SOUND=1 \
|
||||
-DUSE_LEVELDB=0 \
|
||||
-DUSE_GETTEXT=1 \
|
||||
-DVERSION_MAJOR=${versionMajor} \
|
||||
-DVERSION_MINOR=${versionMinor} \
|
||||
-DVERSION_PATCH=${versionPatch} \
|
||||
-DVERSION_EXTRA=${versionExtra} \
|
||||
-DDEVELOPMENT_BUILD=${developmentBuild} \
|
||||
$(GPROF_DEF)
|
||||
|
||||
ifdef USE_BUILTIN_LUA
|
||||
LOCAL_CFLAGS += -DUSE_LUAJIT=0
|
||||
else
|
||||
LOCAL_CFLAGS += -DUSE_LUAJIT=1
|
||||
endif
|
||||
|
||||
ifdef NDEBUG
|
||||
LOCAL_CFLAGS += -DNDEBUG=1
|
||||
endif
|
||||
|
||||
ifdef GPROF
|
||||
GPROF_DEF := -DGPROF
|
||||
PROFILER_LIBS := android-ndk-profiler
|
||||
LOCAL_CFLAGS += -pg
|
||||
endif
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
../../src \
|
||||
../../src/script \
|
||||
../../lib/gmp \
|
||||
../../lib/jsoncpp \
|
||||
deps/$(APP_ABI)/Curl/include \
|
||||
deps/$(APP_ABI)/Freetype/include/freetype2 \
|
||||
deps/$(APP_ABI)/Irrlicht/include \
|
||||
deps/$(APP_ABI)/Gettext/include \
|
||||
deps/$(APP_ABI)/Iconv/include \
|
||||
deps/$(APP_ABI)/OpenAL-Soft/include \
|
||||
deps/$(APP_ABI)/SQLite/include \
|
||||
deps/$(APP_ABI)/Vorbis/include \
|
||||
deps/$(APP_ABI)/Zstd/include
|
||||
|
||||
ifdef USE_BUILTIN_LUA
|
||||
LOCAL_C_INCLUDES += \
|
||||
../../lib/lua/src \
|
||||
../../lib/bitop
|
||||
else
|
||||
LOCAL_C_INCLUDES += deps/$(APP_ABI)/LuaJIT/include
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(wildcard ../../src/client/*.cpp) \
|
||||
$(wildcard ../../src/client/*/*.cpp) \
|
||||
$(wildcard ../../src/content/*.cpp) \
|
||||
../../src/database/database.cpp \
|
||||
../../src/database/database-dummy.cpp \
|
||||
../../src/database/database-files.cpp \
|
||||
../../src/database/database-sqlite3.cpp \
|
||||
$(wildcard ../../src/gui/*.cpp) \
|
||||
$(wildcard ../../src/irrlicht_changes/*.cpp) \
|
||||
$(wildcard ../../src/mapgen/*.cpp) \
|
||||
$(wildcard ../../src/network/*.cpp) \
|
||||
$(wildcard ../../src/script/*.cpp) \
|
||||
$(wildcard ../../src/script/*/*.cpp) \
|
||||
$(wildcard ../../src/server/*.cpp) \
|
||||
$(wildcard ../../src/threading/*.cpp) \
|
||||
$(wildcard ../../src/util/*.c) \
|
||||
$(wildcard ../../src/util/*.cpp) \
|
||||
../../src/ban.cpp \
|
||||
../../src/chat.cpp \
|
||||
../../src/clientiface.cpp \
|
||||
../../src/collision.cpp \
|
||||
../../src/content_mapnode.cpp \
|
||||
../../src/content_nodemeta.cpp \
|
||||
../../src/convert_json.cpp \
|
||||
../../src/craftdef.cpp \
|
||||
../../src/debug.cpp \
|
||||
../../src/defaultsettings.cpp \
|
||||
../../src/emerge.cpp \
|
||||
../../src/environment.cpp \
|
||||
../../src/face_position_cache.cpp \
|
||||
../../src/filesys.cpp \
|
||||
../../src/gettext.cpp \
|
||||
../../src/httpfetch.cpp \
|
||||
../../src/hud.cpp \
|
||||
../../src/inventory.cpp \
|
||||
../../src/inventorymanager.cpp \
|
||||
../../src/itemdef.cpp \
|
||||
../../src/itemstackmetadata.cpp \
|
||||
../../src/light.cpp \
|
||||
../../src/log.cpp \
|
||||
../../src/main.cpp \
|
||||
../../src/map.cpp \
|
||||
../../src/map_settings_manager.cpp \
|
||||
../../src/mapblock.cpp \
|
||||
../../src/mapnode.cpp \
|
||||
../../src/mapsector.cpp \
|
||||
../../src/metadata.cpp \
|
||||
../../src/modchannels.cpp \
|
||||
../../src/nameidmapping.cpp \
|
||||
../../src/nodedef.cpp \
|
||||
../../src/nodemetadata.cpp \
|
||||
../../src/nodetimer.cpp \
|
||||
../../src/noise.cpp \
|
||||
../../src/objdef.cpp \
|
||||
../../src/object_properties.cpp \
|
||||
../../src/particles.cpp \
|
||||
../../src/pathfinder.cpp \
|
||||
../../src/player.cpp \
|
||||
../../src/porting.cpp \
|
||||
../../src/porting_android.cpp \
|
||||
../../src/profiler.cpp \
|
||||
../../src/raycast.cpp \
|
||||
../../src/reflowscan.cpp \
|
||||
../../src/remoteplayer.cpp \
|
||||
../../src/rollback.cpp \
|
||||
../../src/rollback_interface.cpp \
|
||||
../../src/serialization.cpp \
|
||||
../../src/server.cpp \
|
||||
../../src/serverenvironment.cpp \
|
||||
../../src/serverlist.cpp \
|
||||
../../src/settings.cpp \
|
||||
../../src/staticobject.cpp \
|
||||
../../src/texture_override.cpp \
|
||||
../../src/tileanimation.cpp \
|
||||
../../src/tool.cpp \
|
||||
../../src/translation.cpp \
|
||||
../../src/version.cpp \
|
||||
../../src/voxel.cpp \
|
||||
../../src/voxelalgorithms.cpp
|
||||
|
||||
# Built-in Lua
|
||||
ifdef USE_BUILTIN_LUA
|
||||
LOCAL_SRC_FILES += \
|
||||
../../lib/lua/src/lapi.c \
|
||||
../../lib/lua/src/lauxlib.c \
|
||||
../../lib/lua/src/lbaselib.c \
|
||||
../../lib/lua/src/lcode.c \
|
||||
../../lib/lua/src/ldblib.c \
|
||||
../../lib/lua/src/ldebug.c \
|
||||
../../lib/lua/src/ldo.c \
|
||||
../../lib/lua/src/ldump.c \
|
||||
../../lib/lua/src/lfunc.c \
|
||||
../../lib/lua/src/lgc.c \
|
||||
../../lib/lua/src/linit.c \
|
||||
../../lib/lua/src/liolib.c \
|
||||
../../lib/lua/src/llex.c \
|
||||
../../lib/lua/src/lmathlib.c \
|
||||
../../lib/lua/src/lmem.c \
|
||||
../../lib/lua/src/loadlib.c \
|
||||
../../lib/lua/src/lobject.c \
|
||||
../../lib/lua/src/lopcodes.c \
|
||||
../../lib/lua/src/loslib.c \
|
||||
../../lib/lua/src/lparser.c \
|
||||
../../lib/lua/src/lstate.c \
|
||||
../../lib/lua/src/lstring.c \
|
||||
../../lib/lua/src/lstrlib.c \
|
||||
../../lib/lua/src/ltable.c \
|
||||
../../lib/lua/src/ltablib.c \
|
||||
../../lib/lua/src/ltm.c \
|
||||
../../lib/lua/src/lundump.c \
|
||||
../../lib/lua/src/lvm.c \
|
||||
../../lib/lua/src/lzio.c \
|
||||
../../lib/bitop/bit.c
|
||||
endif
|
||||
|
||||
# GMP
|
||||
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
|
||||
|
||||
# JSONCPP
|
||||
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += \
|
||||
Curl libmbedcrypto libmbedtls libmbedx509 \
|
||||
Freetype \
|
||||
Iconv libcharset \
|
||||
Irrlicht \
|
||||
OpenAL \
|
||||
Gettext \
|
||||
SQLite3 \
|
||||
Vorbis libvorbisfile libogg \
|
||||
Zstd
|
||||
ifndef USE_BUILTIN_LUA
|
||||
LOCAL_STATIC_LIBRARIES += LuaJIT
|
||||
endif
|
||||
LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
|
||||
|
||||
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
ifdef GPROF
|
||||
$(call import-module,android-ndk-profiler)
|
||||
endif
|
||||
$(call import-module,android/native_app_glue)
|
32
android/native/jni/Application.mk
Normal file
@ -0,0 +1,32 @@
|
||||
APP_PLATFORM := ${APP_PLATFORM}
|
||||
APP_ABI := ${TARGET_ABI}
|
||||
APP_STL := c++_shared
|
||||
NDK_TOOLCHAIN_VERSION := clang
|
||||
APP_SHORT_COMMANDS := true
|
||||
APP_MODULES := Minetest
|
||||
|
||||
APP_CPPFLAGS := -O2 -fvisibility=hidden
|
||||
|
||||
ifeq ($(APP_ABI),armeabi-v7a)
|
||||
APP_CPPFLAGS += -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
|
||||
endif
|
||||
|
||||
ifeq ($(APP_ABI),x86)
|
||||
APP_CPPFLAGS += -mssse3 -mfpmath=sse -funroll-loops
|
||||
endif
|
||||
|
||||
ifndef NDEBUG
|
||||
APP_CPPFLAGS := -g -Og -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-inconsistent-missing-override -Wno-parentheses-equality
|
||||
APP_CXXFLAGS := $(APP_CPPFLAGS) -fexceptions -frtti -std=gnu++14
|
||||
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections,--icf=safe
|
||||
|
||||
ifeq ($(APP_ABI),arm64-v8a)
|
||||
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections
|
||||
endif
|
||||
|
||||
ifndef NDEBUG
|
||||
APP_LDFLAGS :=
|
||||
endif
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class CopyZipTask extends AsyncTask<String, Void, String> {
|
||||
|
||||
private final WeakReference<AppCompatActivity> activityRef;
|
||||
|
||||
CopyZipTask(AppCompatActivity activity) {
|
||||
activityRef = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
protected String doInBackground(String... params) {
|
||||
copyAsset(params[0]);
|
||||
return params[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
startUnzipService(result);
|
||||
}
|
||||
|
||||
private void copyAsset(String zipName) {
|
||||
String filename = zipName.substring(zipName.lastIndexOf("/") + 1);
|
||||
try (InputStream in = activityRef.get().getAssets().open(filename);
|
||||
OutputStream out = new FileOutputStream(zipName)) {
|
||||
copyFile(in, out);
|
||||
} catch (IOException e) {
|
||||
AppCompatActivity activity = activityRef.get();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(() -> Toast.makeText(activityRef.get(), e.getLocalizedMessage(), Toast.LENGTH_LONG).show());
|
||||
}
|
||||
cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFile(InputStream in, OutputStream out) throws IOException {
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1)
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
private void startUnzipService(String file) {
|
||||
Intent intent = new Intent(activityRef.get(), UnzipService.class);
|
||||
intent.putExtra(UnzipService.EXTRA_KEY_IN_FILE, file);
|
||||
AppCompatActivity activity = activityRef.get();
|
||||
if (activity != null) {
|
||||
activity.startService(intent);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.app.NativeActivity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
public class GameActivity extends NativeActivity {
|
||||
static {
|
||||
System.loadLibrary("c++_shared");
|
||||
System.loadLibrary("Minetest");
|
||||
}
|
||||
|
||||
private int messageReturnCode;
|
||||
private String messageReturnValue;
|
||||
|
||||
public static native void putMessageBoxResult(String text);
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
messageReturnCode = -1;
|
||||
messageReturnValue = "";
|
||||
}
|
||||
|
||||
private void makeFullScreen() {
|
||||
if (Build.VERSION.SDK_INT >= 19)
|
||||
this.getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus)
|
||||
makeFullScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
makeFullScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Ignore the back press so Minetest can handle it
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == 101) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
String text = data.getStringExtra("text");
|
||||
messageReturnCode = 0;
|
||||
messageReturnValue = text;
|
||||
} else
|
||||
messageReturnCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void showDialog(String acceptButton, String hint, String current, int editType) {
|
||||
Intent intent = new Intent(this, InputDialogActivity.class);
|
||||
Bundle params = new Bundle();
|
||||
params.putString("acceptButton", acceptButton);
|
||||
params.putString("hint", hint);
|
||||
params.putString("current", current);
|
||||
params.putInt("editType", editType);
|
||||
intent.putExtras(params);
|
||||
startActivityForResult(intent, 101);
|
||||
messageReturnValue = "";
|
||||
messageReturnCode = -1;
|
||||
}
|
||||
|
||||
public int getDialogState() {
|
||||
return messageReturnCode;
|
||||
}
|
||||
|
||||
public String getDialogValue() {
|
||||
messageReturnCode = -1;
|
||||
return messageReturnValue;
|
||||
}
|
||||
|
||||
public float getDensity() {
|
||||
return getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
public int getDisplayHeight() {
|
||||
return getResources().getDisplayMetrics().heightPixels;
|
||||
}
|
||||
|
||||
public int getDisplayWidth() {
|
||||
return getResources().getDisplayMetrics().widthPixels;
|
||||
}
|
||||
|
||||
public void openURL(String url) {
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class InputDialogActivity extends AppCompatActivity {
|
||||
private AlertDialog alertDialog;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Bundle b = getIntent().getExtras();
|
||||
int editType = Objects.requireNonNull(b).getInt("editType");
|
||||
String hint = b.getString("hint");
|
||||
String current = b.getString("current");
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
EditText editText = new EditText(this);
|
||||
builder.setView(editText);
|
||||
editText.requestFocus();
|
||||
editText.setHint(hint);
|
||||
editText.setText(current);
|
||||
final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED,
|
||||
InputMethodManager.HIDE_IMPLICIT_ONLY);
|
||||
if (editType == 3)
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
else
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
editText.setOnKeyListener((view, KeyCode, event) -> {
|
||||
if (KeyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
pushResult(editText.getText().toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
alertDialog = builder.create();
|
||||
if (!this.isFinishing())
|
||||
alertDialog.show();
|
||||
alertDialog.setOnCancelListener(dialog -> {
|
||||
pushResult(editText.getText().toString());
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
alertDialog.dismiss();
|
||||
makeFullScreen();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private void pushResult(String text) {
|
||||
Intent resultData = new Intent();
|
||||
resultData.putExtra("text", text);
|
||||
setResult(AppCompatActivity.RESULT_OK, resultData);
|
||||
alertDialog.dismiss();
|
||||
makeFullScreen();
|
||||
finish();
|
||||
}
|
||||
|
||||
private void makeFullScreen() {
|
||||
if (Build.VERSION.SDK_INT >= 19)
|
||||
this.getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
|
||||
Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package net.minetest.minetest;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class UnzipService extends IntentService {
|
||||
public static final String ACTION_UPDATE = "net.minetest.minetest.UPDATE";
|
||||
public static final String ACTION_PROGRESS = "net.minetest.minetest.PROGRESS";
|
||||
public static final String ACTION_FAILURE = "net.minetest.minetest.FAILURE";
|
||||
public static final String EXTRA_KEY_IN_FILE = "file";
|
||||
public static final int SUCCESS = -1;
|
||||
public static final int FAILURE = -2;
|
||||
private final int id = 1;
|
||||
private NotificationManager mNotifyManager;
|
||||
private boolean isSuccess = true;
|
||||
private String failureMessage;
|
||||
|
||||
public UnzipService() {
|
||||
super("net.minetest.minetest.UnzipService");
|
||||
}
|
||||
|
||||
private void isDir(String dir, String location) {
|
||||
File f = new File(location, dir);
|
||||
if (!f.isDirectory())
|
||||
f.mkdirs();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
createNotification();
|
||||
unzip(intent);
|
||||
}
|
||||
|
||||
private void createNotification() {
|
||||
String name = "net.minetest.minetest";
|
||||
String channelId = "Minetest channel";
|
||||
String description = "notifications from Minetest";
|
||||
Notification.Builder builder;
|
||||
if (mNotifyManager == null)
|
||||
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||
NotificationChannel mChannel = null;
|
||||
if (mNotifyManager != null)
|
||||
mChannel = mNotifyManager.getNotificationChannel(channelId);
|
||||
if (mChannel == null) {
|
||||
mChannel = new NotificationChannel(channelId, name, importance);
|
||||
mChannel.setDescription(description);
|
||||
// Configure the notification channel, NO SOUND
|
||||
mChannel.setSound(null, null);
|
||||
mChannel.enableLights(false);
|
||||
mChannel.enableVibration(false);
|
||||
mNotifyManager.createNotificationChannel(mChannel);
|
||||
}
|
||||
builder = new Notification.Builder(this, channelId);
|
||||
} else {
|
||||
builder = new Notification.Builder(this);
|
||||
}
|
||||
builder.setContentTitle(getString(R.string.notification_title))
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentText(getString(R.string.notification_description));
|
||||
mNotifyManager.notify(id, builder.build());
|
||||
}
|
||||
|
||||
private void unzip(Intent intent) {
|
||||
String zip = intent.getStringExtra(EXTRA_KEY_IN_FILE);
|
||||
isDir("Minetest", Environment.getExternalStorageDirectory().toString());
|
||||
String location = Environment.getExternalStorageDirectory() + File.separator + "Minetest" + File.separator;
|
||||
int per = 0;
|
||||
int size = getSummarySize(zip);
|
||||
File zipFile = new File(zip);
|
||||
int readLen;
|
||||
byte[] readBuffer = new byte[8192];
|
||||
try (FileInputStream fileInputStream = new FileInputStream(zipFile);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
|
||||
ZipEntry ze;
|
||||
while ((ze = zipInputStream.getNextEntry()) != null) {
|
||||
if (ze.isDirectory()) {
|
||||
++per;
|
||||
isDir(ze.getName(), location);
|
||||
} else {
|
||||
publishProgress(100 * ++per / size);
|
||||
try (OutputStream outputStream = new FileOutputStream(location + ze.getName())) {
|
||||
while ((readLen = zipInputStream.read(readBuffer)) != -1) {
|
||||
outputStream.write(readBuffer, 0, readLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
zipFile.delete();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
isSuccess = false;
|
||||
failureMessage = e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void publishProgress(int progress) {
|
||||
Intent intentUpdate = new Intent(ACTION_UPDATE);
|
||||
intentUpdate.putExtra(ACTION_PROGRESS, progress);
|
||||
if (!isSuccess) intentUpdate.putExtra(ACTION_FAILURE, failureMessage);
|
||||
sendBroadcast(intentUpdate);
|
||||
}
|
||||
|
||||
private int getSummarySize(String zip) {
|
||||
int size = 0;
|
||||
try {
|
||||
ZipFile zipSize = new ZipFile(zip);
|
||||
size += zipSize.size();
|
||||
} catch (IOException e) {
|
||||
Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mNotifyManager.cancel(id);
|
||||
publishProgress(isSuccess ? SUCCESS : FAILURE);
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
#Mon Apr 06 00:06:16 CEST 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
|
@ -1,411 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 135.46666 135.46667"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="aux_btn.svg"
|
||||
inkscape:export-filename="/home/stu/Desktop/icons/png/aux_btn.png"
|
||||
inkscape:export-xdpi="24.000002"
|
||||
inkscape:export-ydpi="24.000002">
|
||||
<defs
|
||||
id="defs2">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Colorize"
|
||||
id="filter4628">
|
||||
<feComposite
|
||||
in2="SourceGraphic"
|
||||
operator="arithmetic"
|
||||
k1="0"
|
||||
k2="1"
|
||||
result="composite1"
|
||||
id="feComposite4614" />
|
||||
<feColorMatrix
|
||||
in="composite1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="colormatrix1"
|
||||
id="feColorMatrix4616" />
|
||||
<feFlood
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(158,0,0)"
|
||||
result="flood1"
|
||||
id="feFlood4618" />
|
||||
<feBlend
|
||||
in="flood1"
|
||||
in2="colormatrix1"
|
||||
mode="multiply"
|
||||
result="blend1"
|
||||
id="feBlend4620" />
|
||||
<feBlend
|
||||
in2="blend1"
|
||||
mode="screen"
|
||||
result="blend2"
|
||||
id="feBlend4622" />
|
||||
<feColorMatrix
|
||||
in="blend2"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="colormatrix2"
|
||||
id="feColorMatrix4624" />
|
||||
<feComposite
|
||||
in="colormatrix2"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
k2="1"
|
||||
result="composite2"
|
||||
id="feComposite4626" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Sharpen More"
|
||||
id="filter5109"
|
||||
inkscape:menu="Image Effects"
|
||||
inkscape:menu-tooltip="Sharpen edges and boundaries within the object, force=0.3">
|
||||
<feComposite
|
||||
in2="SourceGraphic"
|
||||
operator="arithmetic"
|
||||
k1="0"
|
||||
k2="1"
|
||||
result="composite1"
|
||||
id="feComposite5095" />
|
||||
<feColorMatrix
|
||||
in="composite1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="colormatrix1"
|
||||
id="feColorMatrix5097" />
|
||||
<feFlood
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(158,67,0)"
|
||||
result="flood1"
|
||||
id="feFlood5099" />
|
||||
<feBlend
|
||||
in="flood1"
|
||||
in2="colormatrix1"
|
||||
mode="multiply"
|
||||
result="blend1"
|
||||
id="feBlend5101" />
|
||||
<feBlend
|
||||
in2="blend1"
|
||||
mode="screen"
|
||||
result="blend2"
|
||||
id="feBlend5103" />
|
||||
<feColorMatrix
|
||||
in="blend2"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="colormatrix2"
|
||||
id="feColorMatrix5105" />
|
||||
<feComposite
|
||||
in="colormatrix2"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
k2="1"
|
||||
result="fbSourceGraphic"
|
||||
id="feComposite5107" />
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphicAlpha"
|
||||
in="fbSourceGraphic"
|
||||
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||
id="feColorMatrix5111" />
|
||||
<feComposite
|
||||
in2="fbSourceGraphic"
|
||||
id="feComposite5113"
|
||||
operator="arithmetic"
|
||||
k1="0"
|
||||
k2="1"
|
||||
result="composite1"
|
||||
in="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix5115"
|
||||
in="composite1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="colormatrix1" />
|
||||
<feFlood
|
||||
id="feFlood5117"
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(158,0,0)"
|
||||
result="flood1" />
|
||||
<feBlend
|
||||
in2="colormatrix1"
|
||||
id="feBlend5119"
|
||||
in="flood1"
|
||||
mode="multiply"
|
||||
result="blend1" />
|
||||
<feBlend
|
||||
in2="blend1"
|
||||
id="feBlend5121"
|
||||
mode="screen"
|
||||
result="blend2" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix5123"
|
||||
in="blend2"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="colormatrix2" />
|
||||
<feComposite
|
||||
in2="fbSourceGraphic"
|
||||
id="feComposite5125"
|
||||
in="colormatrix2"
|
||||
operator="in"
|
||||
k2="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphicAlpha"
|
||||
in="fbSourceGraphic"
|
||||
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||
id="feColorMatrix7007" />
|
||||
<feConvolveMatrix
|
||||
id="feConvolveMatrix7009"
|
||||
order="3 3"
|
||||
kernelMatrix="0 -0.15 0 -0.15 1.6 -0.15 0 -0.15 0"
|
||||
divisor="1"
|
||||
in="fbSourceGraphic"
|
||||
targetX="1"
|
||||
targetY="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphicAlpha"
|
||||
in="fbSourceGraphic"
|
||||
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||
id="feColorMatrix7011" />
|
||||
<feConvolveMatrix
|
||||
id="feConvolveMatrix7013"
|
||||
targetY="1"
|
||||
targetX="1"
|
||||
in="fbSourceGraphic"
|
||||
divisor="1"
|
||||
kernelMatrix="0 -0.3 0 -0.3 2.2 -0.3 0 -0.3 0"
|
||||
order="3 3"
|
||||
result="result1" />
|
||||
<feBlend
|
||||
in2="fbSourceGraphic"
|
||||
id="feBlend7015"
|
||||
mode="normal"
|
||||
result="result2" />
|
||||
</filter>
|
||||
<marker
|
||||
style="overflow:visible"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
orient="auto"
|
||||
id="DistanceX">
|
||||
<path
|
||||
id="path7410"
|
||||
style="stroke:#000000; stroke-width:0.5"
|
||||
d="M 3,-3 L -3,3 M 0,-5 L 0,5" />
|
||||
</marker>
|
||||
<pattern
|
||||
y="0"
|
||||
x="0"
|
||||
width="8"
|
||||
patternUnits="userSpaceOnUse"
|
||||
id="Hatch"
|
||||
height="8">
|
||||
<path
|
||||
id="path7413"
|
||||
stroke-width="0.25"
|
||||
stroke="#000000"
|
||||
linecap="square"
|
||||
d="M8 4 l-4,4" />
|
||||
<path
|
||||
id="path7415"
|
||||
stroke-width="0.25"
|
||||
stroke="#000000"
|
||||
linecap="square"
|
||||
d="M6 2 l-4,4" />
|
||||
<path
|
||||
id="path7417"
|
||||
stroke-width="0.25"
|
||||
stroke="#000000"
|
||||
linecap="square"
|
||||
d="M4 0 l-4,4" />
|
||||
</pattern>
|
||||
<symbol
|
||||
id="*Model_Space" />
|
||||
<symbol
|
||||
id="*Paper_Space" />
|
||||
<symbol
|
||||
id="*Paper_Space0" />
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Colorize"
|
||||
id="filter4883">
|
||||
<feComposite
|
||||
in2="SourceGraphic"
|
||||
operator="arithmetic"
|
||||
k1="0"
|
||||
k2="1"
|
||||
result="composite1"
|
||||
id="feComposite4869" />
|
||||
<feColorMatrix
|
||||
in="composite1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="colormatrix1"
|
||||
id="feColorMatrix4871" />
|
||||
<feFlood
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(158,21,0)"
|
||||
result="flood1"
|
||||
id="feFlood4873" />
|
||||
<feBlend
|
||||
in="flood1"
|
||||
in2="colormatrix1"
|
||||
mode="multiply"
|
||||
result="blend1"
|
||||
id="feBlend4875" />
|
||||
<feBlend
|
||||
in2="blend1"
|
||||
mode="screen"
|
||||
result="blend2"
|
||||
id="feBlend4877" />
|
||||
<feColorMatrix
|
||||
in="blend2"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="colormatrix2"
|
||||
id="feColorMatrix4879" />
|
||||
<feComposite
|
||||
in="colormatrix2"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
k2="1"
|
||||
result="composite2"
|
||||
id="feComposite4881" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#404040"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="-341.34157"
|
||||
inkscape:cy="210.02973"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer2"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1023"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="34"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:pagecheckerboard="false"
|
||||
inkscape:snap-grids="false"
|
||||
inkscape:snap-page="true"
|
||||
showguides="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-to-guides="true"
|
||||
inkscape:snap-object-midpoints="false"
|
||||
inkscape:snap-others="true"
|
||||
inkscape:snap-bbox-midpoints="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid16"
|
||||
spacingx="0.26458333"
|
||||
spacingy="0.26458333"
|
||||
empspacing="4"
|
||||
color="#40ff40"
|
||||
opacity="0.1254902"
|
||||
empcolor="#40ff40"
|
||||
empopacity="0.25098039" />
|
||||
</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" />
|
||||
<dc:title></dc:title>
|
||||
<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
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer 2"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d=""
|
||||
id="path7055"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d=""
|
||||
id="path7035"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d=""
|
||||
id="path7005"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d=""
|
||||
id="path5127"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:48.47851181px;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"
|
||||
x="67.78315"
|
||||
y="85.59491"
|
||||
id="text4716"
|
||||
transform="scale(1.0078883,0.99217343)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4714"
|
||||
x="67.78315"
|
||||
y="85.59491"
|
||||
style="fill:#d9d9d9;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">AUX</tspan></text>
|
||||
<flowRoot
|
||||
xml:space="preserve"
|
||||
id="flowRoot4718"
|
||||
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"
|
||||
transform="scale(0.26458333)"><flowRegion
|
||||
id="flowRegion4720"
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"><rect
|
||||
id="rect4722"
|
||||
width="157.5838"
|
||||
height="136.37059"
|
||||
x="264.65997"
|
||||
y="124.10143"
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" /></flowRegion><flowPara
|
||||
id="flowPara4724" /></flowRoot> </g>
|
||||
</svg>
|
Before Width: | Height: | Size: 12 KiB |
@ -1,59 +0,0 @@
|
||||
apply plugin: 'com.android.library'
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.3'
|
||||
ndkVersion '21.1.6352462'
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments '-j8',
|
||||
"versionMajor=${versionMajor}",
|
||||
"versionMinor=${versionMinor}",
|
||||
"versionPatch=${versionPatch}",
|
||||
"versionExtra=${versionExtra}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path file('jni/Android.mk')
|
||||
}
|
||||
}
|
||||
|
||||
// supported architectures
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'armeabi-v7a', 'arm64-v8a'//, 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
arguments 'NDEBUG=1'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task cloneGitRepo() {
|
||||
def destination = file('deps')
|
||||
if(!destination.exists()) {
|
||||
def grgit = Grgit.clone(
|
||||
dir: destination,
|
||||
uri: 'https://github.com/minetest/minetest_android_deps_binaries'
|
||||
)
|
||||
grgit.close()
|
||||
}
|
||||
}
|
||||
|
||||
preBuild.dependsOn cloneGitRepo
|
@ -1,219 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)/..
|
||||
|
||||
#LOCAL_ADDRESS_SANITIZER:=true
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Curl
|
||||
LOCAL_SRC_FILES := deps/Android/Curl/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcurl.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Freetype
|
||||
LOCAL_SRC_FILES := deps/Android/Freetype/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libfreetype.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Irrlicht
|
||||
LOCAL_SRC_FILES := deps/Android/Irrlicht/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libIrrlicht.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
#include $(CLEAR_VARS)
|
||||
#LOCAL_MODULE := LevelDB
|
||||
#LOCAL_SRC_FILES := deps/Android/LevelDB/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libleveldb.a
|
||||
#include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := LuaJIT
|
||||
LOCAL_SRC_FILES := deps/Android/LuaJIT/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libluajit.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedTLS
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedtls.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedx509
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedx509.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := mbedcrypto
|
||||
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedcrypto.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := OpenAL
|
||||
LOCAL_SRC_FILES := deps/Android/OpenAL-Soft/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libopenal.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
# You can use `OpenSSL and Crypto` instead `mbedTLS mbedx509 mbedcrypto`,
|
||||
#but it increase APK size on ~0.7MB
|
||||
#include $(CLEAR_VARS)
|
||||
#LOCAL_MODULE := OpenSSL
|
||||
#LOCAL_SRC_FILES := deps/Android/OpenSSL/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libssl.a
|
||||
#include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
#include $(CLEAR_VARS)
|
||||
#LOCAL_MODULE := Crypto
|
||||
#LOCAL_SRC_FILES := deps/Android/OpenSSL/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcrypto.a
|
||||
#include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Vorbis
|
||||
LOCAL_SRC_FILES := deps/Android/Vorbis/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libvorbis.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := Minetest
|
||||
|
||||
LOCAL_CFLAGS += \
|
||||
-DJSONCPP_NO_LOCALE_SUPPORT \
|
||||
-DHAVE_TOUCHSCREENGUI \
|
||||
-DENABLE_GLES=1 \
|
||||
-DUSE_CURL=1 \
|
||||
-DUSE_SOUND=1 \
|
||||
-DUSE_FREETYPE=1 \
|
||||
-DUSE_LEVELDB=0 \
|
||||
-DUSE_LUAJIT=1 \
|
||||
-DVERSION_MAJOR=${versionMajor} \
|
||||
-DVERSION_MINOR=${versionMinor} \
|
||||
-DVERSION_PATCH=${versionPatch} \
|
||||
-DVERSION_EXTRA=${versionExtra} \
|
||||
$(GPROF_DEF)
|
||||
|
||||
ifdef NDEBUG
|
||||
LOCAL_CFLAGS += -DNDEBUG=1
|
||||
endif
|
||||
|
||||
ifdef GPROF
|
||||
GPROF_DEF := -DGPROF
|
||||
PROFILER_LIBS := android-ndk-profiler
|
||||
LOCAL_CFLAGS += -pg
|
||||
endif
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
../../../src \
|
||||
../../../src/script \
|
||||
../../../lib/gmp \
|
||||
../../../lib/jsoncpp \
|
||||
deps/Android/Curl/include \
|
||||
deps/Android/Freetype/include \
|
||||
deps/Android/Irrlicht/include \
|
||||
deps/Android/LevelDB/include \
|
||||
deps/Android/libiconv/include \
|
||||
deps/Android/libiconv/libcharset/include \
|
||||
deps/Android/LuaJIT/src \
|
||||
deps/Android/OpenAL-Soft/include \
|
||||
deps/Android/sqlite \
|
||||
deps/Android/Vorbis/include
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(wildcard ../../../src/client/*.cpp) \
|
||||
$(wildcard ../../../src/client/*/*.cpp) \
|
||||
$(wildcard ../../../src/content/*.cpp) \
|
||||
../../../src/database/database.cpp \
|
||||
../../../src/database/database-dummy.cpp \
|
||||
../../../src/database/database-files.cpp \
|
||||
../../../src/database/database-sqlite3.cpp \
|
||||
$(wildcard ../../../src/gui/*.cpp) \
|
||||
$(wildcard ../../../src/irrlicht_changes/*.cpp) \
|
||||
$(wildcard ../../../src/mapgen/*.cpp) \
|
||||
$(wildcard ../../../src/network/*.cpp) \
|
||||
$(wildcard ../../../src/script/*.cpp) \
|
||||
$(wildcard ../../../src/script/*/*.cpp) \
|
||||
$(wildcard ../../../src/server/*.cpp) \
|
||||
$(wildcard ../../../src/threading/*.cpp) \
|
||||
$(wildcard ../../../src/util/*.c) \
|
||||
$(wildcard ../../../src/util/*.cpp) \
|
||||
../../../src/ban.cpp \
|
||||
../../../src/chat.cpp \
|
||||
../../../src/clientiface.cpp \
|
||||
../../../src/collision.cpp \
|
||||
../../../src/content_mapnode.cpp \
|
||||
../../../src/content_nodemeta.cpp \
|
||||
../../../src/convert_json.cpp \
|
||||
../../../src/craftdef.cpp \
|
||||
../../../src/debug.cpp \
|
||||
../../../src/defaultsettings.cpp \
|
||||
../../../src/emerge.cpp \
|
||||
../../../src/environment.cpp \
|
||||
../../../src/face_position_cache.cpp \
|
||||
../../../src/filesys.cpp \
|
||||
../../../src/gettext.cpp \
|
||||
../../../src/httpfetch.cpp \
|
||||
../../../src/hud.cpp \
|
||||
../../../src/inventory.cpp \
|
||||
../../../src/inventorymanager.cpp \
|
||||
../../../src/itemdef.cpp \
|
||||
../../../src/itemstackmetadata.cpp \
|
||||
../../../src/light.cpp \
|
||||
../../../src/log.cpp \
|
||||
../../../src/main.cpp \
|
||||
../../../src/map.cpp \
|
||||
../../../src/map_settings_manager.cpp \
|
||||
../../../src/mapblock.cpp \
|
||||
../../../src/mapnode.cpp \
|
||||
../../../src/mapsector.cpp \
|
||||
../../../src/metadata.cpp \
|
||||
../../../src/modchannels.cpp \
|
||||
../../../src/nameidmapping.cpp \
|
||||
../../../src/nodedef.cpp \
|
||||
../../../src/nodemetadata.cpp \
|
||||
../../../src/nodetimer.cpp \
|
||||
../../../src/noise.cpp \
|
||||
../../../src/objdef.cpp \
|
||||
../../../src/object_properties.cpp \
|
||||
../../../src/particles.cpp \
|
||||
../../../src/pathfinder.cpp \
|
||||
../../../src/player.cpp \
|
||||
../../../src/porting.cpp \
|
||||
../../../src/porting_android.cpp \
|
||||
../../../src/profiler.cpp \
|
||||
../../../src/raycast.cpp \
|
||||
../../../src/reflowscan.cpp \
|
||||
../../../src/remoteplayer.cpp \
|
||||
../../../src/rollback.cpp \
|
||||
../../../src/rollback_interface.cpp \
|
||||
../../../src/serialization.cpp \
|
||||
../../../src/server.cpp \
|
||||
../../../src/serverenvironment.cpp \
|
||||
../../../src/serverlist.cpp \
|
||||
../../../src/settings.cpp \
|
||||
../../../src/staticobject.cpp \
|
||||
../../../src/texture_override.cpp \
|
||||
../../../src/tileanimation.cpp \
|
||||
../../../src/tool.cpp \
|
||||
../../../src/translation.cpp \
|
||||
../../../src/version.cpp \
|
||||
../../../src/voxel.cpp \
|
||||
../../../src/voxelalgorithms.cpp
|
||||
|
||||
# LevelDB backend is disabled
|
||||
# ../../../src/database/database-leveldb.cpp
|
||||
|
||||
# GMP
|
||||
LOCAL_SRC_FILES += ../../../lib/gmp/mini-gmp.c
|
||||
|
||||
# JSONCPP
|
||||
LOCAL_SRC_FILES += ../../../lib/jsoncpp/jsoncpp.cpp
|
||||
|
||||
# iconv
|
||||
LOCAL_SRC_FILES += \
|
||||
deps/Android/libiconv/lib/iconv.c \
|
||||
deps/Android/libiconv/libcharset/lib/localcharset.c
|
||||
|
||||
# SQLite3
|
||||
LOCAL_SRC_FILES += deps/Android/sqlite/sqlite3.c
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += Curl Freetype Irrlicht OpenAL mbedTLS mbedx509 mbedcrypto Vorbis LuaJIT android_native_app_glue $(PROFILER_LIBS) #LevelDB
|
||||
#OpenSSL Crypto
|
||||
|
||||
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
ifdef GPROF
|
||||
$(call import-module,android-ndk-profiler)
|
||||
endif
|
||||
$(call import-module,android/native_app_glue)
|
@ -1,32 +0,0 @@
|
||||
APP_PLATFORM := ${APP_PLATFORM}
|
||||
APP_ABI := ${TARGET_ABI}
|
||||
APP_STL := c++_shared
|
||||
NDK_TOOLCHAIN_VERSION := clang
|
||||
APP_SHORT_COMMANDS := true
|
||||
APP_MODULES := Minetest
|
||||
|
||||
APP_CPPFLAGS := -Ofast -fvisibility=hidden -fexceptions -Wno-deprecated-declarations -Wno-extra-tokens
|
||||
|
||||
ifeq ($(APP_ABI),armeabi-v7a)
|
||||
APP_CPPFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb
|
||||
endif
|
||||
|
||||
#ifeq ($(APP_ABI),x86)
|
||||
#APP_CPPFLAGS += -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32 -funroll-loops
|
||||
#endif
|
||||
|
||||
ifndef NDEBUG
|
||||
APP_CPPFLAGS := -g -D_DEBUG -O0 -fno-omit-frame-pointer -fexceptions
|
||||
endif
|
||||
|
||||
APP_CFLAGS := $(APP_CPPFLAGS) -Wno-parentheses-equality #-Werror=shorten-64-to-32
|
||||
APP_CXXFLAGS := $(APP_CPPFLAGS) -frtti -std=gnu++17
|
||||
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections,--icf=safe
|
||||
|
||||
ifeq ($(APP_ABI),arm64-v8a)
|
||||
APP_LDFLAGS := -Wl,--no-warn-mismatch,--gc-sections
|
||||
endif
|
||||
|
||||
ifndef NDEBUG
|
||||
APP_LDFLAGS :=
|
||||
endif
|
59
builtin/async/game.lua
Normal file
@ -0,0 +1,59 @@
|
||||
core.log("info", "Initializing asynchronous environment (game)")
|
||||
|
||||
local function pack2(...)
|
||||
return {n=select('#', ...), ...}
|
||||
end
|
||||
|
||||
-- Entrypoint to run async jobs, called by C++
|
||||
function core.job_processor(func, params)
|
||||
local retval = pack2(func(unpack(params, 1, params.n)))
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
-- Import a bunch of individual files from builtin/game/
|
||||
local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM
|
||||
|
||||
dofile(gamepath .. "constants.lua")
|
||||
dofile(gamepath .. "item_s.lua")
|
||||
dofile(gamepath .. "misc_s.lua")
|
||||
dofile(gamepath .. "features.lua")
|
||||
dofile(gamepath .. "voxelarea.lua")
|
||||
|
||||
-- Transfer of globals
|
||||
do
|
||||
local all = assert(core.transferred_globals)
|
||||
core.transferred_globals = nil
|
||||
|
||||
all.registered_nodes = {}
|
||||
all.registered_craftitems = {}
|
||||
all.registered_tools = {}
|
||||
for k, v in pairs(all.registered_items) do
|
||||
-- Disable further modification
|
||||
setmetatable(v, {__newindex = {}})
|
||||
-- Reassemble the other tables
|
||||
if v.type == "node" then
|
||||
all.registered_nodes[k] = v
|
||||
elseif v.type == "craftitem" then
|
||||
all.registered_craftitems[k] = v
|
||||
elseif v.type == "tool" then
|
||||
all.registered_tools[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(all) do
|
||||
core[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- For tables that are indexed by item name:
|
||||
-- If table[X] does not exist, default to table[core.registered_aliases[X]]
|
||||
local alias_metatable = {
|
||||
__index = function(t, name)
|
||||
return rawget(t, core.registered_aliases[name])
|
||||
end
|
||||
}
|
||||
setmetatable(core.registered_items, alias_metatable)
|
||||
setmetatable(core.registered_nodes, alias_metatable)
|
||||
setmetatable(core.registered_craftitems, alias_metatable)
|
||||
setmetatable(core.registered_tools, alias_metatable)
|
@ -1,17 +0,0 @@
|
||||
|
||||
core.log("info", "Initializing Asynchronous environment")
|
||||
|
||||
function core.job_processor(serialized_func, serialized_param)
|
||||
local func = loadstring(serialized_func)
|
||||
local param = core.deserialize(serialized_param)
|
||||
local retval = nil
|
||||
|
||||
if type(func) == "function" then
|
||||
retval = core.serialize(func(param))
|
||||
else
|
||||
core.log("error", "ASYNC WORKER: Unable to deserialize function")
|
||||
end
|
||||
|
||||
return retval or core.serialize(nil)
|
||||
end
|
||||
|
9
builtin/async/mainmenu.lua
Normal file
@ -0,0 +1,9 @@
|
||||
core.log("info", "Initializing asynchronous environment")
|
||||
|
||||
function core.job_processor(func, serialized_param)
|
||||
local param = core.deserialize(serialized_param)
|
||||
|
||||
local retval = core.serialize(func(param))
|
||||
|
||||
return retval or core.serialize(nil)
|
||||
end
|
@ -1,6 +1,5 @@
|
||||
-- Minetest: builtin/client/chatcommands.lua
|
||||
|
||||
|
||||
core.register_on_sending_chat_message(function(message)
|
||||
if message:sub(1,2) == ".." then
|
||||
return false
|
||||
@ -8,7 +7,7 @@ core.register_on_sending_chat_message(function(message)
|
||||
|
||||
local first_char = message:sub(1,1)
|
||||
if first_char == "/" or first_char == "." then
|
||||
core.display_chat_message(core.gettext("issued command: ") .. message)
|
||||
core.display_chat_message(core.gettext("Issued command: ") .. message)
|
||||
end
|
||||
|
||||
if first_char ~= "." then
|
||||
@ -19,7 +18,12 @@ core.register_on_sending_chat_message(function(message)
|
||||
param = param or ""
|
||||
|
||||
if not cmd then
|
||||
core.display_chat_message(core.gettext("-!- Empty command"))
|
||||
core.display_chat_message("-!- " .. core.gettext("Empty command."))
|
||||
return true
|
||||
end
|
||||
|
||||
-- Run core.registered_on_chatcommand callbacks.
|
||||
if core.run_callbacks(core.registered_on_chatcommand, 5, cmd, param) then
|
||||
return true
|
||||
end
|
||||
|
||||
@ -31,7 +35,7 @@ core.register_on_sending_chat_message(function(message)
|
||||
core.display_chat_message(result)
|
||||
end
|
||||
else
|
||||
core.display_chat_message(core.gettext("-!- Invalid command: ") .. cmd)
|
||||
core.display_chat_message("-!- " .. core.gettext("Invalid command: ") .. cmd)
|
||||
end
|
||||
|
||||
return true
|
||||
@ -61,7 +65,7 @@ core.register_chatcommand("clear_chat_queue", {
|
||||
description = core.gettext("Clear the out chat queue"),
|
||||
func = function(param)
|
||||
core.clear_out_chat_queue()
|
||||
return true, core.gettext("The out chat queue is now empty")
|
||||
return true, core.gettext("The out chat queue is now empty.")
|
||||
end,
|
||||
})
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
-- handled by the engine.
|
||||
|
||||
core.register_on_death(function()
|
||||
core.display_chat_message("You died.")
|
||||
local formspec = "size[11,5.5]bgcolor[#320000b4;true]" ..
|
||||
"label[4.85,1.35;" .. fgettext("You died") ..
|
||||
"]button_exit[4,3;3,0.5;btn_respawn;".. fgettext("Respawn") .."]"
|
||||
|
@ -5,7 +5,8 @@ local commonpath = scriptpath.."common"..DIR_DELIM
|
||||
|
||||
dofile(clientpath .. "register.lua")
|
||||
dofile(commonpath .. "after.lua")
|
||||
dofile(commonpath .. "mod_storage.lua")
|
||||
dofile(commonpath .. "chatcommands.lua")
|
||||
dofile(clientpath .. "chatcommands.lua")
|
||||
dofile(commonpath .. "vector.lua")
|
||||
dofile(clientpath .. "death_formspec.lua")
|
||||
dofile(clientpath .. "misc.lua")
|
||||
|
7
builtin/client/misc.lua
Normal file
@ -0,0 +1,7 @@
|
||||
function core.setting_get_pos(name)
|
||||
local value = core.settings:get(name)
|
||||
if not value then
|
||||
return nil
|
||||
end
|
||||
return core.string_to_pos(value)
|
||||
end
|
@ -1,9 +1,15 @@
|
||||
|
||||
core.callback_origins = {}
|
||||
|
||||
local getinfo = debug.getinfo
|
||||
debug.getinfo = nil
|
||||
|
||||
--- Runs given callbacks.
|
||||
--
|
||||
-- Note: this function is also called from C++
|
||||
-- @tparam table callbacks a table with registered callbacks, like `core.registered_on_*`
|
||||
-- @tparam number mode a RunCallbacksMode, as defined in src/script/common/c_internal.h
|
||||
-- @param ... arguments for the callback
|
||||
-- @return depends on mode
|
||||
function core.run_callbacks(callbacks, mode, ...)
|
||||
assert(type(callbacks) == "table")
|
||||
local cb_len = #callbacks
|
||||
@ -63,6 +69,7 @@ core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration
|
||||
core.registered_on_shutdown, core.register_on_shutdown = make_registration()
|
||||
core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration()
|
||||
core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration()
|
||||
core.registered_on_chatcommand, core.register_on_chatcommand = make_registration()
|
||||
core.registered_on_death, core.register_on_death = make_registration()
|
||||
core.registered_on_hp_modification, core.register_on_hp_modification = make_registration()
|
||||
core.registered_on_damage_taken, core.register_on_damage_taken = make_registration()
|
||||
|
@ -37,7 +37,14 @@ function core.after(after, func, ...)
|
||||
arg = {...},
|
||||
mod_origin = core.get_last_run_mod(),
|
||||
}
|
||||
|
||||
jobs[#jobs + 1] = new_job
|
||||
time_next = math.min(time_next, expire)
|
||||
return { cancel = function() new_job.func = function() end end }
|
||||
|
||||
return {
|
||||
cancel = function()
|
||||
new_job.func = function() end
|
||||
new_job.args = {}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
@ -1,7 +1,47 @@
|
||||
-- Minetest: builtin/common/chatcommands.lua
|
||||
|
||||
-- For server-side translations (if INIT == "game")
|
||||
-- Otherwise, use core.gettext
|
||||
local S = core.get_translator("__builtin")
|
||||
|
||||
core.registered_chatcommands = {}
|
||||
|
||||
-- Interpret the parameters of a command, separating options and arguments.
|
||||
-- Input: command, param
|
||||
-- command: name of command
|
||||
-- param: parameters of command
|
||||
-- Returns: opts, args
|
||||
-- opts is a string of option letters, or false on error
|
||||
-- args is an array with the non-option arguments in order, or an error message
|
||||
-- Example: for this command line:
|
||||
-- /command a b -cd e f -g
|
||||
-- the function would receive:
|
||||
-- a b -cd e f -g
|
||||
-- and it would return:
|
||||
-- "cdg", {"a", "b", "e", "f"}
|
||||
-- Negative numbers are taken as arguments. Long options (--option) are
|
||||
-- currently rejected as reserved.
|
||||
local function getopts(command, param)
|
||||
local opts = ""
|
||||
local args = {}
|
||||
for match in param:gmatch("%S+") do
|
||||
if match:byte(1) == 45 then -- 45 = '-'
|
||||
local second = match:byte(2)
|
||||
if second == 45 then
|
||||
return false, S("Invalid parameters (see /help @1).", command)
|
||||
elseif second and (second < 48 or second > 57) then -- 48 = '0', 57 = '9'
|
||||
opts = opts .. match:sub(2)
|
||||
else
|
||||
-- numeric, add it to args
|
||||
args[#args + 1] = match
|
||||
end
|
||||
else
|
||||
args[#args + 1] = match
|
||||
end
|
||||
end
|
||||
return opts, args
|
||||
end
|
||||
|
||||
function core.register_chatcommand(cmd, def)
|
||||
def = def or {}
|
||||
def.params = def.params or ""
|
||||
@ -29,25 +69,8 @@ function core.override_chatcommand(name, redefinition)
|
||||
core.registered_chatcommands[name] = chatcommand
|
||||
end
|
||||
|
||||
local cmd_marker = "/"
|
||||
|
||||
local function gettext(...)
|
||||
return ...
|
||||
end
|
||||
|
||||
local function gettext_replace(text, replace)
|
||||
return text:gsub("$1", replace)
|
||||
end
|
||||
|
||||
|
||||
if INIT == "client" then
|
||||
cmd_marker = "."
|
||||
gettext = core.gettext
|
||||
gettext_replace = fgettext_ne
|
||||
end
|
||||
|
||||
local function do_help_cmd(name, param)
|
||||
local function format_help_line(cmd, def)
|
||||
local cmd_marker = INIT == "client" and "." or "/"
|
||||
local msg = core.colorize("#00ffff", cmd_marker .. cmd)
|
||||
if def.params and def.params ~= "" then
|
||||
msg = msg .. " " .. def.params
|
||||
@ -57,7 +80,19 @@ local function do_help_cmd(name, param)
|
||||
end
|
||||
return msg
|
||||
end
|
||||
if param == "" then
|
||||
|
||||
local function do_help_cmd(name, param)
|
||||
local opts, args = getopts("help", param)
|
||||
if not opts then
|
||||
return false, args
|
||||
end
|
||||
if #args > 1 then
|
||||
return false, S("Too many arguments, try using just /help <command>")
|
||||
end
|
||||
local use_gui = INIT ~= "client" and core.get_player_by_name(name)
|
||||
use_gui = use_gui and not opts:find("t")
|
||||
|
||||
if #args == 0 and not use_gui then
|
||||
local cmds = {}
|
||||
for cmd, def in pairs(core.registered_chatcommands) do
|
||||
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
||||
@ -65,10 +100,25 @@ local function do_help_cmd(name, param)
|
||||
end
|
||||
end
|
||||
table.sort(cmds)
|
||||
return true, gettext("Available commands: ") .. table.concat(cmds, " ") .. "\n"
|
||||
.. gettext_replace("Use '$1help <cmd>' to get more information,"
|
||||
.. " or '$1help all' to list everything.", cmd_marker)
|
||||
elseif param == "all" then
|
||||
local msg
|
||||
if INIT == "game" then
|
||||
msg = S("Available commands: @1",
|
||||
table.concat(cmds, " ")) .. "\n"
|
||||
.. S("Use '/help <cmd>' to get more "
|
||||
.. "information, or '/help all' to list "
|
||||
.. "everything.")
|
||||
else
|
||||
msg = core.gettext("Available commands: ")
|
||||
.. table.concat(cmds, " ") .. "\n"
|
||||
.. core.gettext("Use '.help <cmd>' to get more "
|
||||
.. "information, or '.help all' to list "
|
||||
.. "everything.")
|
||||
end
|
||||
return true, msg
|
||||
elseif #args == 0 or (args[1] == "all" and use_gui) then
|
||||
core.show_general_help_formspec(name)
|
||||
return true
|
||||
elseif args[1] == "all" then
|
||||
local cmds = {}
|
||||
for cmd, def in pairs(core.registered_chatcommands) do
|
||||
if INIT == "client" or core.check_player_privs(name, def.privs) then
|
||||
@ -76,19 +126,35 @@ local function do_help_cmd(name, param)
|
||||
end
|
||||
end
|
||||
table.sort(cmds)
|
||||
return true, gettext("Available commands:").."\n"..table.concat(cmds, "\n")
|
||||
elseif INIT == "game" and param == "privs" then
|
||||
local msg
|
||||
if INIT == "game" then
|
||||
msg = S("Available commands:")
|
||||
else
|
||||
msg = core.gettext("Available commands:")
|
||||
end
|
||||
return true, msg.."\n"..table.concat(cmds, "\n")
|
||||
elseif INIT == "game" and args[1] == "privs" then
|
||||
if use_gui then
|
||||
core.show_privs_help_formspec(name)
|
||||
return true
|
||||
end
|
||||
local privs = {}
|
||||
for priv, def in pairs(core.registered_privileges) do
|
||||
privs[#privs + 1] = priv .. ": " .. def.description
|
||||
end
|
||||
table.sort(privs)
|
||||
return true, "Available privileges:\n"..table.concat(privs, "\n")
|
||||
return true, S("Available privileges:").."\n"..table.concat(privs, "\n")
|
||||
else
|
||||
local cmd = param
|
||||
local cmd = args[1]
|
||||
local def = core.registered_chatcommands[cmd]
|
||||
if not def then
|
||||
return false, gettext("Command not available: ")..cmd
|
||||
local msg
|
||||
if INIT == "game" then
|
||||
msg = S("Command not available: @1", cmd)
|
||||
else
|
||||
msg = core.gettext("Command not available: ") .. cmd
|
||||
end
|
||||
return false, msg
|
||||
else
|
||||
return true, format_help_line(cmd, def)
|
||||
end
|
||||
@ -97,16 +163,16 @@ end
|
||||
|
||||
if INIT == "client" then
|
||||
core.register_chatcommand("help", {
|
||||
params = gettext("[all | <cmd>]"),
|
||||
description = gettext("Get help for commands"),
|
||||
params = core.gettext("[all | <cmd>]"),
|
||||
description = core.gettext("Get help for commands"),
|
||||
func = function(param)
|
||||
return do_help_cmd(nil, param)
|
||||
end,
|
||||
})
|
||||
else
|
||||
core.register_chatcommand("help", {
|
||||
params = "[all | privs | <cmd>]",
|
||||
description = "Get help for commands or list privileges",
|
||||
params = S("[all | privs | <cmd>] [-t]"),
|
||||
description = S("Get help for commands or list privileges (-t: output in chat)"),
|
||||
func = do_help_cmd,
|
||||
})
|
||||
end
|
||||
|
@ -20,8 +20,8 @@ local LIST_FORMSPEC_DESCRIPTION = [[
|
||||
button_exit[5,7;3,1;quit;%s]
|
||||
]]
|
||||
|
||||
local formspec_escape = core.formspec_escape
|
||||
local check_player_privs = core.check_player_privs
|
||||
local F = core.formspec_escape
|
||||
local S = core.get_translator("__builtin")
|
||||
|
||||
|
||||
-- CHAT COMMANDS FORMSPEC
|
||||
@ -51,22 +51,24 @@ core.after(0, load_mod_command_tree)
|
||||
|
||||
local function build_chatcommands_formspec(name, sel, copy)
|
||||
local rows = {}
|
||||
rows[1] = "#FFF,0,Command,Parameters"
|
||||
rows[1] = "#FFF,0,"..F(S("Command"))..","..F(S("Parameters"))
|
||||
|
||||
local description = "For more information, click on any entry in the list.\n" ..
|
||||
"Double-click to copy the entry to the chat history."
|
||||
local description = S("For more information, click on "
|
||||
.. "any entry in the list.").. "\n" ..
|
||||
S("Double-click to copy the entry to the chat history.")
|
||||
|
||||
local privs = core.get_player_privs(name)
|
||||
for i, data in ipairs(mod_cmds) do
|
||||
rows[#rows + 1] = COLOR_BLUE .. ",0," .. formspec_escape(data[1]) .. ","
|
||||
rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
|
||||
for j, cmds in ipairs(data[2]) do
|
||||
local has_priv = check_player_privs(name, cmds[2].privs)
|
||||
local has_priv = privs[cmds[2].privs]
|
||||
rows[#rows + 1] = ("%s,1,%s,%s"):format(
|
||||
has_priv and COLOR_GREEN or COLOR_GRAY,
|
||||
cmds[1], formspec_escape(cmds[2].params))
|
||||
cmds[1], F(cmds[2].params))
|
||||
if sel == #rows then
|
||||
description = cmds[2].description
|
||||
if copy then
|
||||
core.chat_send_player(name, ("Command: %s %s"):format(
|
||||
core.chat_send_player(name, S("Command: @1 @2",
|
||||
core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params))
|
||||
end
|
||||
end
|
||||
@ -74,9 +76,9 @@ local function build_chatcommands_formspec(name, sel, copy)
|
||||
end
|
||||
|
||||
return LIST_FORMSPEC_DESCRIPTION:format(
|
||||
"Available commands: (see also: /help <cmd>)",
|
||||
F(S("Available commands: (see also: /help <cmd>)")),
|
||||
table.concat(rows, ","), sel or 0,
|
||||
description, "Close"
|
||||
F(description), F(S("Close"))
|
||||
)
|
||||
end
|
||||
|
||||
@ -91,19 +93,19 @@ local function build_privs_formspec(name)
|
||||
table.sort(privs, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local rows = {}
|
||||
rows[1] = "#FFF,0,Privilege,Description"
|
||||
rows[1] = "#FFF,0,"..F(S("Privilege"))..","..F(S("Description"))
|
||||
|
||||
local player_privs = core.get_player_privs(name)
|
||||
for i, data in ipairs(privs) do
|
||||
rows[#rows + 1] = ("%s,0,%s,%s"):format(
|
||||
player_privs[data[1]] and COLOR_GREEN or COLOR_GRAY,
|
||||
data[1], formspec_escape(data[2].description))
|
||||
data[1], F(data[2].description))
|
||||
end
|
||||
|
||||
return LIST_FORMSPEC:format(
|
||||
"Available privileges:",
|
||||
F(S("Available privileges:")),
|
||||
table.concat(rows, ","),
|
||||
"Close"
|
||||
F(S("Close"))
|
||||
)
|
||||
end
|
||||
|
||||
@ -115,7 +117,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
|
||||
return
|
||||
end
|
||||
|
||||
local event = minetest.explode_table_event(fields.list)
|
||||
local event = core.explode_table_event(fields.list)
|
||||
if event.type ~= "INV" then
|
||||
local name = player:get_player_name()
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
@ -123,30 +125,12 @@ core.register_on_player_receive_fields(function(player, formname, fields)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
local help_command = core.registered_chatcommands["help"]
|
||||
local old_help_func = help_command.func
|
||||
|
||||
help_command.func = function(name, param)
|
||||
local admin = core.settings:get("name")
|
||||
|
||||
-- If the admin ran help, put the output in the chat buffer as well to
|
||||
-- work with the server terminal
|
||||
if param == "privs" then
|
||||
core.show_formspec(name, "__builtin:help_privs",
|
||||
build_privs_formspec(name))
|
||||
if name ~= admin then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if param == "" or param == "all" then
|
||||
function core.show_general_help_formspec(name)
|
||||
core.show_formspec(name, "__builtin:help_cmds",
|
||||
build_chatcommands_formspec(name))
|
||||
if name ~= admin then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return old_help_func(name, param)
|
||||
function core.show_privs_help_formspec(name)
|
||||
core.show_formspec(name, "__builtin:help_privs",
|
||||
build_privs_formspec(name))
|
||||
end
|
||||
|
||||
|
@ -204,19 +204,12 @@ end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function string:trim()
|
||||
return (self:gsub("^%s*(.-)%s*$", "%1"))
|
||||
return self:match("^%s*(.-)%s*$")
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function math.hypot(x, y)
|
||||
local t
|
||||
x = math.abs(x)
|
||||
y = math.abs(y)
|
||||
t = math.min(x, y)
|
||||
x = math.max(x, y)
|
||||
if x == 0 then return 0 end
|
||||
t = t / x
|
||||
return x * math.sqrt(1 + t * t)
|
||||
return math.sqrt(x * x + y * y)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -244,15 +237,24 @@ function math.factorial(x)
|
||||
return v
|
||||
end
|
||||
|
||||
function core.formspec_escape(text)
|
||||
if text ~= nil then
|
||||
text = string.gsub(text,"\\","\\\\")
|
||||
text = string.gsub(text,"%]","\\]")
|
||||
text = string.gsub(text,"%[","\\[")
|
||||
text = string.gsub(text,";","\\;")
|
||||
text = string.gsub(text,",","\\,")
|
||||
|
||||
function math.round(x)
|
||||
if x >= 0 then
|
||||
return math.floor(x + 0.5)
|
||||
end
|
||||
return text
|
||||
return math.ceil(x - 0.5)
|
||||
end
|
||||
|
||||
local formspec_escapes = {
|
||||
["\\"] = "\\\\",
|
||||
["["] = "\\[",
|
||||
["]"] = "\\]",
|
||||
[";"] = "\\;",
|
||||
[","] = "\\,"
|
||||
}
|
||||
function core.formspec_escape(text)
|
||||
-- Use explicit character set instead of dot here because it doubles the performance
|
||||
return text and string.gsub(text, "[\\%[%];,]", formspec_escapes)
|
||||
end
|
||||
|
||||
|
||||
@ -263,18 +265,21 @@ function core.wrap_text(text, max_length, as_table)
|
||||
return as_table and {text} or text
|
||||
end
|
||||
|
||||
for word in text:gmatch('%S+') do
|
||||
local cur_length = #table.concat(line, ' ')
|
||||
if cur_length > 0 and cur_length + #word + 1 >= max_length then
|
||||
local line_length = 0
|
||||
for word in text:gmatch("%S+") do
|
||||
if line_length > 0 and line_length + #word + 1 >= max_length then
|
||||
-- word wouldn't fit on current line, move to next line
|
||||
table.insert(result, table.concat(line, ' '))
|
||||
line = {}
|
||||
end
|
||||
table.insert(result, table.concat(line, " "))
|
||||
line = {word}
|
||||
line_length = #word
|
||||
else
|
||||
table.insert(line, word)
|
||||
line_length = line_length + 1 + #word
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(result, table.concat(line, ' '))
|
||||
return as_table and result or table.concat(result, '\n')
|
||||
table.insert(result, table.concat(line, " "))
|
||||
return as_table and result or table.concat(result, "\n")
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -423,56 +428,50 @@ function core.string_to_pos(value)
|
||||
return nil
|
||||
end
|
||||
|
||||
local p = {}
|
||||
p.x, p.y, p.z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
||||
if p.x and p.y and p.z then
|
||||
p.x = tonumber(p.x)
|
||||
p.y = tonumber(p.y)
|
||||
p.z = tonumber(p.z)
|
||||
return p
|
||||
end
|
||||
p = {}
|
||||
p.x, p.y, p.z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
|
||||
if p.x and p.y and p.z then
|
||||
p.x = tonumber(p.x)
|
||||
p.y = tonumber(p.y)
|
||||
p.z = tonumber(p.z)
|
||||
return p
|
||||
value = value:match("^%((.-)%)$") or value -- strip parentheses
|
||||
|
||||
local x, y, z = value:trim():match("^([%d.-]+)[,%s]%s*([%d.-]+)[,%s]%s*([%d.-]+)$")
|
||||
if x and y and z then
|
||||
x = tonumber(x)
|
||||
y = tonumber(y)
|
||||
z = tonumber(z)
|
||||
return vector.new(x, y, z)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function core.string_to_area(value)
|
||||
local p1, p2 = unpack(value:split(") ("))
|
||||
if p1 == nil or p2 == nil then
|
||||
return nil
|
||||
|
||||
do
|
||||
local rel_num_cap = "(~?-?%d*%.?%d*)" -- may be overly permissive as this will be tonumber'ed anyways
|
||||
local num_delim = "[,%s]%s*"
|
||||
local pattern = "^" .. table.concat({rel_num_cap, rel_num_cap, rel_num_cap}, num_delim) .. "$"
|
||||
|
||||
local function parse_area_string(pos, relative_to)
|
||||
local pp = {}
|
||||
pp.x, pp.y, pp.z = pos:trim():match(pattern)
|
||||
return core.parse_coordinates(pp.x, pp.y, pp.z, relative_to)
|
||||
end
|
||||
|
||||
p1 = core.string_to_pos(p1 .. ")")
|
||||
p2 = core.string_to_pos("(" .. p2)
|
||||
function core.string_to_area(value, relative_to)
|
||||
local p1, p2 = value:match("^%((.-)%)%s*%((.-)%)$")
|
||||
if not p1 then
|
||||
return
|
||||
end
|
||||
|
||||
p1 = parse_area_string(p1, relative_to)
|
||||
p2 = parse_area_string(p2, relative_to)
|
||||
|
||||
if p1 == nil or p2 == nil then
|
||||
return nil
|
||||
return
|
||||
end
|
||||
|
||||
return p1, p2
|
||||
end
|
||||
|
||||
local function test_string_to_area()
|
||||
local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2, 4, -12.53)")
|
||||
assert(p1.x == 10.0 and p1.y == 5 and p1.z == -2)
|
||||
assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
|
||||
|
||||
p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end
|
||||
|
||||
test_string_to_area()
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function table.copy(t, seen)
|
||||
local n = {}
|
||||
@ -532,7 +531,7 @@ if INIT == "mainmenu" then
|
||||
end
|
||||
end
|
||||
|
||||
if INIT == "client" or INIT == "mainmenu" then
|
||||
if core.gettext then -- for client and mainmenu
|
||||
function fgettext_ne(text, ...)
|
||||
text = core.gettext(text)
|
||||
local arg = {n=select('#', ...), ...}
|
||||
@ -697,3 +696,75 @@ function core.privs_to_string(privs, delim)
|
||||
end
|
||||
return table.concat(list, delim)
|
||||
end
|
||||
|
||||
function core.is_nan(number)
|
||||
return number ~= number
|
||||
end
|
||||
|
||||
--[[ Helper function for parsing an optionally relative number
|
||||
of a chat command parameter, using the chat command tilde notation.
|
||||
|
||||
Parameters:
|
||||
* arg: String snippet containing the number; possible values:
|
||||
* "<number>": return as number
|
||||
* "~<number>": return relative_to + <number>
|
||||
* "~": return relative_to
|
||||
* Anything else will return `nil`
|
||||
* relative_to: Number to which the `arg` number might be relative to
|
||||
|
||||
Returns:
|
||||
A number or `nil`, depending on `arg.
|
||||
|
||||
Examples:
|
||||
* `core.parse_relative_number("5", 10)` returns 5
|
||||
* `core.parse_relative_number("~5", 10)` returns 15
|
||||
* `core.parse_relative_number("~", 10)` returns 10
|
||||
]]
|
||||
function core.parse_relative_number(arg, relative_to)
|
||||
if not arg then
|
||||
return nil
|
||||
elseif arg == "~" then
|
||||
return relative_to
|
||||
elseif string.sub(arg, 1, 1) == "~" then
|
||||
local number = tonumber(string.sub(arg, 2))
|
||||
if not number then
|
||||
return nil
|
||||
end
|
||||
if core.is_nan(number) or number == math.huge or number == -math.huge then
|
||||
return nil
|
||||
end
|
||||
return relative_to + number
|
||||
else
|
||||
local number = tonumber(arg)
|
||||
if core.is_nan(number) or number == math.huge or number == -math.huge then
|
||||
return nil
|
||||
end
|
||||
return number
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Helper function to parse coordinates that might be relative
|
||||
to another position; supports chat command tilde notation.
|
||||
Intended to be used in chat command parameter parsing.
|
||||
|
||||
Parameters:
|
||||
* x, y, z: Parsed x, y, and z coordinates as strings
|
||||
* relative_to: Position to which to compare the position
|
||||
|
||||
Syntax of x, y and z:
|
||||
* "<number>": return as number
|
||||
* "~<number>": return <number> + player position on this axis
|
||||
* "~": return player position on this axis
|
||||
|
||||
Returns: a vector or nil for invalid input or if player does not exist
|
||||
]]
|
||||
function core.parse_coordinates(x, y, z, relative_to)
|
||||
if not relative_to then
|
||||
x, y, z = tonumber(x), tonumber(y), tonumber(z)
|
||||
return x and y and z and { x = x, y = y, z = z }
|
||||
end
|
||||
local rx = core.parse_relative_number(x, relative_to.x)
|
||||
local ry = core.parse_relative_number(y, relative_to.y)
|
||||
local rz = core.parse_relative_number(z, relative_to.z)
|
||||
return rx and ry and rz and { x = rx, y = ry, z = rz }
|
||||
end
|
||||
|
19
builtin/common/mod_storage.lua
Normal file
@ -0,0 +1,19 @@
|
||||
-- Modify core.get_mod_storage to return the storage for the current mod.
|
||||
|
||||
local get_current_modname = core.get_current_modname
|
||||
|
||||
local old_get_mod_storage = core.get_mod_storage
|
||||
|
||||
local storages = setmetatable({}, {
|
||||
__mode = "v", -- values are weak references (can be garbage-collected)
|
||||
__index = function(self, modname)
|
||||
local storage = old_get_mod_storage(modname)
|
||||
self[modname] = storage
|
||||
return storage
|
||||
end,
|
||||
})
|
||||
|
||||
function core.get_mod_storage()
|
||||
local modname = get_current_modname()
|
||||
return modname and storages[modname]
|
||||
end
|
@ -1,205 +1,224 @@
|
||||
--- Lua module to serialize values as Lua code.
|
||||
-- From: https://github.com/fab13n/metalua/blob/no-dll/src/lib/serialize.lua
|
||||
-- From: https://github.com/appgurueu/modlib/blob/master/luon.lua
|
||||
-- License: MIT
|
||||
-- @copyright 2006-2997 Fabien Fleutot <metalua@gmail.com>
|
||||
-- @author Fabien Fleutot <metalua@gmail.com>
|
||||
-- @author ShadowNinja <shadowninja@minetest.net>
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--- Serialize an object into a source code string. This string, when passed as
|
||||
-- an argument to deserialize(), returns an object structurally identical to
|
||||
-- the original one. The following are currently supported:
|
||||
-- * Booleans, numbers, strings, and nil.
|
||||
-- * Functions; uses interpreter-dependent (and sometimes platform-dependent) bytecode!
|
||||
-- * Tables; they can cantain multiple references and can be recursive, but metatables aren't saved.
|
||||
-- This works in two phases:
|
||||
-- 1. Recursively find and record multiple references and recursion.
|
||||
-- 2. Recursively dump the value into a string.
|
||||
-- @param x Value to serialize (nil is allowed).
|
||||
-- @return load()able string containing the value.
|
||||
function core.serialize(x)
|
||||
local local_index = 1 -- Top index of the "_" local table in the dump
|
||||
-- table->nil/1/2 set of tables seen.
|
||||
-- nil = not seen, 1 = seen once, 2 = seen multiple times.
|
||||
local seen = {}
|
||||
local next, rawget, pairs, pcall, error, type, setfenv, loadstring
|
||||
= next, rawget, pairs, pcall, error, type, setfenv, loadstring
|
||||
|
||||
-- nest_points are places where a table appears within itself, directly
|
||||
-- or not. For instance, all of these chunks create nest points in
|
||||
-- table x: "x = {}; x[x] = 1", "x = {}; x[1] = x",
|
||||
-- "x = {}; x[1] = {y = {x}}".
|
||||
-- To handle those, two tables are used by mark_nest_point:
|
||||
-- * nested - Transient set of tables being currently traversed.
|
||||
-- Used for detecting nested tables.
|
||||
-- * nest_points - parent->{key=value, ...} table cantaining the nested
|
||||
-- keys and values in the parent. They're all dumped after all the
|
||||
-- other table operations have been performed.
|
||||
--
|
||||
-- mark_nest_point(p, k, v) fills nest_points with information required
|
||||
-- to remember that key/value (k, v) creates a nest point in table
|
||||
-- parent. It also marks "parent" and the nested item(s) as occuring
|
||||
-- multiple times, since several references to it will be required in
|
||||
-- order to patch the nest points.
|
||||
local nest_points = {}
|
||||
local nested = {}
|
||||
local function mark_nest_point(parent, k, v)
|
||||
local nk, nv = nested[k], nested[v]
|
||||
local np = nest_points[parent]
|
||||
if not np then
|
||||
np = {}
|
||||
nest_points[parent] = np
|
||||
local table_concat, string_dump, string_format, string_match, math_huge
|
||||
= table.concat, string.dump, string.format, string.match, math.huge
|
||||
|
||||
-- Recursively counts occurences of objects (non-primitives including strings) in a table.
|
||||
local function count_objects(value)
|
||||
local counts = {}
|
||||
if value == nil then
|
||||
-- Early return for nil; tables can't contain nil
|
||||
return counts
|
||||
end
|
||||
np[k] = v
|
||||
seen[parent] = 2
|
||||
if nk then seen[k] = 2 end
|
||||
if nv then seen[v] = 2 end
|
||||
end
|
||||
|
||||
-- First phase, list the tables and functions which appear more than
|
||||
-- once in x.
|
||||
local function mark_multiple_occurences(x)
|
||||
local tp = type(x)
|
||||
if tp ~= "table" and tp ~= "function" then
|
||||
-- No identity (comparison is done by value, not by instance)
|
||||
local function count_values(val)
|
||||
local type_ = type(val)
|
||||
if type_ == "boolean" or type_ == "number" then
|
||||
return
|
||||
end
|
||||
if seen[x] == 1 then
|
||||
seen[x] = 2
|
||||
elseif seen[x] ~= 2 then
|
||||
seen[x] = 1
|
||||
local count = counts[val]
|
||||
counts[val] = (count or 0) + 1
|
||||
if type_ == "table" then
|
||||
if not count then
|
||||
for k, v in pairs(val) do
|
||||
count_values(k)
|
||||
count_values(v)
|
||||
end
|
||||
end
|
||||
elseif type_ ~= "string" and type_ ~= "function" then
|
||||
error("unsupported type: " .. type_)
|
||||
end
|
||||
end
|
||||
count_values(value)
|
||||
return counts
|
||||
end
|
||||
|
||||
if tp == "table" then
|
||||
nested[x] = true
|
||||
for k, v in pairs(x) do
|
||||
if nested[k] or nested[v] then
|
||||
mark_nest_point(x, k, v)
|
||||
-- Build a "set" of Lua keywords. These can't be used as short key names.
|
||||
-- See https://www.lua.org/manual/5.1/manual.html#2.1
|
||||
local keywords = {}
|
||||
for _, keyword in pairs({
|
||||
"and", "break", "do", "else", "elseif",
|
||||
"end", "false", "for", "function", "if",
|
||||
"in", "local", "nil", "not", "or",
|
||||
"repeat", "return", "then", "true", "until", "while",
|
||||
"goto" -- LuaJIT, Lua 5.2+
|
||||
}) do
|
||||
keywords[keyword] = true
|
||||
end
|
||||
|
||||
local function quote(string)
|
||||
return string_format("%q", string)
|
||||
end
|
||||
|
||||
local function dump_func(func)
|
||||
return string_format("loadstring(%q)", string_dump(func))
|
||||
end
|
||||
|
||||
-- Serializes Lua nil, booleans, numbers, strings, tables and even functions
|
||||
-- Tables are referenced by reference, strings are referenced by value. Supports circular tables.
|
||||
local function serialize(value, write)
|
||||
local reference, refnum = "r1", 1
|
||||
-- [object] = reference string
|
||||
local references = {}
|
||||
-- Circular tables that must be filled using `table[key] = value` statements
|
||||
local to_fill = {}
|
||||
for object, count in pairs(count_objects(value)) do
|
||||
local type_ = type(object)
|
||||
-- Object must appear more than once. If it is a string, the reference has to be shorter than the string.
|
||||
if count >= 2 and (type_ ~= "string" or #reference + 2 < #object) then
|
||||
write(reference)
|
||||
write("=")
|
||||
if type_ == "table" then
|
||||
write("{}")
|
||||
elseif type_ == "function" then
|
||||
write(dump_func(object))
|
||||
elseif type_ == "string" then
|
||||
write(quote(object))
|
||||
end
|
||||
write(";")
|
||||
references[object] = reference
|
||||
if type_ == "table" then
|
||||
to_fill[object] = reference
|
||||
end
|
||||
refnum = refnum + 1
|
||||
reference = ("r%X"):format(refnum)
|
||||
end
|
||||
end
|
||||
-- Used to decide whether we should do "key=..."
|
||||
local function use_short_key(key)
|
||||
return not references[key] and type(key) == "string" and (not keywords[key]) and string_match(key, "^[%a_][%a%d_]*$")
|
||||
end
|
||||
local function dump(value)
|
||||
-- Primitive types
|
||||
if value == nil then
|
||||
return write("nil")
|
||||
end
|
||||
if value == true then
|
||||
return write("true")
|
||||
end
|
||||
if value == false then
|
||||
return write("false")
|
||||
end
|
||||
local type_ = type(value)
|
||||
if type_ == "number" then
|
||||
return write(string_format("%.17g", value))
|
||||
end
|
||||
-- Reference types: table, function and string
|
||||
local ref = references[value]
|
||||
if ref then
|
||||
return write(ref)
|
||||
end
|
||||
if type_ == "string" then
|
||||
return write(quote(value))
|
||||
end
|
||||
if type_ == "function" then
|
||||
return write(dump_func(value))
|
||||
end
|
||||
if type_ == "table" then
|
||||
write("{")
|
||||
-- First write list keys:
|
||||
-- Don't use the table length #value here as it may horribly fail
|
||||
-- for tables which use large integers as keys in the hash part;
|
||||
-- stop at the first "hole" (nil value) instead
|
||||
local len = 0
|
||||
local first = true -- whether this is the first entry, which may not have a leading comma
|
||||
while true do
|
||||
local v = rawget(value, len + 1) -- use rawget to avoid metatables like the vector metatable
|
||||
if v == nil then break end
|
||||
if first then first = false else write(",") end
|
||||
dump(v)
|
||||
len = len + 1
|
||||
end
|
||||
-- Now write map keys ([key] = value)
|
||||
for k, v in next, value do
|
||||
-- We have written all non-float keys in [1, len] already
|
||||
if type(k) ~= "number" or k % 1 ~= 0 or k < 1 or k > len then
|
||||
if first then first = false else write(",") end
|
||||
if use_short_key(k) then
|
||||
write(k)
|
||||
else
|
||||
mark_multiple_occurences(k)
|
||||
mark_multiple_occurences(v)
|
||||
write("[")
|
||||
dump(k)
|
||||
write("]")
|
||||
end
|
||||
write("=")
|
||||
dump(v)
|
||||
end
|
||||
end
|
||||
nested[x] = nil
|
||||
write("}")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local dumped = {} -- object->varname set
|
||||
local local_defs = {} -- Dumped local definitions as source code lines
|
||||
|
||||
-- Mutually recursive local functions:
|
||||
local dump_val, dump_or_ref_val
|
||||
|
||||
-- If x occurs multiple times, dump the local variable rather than
|
||||
-- the value. If it's the first time it's dumped, also dump the
|
||||
-- content in local_defs.
|
||||
function dump_or_ref_val(x)
|
||||
if seen[x] ~= 2 then
|
||||
return dump_val(x)
|
||||
end
|
||||
local var = dumped[x]
|
||||
if var then -- Already referenced
|
||||
return var
|
||||
end
|
||||
-- First occurence, create and register reference
|
||||
local val = dump_val(x)
|
||||
local i = local_index
|
||||
local_index = local_index + 1
|
||||
var = "_["..i.."]"
|
||||
local_defs[#local_defs + 1] = var.." = "..val
|
||||
dumped[x] = var
|
||||
return var
|
||||
end
|
||||
|
||||
-- Second phase. Dump the object; subparts occuring multiple times
|
||||
-- are dumped in local variables which can be referenced multiple
|
||||
-- times. Care is taken to dump local vars in a sensible order.
|
||||
function dump_val(x)
|
||||
local tp = type(x)
|
||||
if x == nil then return "nil"
|
||||
elseif tp == "string" then return string.format("%q", x)
|
||||
elseif tp == "boolean" then return x and "true" or "false"
|
||||
elseif tp == "function" then
|
||||
return string.format("loadstring(%q)", string.dump(x))
|
||||
elseif tp == "number" then
|
||||
-- Serialize numbers reversibly with string.format
|
||||
return string.format("%.17g", x)
|
||||
elseif tp == "table" then
|
||||
local vals = {}
|
||||
local idx_dumped = {}
|
||||
local np = nest_points[x]
|
||||
for i, v in ipairs(x) do
|
||||
if not np or not np[i] then
|
||||
vals[#vals + 1] = dump_or_ref_val(v)
|
||||
end
|
||||
idx_dumped[i] = true
|
||||
end
|
||||
for k, v in pairs(x) do
|
||||
if (not np or not np[k]) and
|
||||
not idx_dumped[k] then
|
||||
vals[#vals + 1] = "["..dump_or_ref_val(k).."] = "
|
||||
..dump_or_ref_val(v)
|
||||
end
|
||||
end
|
||||
return "{"..table.concat(vals, ", ").."}"
|
||||
-- Write the statements to fill circular tables
|
||||
for table, ref in pairs(to_fill) do
|
||||
for k, v in pairs(table) do
|
||||
write(ref)
|
||||
if use_short_key(k) then
|
||||
write(".")
|
||||
write(k)
|
||||
else
|
||||
error("Can't serialize data of type "..tp)
|
||||
write("[")
|
||||
dump(k)
|
||||
write("]")
|
||||
end
|
||||
write("=")
|
||||
dump(v)
|
||||
write(";")
|
||||
end
|
||||
end
|
||||
write("return ")
|
||||
dump(value)
|
||||
end
|
||||
|
||||
local function dump_nest_points()
|
||||
for parent, vals in pairs(nest_points) do
|
||||
for k, v in pairs(vals) do
|
||||
local_defs[#local_defs + 1] = dump_or_ref_val(parent)
|
||||
.."["..dump_or_ref_val(k).."] = "
|
||||
..dump_or_ref_val(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mark_multiple_occurences(x)
|
||||
local top_level = dump_or_ref_val(x)
|
||||
dump_nest_points()
|
||||
|
||||
if next(local_defs) then
|
||||
return "local _ = {}\n"
|
||||
..table.concat(local_defs, "\n")
|
||||
.."\nreturn "..top_level
|
||||
else
|
||||
return "return "..top_level
|
||||
end
|
||||
end
|
||||
|
||||
-- Deserialization
|
||||
|
||||
local function safe_loadstring(...)
|
||||
local func, err = loadstring(...)
|
||||
if func then
|
||||
setfenv(func, {})
|
||||
return func
|
||||
end
|
||||
return nil, err
|
||||
function core.serialize(value)
|
||||
local rope = {}
|
||||
serialize(value, function(text)
|
||||
-- Faster than table.insert(rope, text) on PUC Lua 5.1
|
||||
rope[#rope + 1] = text
|
||||
end)
|
||||
return table_concat(rope)
|
||||
end
|
||||
|
||||
local function dummy_func() end
|
||||
|
||||
local nan = (0/0)^1 -- +nan
|
||||
|
||||
function core.deserialize(str, safe)
|
||||
if type(str) ~= "string" then
|
||||
return nil, "Cannot deserialize type '"..type(str)
|
||||
.."'. Argument must be a string."
|
||||
-- Backwards compatibility
|
||||
if str == nil then
|
||||
core.log("deprecated", "minetest.deserialize called with nil (expected string).")
|
||||
return nil, "Invalid type: Expected a string, got nil"
|
||||
end
|
||||
if str:byte(1) == 0x1B then
|
||||
return nil, "Bytecode prohibited"
|
||||
local t = type(str)
|
||||
if t ~= "string" then
|
||||
error(("minetest.deserialize called with %s (expected string)."):format(t))
|
||||
end
|
||||
local f, err = loadstring(str)
|
||||
if not f then return nil, err end
|
||||
|
||||
-- The environment is recreated every time so deseralized code cannot
|
||||
-- pollute it with permanent references.
|
||||
setfenv(f, {loadstring = safe and dummy_func or safe_loadstring})
|
||||
local func, err = loadstring(str)
|
||||
if not func then return nil, err end
|
||||
|
||||
local good, data = pcall(f)
|
||||
if good then
|
||||
return data
|
||||
-- math.huge is serialized to inf, NaNs are serialized to nan by Lua
|
||||
local env = {inf = math_huge, nan = nan}
|
||||
if safe then
|
||||
env.loadstring = dummy_func
|
||||
else
|
||||
return nil, data
|
||||
env.loadstring = function(str, ...)
|
||||
local func, err = loadstring(str, ...)
|
||||
if func then
|
||||
setfenv(func, env)
|
||||
return func
|
||||
end
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
setfenv(func, env)
|
||||
local success, value_or_err = pcall(func)
|
||||
if success then
|
||||
return value_or_err
|
||||
end
|
||||
return nil, value_or_err
|
||||
end
|
||||
|
@ -1,9 +1,4 @@
|
||||
|
||||
-- Always warn when creating a global variable, even outside of a function.
|
||||
-- This ignores mod namespaces (variables with the same name as the current mod).
|
||||
local WARN_INIT = false
|
||||
|
||||
local getinfo = debug.getinfo
|
||||
local getinfo, rawget, rawset = debug.getinfo, rawget, rawset
|
||||
|
||||
function core.global_exists(name)
|
||||
if type(name) ~= "string" then
|
||||
@ -19,39 +14,33 @@ local declared = {}
|
||||
local warned = {}
|
||||
|
||||
function meta:__newindex(name, value)
|
||||
if declared[name] then
|
||||
return
|
||||
end
|
||||
local info = getinfo(2, "Sl")
|
||||
local desc = ("%s:%d"):format(info.short_src, info.currentline)
|
||||
if not declared[name] then
|
||||
local warn_key = ("%s\0%d\0%s"):format(info.source,
|
||||
info.currentline, name)
|
||||
if not warned[warn_key] and info.what ~= "main" and
|
||||
info.what ~= "C" then
|
||||
core.log("warning", ("Assignment to undeclared "..
|
||||
"global %q inside a function at %s.")
|
||||
local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name)
|
||||
if not warned[warn_key] and info.what ~= "main" and info.what ~= "C" then
|
||||
core.log("warning", ("Assignment to undeclared global %q inside a function at %s.")
|
||||
:format(name, desc))
|
||||
warned[warn_key] = true
|
||||
end
|
||||
declared[name] = true
|
||||
end
|
||||
-- Ignore mod namespaces
|
||||
if WARN_INIT and name ~= core.get_current_modname() then
|
||||
core.log("warning", ("Global variable %q created at %s.")
|
||||
:format(name, desc))
|
||||
end
|
||||
rawset(self, name, value)
|
||||
declared[name] = true
|
||||
end
|
||||
|
||||
|
||||
function meta:__index(name)
|
||||
if declared[name] then
|
||||
return
|
||||
end
|
||||
local info = getinfo(2, "Sl")
|
||||
local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name)
|
||||
if not declared[name] and not warned[warn_key] and info.what ~= "C" then
|
||||
if not warned[warn_key] and info.what ~= "C" then
|
||||
core.log("warning", ("Undeclared global variable %q accessed at %s:%s")
|
||||
:format(name, info.short_src, info.currentline))
|
||||
warned[warn_key] = true
|
||||
end
|
||||
return rawget(self, name)
|
||||
end
|
||||
|
||||
setmetatable(_G, meta)
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
_G.core = {}
|
||||
_G.vector = {metatable = {}}
|
||||
dofile("builtin/common/vector.lua")
|
||||
dofile("builtin/common/misc_helpers.lua")
|
||||
|
||||
describe("string", function()
|
||||
@ -55,8 +57,8 @@ end)
|
||||
|
||||
describe("pos", function()
|
||||
it("from string", function()
|
||||
assert.same({ x = 10, y = 5.1, z = -2}, core.string_to_pos("10.0, 5.1, -2"))
|
||||
assert.same({ x = 10, y = 5.1, z = -2}, core.string_to_pos("( 10.0, 5.1, -2)"))
|
||||
assert.equal(vector.new(10, 5.1, -2), core.string_to_pos("10.0, 5.1, -2"))
|
||||
assert.equal(vector.new(10, 5.1, -2), core.string_to_pos("( 10.0, 5.1, -2)"))
|
||||
assert.is_nil(core.string_to_pos("asd, 5, -2)"))
|
||||
end)
|
||||
|
||||
@ -65,9 +67,107 @@ describe("pos", function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("area parsing", function()
|
||||
describe("valid inputs", function()
|
||||
it("accepts absolute numbers", function()
|
||||
local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2 4 -12.53)")
|
||||
assert(p1.x == 10 and p1.y == 5 and p1.z == -2)
|
||||
assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
|
||||
end)
|
||||
|
||||
it("accepts relative numbers", function()
|
||||
local p1, p2 = core.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})
|
||||
assert(type(p1) == "table" and type(p2) == "table")
|
||||
assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
|
||||
assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
|
||||
|
||||
p1, p2 = core.string_to_area("(1 2 3) (~5 ~-5 ~)", {x=10,y=10,z=10})
|
||||
assert(type(p1) == "table" and type(p2) == "table")
|
||||
assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
|
||||
assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
|
||||
end)
|
||||
end)
|
||||
describe("invalid inputs", function()
|
||||
it("rejects too few numbers", function()
|
||||
local p1, p2 = core.string_to_area("(1,1) (1,1,1,1)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
|
||||
it("rejects too many numbers", function()
|
||||
local p1, p2 = core.string_to_area("(1,1,1,1) (1,1,1,1)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
|
||||
it("rejects nan & inf", function()
|
||||
local p1, p2 = core.string_to_area("(1,1,1) (1,1,nan)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,1,~nan)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,~nan,1)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,1,inf)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,1,~inf)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(1,1,1) (1,~inf,1)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
|
||||
it("rejects words", function()
|
||||
local p1, p2 = core.string_to_area("bananas", {x=1,y=1,z=1})
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("bananas", "foobar")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("bananas")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(bananas,bananas,bananas)")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(bananas,bananas,bananas) (bananas,bananas,bananas)")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
|
||||
it("requires parenthesis & valid numbers", function()
|
||||
local p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
|
||||
p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
|
||||
assert(p1 == nil and p2 == nil)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("table", function()
|
||||
it("indexof()", function()
|
||||
assert.equal(1, table.indexof({"foo", "bar"}, "foo"))
|
||||
assert.equal(-1, table.indexof({"foo", "bar"}, "baz"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("formspec_escape", function()
|
||||
it("escapes", function()
|
||||
assert.equal(nil, core.formspec_escape(nil))
|
||||
assert.equal("", core.formspec_escape(""))
|
||||
assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\["))
|
||||
end)
|
||||
end)
|
||||
|
@ -1,41 +1,97 @@
|
||||
_G.core = {}
|
||||
_G.vector = {metatable = {}}
|
||||
|
||||
_G.setfenv = require 'busted.compatibility'.setfenv
|
||||
|
||||
dofile("builtin/common/serialize.lua")
|
||||
dofile("builtin/common/vector.lua")
|
||||
|
||||
-- Supports circular tables; does not support table keys
|
||||
-- Correctly checks whether a mapping of references ("same") exists
|
||||
-- Is significantly more efficient than assert.same
|
||||
local function assert_same(a, b, same)
|
||||
same = same or {}
|
||||
if same[a] or same[b] then
|
||||
assert(same[a] == b and same[b] == a)
|
||||
return
|
||||
end
|
||||
if a == b then
|
||||
return
|
||||
end
|
||||
if type(a) ~= "table" or type(b) ~= "table" then
|
||||
assert(a == b)
|
||||
return
|
||||
end
|
||||
same[a] = b
|
||||
same[b] = a
|
||||
local count = 0
|
||||
for k, v in pairs(a) do
|
||||
count = count + 1
|
||||
assert(type(k) ~= "table")
|
||||
assert_same(v, b[k], same)
|
||||
end
|
||||
for _ in pairs(b) do
|
||||
count = count - 1
|
||||
end
|
||||
assert(count == 0)
|
||||
end
|
||||
|
||||
local x, y = {}, {}
|
||||
local t1, t2 = {x, x, y, y}, {x, y, x, y}
|
||||
assert.same(t1, t2) -- will succeed because it only checks whether the depths match
|
||||
assert(not pcall(assert_same, t1, t2)) -- will correctly fail because it checks whether the refs match
|
||||
|
||||
describe("serialize", function()
|
||||
local function assert_preserves(value)
|
||||
local preserved_value = core.deserialize(core.serialize(value))
|
||||
assert_same(value, preserved_value)
|
||||
end
|
||||
it("works", function()
|
||||
local test_in = {cat={sound="nyan", speed=400}, dog={sound="woof"}}
|
||||
local test_out = core.deserialize(core.serialize(test_in))
|
||||
|
||||
assert.same(test_in, test_out)
|
||||
assert_preserves({cat={sound="nyan", speed=400}, dog={sound="woof"}})
|
||||
end)
|
||||
|
||||
it("handles characters", function()
|
||||
local test_in = {escape_chars="\n\r\t\v\\\"\'", non_european="θשׁ٩∂"}
|
||||
local test_out = core.deserialize(core.serialize(test_in))
|
||||
assert.same(test_in, test_out)
|
||||
assert_preserves({escape_chars="\n\r\t\v\\\"\'", non_european="θשׁ٩∂"})
|
||||
end)
|
||||
|
||||
it("handles NaN & infinities", function()
|
||||
local nan = core.deserialize(core.serialize(0/0))
|
||||
assert(nan ~= nan)
|
||||
assert_preserves(math.huge)
|
||||
assert_preserves(-math.huge)
|
||||
end)
|
||||
|
||||
it("handles precise numbers", function()
|
||||
local test_in = 0.2695949158945771
|
||||
local test_out = core.deserialize(core.serialize(test_in))
|
||||
assert.same(test_in, test_out)
|
||||
assert_preserves(0.2695949158945771)
|
||||
end)
|
||||
|
||||
it("handles big integers", function()
|
||||
local test_in = 269594915894577
|
||||
local test_out = core.deserialize(core.serialize(test_in))
|
||||
assert.same(test_in, test_out)
|
||||
assert_preserves(269594915894577)
|
||||
end)
|
||||
|
||||
it("handles recursive structures", function()
|
||||
local test_in = { hello = "world" }
|
||||
test_in.foo = test_in
|
||||
assert_preserves(test_in)
|
||||
end)
|
||||
|
||||
local test_out = core.deserialize(core.serialize(test_in))
|
||||
assert.same(test_in, test_out)
|
||||
it("handles cross-referencing structures", function()
|
||||
local test_in = {
|
||||
foo = {
|
||||
baz = {
|
||||
{}
|
||||
},
|
||||
},
|
||||
bar = {
|
||||
baz = {},
|
||||
},
|
||||
}
|
||||
|
||||
test_in.foo.baz[1].foo = test_in.foo
|
||||
test_in.foo.baz[1].bar = test_in.bar
|
||||
test_in.bar.baz[1] = test_in.foo.baz[1]
|
||||
|
||||
assert_preserves(test_in)
|
||||
end)
|
||||
|
||||
it("strips functions in safe mode", function()
|
||||
@ -45,6 +101,7 @@ describe("serialize", function()
|
||||
end,
|
||||
foo = "bar"
|
||||
}
|
||||
setfenv(test_in.func, _G)
|
||||
|
||||
local str = core.serialize(test_in)
|
||||
assert.not_nil(str:find("loadstring"))
|
||||
@ -53,4 +110,80 @@ describe("serialize", function()
|
||||
assert.is_nil(test_out.func)
|
||||
assert.equals(test_out.foo, "bar")
|
||||
end)
|
||||
|
||||
it("vectors work", function()
|
||||
local v = vector.new(1, 2, 3)
|
||||
assert_preserves({v})
|
||||
assert_preserves(v)
|
||||
|
||||
-- abuse
|
||||
v = vector.new(1, 2, 3)
|
||||
v.a = "bla"
|
||||
assert_preserves(v)
|
||||
end)
|
||||
|
||||
it("handles keywords as keys", function()
|
||||
assert_preserves({["and"] = "keyword", ["for"] = "keyword"})
|
||||
end)
|
||||
|
||||
describe("fuzzing", function()
|
||||
local atomics = {true, false, math.huge, -math.huge} -- no NaN or nil
|
||||
local function atomic()
|
||||
return atomics[math.random(1, #atomics)]
|
||||
end
|
||||
local function num()
|
||||
local sign = math.random() < 0.5 and -1 or 1
|
||||
-- HACK math.random(a, b) requires a, b & b - a to fit within a 32-bit int
|
||||
-- Use two random calls to generate a random number from 0 - 2^50 as lower & upper 25 bits
|
||||
local val = math.random(0, 2^25) * 2^25 + math.random(0, 2^25 - 1)
|
||||
local exp = math.random() < 0.5 and 1 or 2^(math.random(-120, 120))
|
||||
return sign * val * exp
|
||||
end
|
||||
local function charcodes(count)
|
||||
if count == 0 then return end
|
||||
return math.random(0, 0xFF), charcodes(count - 1)
|
||||
end
|
||||
local function str()
|
||||
return string.char(charcodes(math.random(0, 100)))
|
||||
end
|
||||
local primitives = {atomic, num, str}
|
||||
local function primitive()
|
||||
return primitives[math.random(1, #primitives)]()
|
||||
end
|
||||
local function tab(max_actions)
|
||||
local root = {}
|
||||
local tables = {root}
|
||||
local function random_table()
|
||||
return tables[math.random(1, #tables)]
|
||||
end
|
||||
for _ = 1, math.random(1, max_actions) do
|
||||
local tab = random_table()
|
||||
local value
|
||||
if math.random() < 0.5 then
|
||||
if math.random() < 0.5 then
|
||||
value = random_table()
|
||||
else
|
||||
value = {}
|
||||
table.insert(tables, value)
|
||||
end
|
||||
else
|
||||
value = primitive()
|
||||
end
|
||||
tab[math.random() < 0.5 and (#tab + 1) or primitive()] = value
|
||||
end
|
||||
return root
|
||||
end
|
||||
it("primitives work", function()
|
||||
for _ = 1, 1e3 do
|
||||
assert_preserves(primitive())
|
||||
end
|
||||
end)
|
||||
it("tables work", function()
|
||||
for _ = 1, 100 do
|
||||
local fuzzed_table = tab(1e3)
|
||||
assert_same(fuzzed_table, table.copy(fuzzed_table))
|
||||
assert_preserves(fuzzed_table)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
@ -1,4 +1,4 @@
|
||||
_G.vector = {}
|
||||
_G.vector = {metatable = {}}
|
||||
dofile("builtin/common/vector.lua")
|
||||
|
||||
describe("vector", function()
|
||||
@ -8,10 +8,16 @@ describe("vector", function()
|
||||
assert.same({x = 1, y = 2, z = 3}, vector.new(1, 2, 3))
|
||||
assert.same({x = 3, y = 2, z = 1}, vector.new({x = 3, y = 2, z = 1}))
|
||||
|
||||
assert.is_true(vector.check(vector.new()))
|
||||
assert.is_true(vector.check(vector.new(1, 2, 3)))
|
||||
assert.is_true(vector.check(vector.new({x = 3, y = 2, z = 1})))
|
||||
|
||||
local input = vector.new({ x = 3, y = 2, z = 1 })
|
||||
local output = vector.new(input)
|
||||
assert.same(input, output)
|
||||
assert.are_not.equal(input, output)
|
||||
assert.equal(input, output)
|
||||
assert.is_false(rawequal(input, output))
|
||||
assert.equal(input, input:new())
|
||||
end)
|
||||
|
||||
it("throws on invalid input", function()
|
||||
@ -25,7 +31,112 @@ describe("vector", function()
|
||||
end)
|
||||
end)
|
||||
|
||||
it("equal()", function()
|
||||
it("zero()", function()
|
||||
assert.same({x = 0, y = 0, z = 0}, vector.zero())
|
||||
assert.same(vector.new(), vector.zero())
|
||||
assert.equal(vector.new(), vector.zero())
|
||||
assert.is_true(vector.check(vector.zero()))
|
||||
end)
|
||||
|
||||
it("copy()", function()
|
||||
local v = vector.new(1, 2, 3)
|
||||
assert.same(v, vector.copy(v))
|
||||
assert.same(vector.new(v), vector.copy(v))
|
||||
assert.equal(vector.new(v), vector.copy(v))
|
||||
assert.is_true(vector.check(vector.copy(v)))
|
||||
end)
|
||||
|
||||
it("indexes", function()
|
||||
local some_vector = vector.new(24, 42, 13)
|
||||
assert.equal(24, some_vector[1])
|
||||
assert.equal(24, some_vector.x)
|
||||
assert.equal(42, some_vector[2])
|
||||
assert.equal(42, some_vector.y)
|
||||
assert.equal(13, some_vector[3])
|
||||
assert.equal(13, some_vector.z)
|
||||
|
||||
some_vector[1] = 100
|
||||
assert.equal(100, some_vector.x)
|
||||
some_vector.x = 101
|
||||
assert.equal(101, some_vector[1])
|
||||
|
||||
some_vector[2] = 100
|
||||
assert.equal(100, some_vector.y)
|
||||
some_vector.y = 102
|
||||
assert.equal(102, some_vector[2])
|
||||
|
||||
some_vector[3] = 100
|
||||
assert.equal(100, some_vector.z)
|
||||
some_vector.z = 103
|
||||
assert.equal(103, some_vector[3])
|
||||
end)
|
||||
|
||||
it("direction()", function()
|
||||
local a = vector.new(1, 0, 0)
|
||||
local b = vector.new(1, 42, 0)
|
||||
assert.equal(vector.new(0, 1, 0), vector.direction(a, b))
|
||||
assert.equal(vector.new(0, 1, 0), a:direction(b))
|
||||
end)
|
||||
|
||||
it("distance()", function()
|
||||
local a = vector.new(1, 0, 0)
|
||||
local b = vector.new(3, 42, 9)
|
||||
assert.is_true(math.abs(43 - vector.distance(a, b)) < 1.0e-12)
|
||||
assert.is_true(math.abs(43 - a:distance(b)) < 1.0e-12)
|
||||
assert.equal(0, vector.distance(a, a))
|
||||
assert.equal(0, b:distance(b))
|
||||
end)
|
||||
|
||||
it("length()", function()
|
||||
local a = vector.new(0, 0, -23)
|
||||
assert.equal(0, vector.length(vector.new()))
|
||||
assert.equal(23, vector.length(a))
|
||||
assert.equal(23, a:length())
|
||||
end)
|
||||
|
||||
it("normalize()", function()
|
||||
local a = vector.new(0, 0, -23)
|
||||
assert.equal(vector.new(0, 0, -1), vector.normalize(a))
|
||||
assert.equal(vector.new(0, 0, -1), a:normalize())
|
||||
assert.equal(vector.new(), vector.normalize(vector.new()))
|
||||
end)
|
||||
|
||||
it("floor()", function()
|
||||
local a = vector.new(0.1, 0.9, -0.5)
|
||||
assert.equal(vector.new(0, 0, -1), vector.floor(a))
|
||||
assert.equal(vector.new(0, 0, -1), a:floor())
|
||||
end)
|
||||
|
||||
it("round()", function()
|
||||
local a = vector.new(0.1, 0.9, -0.5)
|
||||
assert.equal(vector.new(0, 1, -1), vector.round(a))
|
||||
assert.equal(vector.new(0, 1, -1), a:round())
|
||||
end)
|
||||
|
||||
it("apply()", function()
|
||||
local i = 0
|
||||
local f = function(x)
|
||||
i = i + 1
|
||||
return x + i
|
||||
end
|
||||
local a = vector.new(0.1, 0.9, -0.5)
|
||||
assert.equal(vector.new(1, 1, 0), vector.apply(a, math.ceil))
|
||||
assert.equal(vector.new(1, 1, 0), a:apply(math.ceil))
|
||||
assert.equal(vector.new(0.1, 0.9, 0.5), vector.apply(a, math.abs))
|
||||
assert.equal(vector.new(0.1, 0.9, 0.5), a:apply(math.abs))
|
||||
assert.equal(vector.new(1.1, 2.9, 2.5), vector.apply(a, f))
|
||||
assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f))
|
||||
end)
|
||||
|
||||
it("combine()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(3, 2, 1)
|
||||
assert.equal(vector.add(a, b), vector.combine(a, b, function(x, y) return x + y end))
|
||||
assert.equal(vector.new(3, 2, 3), vector.combine(a, b, math.max))
|
||||
assert.equal(vector.new(1, 2, 1), vector.combine(a, b, math.min))
|
||||
end)
|
||||
|
||||
it("equals()", function()
|
||||
local function assertE(a, b)
|
||||
assert.is_true(vector.equals(a, b))
|
||||
end
|
||||
@ -35,17 +146,179 @@ describe("vector", function()
|
||||
|
||||
assertE({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
assertE({x = -1, y = 0, z = 1}, {x = -1, y = 0, z = 1})
|
||||
assertE({x = -1, y = 0, z = 1}, vector.new(-1, 0, 1))
|
||||
local a = {x = 2, y = 4, z = -10}
|
||||
assertE(a, a)
|
||||
assertNE({x = -1, y = 0, z = 1}, a)
|
||||
|
||||
assert.equal(vector.new(1, 2, 3), vector.new(1, 2, 3))
|
||||
assert.is_true(vector.new(1, 2, 3):equals(vector.new(1, 2, 3)))
|
||||
assert.not_equal(vector.new(1, 2, 3), vector.new(1, 2, 4))
|
||||
assert.is_true(vector.new(1, 2, 3) == vector.new(1, 2, 3))
|
||||
assert.is_false(vector.new(1, 2, 3) == vector.new(1, 3, 3))
|
||||
end)
|
||||
|
||||
it("add()", function()
|
||||
assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 }))
|
||||
it("metatable is same", function()
|
||||
local a = vector.new()
|
||||
local b = vector.new(1, 2, 3)
|
||||
|
||||
assert.equal(true, vector.check(a))
|
||||
assert.equal(true, vector.check(b))
|
||||
|
||||
assert.equal(vector.metatable, getmetatable(a))
|
||||
assert.equal(vector.metatable, getmetatable(b))
|
||||
assert.equal(vector.metatable, a.metatable)
|
||||
end)
|
||||
|
||||
it("sort()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(0.5, 232, -2)
|
||||
local sorted = {vector.new(0.5, 2, -2), vector.new(1, 232, 3)}
|
||||
assert.same(sorted, {vector.sort(a, b)})
|
||||
assert.same(sorted, {a:sort(b)})
|
||||
end)
|
||||
|
||||
it("angle()", function()
|
||||
assert.equal(math.pi, vector.angle(vector.new(-1, -2, -3), vector.new(1, 2, 3)))
|
||||
assert.equal(math.pi/2, vector.new(0, 1, 0):angle(vector.new(1, 0, 0)))
|
||||
end)
|
||||
|
||||
it("dot()", function()
|
||||
assert.equal(-14, vector.dot(vector.new(-1, -2, -3), vector.new(1, 2, 3)))
|
||||
assert.equal(0, vector.new():dot(vector.new(1, 2, 3)))
|
||||
end)
|
||||
|
||||
it("cross()", function()
|
||||
local a = vector.new(-1, -2, 0)
|
||||
local b = vector.new(1, 2, 3)
|
||||
assert.equal(vector.new(-6, 3, 0), vector.cross(a, b))
|
||||
assert.equal(vector.new(-6, 3, 0), a:cross(b))
|
||||
end)
|
||||
|
||||
it("offset()", function()
|
||||
assert.same({x = 41, y = 52, z = 63}, vector.offset(vector.new(1, 2, 3), 40, 50, 60))
|
||||
assert.equal(vector.new(41, 52, 63), vector.offset(vector.new(1, 2, 3), 40, 50, 60))
|
||||
assert.equal(vector.new(41, 52, 63), vector.new(1, 2, 3):offset(40, 50, 60))
|
||||
end)
|
||||
|
||||
it("is()", function()
|
||||
local some_table1 = {foo = 13, [42] = 1, "bar", 2}
|
||||
local some_table2 = {1, 2, 3}
|
||||
local some_table3 = {x = 1, 2, 3}
|
||||
local some_table4 = {1, 2, z = 3}
|
||||
local old = {x = 1, y = 2, z = 3}
|
||||
local real = vector.new(1, 2, 3)
|
||||
|
||||
assert.is_false(vector.check(nil))
|
||||
assert.is_false(vector.check(1))
|
||||
assert.is_false(vector.check(true))
|
||||
assert.is_false(vector.check("foo"))
|
||||
assert.is_false(vector.check(some_table1))
|
||||
assert.is_false(vector.check(some_table2))
|
||||
assert.is_false(vector.check(some_table3))
|
||||
assert.is_false(vector.check(some_table4))
|
||||
assert.is_false(vector.check(old))
|
||||
assert.is_true(vector.check(real))
|
||||
assert.is_true(real:check())
|
||||
end)
|
||||
|
||||
it("global pairs", function()
|
||||
local out = {}
|
||||
local vec = vector.new(10, 20, 30)
|
||||
for k, v in pairs(vec) do
|
||||
out[k] = v
|
||||
end
|
||||
assert.same({x = 10, y = 20, z = 30}, out)
|
||||
end)
|
||||
|
||||
it("abusing works", function()
|
||||
local v = vector.new(1, 2, 3)
|
||||
v.a = 1
|
||||
assert.equal(1, v.a)
|
||||
|
||||
local a_is_there = false
|
||||
for key, value in pairs(v) do
|
||||
if key == "a" then
|
||||
a_is_there = true
|
||||
assert.equal(value, 1)
|
||||
break
|
||||
end
|
||||
end
|
||||
assert.is_true(a_is_there)
|
||||
end)
|
||||
|
||||
it("add()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(1, 4, 3)
|
||||
local c = vector.new(2, 6, 6)
|
||||
assert.equal(c, vector.add(a, {x = 1, y = 4, z = 3}))
|
||||
assert.equal(c, vector.add(a, b))
|
||||
assert.equal(c, a:add(b))
|
||||
assert.equal(c, a + b)
|
||||
assert.equal(c, b + a)
|
||||
end)
|
||||
|
||||
it("subtract()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(2, 4, 3)
|
||||
local c = vector.new(-1, -2, 0)
|
||||
assert.equal(c, vector.subtract(a, {x = 2, y = 4, z = 3}))
|
||||
assert.equal(c, vector.subtract(a, b))
|
||||
assert.equal(c, a:subtract(b))
|
||||
assert.equal(c, a - b)
|
||||
assert.equal(c, -b + a)
|
||||
end)
|
||||
|
||||
it("multiply()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(2, 4, 3)
|
||||
local c = vector.new(2, 8, 9)
|
||||
local s = 2
|
||||
local d = vector.new(2, 4, 6)
|
||||
assert.equal(c, vector.multiply(a, {x = 2, y = 4, z = 3}))
|
||||
assert.equal(c, vector.multiply(a, b))
|
||||
assert.equal(d, vector.multiply(a, s))
|
||||
assert.equal(d, a:multiply(s))
|
||||
assert.equal(d, a * s)
|
||||
assert.equal(d, s * a)
|
||||
assert.equal(-a, -1 * a)
|
||||
end)
|
||||
|
||||
it("divide()", function()
|
||||
local a = vector.new(1, 2, 3)
|
||||
local b = vector.new(2, 4, 3)
|
||||
local c = vector.new(0.5, 0.5, 1)
|
||||
local s = 2
|
||||
local d = vector.new(0.5, 1, 1.5)
|
||||
assert.equal(c, vector.divide(a, {x = 2, y = 4, z = 3}))
|
||||
assert.equal(c, vector.divide(a, b))
|
||||
assert.equal(d, vector.divide(a, s))
|
||||
assert.equal(d, a:divide(s))
|
||||
assert.equal(d, a / s)
|
||||
assert.equal(d, 1/s * a)
|
||||
assert.equal(-a, a / -1)
|
||||
end)
|
||||
|
||||
it("to_string()", function()
|
||||
local v = vector.new(1, 2, 3.14)
|
||||
assert.same("(1, 2, 3.14)", vector.to_string(v))
|
||||
assert.same("(1, 2, 3.14)", v:to_string())
|
||||
assert.same("(1, 2, 3.14)", tostring(v))
|
||||
end)
|
||||
|
||||
it("from_string()", function()
|
||||
local v = vector.new(1, 2, 3.14)
|
||||
assert.is_true(vector.check(vector.from_string("(1, 2, 3.14)")))
|
||||
assert.same({v, 13}, {vector.from_string("(1, 2, 3.14)")})
|
||||
assert.same({v, 12}, {vector.from_string("(1,2 ,3.14)")})
|
||||
assert.same({v, 12}, {vector.from_string("(1,2,3.14,)")})
|
||||
assert.same({v, 11}, {vector.from_string("(1 2 3.14)")})
|
||||
assert.same({v, 15}, {vector.from_string("( 1, 2, 3.14 )")})
|
||||
assert.same({v, 15}, {vector.from_string(" ( 1, 2, 3.14) ")})
|
||||
assert.same({vector.new(), 8}, {vector.from_string("(0,0,0) ( 1, 2, 3.14) ")})
|
||||
assert.same({v, 22}, {vector.from_string("(0,0,0) ( 1, 2, 3.14) ", 8)})
|
||||
assert.same({v, 22}, {vector.from_string("(0,0,0) ( 1, 2, 3.14) ", 9)})
|
||||
assert.same(nil, vector.from_string("nothing"))
|
||||
end)
|
||||
|
||||
-- This function is needed because of floating point imprecision.
|
||||
|
@ -1,73 +1,132 @@
|
||||
--[[
|
||||
Vector helpers
|
||||
Note: The vector.*-functions must be able to accept old vectors that had no metatables
|
||||
]]
|
||||
|
||||
vector = {}
|
||||
-- localize functions
|
||||
local setmetatable = setmetatable
|
||||
|
||||
-- vector.metatable is set by C++.
|
||||
local metatable = vector.metatable
|
||||
|
||||
local xyz = {"x", "y", "z"}
|
||||
|
||||
-- only called when rawget(v, key) returns nil
|
||||
function metatable.__index(v, key)
|
||||
return rawget(v, xyz[key]) or vector[key]
|
||||
end
|
||||
|
||||
-- only called when rawget(v, key) returns nil
|
||||
function metatable.__newindex(v, key, value)
|
||||
rawset(v, xyz[key] or key, value)
|
||||
end
|
||||
|
||||
-- constructors
|
||||
|
||||
local function fast_new(x, y, z)
|
||||
return setmetatable({x = x, y = y, z = z}, metatable)
|
||||
end
|
||||
|
||||
function vector.new(a, b, c)
|
||||
if a and b and c then
|
||||
return fast_new(a, b, c)
|
||||
end
|
||||
|
||||
-- deprecated, use vector.copy and vector.zero directly
|
||||
if type(a) == "table" then
|
||||
assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()")
|
||||
return {x=a.x, y=a.y, z=a.z}
|
||||
elseif a then
|
||||
assert(b and c, "Invalid arguments for vector.new()")
|
||||
return {x=a, y=b, z=c}
|
||||
return vector.copy(a)
|
||||
else
|
||||
assert(not a, "Invalid arguments for vector.new()")
|
||||
return vector.zero()
|
||||
end
|
||||
return {x=0, y=0, z=0}
|
||||
end
|
||||
|
||||
function vector.zero()
|
||||
return fast_new(0, 0, 0)
|
||||
end
|
||||
|
||||
function vector.copy(v)
|
||||
assert(v.x and v.y and v.z, "Invalid vector passed to vector.copy()")
|
||||
return fast_new(v.x, v.y, v.z)
|
||||
end
|
||||
|
||||
function vector.from_string(s, init)
|
||||
local x, y, z, np = string.match(s, "^%s*%(%s*([^%s,]+)%s*[,%s]%s*([^%s,]+)%s*[,%s]" ..
|
||||
"%s*([^%s,]+)%s*[,%s]?%s*%)()", init)
|
||||
x = tonumber(x)
|
||||
y = tonumber(y)
|
||||
z = tonumber(z)
|
||||
if not (x and y and z) then
|
||||
return nil
|
||||
end
|
||||
return fast_new(x, y, z), np
|
||||
end
|
||||
|
||||
function vector.to_string(v)
|
||||
return string.format("(%g, %g, %g)", v.x, v.y, v.z)
|
||||
end
|
||||
metatable.__tostring = vector.to_string
|
||||
|
||||
function vector.equals(a, b)
|
||||
return a.x == b.x and
|
||||
a.y == b.y and
|
||||
a.z == b.z
|
||||
end
|
||||
metatable.__eq = vector.equals
|
||||
|
||||
-- unary operations
|
||||
|
||||
function vector.length(v)
|
||||
return math.hypot(v.x, math.hypot(v.y, v.z))
|
||||
return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
|
||||
end
|
||||
-- Note: we can not use __len because it is already used for primitive table length
|
||||
|
||||
function vector.normalize(v)
|
||||
local len = vector.length(v)
|
||||
if len == 0 then
|
||||
return {x=0, y=0, z=0}
|
||||
return fast_new(0, 0, 0)
|
||||
else
|
||||
return vector.divide(v, len)
|
||||
end
|
||||
end
|
||||
|
||||
function vector.floor(v)
|
||||
return {
|
||||
x = math.floor(v.x),
|
||||
y = math.floor(v.y),
|
||||
z = math.floor(v.z)
|
||||
}
|
||||
return vector.apply(v, math.floor)
|
||||
end
|
||||
|
||||
function vector.round(v)
|
||||
return {
|
||||
x = math.floor(v.x + 0.5),
|
||||
y = math.floor(v.y + 0.5),
|
||||
z = math.floor(v.z + 0.5)
|
||||
}
|
||||
return fast_new(
|
||||
math.round(v.x),
|
||||
math.round(v.y),
|
||||
math.round(v.z)
|
||||
)
|
||||
end
|
||||
|
||||
function vector.apply(v, func)
|
||||
return {
|
||||
x = func(v.x),
|
||||
y = func(v.y),
|
||||
z = func(v.z)
|
||||
}
|
||||
return fast_new(
|
||||
func(v.x),
|
||||
func(v.y),
|
||||
func(v.z)
|
||||
)
|
||||
end
|
||||
|
||||
function vector.combine(a, b, func)
|
||||
return fast_new(
|
||||
func(a.x, b.x),
|
||||
func(a.y, b.y),
|
||||
func(a.z, b.z)
|
||||
)
|
||||
end
|
||||
|
||||
function vector.distance(a, b)
|
||||
local x = a.x - b.x
|
||||
local y = a.y - b.y
|
||||
local z = a.z - b.z
|
||||
return math.hypot(x, math.hypot(y, z))
|
||||
return math.sqrt(x * x + y * y + z * z)
|
||||
end
|
||||
|
||||
function vector.direction(pos1, pos2)
|
||||
return vector.normalize({
|
||||
x = pos2.x - pos1.x,
|
||||
y = pos2.y - pos1.y,
|
||||
z = pos2.z - pos1.z
|
||||
})
|
||||
return vector.subtract(pos2, pos1):normalize()
|
||||
end
|
||||
|
||||
function vector.angle(a, b)
|
||||
@ -82,70 +141,137 @@ function vector.dot(a, b)
|
||||
end
|
||||
|
||||
function vector.cross(a, b)
|
||||
return {
|
||||
x = a.y * b.z - a.z * b.y,
|
||||
y = a.z * b.x - a.x * b.z,
|
||||
z = a.x * b.y - a.y * b.x
|
||||
}
|
||||
return fast_new(
|
||||
a.y * b.z - a.z * b.y,
|
||||
a.z * b.x - a.x * b.z,
|
||||
a.x * b.y - a.y * b.x
|
||||
)
|
||||
end
|
||||
|
||||
function metatable.__unm(v)
|
||||
return fast_new(-v.x, -v.y, -v.z)
|
||||
end
|
||||
|
||||
-- add, sub, mul, div operations
|
||||
|
||||
function vector.add(a, b)
|
||||
if type(b) == "table" then
|
||||
return {x = a.x + b.x,
|
||||
y = a.y + b.y,
|
||||
z = a.z + b.z}
|
||||
return fast_new(
|
||||
a.x + b.x,
|
||||
a.y + b.y,
|
||||
a.z + b.z
|
||||
)
|
||||
else
|
||||
return {x = a.x + b,
|
||||
y = a.y + b,
|
||||
z = a.z + b}
|
||||
return fast_new(
|
||||
a.x + b,
|
||||
a.y + b,
|
||||
a.z + b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__add(a, b)
|
||||
return fast_new(
|
||||
a.x + b.x,
|
||||
a.y + b.y,
|
||||
a.z + b.z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.subtract(a, b)
|
||||
if type(b) == "table" then
|
||||
return {x = a.x - b.x,
|
||||
y = a.y - b.y,
|
||||
z = a.z - b.z}
|
||||
return fast_new(
|
||||
a.x - b.x,
|
||||
a.y - b.y,
|
||||
a.z - b.z
|
||||
)
|
||||
else
|
||||
return {x = a.x - b,
|
||||
y = a.y - b,
|
||||
z = a.z - b}
|
||||
return fast_new(
|
||||
a.x - b,
|
||||
a.y - b,
|
||||
a.z - b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__sub(a, b)
|
||||
return fast_new(
|
||||
a.x - b.x,
|
||||
a.y - b.y,
|
||||
a.z - b.z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.multiply(a, b)
|
||||
if type(b) == "table" then
|
||||
return {x = a.x * b.x,
|
||||
y = a.y * b.y,
|
||||
z = a.z * b.z}
|
||||
return fast_new(
|
||||
a.x * b.x,
|
||||
a.y * b.y,
|
||||
a.z * b.z
|
||||
)
|
||||
else
|
||||
return {x = a.x * b,
|
||||
y = a.y * b,
|
||||
z = a.z * b}
|
||||
return fast_new(
|
||||
a.x * b,
|
||||
a.y * b,
|
||||
a.z * b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__mul(a, b)
|
||||
if type(a) == "table" then
|
||||
return fast_new(
|
||||
a.x * b,
|
||||
a.y * b,
|
||||
a.z * b
|
||||
)
|
||||
else
|
||||
return fast_new(
|
||||
a * b.x,
|
||||
a * b.y,
|
||||
a * b.z
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function vector.divide(a, b)
|
||||
if type(b) == "table" then
|
||||
return {x = a.x / b.x,
|
||||
y = a.y / b.y,
|
||||
z = a.z / b.z}
|
||||
return fast_new(
|
||||
a.x / b.x,
|
||||
a.y / b.y,
|
||||
a.z / b.z
|
||||
)
|
||||
else
|
||||
return {x = a.x / b,
|
||||
y = a.y / b,
|
||||
z = a.z / b}
|
||||
return fast_new(
|
||||
a.x / b,
|
||||
a.y / b,
|
||||
a.z / b
|
||||
)
|
||||
end
|
||||
end
|
||||
function metatable.__div(a, b)
|
||||
-- scalar/vector makes no sense
|
||||
return fast_new(
|
||||
a.x / b,
|
||||
a.y / b,
|
||||
a.z / b
|
||||
)
|
||||
end
|
||||
|
||||
-- misc stuff
|
||||
|
||||
function vector.offset(v, x, y, z)
|
||||
return {x = v.x + x,
|
||||
y = v.y + y,
|
||||
z = v.z + z}
|
||||
return fast_new(
|
||||
v.x + x,
|
||||
v.y + y,
|
||||
v.z + z
|
||||
)
|
||||
end
|
||||
|
||||
function vector.sort(a, b)
|
||||
return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)},
|
||||
{x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)}
|
||||
return fast_new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z)),
|
||||
fast_new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z))
|
||||
end
|
||||
|
||||
function vector.check(v)
|
||||
return getmetatable(v) == metatable
|
||||
end
|
||||
|
||||
local function sin(x)
|
||||
@ -213,7 +339,7 @@ end
|
||||
|
||||
function vector.dir_to_rotation(forward, up)
|
||||
forward = vector.normalize(forward)
|
||||
local rot = {x = math.asin(forward.y), y = -math.atan2(forward.x, forward.z), z = 0}
|
||||
local rot = vector.new(math.asin(forward.y), -math.atan2(forward.x, forward.z), 0)
|
||||
if not up then
|
||||
return rot
|
||||
end
|
||||
@ -221,7 +347,7 @@ function vector.dir_to_rotation(forward, up)
|
||||
"Invalid vectors passed to vector.dir_to_rotation().")
|
||||
up = vector.normalize(up)
|
||||
-- Calculate vector pointing up with roll = 0, just based on forward vector.
|
||||
local forwup = vector.rotate({x = 0, y = 1, z = 0}, rot)
|
||||
local forwup = vector.rotate(vector.new(0, 1, 0), rot)
|
||||
-- 'forwup' and 'up' are now in a plane with 'forward' as normal.
|
||||
-- The angle between them is the absolute of the roll value we're looking for.
|
||||
rot.z = vector.angle(forwup, up)
|
||||
|
@ -58,26 +58,20 @@ end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
local function get_formspec(self)
|
||||
local formspec = ""
|
||||
if self.hidden or (self.parent ~= nil and self.parent.hidden) then
|
||||
return ""
|
||||
end
|
||||
local tab = self.tablist[self.last_tab_index]
|
||||
|
||||
if not self.hidden and (self.parent == nil or not self.parent.hidden) then
|
||||
local content, prepend = tab.get_formspec(self, tab.name, tab.tabdata, tab.tabsize)
|
||||
|
||||
if self.parent == nil then
|
||||
local tsize = self.tablist[self.last_tab_index].tabsize or
|
||||
{width=self.width, height=self.height}
|
||||
formspec = formspec ..
|
||||
string.format("size[%f,%f,%s]",tsize.width,tsize.height,
|
||||
if self.parent == nil and not prepend then
|
||||
local tsize = tab.tabsize or {width=self.width, height=self.height}
|
||||
prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height,
|
||||
dump(self.fixed_size))
|
||||
end
|
||||
formspec = formspec .. self:tab_header()
|
||||
formspec = formspec ..
|
||||
self.tablist[self.last_tab_index].get_formspec(
|
||||
self,
|
||||
self.tablist[self.last_tab_index].name,
|
||||
self.tablist[self.last_tab_index].tabdata,
|
||||
self.tablist[self.last_tab_index].tabsize
|
||||
)
|
||||
end
|
||||
|
||||
local formspec = (prepend or "") .. self:tab_header() .. content
|
||||
return formspec
|
||||
end
|
||||
|
||||
@ -97,14 +91,9 @@ local function handle_buttons(self,fields)
|
||||
return true
|
||||
end
|
||||
|
||||
if self.tablist[self.last_tab_index].button_handler ~= nil then
|
||||
return
|
||||
self.tablist[self.last_tab_index].button_handler(
|
||||
self,
|
||||
fields,
|
||||
self.tablist[self.last_tab_index].name,
|
||||
self.tablist[self.last_tab_index].tabdata
|
||||
)
|
||||
local tab = self.tablist[self.last_tab_index]
|
||||
if tab.button_handler ~= nil then
|
||||
return tab.button_handler(self, fields, tab.name, tab.tabdata)
|
||||
end
|
||||
|
||||
return false
|
||||
@ -122,14 +111,9 @@ local function handle_events(self,event)
|
||||
return true
|
||||
end
|
||||
|
||||
if self.tablist[self.last_tab_index].evt_handler ~= nil then
|
||||
return
|
||||
self.tablist[self.last_tab_index].evt_handler(
|
||||
self,
|
||||
event,
|
||||
self.tablist[self.last_tab_index].name,
|
||||
self.tablist[self.last_tab_index].tabdata
|
||||
)
|
||||
local tab = self.tablist[self.last_tab_index]
|
||||
if tab.evt_handler ~= nil then
|
||||
return tab.evt_handler(self, event, tab.name, tab.tabdata)
|
||||
end
|
||||
|
||||
return false
|
||||
|
@ -18,6 +18,8 @@
|
||||
ui = {}
|
||||
ui.childlist = {}
|
||||
ui.default = nil
|
||||
-- Whether fstk is currently showing its own formspec instead of active ui elements.
|
||||
ui.overridden = false
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function ui.add(child)
|
||||
@ -55,12 +57,13 @@ end
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function ui.update()
|
||||
ui.overridden = false
|
||||
local formspec = {}
|
||||
|
||||
-- handle errors
|
||||
if gamedata ~= nil and gamedata.reconnect_requested then
|
||||
local error_message = core.formspec_escape(
|
||||
gamedata.errormessage or "<none available>")
|
||||
gamedata.errormessage or fgettext("<none available>"))
|
||||
formspec = {
|
||||
"size[14,8]",
|
||||
"real_coordinates[true]",
|
||||
@ -71,6 +74,7 @@ function ui.update()
|
||||
"button[2,6.6;4,1;btn_reconnect_yes;" .. fgettext("Reconnect") .. "]",
|
||||
"button[8,6.6;4,1;btn_reconnect_no;" .. fgettext("Main menu") .. "]"
|
||||
}
|
||||
ui.overridden = true
|
||||
elseif gamedata ~= nil and gamedata.errormessage ~= nil then
|
||||
local error_message = core.formspec_escape(gamedata.errormessage)
|
||||
|
||||
@ -89,6 +93,7 @@ function ui.update()
|
||||
error_title, error_message),
|
||||
"button[5,6.6;4,1;btn_error_confirm;" .. fgettext("OK") .. "]"
|
||||
}
|
||||
ui.overridden = true
|
||||
else
|
||||
local active_toplevel_ui_elements = 0
|
||||
for key,value in pairs(ui.childlist) do
|
||||
@ -185,6 +190,16 @@ end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
core.event_handler = function(event)
|
||||
-- Handle error messages
|
||||
if ui.overridden then
|
||||
if event == "MenuQuit" then
|
||||
gamedata.errormessage = nil
|
||||
gamedata.reconnect_requested = false
|
||||
ui.update()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if ui.handle_events(event) then
|
||||
ui.update()
|
||||
return
|
||||
|
22
builtin/game/async.lua
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
core.async_jobs = {}
|
||||
|
||||
function core.async_event_handler(jobid, retval)
|
||||
local callback = core.async_jobs[jobid]
|
||||
assert(type(callback) == "function")
|
||||
callback(unpack(retval, 1, retval.n))
|
||||
core.async_jobs[jobid] = nil
|
||||
end
|
||||
|
||||
function core.handle_async(func, callback, ...)
|
||||
assert(type(func) == "function" and type(callback) == "function",
|
||||
"Invalid minetest.handle_async invocation")
|
||||
local args = {n = select("#", ...), ...}
|
||||
local mod_origin = core.get_last_run_mod()
|
||||
|
||||
local jobid = core.do_async_callback(func, args, mod_origin)
|
||||
core.async_jobs[jobid] = callback
|
||||
|
||||
return true
|
||||
end
|
||||
|