forked from Mirrorlandia_minetest/minetest
Merge pull request 'master' (#6) from Mirrorlandia_minetest/minetest:master into master
Reviewed-on: #6
This commit is contained in:
commit
5adbc138c7
@ -1,33 +0,0 @@
|
|||||||
BasedOnStyle: LLVM
|
|
||||||
IndentWidth: 4
|
|
||||||
UseTab: Always
|
|
||||||
TabWidth: 4
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
Standard: c++17
|
|
||||||
BraceWrapping:
|
|
||||||
AfterClass: true
|
|
||||||
AfterControlStatement: Never
|
|
||||||
AfterEnum: true
|
|
||||||
AfterFunction: true
|
|
||||||
AfterNamespace: true
|
|
||||||
AfterStruct: true
|
|
||||||
AfterUnion: true
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
FixNamespaceComments: false
|
|
||||||
AllowShortIfStatementsOnASingleLine: false
|
|
||||||
IndentCaseLabels: false
|
|
||||||
AccessModifierOffset: -4
|
|
||||||
ColumnLimit: 90
|
|
||||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
|
||||||
SortIncludes: Never
|
|
||||||
IncludeCategories:
|
|
||||||
- Regex: '^".*'
|
|
||||||
Priority: 2
|
|
||||||
- Regex: '^<.*'
|
|
||||||
Priority: 1
|
|
||||||
AlignAfterOpenBracket: DontAlign
|
|
||||||
ContinuationIndentWidth: 8
|
|
||||||
ConstructorInitializerIndentWidth: 8
|
|
||||||
BreakConstructorInitializers: AfterColon
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
14
.github/CONTRIBUTING.md
vendored
14
.github/CONTRIBUTING.md
vendored
@ -67,20 +67,6 @@ Contributions are welcome! Here's how you can help:
|
|||||||
might need more work in the future.
|
might need more work in the future.
|
||||||
5. It uses protocols and formats which include the required compatibility.
|
5. It uses protocols and formats which include the required compatibility.
|
||||||
|
|
||||||
### 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
|
## Issues
|
||||||
|
|
||||||
If you experience an issue, we would like to know the details - especially when
|
If you experience an issue, we would like to know the details - especially when
|
||||||
|
4
.github/workflows/android.yml
vendored
4
.github/workflows/android.yml
vendored
@ -8,6 +8,8 @@ on:
|
|||||||
- 'lib/**.cpp'
|
- 'lib/**.cpp'
|
||||||
- 'src/**.[ch]'
|
- 'src/**.[ch]'
|
||||||
- 'src/**.cpp'
|
- 'src/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
- 'android/**'
|
- 'android/**'
|
||||||
- '.github/workflows/android.yml'
|
- '.github/workflows/android.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -16,6 +18,8 @@ on:
|
|||||||
- 'lib/**.cpp'
|
- 'lib/**.cpp'
|
||||||
- 'src/**.[ch]'
|
- 'src/**.[ch]'
|
||||||
- 'src/**.cpp'
|
- 'src/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
- 'android/**'
|
- 'android/**'
|
||||||
- '.github/workflows/android.yml'
|
- '.github/workflows/android.yml'
|
||||||
|
|
||||||
|
274
.github/workflows/build.yml
vendored
274
.github/workflows/build.yml
vendored
@ -1,274 +0,0 @@
|
|||||||
name: build
|
|
||||||
|
|
||||||
# build on c/cpp changes or workflow changes
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- 'lib/**.[ch]'
|
|
||||||
- 'lib/**.cpp'
|
|
||||||
- 'src/**.[ch]'
|
|
||||||
- 'src/**.cpp'
|
|
||||||
- '**/CMakeLists.txt'
|
|
||||||
- 'cmake/Modules/**'
|
|
||||||
- 'util/buildbot/**'
|
|
||||||
- 'util/ci/**'
|
|
||||||
- '.github/workflows/**.yml'
|
|
||||||
- 'Dockerfile'
|
|
||||||
- '.dockerignore'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'lib/**.[ch]'
|
|
||||||
- 'lib/**.cpp'
|
|
||||||
- 'src/**.[ch]'
|
|
||||||
- 'src/**.cpp'
|
|
||||||
- '**/CMakeLists.txt'
|
|
||||||
- 'cmake/Modules/**'
|
|
||||||
- 'util/buildbot/**'
|
|
||||||
- 'util/ci/**'
|
|
||||||
- '.github/workflows/**.yml'
|
|
||||||
- 'Dockerfile'
|
|
||||||
- '.dockerignore'
|
|
||||||
|
|
||||||
env:
|
|
||||||
MINETEST_POSTGRESQL_CONNECT_STRING: 'host=localhost user=minetest password=minetest dbname=minetest'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Older gcc version (should be close to our minimum supported version)
|
|
||||||
gcc_7:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps g++-7
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: gcc-7
|
|
||||||
CXX: g++-7
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
./bin/minetest --run-unittests
|
|
||||||
|
|
||||||
# Current gcc version
|
|
||||||
gcc_12:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps g++-12 libluajit-5.1-dev
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: gcc-12
|
|
||||||
CXX: g++-12
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
./bin/minetest --run-unittests
|
|
||||||
|
|
||||||
# Older clang version (should be close to our minimum supported version)
|
|
||||||
clang_7:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps clang-7 valgrind
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: clang-7
|
|
||||||
CXX: clang++-7
|
|
||||||
|
|
||||||
- name: Unittest
|
|
||||||
run: |
|
|
||||||
./bin/minetest --run-unittests
|
|
||||||
|
|
||||||
- name: Valgrind
|
|
||||||
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-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
source ./util/ci/common.sh
|
|
||||||
install_linux_deps clang-9
|
|
||||||
|
|
||||||
- name: Build prometheus-cpp
|
|
||||||
run: |
|
|
||||||
./util/ci/build_prometheus_cpp.sh
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
./util/ci/build.sh
|
|
||||||
env:
|
|
||||||
CC: clang-9
|
|
||||||
CXX: clang++-9
|
|
||||||
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0"
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
./bin/minetestserver --run-unittests
|
|
||||||
|
|
||||||
docker:
|
|
||||||
name: "Docker image"
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Build docker image
|
|
||||||
run: |
|
|
||||||
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-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install compiler
|
|
||||||
run: |
|
|
||||||
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
|
|
||||||
run: |
|
|
||||||
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh winbuild
|
|
||||||
env:
|
|
||||||
NO_PACKAGE: 1
|
|
||||||
|
|
||||||
win64:
|
|
||||||
name: "MinGW cross-compiler (64-bit)"
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install compiler
|
|
||||||
run: |
|
|
||||||
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
|
|
||||||
run: |
|
|
||||||
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh winbuild
|
|
||||||
env:
|
|
||||||
NO_PACKAGE: 1
|
|
||||||
|
|
||||||
msvc:
|
|
||||||
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
|
||||||
runs-on: windows-2019
|
|
||||||
env:
|
|
||||||
VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50
|
|
||||||
# 2023.10.19
|
|
||||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- {
|
|
||||||
arch: x86,
|
|
||||||
generator: "-G'Visual Studio 16 2019' -A Win32",
|
|
||||||
vcpkg_triplet: x86-windows
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
arch: x64,
|
|
||||||
generator: "-G'Visual Studio 16 2019' -A x64",
|
|
||||||
vcpkg_triplet: x64-windows
|
|
||||||
}
|
|
||||||
type: [portable]
|
|
||||||
# type: [portable, installer]
|
|
||||||
# The installer type is working, but disabled, to save runner jobs.
|
|
||||||
# Enable it, when working on the installer.
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- 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@v7
|
|
||||||
with:
|
|
||||||
vcpkgArguments: ${{env.vcpkg_packages}}
|
|
||||||
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
|
||||||
appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
|
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
|
||||||
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
|
||||||
|
|
||||||
- name: Minetest CMake
|
|
||||||
run: |
|
|
||||||
cmake ${{matrix.config.generator}} `
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
|
||||||
-DCMAKE_BUILD_TYPE=Release `
|
|
||||||
-DENABLE_POSTGRESQL=OFF `
|
|
||||||
-DENABLE_LUAJIT=TRUE `
|
|
||||||
-DREQUIRE_LUAJIT=TRUE `
|
|
||||||
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
|
||||||
|
|
||||||
- name: Build Minetest
|
|
||||||
run: cmake --build . --config Release
|
|
||||||
|
|
||||||
- name: CPack
|
|
||||||
run: |
|
|
||||||
If ($env:TYPE -eq "installer")
|
|
||||||
{
|
|
||||||
cpack -G WIX -B "$env:GITHUB_WORKSPACE\Package"
|
|
||||||
}
|
|
||||||
ElseIf($env:TYPE -eq "portable")
|
|
||||||
{
|
|
||||||
cpack -G ZIP -B "$env:GITHUB_WORKSPACE\Package"
|
|
||||||
}
|
|
||||||
env:
|
|
||||||
TYPE: ${{matrix.type}}
|
|
||||||
|
|
||||||
- name: Package Clean
|
|
||||||
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
|
||||||
path: .\Package\
|
|
24
.github/workflows/cpp_lint.yml
vendored
24
.github/workflows/cpp_lint.yml
vendored
@ -23,32 +23,18 @@ on:
|
|||||||
- 'util/ci/**'
|
- 'util/ci/**'
|
||||||
- '.github/workflows/**.yml'
|
- '.github/workflows/**.yml'
|
||||||
|
|
||||||
|
env:
|
||||||
|
CLANG_TIDY: clang-tidy-15
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
# 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:
|
clang_tidy:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps clang-tidy-9
|
install_linux_deps $CLANG_TIDY
|
||||||
|
|
||||||
- name: Run clang-tidy
|
- name: Run clang-tidy
|
||||||
run: |
|
run: |
|
||||||
|
163
.github/workflows/linux.yml
vendored
Normal file
163
.github/workflows/linux.yml
vendored
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
name: linux
|
||||||
|
|
||||||
|
# build on c/cpp changes or workflow changes
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'util/ci/**'
|
||||||
|
- 'misc/irrlichtmt_tag.txt'
|
||||||
|
- 'Dockerfile'
|
||||||
|
- '.dockerignore'
|
||||||
|
- '.github/workflows/linux.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'util/ci/**'
|
||||||
|
- 'misc/irrlichtmt_tag.txt'
|
||||||
|
- 'Dockerfile'
|
||||||
|
- '.dockerignore'
|
||||||
|
- '.github/workflows/linux.yml'
|
||||||
|
|
||||||
|
env:
|
||||||
|
MINETEST_POSTGRESQL_CONNECT_STRING: 'host=localhost user=minetest password=minetest dbname=minetest'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Older gcc version (should be close to our minimum supported version)
|
||||||
|
gcc_7:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps g++-7
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: gcc-7
|
||||||
|
CXX: g++-7
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
./bin/minetest --run-unittests
|
||||||
|
|
||||||
|
# Current gcc version
|
||||||
|
gcc_12:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps g++-12 libluajit-5.1-dev
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: gcc-12
|
||||||
|
CXX: g++-12
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
./bin/minetest --run-unittests
|
||||||
|
|
||||||
|
# Older clang version (should be close to our minimum supported version)
|
||||||
|
clang_7:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps clang-7 valgrind
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: clang-7
|
||||||
|
CXX: clang++-7
|
||||||
|
|
||||||
|
- name: Unittest
|
||||||
|
run: |
|
||||||
|
./bin/minetest --run-unittests
|
||||||
|
|
||||||
|
- name: Valgrind
|
||||||
|
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-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install deps
|
||||||
|
run: |
|
||||||
|
source ./util/ci/common.sh
|
||||||
|
install_linux_deps clang-9
|
||||||
|
|
||||||
|
- name: Build prometheus-cpp
|
||||||
|
run: |
|
||||||
|
./util/ci/build_prometheus_cpp.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./util/ci/build.sh
|
||||||
|
env:
|
||||||
|
CC: clang-9
|
||||||
|
CXX: clang++-9
|
||||||
|
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0"
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
./bin/minetestserver --run-unittests
|
||||||
|
|
||||||
|
docker:
|
||||||
|
name: "Docker image"
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Build docker image
|
||||||
|
run: |
|
||||||
|
docker build . -t minetest:latest
|
||||||
|
docker run --rm minetest:latest /usr/local/bin/minetestserver --version
|
147
.github/workflows/windows.yml
vendored
Normal file
147
.github/workflows/windows.yml
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
name: windows
|
||||||
|
|
||||||
|
# build on c/cpp changes or workflow changes
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'util/buildbot/**'
|
||||||
|
- 'misc/irrlichtmt_tag.txt'
|
||||||
|
- 'misc/*.manifest'
|
||||||
|
- '.github/workflows/windows.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'lib/**.[ch]'
|
||||||
|
- 'lib/**.cpp'
|
||||||
|
- 'src/**.[ch]'
|
||||||
|
- 'src/**.cpp'
|
||||||
|
- '**/CMakeLists.txt'
|
||||||
|
- 'cmake/Modules/**'
|
||||||
|
- 'util/buildbot/**'
|
||||||
|
- 'misc/irrlichtmt_tag.txt'
|
||||||
|
- 'misc/*.manifest'
|
||||||
|
- '.github/workflows/windows.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
mingw32:
|
||||||
|
name: "MinGW cross-compiler (32-bit)"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install compiler
|
||||||
|
run: |
|
||||||
|
sudo apt-get update && sudo apt-get install -y gettext
|
||||||
|
sudo ./util/buildbot/download_toolchain.sh i686 /usr
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh B
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: mingw32
|
||||||
|
path: B/build/*.zip
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
mingw64:
|
||||||
|
name: "MinGW cross-compiler (64-bit)"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install compiler
|
||||||
|
run: |
|
||||||
|
sudo apt-get update && sudo apt-get install -y gettext
|
||||||
|
sudo ./util/buildbot/download_toolchain.sh x86_64 /usr
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh B
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: mingw64
|
||||||
|
path: B/build/*.zip
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
msvc:
|
||||||
|
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
|
||||||
|
runs-on: windows-2019
|
||||||
|
env:
|
||||||
|
VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50
|
||||||
|
# 2023.10.19
|
||||||
|
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
- {
|
||||||
|
arch: x86,
|
||||||
|
generator: "-G'Visual Studio 16 2019' -A Win32",
|
||||||
|
vcpkg_triplet: x86-windows
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
arch: x64,
|
||||||
|
generator: "-G'Visual Studio 16 2019' -A x64",
|
||||||
|
vcpkg_triplet: x64-windows
|
||||||
|
}
|
||||||
|
type: [portable]
|
||||||
|
# type: [portable, installer]
|
||||||
|
# The installer type is working, but disabled, to save runner jobs.
|
||||||
|
# Enable it, when working on the installer.
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- 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@v7
|
||||||
|
with:
|
||||||
|
vcpkgArguments: ${{env.vcpkg_packages}}
|
||||||
|
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
|
||||||
|
appendedCacheKey: ${{ matrix.config.vcpkg_triplet }}
|
||||||
|
vcpkgGitCommitId: ${{ env.VCPKG_VERSION }}
|
||||||
|
vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }}
|
||||||
|
|
||||||
|
- name: Minetest CMake
|
||||||
|
run: |
|
||||||
|
cmake ${{matrix.config.generator}} `
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" `
|
||||||
|
-DCMAKE_BUILD_TYPE=Release `
|
||||||
|
-DENABLE_POSTGRESQL=OFF `
|
||||||
|
-DENABLE_LUAJIT=TRUE `
|
||||||
|
-DREQUIRE_LUAJIT=TRUE `
|
||||||
|
-DRUN_IN_PLACE=${{ contains(matrix.type, 'portable') }} .
|
||||||
|
|
||||||
|
- name: Build Minetest
|
||||||
|
run: cmake --build . --config Release
|
||||||
|
|
||||||
|
- name: CPack
|
||||||
|
run: |
|
||||||
|
If ($env:TYPE -eq "installer")
|
||||||
|
{
|
||||||
|
cpack -G WIX -B "$env:GITHUB_WORKSPACE\Package"
|
||||||
|
}
|
||||||
|
ElseIf($env:TYPE -eq "portable")
|
||||||
|
{
|
||||||
|
cpack -G ZIP -B "$env:GITHUB_WORKSPACE\Package"
|
||||||
|
}
|
||||||
|
env:
|
||||||
|
TYPE: ${{matrix.type}}
|
||||||
|
|
||||||
|
- name: Package Clean
|
||||||
|
run: rm -r $env:GITHUB_WORKSPACE\Package\_CPack_Packages
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: msvc-${{ matrix.config.arch }}-${{ matrix.type }}
|
||||||
|
path: .\Package\
|
||||||
|
if-no-files-found: error
|
23
README.md
23
README.md
@ -128,28 +128,7 @@ Docker
|
|||||||
|
|
||||||
- [Developing minetestserver with Docker](doc/developing/docker.md)
|
- [Developing minetestserver with Docker](doc/developing/docker.md)
|
||||||
|
|
||||||
We provide Minetest server Docker images using the GitLab mirror registry.
|
We provide a Dockerfile that can be used to build the server.
|
||||||
|
|
||||||
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
|
Version scheme
|
||||||
|
@ -51,8 +51,13 @@ public class GameActivity extends NativeActivity {
|
|||||||
System.loadLibrary("minetest");
|
System.loadLibrary("minetest");
|
||||||
}
|
}
|
||||||
|
|
||||||
private int messageReturnCode = -1;
|
enum DialogType { TEXT_INPUT, SELECTION_INPUT }
|
||||||
|
enum DialogState { DIALOG_SHOWN, DIALOG_INPUTTED, DIALOG_CANCELED }
|
||||||
|
|
||||||
|
private DialogType lastDialogType = DialogType.TEXT_INPUT;
|
||||||
|
private DialogState inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
private String messageReturnValue = "";
|
private String messageReturnValue = "";
|
||||||
|
private int selectionReturnValue = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@ -85,11 +90,17 @@ public class GameActivity extends NativeActivity {
|
|||||||
// Ignore the back press so Minetest can handle it
|
// Ignore the back press so Minetest can handle it
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showDialog(String acceptButton, String hint, String current, int editType) {
|
public void showTextInputDialog(String hint, String current, int editType) {
|
||||||
runOnUiThread(() -> showDialogUI(hint, current, editType));
|
runOnUiThread(() -> showTextInputDialogUI(hint, current, editType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showDialogUI(String hint, String current, int editType) {
|
public void showSelectionInputDialog(String[] optionList, int selectedIdx) {
|
||||||
|
runOnUiThread(() -> showSelectionInputDialogUI(optionList, selectedIdx));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showTextInputDialogUI(String hint, String current, int editType) {
|
||||||
|
lastDialogType = DialogType.TEXT_INPUT;
|
||||||
|
inputDialogState = DialogState.DIALOG_SHOWN;
|
||||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
LinearLayout container = new LinearLayout(this);
|
LinearLayout container = new LinearLayout(this);
|
||||||
container.setOrientation(LinearLayout.VERTICAL);
|
container.setOrientation(LinearLayout.VERTICAL);
|
||||||
@ -114,7 +125,7 @@ public class GameActivity extends NativeActivity {
|
|||||||
// For multi-line, do not submit the text after pressing Enter key
|
// For multi-line, do not submit the text after pressing Enter key
|
||||||
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
|
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
|
||||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||||
messageReturnCode = 0;
|
inputDialogState = DialogState.DIALOG_INPUTTED;
|
||||||
messageReturnValue = editText.getText().toString();
|
messageReturnValue = editText.getText().toString();
|
||||||
alertDialog.dismiss();
|
alertDialog.dismiss();
|
||||||
return true;
|
return true;
|
||||||
@ -128,29 +139,55 @@ public class GameActivity extends NativeActivity {
|
|||||||
doneButton.setText(R.string.ime_dialog_done);
|
doneButton.setText(R.string.ime_dialog_done);
|
||||||
doneButton.setOnClickListener((view -> {
|
doneButton.setOnClickListener((view -> {
|
||||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||||
messageReturnCode = 0;
|
inputDialogState = DialogState.DIALOG_INPUTTED;
|
||||||
messageReturnValue = editText.getText().toString();
|
messageReturnValue = editText.getText().toString();
|
||||||
alertDialog.dismiss();
|
alertDialog.dismiss();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
alertDialog.setOnCancelListener(dialog -> {
|
alertDialog.setOnCancelListener(dialog -> {
|
||||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||||
|
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
messageReturnValue = current;
|
messageReturnValue = current;
|
||||||
messageReturnCode = -1;
|
|
||||||
});
|
});
|
||||||
alertDialog.show();
|
alertDialog.show();
|
||||||
editText.requestFocusTryShow();
|
editText.requestFocusTryShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDialogState() {
|
public void showSelectionInputDialogUI(String[] optionList, int selectedIdx) {
|
||||||
return messageReturnCode;
|
lastDialogType = DialogType.SELECTION_INPUT;
|
||||||
|
inputDialogState = DialogState.DIALOG_SHOWN;
|
||||||
|
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setSingleChoiceItems(optionList, selectedIdx, (dialog, selection) -> {
|
||||||
|
inputDialogState = DialogState.DIALOG_INPUTTED;
|
||||||
|
selectionReturnValue = selection;
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
builder.setOnCancelListener(dialog -> {
|
||||||
|
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
|
selectionReturnValue = selectedIdx;
|
||||||
|
});
|
||||||
|
AlertDialog alertDialog = builder.create();
|
||||||
|
alertDialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDialogValue() {
|
public int getLastDialogType() {
|
||||||
messageReturnCode = -1;
|
return lastDialogType.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInputDialogState() {
|
||||||
|
return inputDialogState.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDialogMessage() {
|
||||||
|
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
return messageReturnValue;
|
return messageReturnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDialogSelection() {
|
||||||
|
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||||
|
return selectionReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
public float getDensity() {
|
public float getDensity() {
|
||||||
return getResources().getDisplayMetrics().density;
|
return getResources().getDisplayMetrics().density;
|
||||||
}
|
}
|
||||||
|
@ -87,19 +87,20 @@ core.builtin_auth_handler = {
|
|||||||
core.settings:get("default_password")))
|
core.settings:get("default_password")))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local prev_privs = auth_entry.privileges
|
||||||
auth_entry.privileges = privileges
|
auth_entry.privileges = privileges
|
||||||
|
|
||||||
core_auth.save(auth_entry)
|
core_auth.save(auth_entry)
|
||||||
|
|
||||||
-- Run grant callbacks
|
-- Run grant callbacks
|
||||||
for priv, _ in pairs(privileges) do
|
for priv, _ in pairs(privileges) do
|
||||||
if not auth_entry.privileges[priv] then
|
if not prev_privs[priv] then
|
||||||
core.run_priv_callbacks(name, priv, nil, "grant")
|
core.run_priv_callbacks(name, priv, nil, "grant")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Run revoke callbacks
|
-- Run revoke callbacks
|
||||||
for priv, _ in pairs(auth_entry.privileges) do
|
for priv, _ in pairs(prev_privs) do
|
||||||
if not privileges[priv] then
|
if not privileges[priv] then
|
||||||
core.run_priv_callbacks(name, priv, nil, "revoke")
|
core.run_priv_callbacks(name, priv, nil, "revoke")
|
||||||
end
|
end
|
||||||
|
@ -29,6 +29,7 @@ core.features = {
|
|||||||
compress_zstd = true,
|
compress_zstd = true,
|
||||||
sound_params_start_time = true,
|
sound_params_start_time = true,
|
||||||
physics_overrides_v2 = true,
|
physics_overrides_v2 = true,
|
||||||
|
hud_def_type_field = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function core.has_feature(arg)
|
function core.has_feature(arg)
|
||||||
|
@ -64,6 +64,13 @@ function core.encode_png(width, height, data, compression)
|
|||||||
error("Incorrect type for 'height', expected number, got " .. type(height))
|
error("Incorrect type for 'height', expected number, got " .. type(height))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if width < 1 then
|
||||||
|
error("Incorrect value for 'width', must be at least 1")
|
||||||
|
end
|
||||||
|
if height < 1 then
|
||||||
|
error("Incorrect value for 'height', must be at least 1")
|
||||||
|
end
|
||||||
|
|
||||||
local expected_byte_count = width * height * 4
|
local expected_byte_count = width * height * 4
|
||||||
|
|
||||||
if type(data) ~= "table" and type(data) ~= "string" then
|
if type(data) ~= "table" and type(data) ~= "string" then
|
||||||
|
@ -3,7 +3,7 @@ local enable_damage = core.settings:get_bool("enable_damage")
|
|||||||
|
|
||||||
local bar_definitions = {
|
local bar_definitions = {
|
||||||
hp = {
|
hp = {
|
||||||
hud_elem_type = "statbar",
|
type = "statbar",
|
||||||
position = {x = 0.5, y = 1},
|
position = {x = 0.5, y = 1},
|
||||||
text = "heart.png",
|
text = "heart.png",
|
||||||
text2 = "heart_gone.png",
|
text2 = "heart_gone.png",
|
||||||
@ -14,7 +14,7 @@ local bar_definitions = {
|
|||||||
offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)},
|
offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)},
|
||||||
},
|
},
|
||||||
breath = {
|
breath = {
|
||||||
hud_elem_type = "statbar",
|
type = "statbar",
|
||||||
position = {x = 0.5, y = 1},
|
position = {x = 0.5, y = 1},
|
||||||
text = "bubble.png",
|
text = "bubble.png",
|
||||||
text2 = "bubble_gone.png",
|
text2 = "bubble_gone.png",
|
||||||
@ -139,7 +139,7 @@ end
|
|||||||
|
|
||||||
function core.hud_replace_builtin(hud_name, definition)
|
function core.hud_replace_builtin(hud_name, definition)
|
||||||
if type(definition) ~= "table" or
|
if type(definition) ~= "table" or
|
||||||
definition.hud_elem_type ~= "statbar" then
|
(definition.type or definition.hud_elem_type) ~= "statbar" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,6 +28,13 @@ end
|
|||||||
|
|
||||||
local has_fetched = false
|
local has_fetched = false
|
||||||
local latest_releases
|
local latest_releases
|
||||||
|
do
|
||||||
|
local tmp = core.get_once("cdb_latest_releases")
|
||||||
|
if tmp then
|
||||||
|
latest_releases = core.deserialize(tmp, true)
|
||||||
|
has_fetched = latest_releases ~= nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local function fetch_latest_releases()
|
local function fetch_latest_releases()
|
||||||
@ -89,8 +96,9 @@ local function fetch()
|
|||||||
has_fetched = false
|
has_fetched = false
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
latest_releases = lowercase_keys(releases)
|
latest_releases = lowercase_keys(releases)
|
||||||
|
core.set_once("cdb_latest_releases", core.serialize(latest_releases))
|
||||||
|
|
||||||
if update_detector.get_count() > 0 then
|
if update_detector.get_count() > 0 then
|
||||||
local maintab = ui.find_by_name("maintab")
|
local maintab = ui.find_by_name("maintab")
|
||||||
if not maintab.hidden then
|
if not maintab.hidden then
|
||||||
|
@ -47,13 +47,13 @@ end
|
|||||||
|
|
||||||
|
|
||||||
local change_keys = {
|
local change_keys = {
|
||||||
query_text = "Change Keys",
|
query_text = "Controls",
|
||||||
requires = {
|
requires = {
|
||||||
keyboard_mouse = true,
|
keyboard_mouse = true,
|
||||||
},
|
},
|
||||||
get_formspec = function(self, avail_w)
|
get_formspec = function(self, avail_w)
|
||||||
local btn_w = math.min(avail_w, 3)
|
local btn_w = math.min(avail_w, 3)
|
||||||
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Change Keys")), 0.8
|
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
|
||||||
end,
|
end,
|
||||||
on_submit = function(self, fields)
|
on_submit = function(self, fields)
|
||||||
if fields.btn_change_keys then
|
if fields.btn_change_keys then
|
||||||
|
@ -46,11 +46,18 @@ local register_functions = {
|
|||||||
register_on_mapblocks_changed = 0,
|
register_on_mapblocks_changed = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local function regex_escape(s)
|
||||||
|
return s:gsub("(%W)", "%%%1")
|
||||||
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Create an unique instrument name.
|
-- Create an unique instrument name.
|
||||||
-- Generate a missing label with a running index number.
|
-- Generate a missing label with a running index number.
|
||||||
--
|
--
|
||||||
local counts = {}
|
local counts = {}
|
||||||
|
local worldmods_path = regex_escape(core.get_worldpath())
|
||||||
|
local user_path = regex_escape(core.get_user_path())
|
||||||
|
local builtin_path = regex_escape(core.get_builtin_path())
|
||||||
local function generate_name(def)
|
local function generate_name(def)
|
||||||
local class, label, func_name = def.class, def.label, def.func_name
|
local class, label, func_name = def.class, def.label, def.func_name
|
||||||
if label then
|
if label then
|
||||||
@ -65,7 +72,16 @@ local function generate_name(def)
|
|||||||
local index_id = def.mod .. (class or func_name)
|
local index_id = def.mod .. (class or func_name)
|
||||||
local index = counts[index_id] or 1
|
local index = counts[index_id] or 1
|
||||||
counts[index_id] = index + 1
|
counts[index_id] = index + 1
|
||||||
return format("%s[%d] %s", class or func_name, index, class and func_name or ""):trim()
|
local info = debug.getinfo(def.func)
|
||||||
|
local modpath = regex_escape(core.get_modpath(def.mod) or "")
|
||||||
|
local source = info.source
|
||||||
|
if modpath ~= "" then
|
||||||
|
source = source:gsub(modpath, def.mod)
|
||||||
|
end
|
||||||
|
source = source:gsub(worldmods_path, "")
|
||||||
|
source = source:gsub(builtin_path, "builtin" .. DIR_DELIM)
|
||||||
|
source = source:gsub(user_path, "")
|
||||||
|
return format("%s[%d] %s#%s", class or func_name, index, source, info.linedefined)
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -77,7 +77,7 @@ local Formatter = {
|
|||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
local widths = { 55, 9, 9, 9, 5, 5, 5 }
|
local widths = { 80, 9, 9, 9, 5, 5, 5 }
|
||||||
local txt_row_format = sprintf(" %%-%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds", unpack(widths))
|
local txt_row_format = sprintf(" %%-%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds", unpack(widths))
|
||||||
|
|
||||||
local HR = {}
|
local HR = {}
|
||||||
|
@ -110,7 +110,7 @@ force_csm (Force client-side mods) bool false
|
|||||||
# Smooths rotation of camera, also called look or mouse smoothing. 0 to disable.
|
# Smooths rotation of camera, also called look or mouse smoothing. 0 to disable.
|
||||||
camera_smoothing (Camera smoothing) float 0.0 0.0 0.99
|
camera_smoothing (Camera smoothing) float 0.0 0.0 0.99
|
||||||
|
|
||||||
# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Change Keys.
|
# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Controls.
|
||||||
#
|
#
|
||||||
# Requires: keyboard_mouse
|
# Requires: keyboard_mouse
|
||||||
cinematic_camera_smoothing (Camera smoothing in cinematic mode) float 0.7 0.0 0.99
|
cinematic_camera_smoothing (Camera smoothing in cinematic mode) float 0.7 0.0 0.99
|
||||||
@ -601,6 +601,17 @@ enable_auto_exposure (Enable Automatic Exposure) bool false
|
|||||||
# Requires: shaders, enable_auto_exposure
|
# Requires: shaders, enable_auto_exposure
|
||||||
exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
||||||
|
|
||||||
|
# Apply dithering to reduce color banding artifacts.
|
||||||
|
# Dithering significantly increases the size of losslessly-compressed
|
||||||
|
# screenshots and it works incorrectly if the display or operating system
|
||||||
|
# performs additional dithering or if the color channels are not quantized
|
||||||
|
# to 8 bits.
|
||||||
|
# With OpenGL ES, dithering only works if the shader supports high
|
||||||
|
# floating-point precision and it may have a higher performance impact.
|
||||||
|
#
|
||||||
|
# Requires: shaders
|
||||||
|
debanding (Enable Debanding) bool true
|
||||||
|
|
||||||
[**Bloom]
|
[**Bloom]
|
||||||
|
|
||||||
# Set to true to enable bloom effect.
|
# Set to true to enable bloom effect.
|
||||||
@ -637,6 +648,10 @@ bloom_strength_factor (Bloom Strength Factor) float 1.0 0.1 10.0
|
|||||||
# Requires: shaders, enable_bloom
|
# Requires: shaders, enable_bloom
|
||||||
bloom_radius (Bloom Radius) float 1 0.1 8
|
bloom_radius (Bloom Radius) float 1 0.1 8
|
||||||
|
|
||||||
|
# Set to true to enable volumetric lighting effect (a.k.a. "Godrays").
|
||||||
|
#
|
||||||
|
# Requires: shaders, enable_bloom
|
||||||
|
enable_volumetric_lighting (Volumetric lighting) bool false
|
||||||
|
|
||||||
[*Audio]
|
[*Audio]
|
||||||
|
|
||||||
@ -820,6 +835,14 @@ bind_address (Bind address) string
|
|||||||
# to new servers, but they may not support all new features that you are expecting.
|
# to new servers, but they may not support all new features that you are expecting.
|
||||||
strict_protocol_version_checking (Strict protocol checking) bool false
|
strict_protocol_version_checking (Strict protocol checking) bool false
|
||||||
|
|
||||||
|
# Define the oldest clients allowed to connect.
|
||||||
|
# Older clients are compatible in the sense that they will not crash when connecting
|
||||||
|
# to new servers, but they may not support all new features that you are expecting.
|
||||||
|
# This allows for more fine-grained control than strict_protocol_version_checking.
|
||||||
|
# Minetest still enforces its own internal minimum, and enabling
|
||||||
|
# strict_protocol_version_checking will effectively override this.
|
||||||
|
protocol_version_min (Protocol version minimum) int 1 1 65535
|
||||||
|
|
||||||
# Specifies URL from which client fetches media instead of using UDP.
|
# Specifies URL from which client fetches media instead of using UDP.
|
||||||
# $filename should be accessible from $remote_media$filename via cURL
|
# $filename should be accessible from $remote_media$filename via cURL
|
||||||
# (obviously, remote_media should end with a slash).
|
# (obviously, remote_media should end with a slash).
|
||||||
@ -1868,6 +1891,11 @@ texture_min_size (Base texture size) int 64 1 32768
|
|||||||
# Systems with a low-end GPU (or no GPU) would benefit from smaller values.
|
# Systems with a low-end GPU (or no GPU) would benefit from smaller values.
|
||||||
client_mesh_chunk (Client Mesh Chunksize) int 1 1 16
|
client_mesh_chunk (Client Mesh Chunksize) int 1 1 16
|
||||||
|
|
||||||
|
[**Sound]
|
||||||
|
# Comma-separated list of AL and ALC extensions that should not be used.
|
||||||
|
# Useful for testing. See al_extensions.[h,cpp] for details.
|
||||||
|
sound_extensions_blacklist (Sound Extensions Blacklist) string
|
||||||
|
|
||||||
[**Font]
|
[**Font]
|
||||||
|
|
||||||
font_bold (Font bold by default) bool false
|
font_bold (Font bold by default) bool false
|
||||||
@ -2082,8 +2110,8 @@ liquid_update (Liquid update tick) float 1.0 0.001
|
|||||||
# as well as sometimes on land).
|
# as well as sometimes on land).
|
||||||
# Setting this to a value greater than max_block_send_distance disables this
|
# Setting this to a value greater than max_block_send_distance disables this
|
||||||
# optimization.
|
# optimization.
|
||||||
# Stated in mapblocks (16 nodes).
|
# Stated in MapBlocks (16 nodes).
|
||||||
block_send_optimize_distance (Block send optimize distance) int 4 2 32767
|
block_send_optimize_distance (Block send optimize distance) int 4 2 2047
|
||||||
|
|
||||||
# If enabled, the server will perform map block occlusion culling based on
|
# If enabled, the server will perform map block occlusion culling based on
|
||||||
# on the eye position of the player. This can reduce the number of blocks
|
# on the eye position of the player. This can reduce the number of blocks
|
||||||
@ -2091,6 +2119,13 @@ block_send_optimize_distance (Block send optimize distance) int 4 2 32767
|
|||||||
# invisible blocks, so that the utility of noclip mode is reduced.
|
# invisible blocks, so that the utility of noclip mode is reduced.
|
||||||
server_side_occlusion_culling (Server-side occlusion culling) bool true
|
server_side_occlusion_culling (Server-side occlusion culling) bool true
|
||||||
|
|
||||||
|
# At this distance the server will perform a simpler and cheaper occlusion check.
|
||||||
|
# Smaller values potentially improve performance, at the expense of temporarily visible
|
||||||
|
# rendering glitches (missing blocks).
|
||||||
|
# This is especially useful for very large viewing range (upwards of 500).
|
||||||
|
# Stated in MapBlocks (16 nodes).
|
||||||
|
block_cull_optimize_distance (Block cull optimize distance) int 25 2 2047
|
||||||
|
|
||||||
[**Mapgen]
|
[**Mapgen]
|
||||||
|
|
||||||
# Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).
|
# Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
#define rendered texture0
|
#define rendered texture0
|
||||||
#define bloom texture1
|
#define bloom texture1
|
||||||
|
|
||||||
|
#ifdef GL_ES
|
||||||
|
// Dithering requires sufficient floating-point precision
|
||||||
|
#ifndef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
#undef ENABLE_DITHERING
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
struct ExposureParams {
|
struct ExposureParams {
|
||||||
float compensationFactor;
|
float compensationFactor;
|
||||||
};
|
};
|
||||||
@ -61,14 +68,17 @@ vec3 uncharted2Tonemap(vec3 x)
|
|||||||
|
|
||||||
vec4 applyToneMapping(vec4 color)
|
vec4 applyToneMapping(vec4 color)
|
||||||
{
|
{
|
||||||
const float exposureBias = 2.0;
|
color = vec4(pow(color.rgb, vec3(2.2)), color.a);
|
||||||
|
const float gamma = 1.6;
|
||||||
|
const float exposureBias = 5.5;
|
||||||
color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
|
color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
|
||||||
// Precalculated white_scale from
|
// Precalculated white_scale from
|
||||||
//vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
|
//vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
|
||||||
vec3 whiteScale = vec3(1.036015346);
|
vec3 whiteScale = vec3(1.036015346);
|
||||||
color.rgb *= whiteScale;
|
color.rgb *= whiteScale;
|
||||||
return color;
|
return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
vec3 applySaturation(vec3 color, float factor)
|
vec3 applySaturation(vec3 color, float factor)
|
||||||
{
|
{
|
||||||
@ -77,6 +87,19 @@ vec3 applySaturation(vec3 color, float factor)
|
|||||||
float brightness = dot(color, vec3(0.2125, 0.7154, 0.0721));
|
float brightness = dot(color, vec3(0.2125, 0.7154, 0.0721));
|
||||||
return mix(vec3(brightness), color, factor);
|
return mix(vec3(brightness), color, factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_DITHERING
|
||||||
|
// From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
|
||||||
|
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
|
||||||
|
// NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
|
||||||
|
vec3 screen_space_dither(highp vec2 frag_coord) {
|
||||||
|
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
|
||||||
|
highp vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord));
|
||||||
|
dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0));
|
||||||
|
|
||||||
|
// Subtract 0.5 to avoid slightly brightening the whole viewport.
|
||||||
|
return (dither.rgb - 0.5) / 255.0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
@ -105,25 +128,31 @@ void main(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef ENABLE_BLOOM
|
#ifdef ENABLE_BLOOM
|
||||||
color = applyBloom(color, uv);
|
color = applyBloom(color, uv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
color.rgb = clamp(color.rgb, vec3(0.), vec3(1.));
|
||||||
|
|
||||||
|
// return to sRGB colorspace (approximate)
|
||||||
|
color.rgb = pow(color.rgb, vec3(1.0 / 2.2));
|
||||||
|
|
||||||
#ifdef ENABLE_BLOOM_DEBUG
|
#ifdef ENABLE_BLOOM_DEBUG
|
||||||
if (uv.x > 0.5 || uv.y > 0.5)
|
if (uv.x > 0.5 || uv.y > 0.5)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
#if ENABLE_TONE_MAPPING
|
#if ENABLE_TONE_MAPPING
|
||||||
color = applyToneMapping(color);
|
color = applyToneMapping(color);
|
||||||
color.rgb = applySaturation(color.rgb, saturation);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
color.rgb = applySaturation(color.rgb, saturation);
|
||||||
}
|
}
|
||||||
|
|
||||||
color.rgb = clamp(color.rgb, vec3(0.), vec3(1.));
|
#ifdef ENABLE_DITHERING
|
||||||
|
// Apply dithering just before quantisation
|
||||||
// return to sRGB colorspace (approximate)
|
color.rgb += screen_space_dither(gl_FragCoord.xy);
|
||||||
color.rgb = pow(color.rgb, vec3(1.0 / 2.2));
|
#endif
|
||||||
|
|
||||||
gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image.
|
gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image.
|
||||||
}
|
}
|
||||||
|
115
client/shaders/volumetric_light/opengl_fragment.glsl
Normal file
115
client/shaders/volumetric_light/opengl_fragment.glsl
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#define rendered texture0
|
||||||
|
#define depthmap texture1
|
||||||
|
|
||||||
|
uniform sampler2D rendered;
|
||||||
|
uniform sampler2D depthmap;
|
||||||
|
|
||||||
|
uniform vec3 sunPositionScreen;
|
||||||
|
uniform float sunBrightness;
|
||||||
|
uniform vec3 moonPositionScreen;
|
||||||
|
uniform float moonBrightness;
|
||||||
|
|
||||||
|
uniform lowp float volumetricLightStrength;
|
||||||
|
|
||||||
|
uniform vec3 dayLight;
|
||||||
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
|
uniform vec3 v_LightDirection;
|
||||||
|
#else
|
||||||
|
const vec3 v_LightDirection = vec3(0.0, -1.0, 0.0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GL_ES
|
||||||
|
varying mediump vec2 varTexCoord;
|
||||||
|
#else
|
||||||
|
centroid varying vec2 varTexCoord;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const float far = 1000.;
|
||||||
|
float mapDepth(float depth)
|
||||||
|
{
|
||||||
|
return min(1., 1. / (1.00001 - depth) / far);
|
||||||
|
}
|
||||||
|
|
||||||
|
float noise(vec3 uvd) {
|
||||||
|
return fract(dot(sin(uvd * vec3(13041.19699, 27723.29171, 61029.77801)), vec3(73137.11101, 37312.92319, 10108.89991)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float sampleVolumetricLight(vec2 uv, vec3 lightVec, float rawDepth)
|
||||||
|
{
|
||||||
|
lightVec = 0.5 * lightVec / lightVec.z + 0.5;
|
||||||
|
const float samples = 30.;
|
||||||
|
float result = texture2D(depthmap, uv).r < 1. ? 0.0 : 1.0;
|
||||||
|
float bias = noise(vec3(uv, rawDepth));
|
||||||
|
vec2 samplepos;
|
||||||
|
for (float i = 1.; i < samples; i++) {
|
||||||
|
samplepos = mix(uv, lightVec.xy, (i + bias) / samples);
|
||||||
|
if (min(samplepos.x, samplepos.y) > 0. && max(samplepos.x, samplepos.y) < 1.)
|
||||||
|
result += texture2D(depthmap, samplepos).r < 1. ? 0.0 : 1.0;
|
||||||
|
}
|
||||||
|
return result / samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getDirectLightScatteringAtGround(vec3 v_LightDirection)
|
||||||
|
{
|
||||||
|
// Based on talk at 2002 Game Developers Conference by Naty Hoffman and Arcot J. Preetham
|
||||||
|
const float beta_r0 = 1e-5; // Rayleigh scattering beta
|
||||||
|
|
||||||
|
// These factors are calculated based on expected value of scattering factor of 1e-5
|
||||||
|
// for Nitrogen at 532nm (green), 2e25 molecules/m3 in atmosphere
|
||||||
|
const vec3 beta_r0_l = vec3(3.3362176e-01, 8.75378289198826e-01, 1.95342379700656) * beta_r0; // wavelength-dependent scattering
|
||||||
|
|
||||||
|
const float atmosphere_height = 15000.; // height of the atmosphere in meters
|
||||||
|
// sun/moon light at the ground level, after going through the atmosphere
|
||||||
|
return exp(-beta_r0_l * atmosphere_height / (1e-5 - dot(v_LightDirection, vec3(0., 1., 0.))));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 applyVolumetricLight(vec3 color, vec2 uv, float rawDepth)
|
||||||
|
{
|
||||||
|
vec3 lookDirection = normalize(vec3(uv.x * 2. - 1., uv.y * 2. - 1., rawDepth));
|
||||||
|
|
||||||
|
const float boost = 4.0;
|
||||||
|
float brightness = 0.;
|
||||||
|
vec3 sourcePosition = vec3(-1., -1., -1);
|
||||||
|
|
||||||
|
if (sunPositionScreen.z > 0. && sunBrightness > 0.) {
|
||||||
|
brightness = sunBrightness;
|
||||||
|
sourcePosition = sunPositionScreen;
|
||||||
|
}
|
||||||
|
else if (moonPositionScreen.z > 0. && moonBrightness > 0.) {
|
||||||
|
brightness = moonBrightness * 0.05;
|
||||||
|
sourcePosition = moonPositionScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cameraDirectionFactor = pow(clamp(dot(sourcePosition, vec3(0., 0., 1.)), 0.0, 0.7), 2.5);
|
||||||
|
float viewAngleFactor = pow(max(0., dot(sourcePosition, lookDirection)), 8.);
|
||||||
|
|
||||||
|
float lightFactor = brightness * sampleVolumetricLight(uv, sourcePosition, rawDepth) *
|
||||||
|
(0.05 * cameraDirectionFactor + 0.95 * viewAngleFactor);
|
||||||
|
|
||||||
|
color = mix(color, boost * getDirectLightScatteringAtGround(v_LightDirection) * dayLight, lightFactor);
|
||||||
|
|
||||||
|
// a factor of 5 tested well
|
||||||
|
color *= volumetricLightStrength * 5.0;
|
||||||
|
|
||||||
|
// if (sunPositionScreen.z < 0.)
|
||||||
|
// color.rg += 1. - clamp(abs((2. * uv.xy - 1.) - sunPositionScreen.xy / sunPositionScreen.z) * 1000., 0., 1.);
|
||||||
|
// if (moonPositionScreen.z < 0.)
|
||||||
|
// color.rg += 1. - clamp(abs((2. * uv.xy - 1.) - moonPositionScreen.xy / moonPositionScreen.z) * 1000., 0., 1.);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec2 uv = varTexCoord.st;
|
||||||
|
vec3 color = texture2D(rendered, uv).rgb;
|
||||||
|
// translate to linear colorspace (approximate)
|
||||||
|
color = pow(color, vec3(2.2));
|
||||||
|
|
||||||
|
if (volumetricLightStrength > 0.0) {
|
||||||
|
float rawDepth = texture2D(depthmap, uv).r;
|
||||||
|
|
||||||
|
color = applyVolumetricLight(color, uv, rawDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color, 1.0); // force full alpha to avoid holes in the image.
|
||||||
|
}
|
12
client/shaders/volumetric_light/opengl_vertex.glsl
Normal file
12
client/shaders/volumetric_light/opengl_vertex.glsl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifdef GL_ES
|
||||||
|
varying mediump vec2 varTexCoord;
|
||||||
|
#else
|
||||||
|
centroid varying vec2 varTexCoord;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
varTexCoord.st = inTexCoord0.st;
|
||||||
|
gl_Position = inVertexPosition;
|
||||||
|
}
|
@ -33,7 +33,7 @@ end)
|
|||||||
core.after(1, function()
|
core.after(1, function()
|
||||||
print("armor: " .. dump(core.localplayer:get_armor_groups()))
|
print("armor: " .. dump(core.localplayer:get_armor_groups()))
|
||||||
id = core.localplayer:hud_add({
|
id = core.localplayer:hud_add({
|
||||||
hud_elem_type = "text",
|
type = "text",
|
||||||
name = "example",
|
name = "example",
|
||||||
number = 0xff0000,
|
number = 0xff0000,
|
||||||
position = {x=0, y=1},
|
position = {x=0, y=1},
|
||||||
|
@ -1332,8 +1332,10 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
{
|
{
|
||||||
hud_elem_type = "image", -- see HUD element types, default "text"
|
type = "image", -- see HUD element types, default "text"
|
||||||
-- ^ type of HUD element, can be either of "image", "text", "statbar", or "inventory"
|
-- ^ type of HUD element, can be either of "image", "text", "statbar", or "inventory"
|
||||||
|
hud_elem_type = "image",
|
||||||
|
-- ^ Deprecated, same as `type`. In case both are specified `type` will be used.
|
||||||
position = {x=0.5, y=0.5},
|
position = {x=0.5, y=0.5},
|
||||||
-- ^ Left corner position of element, default `{x=0,y=0}`.
|
-- ^ Left corner position of element, default `{x=0,y=0}`.
|
||||||
name = "<name>", -- default ""
|
name = "<name>", -- default ""
|
||||||
|
@ -30,7 +30,7 @@ For openSUSE users:
|
|||||||
|
|
||||||
For Arch users:
|
For Arch users:
|
||||||
|
|
||||||
sudo pacman -S base-devel libcurl-gnutls cmake libxi libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd gettext
|
sudo pacman -S --needed base-devel libcurl-gnutls cmake libxi libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd gettext
|
||||||
|
|
||||||
For Alpine users:
|
For Alpine users:
|
||||||
|
|
||||||
|
@ -33,13 +33,15 @@ mkdir build
|
|||||||
cd build
|
cd build
|
||||||
|
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
|
||||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||||
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||||
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE
|
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE
|
||||||
|
|
||||||
make -j$(sysctl -n hw.logicalcpu)
|
make -j$(sysctl -n hw.logicalcpu)
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
# M1 Macs w/ MacOS >= BigSur
|
||||||
|
codesign --force --deep -s - macos/minetest.app
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
@ -5278,6 +5278,8 @@ Utilities
|
|||||||
-- liquid_fluidity, liquid_fluidity_smooth, liquid_sink,
|
-- liquid_fluidity, liquid_fluidity_smooth, liquid_sink,
|
||||||
-- acceleration_default, acceleration_air (5.8.0)
|
-- acceleration_default, acceleration_air (5.8.0)
|
||||||
physics_overrides_v2 = true,
|
physics_overrides_v2 = true,
|
||||||
|
-- In HUD definitions the field `type` is used and `hud_elem_type` is deprecated (5.9.0)
|
||||||
|
hud_def_type_field = true,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -5350,6 +5352,12 @@ Utilities
|
|||||||
-- HUD Scaling multiplier
|
-- HUD Scaling multiplier
|
||||||
-- Equal to the setting `hud_scaling` multiplied by `dpi / 96`
|
-- Equal to the setting `hud_scaling` multiplied by `dpi / 96`
|
||||||
real_hud_scaling = 1,
|
real_hud_scaling = 1,
|
||||||
|
|
||||||
|
-- Whether the touchscreen controls are enabled.
|
||||||
|
-- Usually (but not always) `true` on Android.
|
||||||
|
-- Requires at least Minetest 5.9.0 on the client. For older clients, it
|
||||||
|
-- is always set to `false`.
|
||||||
|
touch_controls = false,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -5412,9 +5420,8 @@ Utilities
|
|||||||
* `compression`: Optional zlib compression level, number in range 0 to 9.
|
* `compression`: Optional zlib compression level, number in range 0 to 9.
|
||||||
The data is one-dimensional, starting in the upper left corner of the image
|
The data is one-dimensional, starting in the upper left corner of the image
|
||||||
and laid out in scanlines going from left to right, then top to bottom.
|
and laid out in scanlines going from left to right, then top to bottom.
|
||||||
Please note that it's not safe to use string.char to generate raw data,
|
You can use `colorspec_to_bytes` to generate raw RGBA values.
|
||||||
use `colorspec_to_bytes` to generate raw RGBA values in a predictable way.
|
Palettes are not supported at the moment.
|
||||||
The resulting PNG image is always 32-bit. Palettes are not supported at the moment.
|
|
||||||
You may use this to procedurally generate textures during server init.
|
You may use this to procedurally generate textures during server init.
|
||||||
* `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a
|
* `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a
|
||||||
percent sign followed by two hex digits. See
|
percent sign followed by two hex digits. See
|
||||||
@ -7458,15 +7465,20 @@ child will follow movement and rotation of that bone.
|
|||||||
* Sets the position of the object.
|
* Sets the position of the object.
|
||||||
* No-op if object is attached.
|
* No-op if object is attached.
|
||||||
* `pos` is a vector `{x=num, y=num, z=num}`
|
* `pos` is a vector `{x=num, y=num, z=num}`
|
||||||
|
* `add_pos(pos)`:
|
||||||
|
* Changes position by adding to the current position.
|
||||||
|
* No-op if object is attached.
|
||||||
|
* `pos` is a vector `{x=num, y=num, z=num}`.
|
||||||
|
* In comparison to using `set_pos`, `add_pos` will avoid synchronization problems.
|
||||||
* `get_velocity()`: returns the velocity, a vector.
|
* `get_velocity()`: returns the velocity, a vector.
|
||||||
* `add_velocity(vel)`
|
* `add_velocity(vel)`
|
||||||
* Changes velocity by adding to the current velocity.
|
* Changes velocity by adding to the current velocity.
|
||||||
* `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}`
|
* `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}`
|
||||||
* In comparison to using get_velocity, adding the velocity and then using
|
* In comparison to using `get_velocity`, adding the velocity and then using
|
||||||
set_velocity, add_velocity is supposed to avoid synchronization problems.
|
`set_velocity`, `add_velocity` is supposed to avoid synchronization problems.
|
||||||
Additionally, players also do not support set_velocity.
|
Additionally, players also do not support `set_velocity`.
|
||||||
* If object is a player:
|
* If object is a player:
|
||||||
* Does not apply during free_move.
|
* Does not apply during `free_move`.
|
||||||
* Note that since the player speed is normalized at each move step,
|
* Note that since the player speed is normalized at each move step,
|
||||||
increasing e.g. Y velocity beyond what would usually be achieved
|
increasing e.g. Y velocity beyond what would usually be achieved
|
||||||
(see: physics overrides) will cause existing X/Z velocity to be reduced.
|
(see: physics overrides) will cause existing X/Z velocity to be reduced.
|
||||||
@ -7550,17 +7562,32 @@ child will follow movement and rotation of that bone.
|
|||||||
object.
|
object.
|
||||||
* `set_detach()`: Detaches object. No-op if object was not attached.
|
* `set_detach()`: Detaches object. No-op if object was not attached.
|
||||||
* `set_bone_position([bone, position, rotation])`
|
* `set_bone_position([bone, position, rotation])`
|
||||||
* `bone`: string. Default is `""`, the root bone
|
* Shorthand for `set_bone_override(bone, {position = position, rotation = rotation:apply(math.rad)})` using absolute values.
|
||||||
* `position`: `{x=num, y=num, z=num}`, relative, `default {x=0, y=0, z=0}`
|
* **Note:** Rotation is in degrees, not radians.
|
||||||
* `rotation`: `{x=num, y=num, z=num}`, default `{x=0, y=0, z=0}`
|
* **Deprecated:** Use `set_bone_override` instead.
|
||||||
* `get_bone_position(bone)`:
|
* `get_bone_position(bone)`: returns the previously set position and rotation of the bone
|
||||||
* returns bone parameters previously set by `set_bone_position`
|
* Shorthand for `get_bone_override(bone).position.vec, get_bone_override(bone).rotation.vec:apply(math.deg)`.
|
||||||
* returns `position, rotation` of the specified bone (as vectors)
|
* **Note:** Returned rotation is in degrees, not radians.
|
||||||
* note: position is relative to the object
|
* **Deprecated:** Use `get_bone_override` instead.
|
||||||
* `set_properties(object property table)`:
|
* `set_bone_override(bone, override)`
|
||||||
* set a number of object properties in the given table
|
* `bone`: string
|
||||||
* only properties listed in the table will be changed
|
* `override`: `{ position = property, rotation = property, scale = property }` or `nil`
|
||||||
* see the 'Object properties' section for details
|
* `property`: `{ vec = vector, interpolation = 0, absolute = false}` or `nil`;
|
||||||
|
* `vec` is in the same coordinate system as the model, and in degrees for rotation
|
||||||
|
* `property = nil` is equivalent to no override on that property
|
||||||
|
* `absolute`: If set to `false`, the override will be relative to the animated property:
|
||||||
|
* Transposition in the case of `position`;
|
||||||
|
* Composition in the case of `rotation`;
|
||||||
|
* Multiplication in the case of `scale`
|
||||||
|
* `interpolation`: Old and new values are interpolated over this timeframe (in seconds)
|
||||||
|
* `override = nil` (including omission) is shorthand for `override = {}` which clears the override
|
||||||
|
* **Note:** Unlike `set_bone_position`, the rotation is in radians, not degrees.
|
||||||
|
* Compatibility note: Clients prior to 5.9.0 only support absolute position and rotation.
|
||||||
|
All values are treated as absolute and are set immediately (no interpolation).
|
||||||
|
* `get_bone_override(bone)`: returns `override` in the above format
|
||||||
|
* **Note:** Unlike `get_bone_position`, the returned rotation is in radians, not degrees.
|
||||||
|
* `get_bone_overrides()`: returns all bone overrides as table `{[bonename] = override, ...}`
|
||||||
|
* `set_properties(object property table)`
|
||||||
* `get_properties()`: returns a table of all object properties
|
* `get_properties()`: returns a table of all object properties
|
||||||
* `is_player()`: returns true for players, false otherwise
|
* `is_player()`: returns true for players, false otherwise
|
||||||
* `get_nametag_attributes()`
|
* `get_nametag_attributes()`
|
||||||
@ -7770,7 +7797,7 @@ child will follow movement and rotation of that bone.
|
|||||||
* `hud_change(id, stat, value)`: change a value of a previously added HUD
|
* `hud_change(id, stat, value)`: change a value of a previously added HUD
|
||||||
element.
|
element.
|
||||||
* `stat` supports the same keys as in the hud definition table except for
|
* `stat` supports the same keys as in the hud definition table except for
|
||||||
`"hud_elem_type"`.
|
`"type"` (or the deprecated `"hud_elem_type"`).
|
||||||
* `hud_get(id)`: gets the HUD element definition structure of the specified ID
|
* `hud_get(id)`: gets the HUD element definition structure of the specified ID
|
||||||
* `hud_set_flags(flags)`: sets specified HUD flags of player.
|
* `hud_set_flags(flags)`: sets specified HUD flags of player.
|
||||||
* `flags`: A table with the following fields set to boolean values
|
* `flags`: A table with the following fields set to boolean values
|
||||||
@ -8001,9 +8028,8 @@ child will follow movement and rotation of that bone.
|
|||||||
* Passing no arguments resets lighting to its default values.
|
* Passing no arguments resets lighting to its default values.
|
||||||
* `light_definition` is a table with the following optional fields:
|
* `light_definition` is a table with the following optional fields:
|
||||||
* `saturation` sets the saturation (vividness; default: `1.0`).
|
* `saturation` sets the saturation (vividness; default: `1.0`).
|
||||||
values > 1 increase the saturation
|
* values > 1 increase the saturation
|
||||||
values in [0,1) decrease the saturation
|
* values in [0,1] decrease the saturation
|
||||||
* This value has no effect on clients who have the "Tone Mapping" shader disabled.
|
|
||||||
* `shadows` is a table that controls ambient shadows
|
* `shadows` is a table that controls ambient shadows
|
||||||
* `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness)
|
* `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness)
|
||||||
* This value has no effect on clients who have the "Dynamic Shadows" shader disabled.
|
* This value has no effect on clients who have the "Dynamic Shadows" shader disabled.
|
||||||
@ -8015,6 +8041,9 @@ child will follow movement and rotation of that bone.
|
|||||||
* `speed_dark_bright` set the speed of adapting to bright light (default: `1000.0`)
|
* `speed_dark_bright` set the speed of adapting to bright light (default: `1000.0`)
|
||||||
* `speed_bright_dark` set the speed of adapting to dark scene (default: `1000.0`)
|
* `speed_bright_dark` set the speed of adapting to dark scene (default: `1000.0`)
|
||||||
* `center_weight_power` set the power factor for center-weighted luminance measurement (default: `1.0`)
|
* `center_weight_power` set the power factor for center-weighted luminance measurement (default: `1.0`)
|
||||||
|
* `volumetric_light`: is a table that controls volumetric light (a.k.a. "godrays")
|
||||||
|
* `strength`: sets the strength of the volumetric light effect from 0 (off, default) to 1 (strongest)
|
||||||
|
* This value has no effect on clients who have the "Volumetric Lighting" or "Bloom" shaders disabled.
|
||||||
|
|
||||||
* `get_lighting()`: returns the current state of lighting for the player.
|
* `get_lighting()`: returns the current state of lighting for the player.
|
||||||
* Result is a table with the same fields as `light_definition` in `set_lighting`.
|
* Result is a table with the same fields as `light_definition` in `set_lighting`.
|
||||||
@ -10027,9 +10056,14 @@ Used by `ObjectRef:hud_add`. Returned by `ObjectRef:hud_get`.
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
{
|
{
|
||||||
hud_elem_type = "image",
|
type = "image",
|
||||||
-- Type of element, can be "image", "text", "statbar", "inventory",
|
-- Type of element, can be "image", "text", "statbar", "inventory",
|
||||||
-- "waypoint", "image_waypoint", "compass" or "minimap"
|
-- "waypoint", "image_waypoint", "compass" or "minimap"
|
||||||
|
-- If undefined "text" will be used.
|
||||||
|
|
||||||
|
hud_elem_type = "image",
|
||||||
|
-- Deprecated, same as `type`.
|
||||||
|
-- In case both are specified `type` will be used.
|
||||||
|
|
||||||
position = {x=0.5, y=0.5},
|
position = {x=0.5, y=0.5},
|
||||||
-- Top left corner position of element
|
-- Top left corner position of element
|
||||||
|
@ -38,7 +38,9 @@ Functions
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
* `core.start()`
|
* `core.start()`
|
||||||
|
* start game session
|
||||||
* `core.close()`
|
* `core.close()`
|
||||||
|
* exit engine
|
||||||
* `core.get_min_supp_proto()`
|
* `core.get_min_supp_proto()`
|
||||||
* returns the minimum supported network protocol version
|
* returns the minimum supported network protocol version
|
||||||
* `core.get_max_supp_proto()`
|
* `core.get_max_supp_proto()`
|
||||||
@ -53,6 +55,10 @@ Functions
|
|||||||
* Android only. Shares file using the share popup
|
* Android only. Shares file using the share popup
|
||||||
* `core.get_version()` (possible in async calls)
|
* `core.get_version()` (possible in async calls)
|
||||||
* returns current core version
|
* returns current core version
|
||||||
|
* `core.set_once(key, value)`:
|
||||||
|
* save a string value that persists even if menu is closed
|
||||||
|
* `core.get_once(key)`:
|
||||||
|
* get a string value saved by above function, or `nil`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -249,6 +255,10 @@ GUI
|
|||||||
-- HUD Scaling multiplier
|
-- HUD Scaling multiplier
|
||||||
-- Equal to the setting `hud_scaling` multiplied by `dpi / 96`
|
-- Equal to the setting `hud_scaling` multiplied by `dpi / 96`
|
||||||
real_hud_scaling = 1,
|
real_hud_scaling = 1,
|
||||||
|
|
||||||
|
-- Whether the touchscreen controls are enabled.
|
||||||
|
-- Usually (but not always) `true` on Android.
|
||||||
|
touch_controls = false,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -133,11 +133,6 @@ are placeholders intended to be overwritten by the game.
|
|||||||
|
|
||||||
### Android textures
|
### Android textures
|
||||||
|
|
||||||
* `down_arrow.png`
|
|
||||||
* `left_arrow.png`
|
|
||||||
* `right_arrow.png`
|
|
||||||
* `up_arrow.png`
|
|
||||||
|
|
||||||
* `drop_btn.png`
|
* `drop_btn.png`
|
||||||
* `fast_btn.png`
|
* `fast_btn.png`
|
||||||
* `fly_btn.png`
|
* `fly_btn.png`
|
||||||
|
@ -66,7 +66,7 @@ minetest.register_globalstep(function()
|
|||||||
local ent = pointed_thing.ref:get_luaentity()
|
local ent = pointed_thing.ref:get_luaentity()
|
||||||
if ent and ent.name == "testentities:selectionbox" then
|
if ent and ent.name == "testentities:selectionbox" then
|
||||||
hud_ids[pname] = hud_id or player:hud_add({
|
hud_ids[pname] = hud_id or player:hud_add({
|
||||||
hud_elem_type = "text", -- See HUD element types
|
type = "text", -- See HUD element types
|
||||||
position = {x=0.5, y=0.5},
|
position = {x=0.5, y=0.5},
|
||||||
text = "X",
|
text = "X",
|
||||||
number = 0xFF0000,
|
number = 0xFF0000,
|
||||||
|
@ -7,6 +7,8 @@ local function show_fullscreen_fs(name)
|
|||||||
print(dump(window))
|
print(dump(window))
|
||||||
|
|
||||||
local size = { x = window.max_formspec_size.x * 1.1, y = window.max_formspec_size.y * 1.1 }
|
local size = { x = window.max_formspec_size.x * 1.1, y = window.max_formspec_size.y * 1.1 }
|
||||||
|
local touch_text = window.touch_controls and "Touch controls enabled" or
|
||||||
|
"Touch controls disabled"
|
||||||
local fs = {
|
local fs = {
|
||||||
"formspec_version[4]",
|
"formspec_version[4]",
|
||||||
("size[%f,%f]"):format(size.x, size.y),
|
("size[%f,%f]"):format(size.x, size.y),
|
||||||
@ -16,7 +18,8 @@ local function show_fullscreen_fs(name)
|
|||||||
("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, size.y - 1, "br", "BR"),
|
("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, size.y - 1, "br", "BR"),
|
||||||
("button[%f,%f;1,1;%s;%s]"):format(0, size.y - 1, "bl", "BL"),
|
("button[%f,%f;1,1;%s;%s]"):format(0, size.y - 1, "bl", "BL"),
|
||||||
|
|
||||||
("label[%f,%f;%s]"):format(size.x / 2, size.y / 2, "Fullscreen")
|
("label[%f,%f;%s]"):format(size.x / 2, size.y / 2, "Fullscreen"),
|
||||||
|
("label[%f,%f;%s]"):format(size.x / 2, size.y / 2 + 1, touch_text),
|
||||||
}
|
}
|
||||||
|
|
||||||
minetest.show_formspec(name, "testfullscreenfs:fs", table.concat(fs))
|
minetest.show_formspec(name, "testfullscreenfs:fs", table.concat(fs))
|
||||||
|
@ -12,7 +12,7 @@ local font_states = {
|
|||||||
|
|
||||||
|
|
||||||
local font_default_def = {
|
local font_default_def = {
|
||||||
hud_elem_type = "text",
|
type = "text",
|
||||||
position = {x = 0.5, y = 0.5},
|
position = {x = 0.5, y = 0.5},
|
||||||
scale = {x = 2, y = 2},
|
scale = {x = 2, y = 2},
|
||||||
alignment = { x = 0, y = 0 },
|
alignment = { x = 0, y = 0 },
|
||||||
@ -102,14 +102,14 @@ minetest.register_chatcommand("hudwaypoints", {
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local regular = player:hud_add {
|
local regular = player:hud_add {
|
||||||
hud_elem_type = "waypoint",
|
type = "waypoint",
|
||||||
name = "regular waypoint",
|
name = "regular waypoint",
|
||||||
text = "m",
|
text = "m",
|
||||||
number = 0xFFFFFF,
|
number = 0xFFFFFF,
|
||||||
world_pos = vector.add(player:get_pos(), {x = 0, y = 1.5, z = 0})
|
world_pos = vector.add(player:get_pos(), {x = 0, y = 1.5, z = 0})
|
||||||
}
|
}
|
||||||
local reduced_precision = player:hud_add {
|
local reduced_precision = player:hud_add {
|
||||||
hud_elem_type = "waypoint",
|
type = "waypoint",
|
||||||
name = "imprecise waypoint",
|
name = "imprecise waypoint",
|
||||||
text = "m (0.1 steps, precision = 10)",
|
text = "m (0.1 steps, precision = 10)",
|
||||||
precision = 10,
|
precision = 10,
|
||||||
@ -117,7 +117,7 @@ minetest.register_chatcommand("hudwaypoints", {
|
|||||||
world_pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0})
|
world_pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0})
|
||||||
}
|
}
|
||||||
local hidden_distance = player:hud_add {
|
local hidden_distance = player:hud_add {
|
||||||
hud_elem_type = "waypoint",
|
type = "waypoint",
|
||||||
name = "waypoint with hidden distance",
|
name = "waypoint with hidden distance",
|
||||||
text = "this text is hidden as well (precision = 0)",
|
text = "this text is hidden as well (precision = 0)",
|
||||||
precision = 0,
|
precision = 0,
|
||||||
@ -149,7 +149,7 @@ minetest.register_chatcommand("hudwaypoints", {
|
|||||||
minetest.after(0.5, change, player)
|
minetest.after(0.5, change, player)
|
||||||
end
|
end
|
||||||
local image_waypoint = player:hud_add {
|
local image_waypoint = player:hud_add {
|
||||||
hud_elem_type = "image_waypoint",
|
type = "image_waypoint",
|
||||||
text = "testhud_waypoint.png",
|
text = "testhud_waypoint.png",
|
||||||
world_pos = player:get_pos(),
|
world_pos = player:get_pos(),
|
||||||
scale = {x = 3, y = 3},
|
scale = {x = 3, y = 3},
|
||||||
|
@ -105,6 +105,19 @@ local function gen_checkers(w, h, tile)
|
|||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- The engine should perform color reduction of the generated PNG in certain
|
||||||
|
-- cases, so we have this helper to check the result
|
||||||
|
local function encode_and_check(w, h, ctype, data)
|
||||||
|
local ret = core.encode_png(w, h, data)
|
||||||
|
assert(ret:sub(1, 8) == "\137PNG\r\n\026\n", "missing png signature")
|
||||||
|
assert(ret:sub(9, 16) == "\000\000\000\rIHDR", "didn't find ihdr chunk")
|
||||||
|
local ctype_actual = ret:byte(26) -- Color Type (1 byte)
|
||||||
|
ctype = ({rgba=6, rgb=2, gray=0})[ctype]
|
||||||
|
assert(ctype_actual == ctype, "png should have color type " .. ctype ..
|
||||||
|
" but actually has " .. ctype_actual)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
local fractal = mandelbrot(512, 512, 128)
|
local fractal = mandelbrot(512, 512, 128)
|
||||||
local frac_emb = mandelbrot(64, 64, 64)
|
local frac_emb = mandelbrot(64, 64, 64)
|
||||||
local checker = gen_checkers(512, 512, 32)
|
local checker = gen_checkers(512, 512, 32)
|
||||||
@ -129,17 +142,21 @@ for i=1, #fractal do
|
|||||||
b = floor(abs(1 - fractal[i]) * 255),
|
b = floor(abs(1 - fractal[i]) * 255),
|
||||||
a = 255,
|
a = 255,
|
||||||
}
|
}
|
||||||
data_ck[i] = checker[i] > 0 and "#F80" or "#000"
|
data_ck[i] = checker[i] > 0 and "#888" or "#000"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
fractal = nil
|
||||||
|
frac_emb = nil
|
||||||
|
checker = nil
|
||||||
|
|
||||||
local textures_path = minetest.get_modpath( minetest.get_current_modname() ) .. "/textures/"
|
local textures_path = minetest.get_modpath( minetest.get_current_modname() ) .. "/textures/"
|
||||||
minetest.safe_file_write(
|
minetest.safe_file_write(
|
||||||
textures_path .. "testnodes_generated_mb.png",
|
textures_path .. "testnodes_generated_mb.png",
|
||||||
minetest.encode_png(512,512,data_mb)
|
encode_and_check(512, 512, "rgb", data_mb)
|
||||||
)
|
)
|
||||||
minetest.safe_file_write(
|
minetest.safe_file_write(
|
||||||
textures_path .. "testnodes_generated_ck.png",
|
textures_path .. "testnodes_generated_ck.png",
|
||||||
minetest.encode_png(512,512,data_ck)
|
encode_and_check(512, 512, "gray", data_ck)
|
||||||
)
|
)
|
||||||
|
|
||||||
minetest.register_node("testnodes:generated_png_mb", {
|
minetest.register_node("testnodes:generated_png_mb", {
|
||||||
@ -155,7 +172,8 @@ minetest.register_node("testnodes:generated_png_ck", {
|
|||||||
groups = { dig_immediate = 2 },
|
groups = { dig_immediate = 2 },
|
||||||
})
|
})
|
||||||
|
|
||||||
local png_emb = "[png:" .. minetest.encode_base64(minetest.encode_png(64,64,data_emb))
|
local png_emb = "[png:" .. minetest.encode_base64(
|
||||||
|
encode_and_check(64, 64, "rgba", data_emb))
|
||||||
|
|
||||||
minetest.register_node("testnodes:generated_png_emb", {
|
minetest.register_node("testnodes:generated_png_emb", {
|
||||||
description = S("Generated In-Band Mandelbrot PNG Test Node"),
|
description = S("Generated In-Band Mandelbrot PNG Test Node"),
|
||||||
@ -182,6 +200,10 @@ minetest.register_node("testnodes:generated_png_dst_emb", {
|
|||||||
groups = { dig_immediate = 2 },
|
groups = { dig_immediate = 2 },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
data_emb = nil
|
||||||
|
data_mb = nil
|
||||||
|
data_ck = nil
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
|
|
||||||
The following nodes can be used to demonstrate the TGA format support.
|
The following nodes can be used to demonstrate the TGA format support.
|
||||||
|
@ -68,3 +68,19 @@ local function run_player_meta_tests(player)
|
|||||||
assert(meta:equals(meta2))
|
assert(meta:equals(meta2))
|
||||||
end
|
end
|
||||||
unittests.register("test_player_meta", run_player_meta_tests, {player=true})
|
unittests.register("test_player_meta", run_player_meta_tests, {player=true})
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Player add pos
|
||||||
|
--
|
||||||
|
local function run_player_add_pos_tests(player)
|
||||||
|
local pos = player:get_pos()
|
||||||
|
player:add_pos(vector.new(0, 1000, 0))
|
||||||
|
local newpos = player:get_pos()
|
||||||
|
player:add_pos(vector.new(0, -1000, 0))
|
||||||
|
local backpos = player:get_pos()
|
||||||
|
local newdist = vector.distance(pos, newpos)
|
||||||
|
assert(math.abs(newdist - 1000) <= 1)
|
||||||
|
assert(vector.distance(pos, backpos) <= 1)
|
||||||
|
end
|
||||||
|
unittests.register("test_player_add_pos", run_player_add_pos_tests, {player=true})
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
# type: float min: 0 max: 0.99
|
# type: float min: 0 max: 0.99
|
||||||
# camera_smoothing = 0.0
|
# camera_smoothing = 0.0
|
||||||
|
|
||||||
# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Change Keys.
|
# Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Controls.
|
||||||
# type: float min: 0 max: 0.99
|
# type: float min: 0 max: 0.99
|
||||||
# cinematic_camera_smoothing = 0.7
|
# cinematic_camera_smoothing = 0.7
|
||||||
|
|
||||||
@ -787,6 +787,15 @@
|
|||||||
# type: bool
|
# type: bool
|
||||||
# strict_protocol_version_checking = false
|
# strict_protocol_version_checking = false
|
||||||
|
|
||||||
|
# Define the oldest clients allowed to connect.
|
||||||
|
# Older clients are compatible in the sense that they will not crash when connecting
|
||||||
|
# to new servers, but they may not support all new features that you are expecting.
|
||||||
|
# This allows more fine-grained control than strict_protocol_version_checking.
|
||||||
|
# Minetest may still enforce its own internal minimum, and enabling
|
||||||
|
# strict_protocol_version_checking will effectively override this.
|
||||||
|
# type: int min: 1 max: 65535
|
||||||
|
# protocol_version_min = 1
|
||||||
|
|
||||||
# Specifies URL from which client fetches media instead of using UDP.
|
# Specifies URL from which client fetches media instead of using UDP.
|
||||||
# $filename should be accessible from $remote_media$filename via cURL
|
# $filename should be accessible from $remote_media$filename via cURL
|
||||||
# (obviously, remote_media should end with a slash).
|
# (obviously, remote_media should end with a slash).
|
||||||
@ -3654,4 +3663,3 @@
|
|||||||
# See https://github.com/minetest/irrlicht/blob/master/include/Keycodes.h
|
# See https://github.com/minetest/irrlicht/blob/master/include/Keycodes.h
|
||||||
# type: key
|
# type: key
|
||||||
# keymap_decrease_viewing_range_min = -
|
# keymap_decrease_viewing_range_min = -
|
||||||
|
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: minetest
|
|
||||||
name: minetest
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: minetest
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: minetest
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- image: registry.gitlab.com/minetest/minetest/server:master
|
|
||||||
name: minetest
|
|
||||||
ports:
|
|
||||||
- containerPort: 30000
|
|
||||||
protocol: UDP
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /var/lib/minetest
|
|
||||||
name: minetest-data
|
|
||||||
- mountPath: /etc/minetest
|
|
||||||
name: config
|
|
||||||
restartPolicy: Always
|
|
||||||
volumes:
|
|
||||||
- name: minetest-data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: minetest-data
|
|
||||||
- configMap:
|
|
||||||
defaultMode: 420
|
|
||||||
name: minetest
|
|
||||||
name: config
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: minetest
|
|
||||||
name: minetest
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
- name: minetest
|
|
||||||
port: 30000
|
|
||||||
protocol: UDP
|
|
||||||
selector:
|
|
||||||
app: minetest
|
|
||||||
type: NodePort
|
|
@ -12,6 +12,7 @@
|
|||||||
<windowsSettings>
|
<windowsSettings>
|
||||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||||
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
|
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
|
||||||
|
<heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
|
||||||
</windowsSettings>
|
</windowsSettings>
|
||||||
</application>
|
</application>
|
||||||
</assembly>
|
</assembly>
|
||||||
|
@ -314,6 +314,18 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# On clang and gcc, some functionalities of std::atomic require -latomic.
|
||||||
|
# See <https://en.cppreference.com/w/cpp/atomic/atomic#Notes>.
|
||||||
|
# Note that find_library does not reliably find it so we have to resort to this.
|
||||||
|
# Also, passing -latomic is not always the same as adding atomic to the library list.
|
||||||
|
include(CheckCSourceCompiles)
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES "-latomic")
|
||||||
|
check_c_source_compiles("int main(){}" HAVE_LINK_ATOMIC)
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES "")
|
||||||
|
if(HAVE_LINK_ATOMIC)
|
||||||
|
set(PLATFORM_LIBS ${PLATFORM_LIBS} "-latomic")
|
||||||
|
endif()
|
||||||
|
|
||||||
check_include_files(endian.h HAVE_ENDIAN_H)
|
check_include_files(endian.h HAVE_ENDIAN_H)
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
@ -761,7 +773,9 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
set(OTHER_FLAGS "${OTHER_FLAGS} -mthreads -fexceptions")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
set(OTHER_FLAGS "${OTHER_FLAGS} -mthreads")
|
||||||
|
endif()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WIN32_WINNT=0x0601 -DWIN32_LEAN_AND_MEAN")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WIN32_WINNT=0x0601 -DWIN32_LEAN_AND_MEAN")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -801,7 +815,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mwindows")
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-mwindows")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -21,7 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#include "irr_aabb3d.h"
|
#include "irr_aabb3d.h"
|
||||||
#include "irr_v3d.h"
|
#include "irr_v3d.h"
|
||||||
|
#include <quaternion.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
enum ActiveObjectType {
|
enum ActiveObjectType {
|
||||||
@ -72,6 +74,78 @@ enum ActiveObjectCommand {
|
|||||||
AO_CMD_SET_ANIMATION_SPEED
|
AO_CMD_SET_ANIMATION_SPEED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BoneOverride
|
||||||
|
{
|
||||||
|
struct PositionProperty
|
||||||
|
{
|
||||||
|
v3f previous;
|
||||||
|
v3f vector;
|
||||||
|
bool absolute = false;
|
||||||
|
f32 interp_timer = 0;
|
||||||
|
} position;
|
||||||
|
|
||||||
|
v3f getPosition(v3f anim_pos) const {
|
||||||
|
f32 progress = dtime_passed / position.interp_timer;
|
||||||
|
if (progress > 1.0f || position.interp_timer == 0.0f)
|
||||||
|
progress = 1.0f;
|
||||||
|
return position.vector.getInterpolated(position.previous, progress)
|
||||||
|
+ (position.absolute ? v3f() : anim_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RotationProperty
|
||||||
|
{
|
||||||
|
core::quaternion previous;
|
||||||
|
core::quaternion next;
|
||||||
|
bool absolute = false;
|
||||||
|
f32 interp_timer = 0;
|
||||||
|
} rotation;
|
||||||
|
|
||||||
|
v3f getRotationEulerDeg(v3f anim_rot_euler) const {
|
||||||
|
core::quaternion rot;
|
||||||
|
|
||||||
|
f32 progress = dtime_passed / rotation.interp_timer;
|
||||||
|
if (progress > 1.0f || rotation.interp_timer == 0.0f)
|
||||||
|
progress = 1.0f;
|
||||||
|
rot.slerp(rotation.previous, rotation.next, progress);
|
||||||
|
if (!rotation.absolute) {
|
||||||
|
core::quaternion anim_rot(anim_rot_euler * core::DEGTORAD);
|
||||||
|
rot = rot * anim_rot; // first rotate by anim. bone rot., then rot.
|
||||||
|
}
|
||||||
|
|
||||||
|
v3f rot_euler;
|
||||||
|
rot.toEuler(rot_euler);
|
||||||
|
return rot_euler * core::RADTODEG;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScaleProperty
|
||||||
|
{
|
||||||
|
v3f previous;
|
||||||
|
v3f vector{1, 1, 1};
|
||||||
|
bool absolute = false;
|
||||||
|
f32 interp_timer = 0;
|
||||||
|
} scale;
|
||||||
|
|
||||||
|
v3f getScale(v3f anim_scale) const {
|
||||||
|
f32 progress = dtime_passed / scale.interp_timer;
|
||||||
|
if (progress > 1.0f || scale.interp_timer == 0.0f)
|
||||||
|
progress = 1.0f;
|
||||||
|
return scale.vector.getInterpolated(scale.previous, progress)
|
||||||
|
* (scale.absolute ? v3f(1) : anim_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 dtime_passed = 0;
|
||||||
|
|
||||||
|
bool isIdentity() const
|
||||||
|
{
|
||||||
|
return !position.absolute && position.vector == v3f()
|
||||||
|
&& !rotation.absolute && rotation.next == core::quaternion()
|
||||||
|
&& !scale.absolute && scale.vector == v3f(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unordered_map<std::string, BoneOverride> BoneOverrideMap;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Parent class for ServerActiveObject and ClientActiveObject
|
Parent class for ServerActiveObject and ClientActiveObject
|
||||||
*/
|
*/
|
||||||
|
@ -2,6 +2,7 @@ set (BENCHMARK_SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapblock.cpp
|
||||||
PARENT_SCOPE)
|
PARENT_SCOPE)
|
||||||
|
|
||||||
set (BENCHMARK_CLIENT_SRCS
|
set (BENCHMARK_CLIENT_SRCS
|
||||||
|
@ -23,10 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#define CATCH_CONFIG_RUNNER
|
#define CATCH_CONFIG_RUNNER
|
||||||
#include "benchmark_setup.h"
|
#include "benchmark_setup.h"
|
||||||
|
|
||||||
int run_benchmarks()
|
bool run_benchmarks(const char *arg)
|
||||||
{
|
{
|
||||||
int argc = 1;
|
const char *const argv[] = {
|
||||||
const char *argv[] = { "MinetestBenchmark", NULL };
|
"MinetestBenchmark", arg, nullptr
|
||||||
|
};
|
||||||
|
const int argc = arg ? 2 : 1;
|
||||||
int errCount = Catch::Session().run(argc, argv);
|
int errCount = Catch::Session().run(argc, argv);
|
||||||
return errCount ? 1 : 0;
|
return errCount == 0;
|
||||||
}
|
}
|
||||||
|
@ -22,5 +22,5 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#if BUILD_BENCHMARKS
|
#if BUILD_BENCHMARKS
|
||||||
extern int run_benchmarks();
|
extern bool run_benchmarks(const char *arg = nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
187
src/benchmark/benchmark_mapblock.cpp
Normal file
187
src/benchmark/benchmark_mapblock.cpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
Minetest
|
||||||
|
Copyright (C) 2023 Minetest Authors
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "benchmark_setup.h"
|
||||||
|
#include "mapblock.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef std::vector<MapBlock*> MBContainer;
|
||||||
|
|
||||||
|
static void allocateSome(MBContainer &vec, u32 n)
|
||||||
|
{
|
||||||
|
vec.reserve(vec.size() + n);
|
||||||
|
for (u32 i = 0; i < n; i++) {
|
||||||
|
auto *mb = new MapBlock({i & 0xff, 0, i >> 8}, nullptr);
|
||||||
|
vec.push_back(mb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeSome(MBContainer &vec, u32 n)
|
||||||
|
{
|
||||||
|
// deallocate from end since that has no cost moving data inside the vector
|
||||||
|
u32 start_i = 0;
|
||||||
|
if (vec.size() > n)
|
||||||
|
start_i = vec.size() - n;
|
||||||
|
for (u32 i = start_i; i < vec.size(); i++)
|
||||||
|
delete vec[i];
|
||||||
|
vec.resize(start_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void freeAll(MBContainer &vec) { freeSome(vec, vec.size()); }
|
||||||
|
|
||||||
|
// usage patterns inspired by ClientMap::updateDrawList()
|
||||||
|
static void workOnMetadata(const MBContainer &vec)
|
||||||
|
{
|
||||||
|
for (MapBlock *block : vec) {
|
||||||
|
#ifndef SERVER
|
||||||
|
bool foo = !!block->mesh;
|
||||||
|
#else
|
||||||
|
bool foo = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (block->refGet() > 2)
|
||||||
|
block->refDrop();
|
||||||
|
|
||||||
|
v3s16 pos = block->getPos() * MAP_BLOCKSIZE;
|
||||||
|
if (foo)
|
||||||
|
pos += v3s16(MAP_BLOCKSIZE / 2);
|
||||||
|
|
||||||
|
if (pos.getDistanceFrom(v3s16(0)) > 30000)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
block->resetUsageTimer();
|
||||||
|
block->refGrab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// usage patterns inspired by LBMManager::applyLBMs()
|
||||||
|
static u32 workOnNodes(const MBContainer &vec)
|
||||||
|
{
|
||||||
|
u32 foo = 0;
|
||||||
|
for (MapBlock *block : vec) {
|
||||||
|
block->resetUsageTimer();
|
||||||
|
|
||||||
|
if (block->isOrphan())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
v3s16 pos_of_block = block->getPosRelative();
|
||||||
|
v3s16 pos;
|
||||||
|
MapNode n;
|
||||||
|
for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) {
|
||||||
|
for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) {
|
||||||
|
for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
|
||||||
|
n = block->getNodeNoCheck(pos);
|
||||||
|
|
||||||
|
if (n.getContent() == CONTENT_AIR) {
|
||||||
|
auto p = pos + pos_of_block;
|
||||||
|
foo ^= p.X + p.Y + p.Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// usage patterns inspired by ABMHandler::apply()
|
||||||
|
// touches both metadata and node data at the same time
|
||||||
|
static u32 workOnBoth(const MBContainer &vec)
|
||||||
|
{
|
||||||
|
int foo = 0;
|
||||||
|
for (MapBlock *block : vec) {
|
||||||
|
block->contents.clear();
|
||||||
|
|
||||||
|
bool want_contents_cached = block->contents.empty() && !block->do_not_cache_contents;
|
||||||
|
|
||||||
|
v3s16 p0;
|
||||||
|
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
|
||||||
|
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
|
||||||
|
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
|
||||||
|
{
|
||||||
|
MapNode n = block->getNodeNoCheck(p0);
|
||||||
|
content_t c = n.getContent();
|
||||||
|
|
||||||
|
if (want_contents_cached && !CONTAINS(block->contents, c)) {
|
||||||
|
if (block->contents.size() >= 10) {
|
||||||
|
want_contents_cached = false;
|
||||||
|
block->do_not_cache_contents = true;
|
||||||
|
block->contents.clear();
|
||||||
|
block->contents.shrink_to_fit();
|
||||||
|
} else {
|
||||||
|
block->contents.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foo += block->contents.size();
|
||||||
|
}
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BENCH1(_count) \
|
||||||
|
BENCHMARK_ADVANCED("allocate_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
const u32 pcount = _count / meter.runs(); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
allocateSome(vec, pcount); \
|
||||||
|
return vec.size(); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
}; \
|
||||||
|
BENCHMARK_ADVANCED("testCase1_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
allocateSome(vec, _count); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
workOnMetadata(vec); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
}; \
|
||||||
|
BENCHMARK_ADVANCED("testCase2_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
allocateSome(vec, _count); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
return workOnNodes(vec); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
}; \
|
||||||
|
BENCHMARK_ADVANCED("testCase3_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
allocateSome(vec, _count); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
return workOnBoth(vec); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
}; \
|
||||||
|
BENCHMARK_ADVANCED("free_" #_count)(Catch::Benchmark::Chronometer meter) { \
|
||||||
|
MBContainer vec; \
|
||||||
|
allocateSome(vec, _count); \
|
||||||
|
/* catch2 does multiple runs so we have to be careful to not dealloc too many */ \
|
||||||
|
const u32 pcount = _count / meter.runs(); \
|
||||||
|
meter.measure([&] { \
|
||||||
|
freeSome(vec, pcount); \
|
||||||
|
return vec.size(); \
|
||||||
|
}); \
|
||||||
|
freeAll(vec); \
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("benchmark_mapblock") {
|
||||||
|
BENCH1(900)
|
||||||
|
BENCH1(2200)
|
||||||
|
BENCH1(7500) // <- default client_mapblock_limit
|
||||||
|
}
|
@ -2,6 +2,7 @@ set(sound_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/sound.cpp)
|
|||||||
|
|
||||||
if(USE_SOUND)
|
if(USE_SOUND)
|
||||||
set(sound_SRCS ${sound_SRCS}
|
set(sound_SRCS ${sound_SRCS}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/sound/al_extensions.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/sound/al_helpers.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/sound/al_helpers.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/sound/ogg_file.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/sound/ogg_file.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/sound/playing_sound.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/sound/playing_sound.cpp
|
||||||
|
@ -50,7 +50,6 @@ void ActiveObjectMgr::step(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
bool ActiveObjectMgr::registerObject(std::unique_ptr<ClientActiveObject> obj)
|
bool ActiveObjectMgr::registerObject(std::unique_ptr<ClientActiveObject> obj)
|
||||||
{
|
{
|
||||||
assert(obj); // Pre-condition
|
assert(obj); // Pre-condition
|
||||||
@ -93,7 +92,6 @@ void ActiveObjectMgr::removeObject(u16 id)
|
|||||||
obj->removeFromScene(true);
|
obj->removeFromScene(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format on
|
|
||||||
void ActiveObjectMgr::getActiveObjects(const v3f &origin, f32 max_d,
|
void ActiveObjectMgr::getActiveObjects(const v3f &origin, f32 max_d,
|
||||||
std::vector<DistanceSortedActiveObject> &dest)
|
std::vector<DistanceSortedActiveObject> &dest)
|
||||||
{
|
{
|
||||||
|
@ -97,7 +97,6 @@ void PacketCounter::print(std::ostream &o) const
|
|||||||
Client::Client(
|
Client::Client(
|
||||||
const char *playername,
|
const char *playername,
|
||||||
const std::string &password,
|
const std::string &password,
|
||||||
const std::string &address_name,
|
|
||||||
MapDrawControl &control,
|
MapDrawControl &control,
|
||||||
IWritableTextureSource *tsrc,
|
IWritableTextureSource *tsrc,
|
||||||
IWritableShaderSource *shsrc,
|
IWritableShaderSource *shsrc,
|
||||||
@ -106,7 +105,6 @@ Client::Client(
|
|||||||
ISoundManager *sound,
|
ISoundManager *sound,
|
||||||
MtEventManager *event,
|
MtEventManager *event,
|
||||||
RenderingEngine *rendering_engine,
|
RenderingEngine *rendering_engine,
|
||||||
bool ipv6,
|
|
||||||
GameUI *game_ui,
|
GameUI *game_ui,
|
||||||
ELoginRegister allow_login_or_register
|
ELoginRegister allow_login_or_register
|
||||||
):
|
):
|
||||||
@ -123,8 +121,6 @@ Client::Client(
|
|||||||
tsrc, this
|
tsrc, this
|
||||||
),
|
),
|
||||||
m_particle_manager(std::make_unique<ParticleManager>(&m_env)),
|
m_particle_manager(std::make_unique<ParticleManager>(&m_env)),
|
||||||
m_con(new con::Connection(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this)),
|
|
||||||
m_address_name(address_name),
|
|
||||||
m_allow_login_or_register(allow_login_or_register),
|
m_allow_login_or_register(allow_login_or_register),
|
||||||
m_server_ser_ver(SER_FMT_VER_INVALID),
|
m_server_ser_ver(SER_FMT_VER_INVALID),
|
||||||
m_last_chat_message_sent(time(NULL)),
|
m_last_chat_message_sent(time(NULL)),
|
||||||
@ -338,7 +334,8 @@ bool Client::isShutdown()
|
|||||||
Client::~Client()
|
Client::~Client()
|
||||||
{
|
{
|
||||||
m_shutdown = true;
|
m_shutdown = true;
|
||||||
m_con->Disconnect();
|
if (m_con)
|
||||||
|
m_con->Disconnect();
|
||||||
|
|
||||||
deleteAuthData();
|
deleteAuthData();
|
||||||
|
|
||||||
@ -381,13 +378,32 @@ Client::~Client()
|
|||||||
m_sounds_client_to_server.clear();
|
m_sounds_client_to_server.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::connect(Address address, bool is_local_server)
|
void Client::connect(const Address &address, const std::string &address_name,
|
||||||
|
bool is_local_server)
|
||||||
{
|
{
|
||||||
initLocalMapSaving(address, m_address_name, is_local_server);
|
if (m_con) {
|
||||||
|
// can't do this if the connection has entered auth phase
|
||||||
|
sanity_check(m_state == LC_Created && m_proto_ver == 0);
|
||||||
|
infostream << "Client connection will be recreated" << std::endl;
|
||||||
|
|
||||||
|
m_access_denied = false;
|
||||||
|
m_access_denied_reconnect = false;
|
||||||
|
m_access_denied_reason.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_address_name = address_name;
|
||||||
|
m_con.reset(new con::Connection(PROTOCOL_ID, 512, CONNECTION_TIMEOUT,
|
||||||
|
address.isIPv6(), this));
|
||||||
|
|
||||||
|
infostream << "Connecting to server at ";
|
||||||
|
address.print(infostream);
|
||||||
|
infostream << std::endl;
|
||||||
|
|
||||||
// Since we use TryReceive() a timeout here would be ineffective anyway
|
// Since we use TryReceive() a timeout here would be ineffective anyway
|
||||||
m_con->SetTimeoutMs(0);
|
m_con->SetTimeoutMs(0);
|
||||||
m_con->Connect(address);
|
m_con->Connect(address);
|
||||||
|
|
||||||
|
initLocalMapSaving(address, m_address_name, is_local_server);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::step(float dtime)
|
void Client::step(float dtime)
|
||||||
@ -908,6 +924,10 @@ void Client::initLocalMapSaving(const Address &address,
|
|||||||
if (!g_settings->getBool("enable_local_map_saving") || is_local_server) {
|
if (!g_settings->getBool("enable_local_map_saving") || is_local_server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (m_localdb) {
|
||||||
|
infostream << "Local map saving already running" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string world_path;
|
std::string world_path;
|
||||||
#define set_world_path(hostname) \
|
#define set_world_path(hostname) \
|
||||||
@ -935,6 +955,8 @@ void Client::ReceiveAll()
|
|||||||
NetworkPacket pkt;
|
NetworkPacket pkt;
|
||||||
u64 start_ms = porting::getTimeMs();
|
u64 start_ms = porting::getTimeMs();
|
||||||
const u64 budget = 10;
|
const u64 budget = 10;
|
||||||
|
|
||||||
|
FATAL_ERROR_IF(!m_con, "Networking not initialized");
|
||||||
for(;;) {
|
for(;;) {
|
||||||
// Limit time even if there would be huge amounts of data to
|
// Limit time even if there would be huge amounts of data to
|
||||||
// process
|
// process
|
||||||
@ -1439,6 +1461,7 @@ void Client::sendUpdateClientInfo(const ClientDynamicInfo& info)
|
|||||||
pkt << info.real_gui_scaling;
|
pkt << info.real_gui_scaling;
|
||||||
pkt << info.real_hud_scaling;
|
pkt << info.real_hud_scaling;
|
||||||
pkt << (f32)info.max_fs_size.X << (f32)info.max_fs_size.Y;
|
pkt << (f32)info.max_fs_size.X << (f32)info.max_fs_size.Y;
|
||||||
|
pkt << info.touch_controls;
|
||||||
|
|
||||||
Send(&pkt);
|
Send(&pkt);
|
||||||
}
|
}
|
||||||
@ -1766,7 +1789,7 @@ ClientEvent *Client::getClientEvent()
|
|||||||
|
|
||||||
const Address Client::getServerAddress()
|
const Address Client::getServerAddress()
|
||||||
{
|
{
|
||||||
return m_con->GetPeerAddress(PEER_ID_SERVER);
|
return m_con ? m_con->GetPeerAddress(PEER_ID_SERVER) : Address();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Client::mediaReceiveProgress()
|
float Client::mediaReceiveProgress()
|
||||||
@ -1872,11 +1895,13 @@ void Client::afterContentReceived()
|
|||||||
|
|
||||||
float Client::getRTT()
|
float Client::getRTT()
|
||||||
{
|
{
|
||||||
|
assert(m_con);
|
||||||
return m_con->getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
|
return m_con->getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Client::getCurRate()
|
float Client::getCurRate()
|
||||||
{
|
{
|
||||||
|
assert(m_con);
|
||||||
return (m_con->getLocalStat(con::CUR_INC_RATE) +
|
return (m_con->getLocalStat(con::CUR_INC_RATE) +
|
||||||
m_con->getLocalStat(con::CUR_DL_RATE));
|
m_con->getLocalStat(con::CUR_DL_RATE));
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,6 @@ public:
|
|||||||
Client(
|
Client(
|
||||||
const char *playername,
|
const char *playername,
|
||||||
const std::string &password,
|
const std::string &password,
|
||||||
const std::string &address_name,
|
|
||||||
MapDrawControl &control,
|
MapDrawControl &control,
|
||||||
IWritableTextureSource *tsrc,
|
IWritableTextureSource *tsrc,
|
||||||
IWritableShaderSource *shsrc,
|
IWritableShaderSource *shsrc,
|
||||||
@ -131,7 +130,6 @@ public:
|
|||||||
ISoundManager *sound,
|
ISoundManager *sound,
|
||||||
MtEventManager *event,
|
MtEventManager *event,
|
||||||
RenderingEngine *rendering_engine,
|
RenderingEngine *rendering_engine,
|
||||||
bool ipv6,
|
|
||||||
GameUI *game_ui,
|
GameUI *game_ui,
|
||||||
ELoginRegister allow_login_or_register
|
ELoginRegister allow_login_or_register
|
||||||
);
|
);
|
||||||
@ -155,11 +153,8 @@ public:
|
|||||||
|
|
||||||
bool isShutdown();
|
bool isShutdown();
|
||||||
|
|
||||||
/*
|
void connect(const Address &address, const std::string &address_name,
|
||||||
The name of the local player should already be set when
|
bool is_local_server);
|
||||||
calling this, as it is sent in the initialization.
|
|
||||||
*/
|
|
||||||
void connect(Address address, bool is_local_server);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Stuff that references the environment is valid only as
|
Stuff that references the environment is valid only as
|
||||||
@ -196,6 +191,7 @@ public:
|
|||||||
void handleCommand_HP(NetworkPacket* pkt);
|
void handleCommand_HP(NetworkPacket* pkt);
|
||||||
void handleCommand_Breath(NetworkPacket* pkt);
|
void handleCommand_Breath(NetworkPacket* pkt);
|
||||||
void handleCommand_MovePlayer(NetworkPacket* pkt);
|
void handleCommand_MovePlayer(NetworkPacket* pkt);
|
||||||
|
void handleCommand_MovePlayerRel(NetworkPacket* pkt);
|
||||||
void handleCommand_DeathScreen(NetworkPacket* pkt);
|
void handleCommand_DeathScreen(NetworkPacket* pkt);
|
||||||
void handleCommand_AnnounceMedia(NetworkPacket* pkt);
|
void handleCommand_AnnounceMedia(NetworkPacket* pkt);
|
||||||
void handleCommand_Media(NetworkPacket* pkt);
|
void handleCommand_Media(NetworkPacket* pkt);
|
||||||
@ -350,7 +346,7 @@ public:
|
|||||||
bool activeObjectsReceived() const
|
bool activeObjectsReceived() const
|
||||||
{ return m_activeobjects_received; }
|
{ return m_activeobjects_received; }
|
||||||
|
|
||||||
u16 getProtoVersion()
|
u16 getProtoVersion() const
|
||||||
{ return m_proto_ver; }
|
{ return m_proto_ver; }
|
||||||
|
|
||||||
bool m_simple_singleplayer_mode;
|
bool m_simple_singleplayer_mode;
|
||||||
@ -362,6 +358,10 @@ public:
|
|||||||
|
|
||||||
float getRTT();
|
float getRTT();
|
||||||
float getCurRate();
|
float getCurRate();
|
||||||
|
// has the server ever replied to us, used for connection retry/fallback
|
||||||
|
bool hasServerReplied() const {
|
||||||
|
return getProtoVersion() != 0; // (set in TOCLIENT_HELLO)
|
||||||
|
}
|
||||||
|
|
||||||
Minimap* getMinimap() { return m_minimap; }
|
Minimap* getMinimap() { return m_minimap; }
|
||||||
void setCamera(Camera* camera) { m_camera = camera; }
|
void setCamera(Camera* camera) { m_camera = camera; }
|
||||||
@ -414,8 +414,10 @@ public:
|
|||||||
|
|
||||||
void showMinimap(bool show = true);
|
void showMinimap(bool show = true);
|
||||||
|
|
||||||
|
// IP and port we're connected to
|
||||||
const Address getServerAddress();
|
const Address getServerAddress();
|
||||||
|
|
||||||
|
// Hostname of the connected server (but can also be a numerical IP)
|
||||||
const std::string &getAddressName() const
|
const std::string &getAddressName() const
|
||||||
{
|
{
|
||||||
return m_address_name;
|
return m_address_name;
|
||||||
|
@ -34,33 +34,43 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
// struct MeshBufListList
|
namespace {
|
||||||
void MeshBufListList::clear()
|
// A helper struct
|
||||||
{
|
struct MeshBufListMaps
|
||||||
for (auto &list : lists)
|
{
|
||||||
list.clear();
|
struct MaterialHash
|
||||||
}
|
{
|
||||||
|
size_t operator()(const video::SMaterial &m) const noexcept
|
||||||
|
{
|
||||||
|
// Only hash first texture. Simple and fast.
|
||||||
|
return std::hash<video::ITexture *>{}(m.TextureLayers[0].Texture);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void MeshBufListList::add(scene::IMeshBuffer *buf, v3s16 position, u8 layer)
|
using MeshBufListMap = std::unordered_map<
|
||||||
{
|
video::SMaterial,
|
||||||
// Append to the correct layer
|
std::vector<std::pair<v3s16, scene::IMeshBuffer *>>,
|
||||||
std::vector<MeshBufList> &list = lists[layer];
|
MaterialHash>;
|
||||||
const video::SMaterial &m = buf->getMaterial();
|
|
||||||
for (MeshBufList &l : list) {
|
|
||||||
// comparing a full material is quite expensive so we don't do it if
|
|
||||||
// not even first texture is equal
|
|
||||||
if (l.m.TextureLayers[0].Texture != m.TextureLayers[0].Texture)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (l.m == m) {
|
std::array<MeshBufListMap, MAX_TILE_LAYERS> maps;
|
||||||
l.bufs.emplace_back(position, buf);
|
|
||||||
return;
|
void clear()
|
||||||
|
{
|
||||||
|
for (auto &map : maps)
|
||||||
|
map.clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
MeshBufList l;
|
void add(scene::IMeshBuffer *buf, v3s16 position, u8 layer)
|
||||||
l.m = m;
|
{
|
||||||
l.bufs.emplace_back(position, buf);
|
assert(layer < MAX_TILE_LAYERS);
|
||||||
list.emplace_back(l);
|
|
||||||
|
// Append to the correct layer
|
||||||
|
auto &map = maps[layer];
|
||||||
|
const video::SMaterial &m = buf->getMaterial();
|
||||||
|
auto &bufs = map[m]; // default constructs if non-existent
|
||||||
|
bufs.emplace_back(position, buf);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_settings_changed(const std::string &name, void *data)
|
static void on_settings_changed(const std::string &name, void *data)
|
||||||
@ -737,7 +747,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
|||||||
Draw the selected MapBlocks
|
Draw the selected MapBlocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
MeshBufListList grouped_buffers;
|
MeshBufListMaps grouped_buffers;
|
||||||
std::vector<DrawDescriptor> draw_order;
|
std::vector<DrawDescriptor> draw_order;
|
||||||
video::SMaterial previous_material;
|
video::SMaterial previous_material;
|
||||||
|
|
||||||
@ -793,7 +803,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// otherwise, group buffers across meshes
|
// otherwise, group buffers across meshes
|
||||||
// using MeshBufListList
|
// using MeshBufListMaps
|
||||||
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
|
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
|
||||||
scene::IMesh *mesh = block_mesh->getMesh(layer);
|
scene::IMesh *mesh = block_mesh->getMesh(layer);
|
||||||
assert(mesh);
|
assert(mesh);
|
||||||
@ -819,11 +829,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Capture draw order for all solid meshes
|
// Capture draw order for all solid meshes
|
||||||
for (auto &lists : grouped_buffers.lists) {
|
for (auto &map : grouped_buffers.maps) {
|
||||||
for (MeshBufList &list : lists) {
|
for (auto &list : map) {
|
||||||
// iterate in reverse to draw closest blocks first
|
// iterate in reverse to draw closest blocks first
|
||||||
for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it) {
|
for (auto it = list.second.rbegin(); it != list.second.rend(); ++it) {
|
||||||
draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
|
draw_order.emplace_back(it->first, it->second, it != list.second.rbegin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1103,7 +1113,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
|||||||
u32 drawcall_count = 0;
|
u32 drawcall_count = 0;
|
||||||
u32 vertex_count = 0;
|
u32 vertex_count = 0;
|
||||||
|
|
||||||
MeshBufListList grouped_buffers;
|
MeshBufListMaps grouped_buffers;
|
||||||
std::vector<DrawDescriptor> draw_order;
|
std::vector<DrawDescriptor> draw_order;
|
||||||
|
|
||||||
|
|
||||||
@ -1144,7 +1154,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// otherwise, group buffers across meshes
|
// otherwise, group buffers across meshes
|
||||||
// using MeshBufListList
|
// using MeshBufListMaps
|
||||||
MapBlockMesh *mapBlockMesh = block->mesh;
|
MapBlockMesh *mapBlockMesh = block->mesh;
|
||||||
assert(mapBlockMesh);
|
assert(mapBlockMesh);
|
||||||
|
|
||||||
@ -1167,18 +1177,18 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
|||||||
}
|
}
|
||||||
|
|
||||||
u32 buffer_count = 0;
|
u32 buffer_count = 0;
|
||||||
for (auto &lists : grouped_buffers.lists)
|
for (auto &map : grouped_buffers.maps)
|
||||||
for (MeshBufList &list : lists)
|
for (auto &list : map)
|
||||||
buffer_count += list.bufs.size();
|
buffer_count += list.second.size();
|
||||||
|
|
||||||
draw_order.reserve(draw_order.size() + buffer_count);
|
draw_order.reserve(draw_order.size() + buffer_count);
|
||||||
|
|
||||||
// Capture draw order for all solid meshes
|
// Capture draw order for all solid meshes
|
||||||
for (auto &lists : grouped_buffers.lists) {
|
for (auto &map : grouped_buffers.maps) {
|
||||||
for (MeshBufList &list : lists) {
|
for (auto &list : map) {
|
||||||
// iterate in reverse to draw closest blocks first
|
// iterate in reverse to draw closest blocks first
|
||||||
for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it)
|
for (auto it = list.second.rbegin(); it != list.second.rend(); ++it)
|
||||||
draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
|
draw_order.emplace_back(it->first, it->second, it != list.second.rbegin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,25 +37,6 @@ struct MapDrawControl
|
|||||||
bool show_wireframe = false;
|
bool show_wireframe = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MeshBufList
|
|
||||||
{
|
|
||||||
video::SMaterial m;
|
|
||||||
std::vector<std::pair<v3s16,scene::IMeshBuffer*>> bufs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MeshBufListList
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
* Stores the mesh buffers of the world.
|
|
||||||
* The array index is the material's layer.
|
|
||||||
* The vector part groups vertices by material.
|
|
||||||
*/
|
|
||||||
std::vector<MeshBufList> lists[MAX_TILE_LAYERS];
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
void add(scene::IMeshBuffer *buf, v3s16 position, u8 layer);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
class ITextureSource;
|
class ITextureSource;
|
||||||
class PartialMeshBuffer;
|
class PartialMeshBuffer;
|
||||||
|
@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "client/shader.h"
|
#include "client/shader.h"
|
||||||
#include "client/minimap.h"
|
#include "client/minimap.h"
|
||||||
|
#include <quaternion.h>
|
||||||
|
|
||||||
class Settings;
|
class Settings;
|
||||||
struct ToolCapabilities;
|
struct ToolCapabilities;
|
||||||
@ -828,7 +829,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
|
|||||||
updateMarker();
|
updateMarker();
|
||||||
updateNodePos();
|
updateNodePos();
|
||||||
updateAnimation();
|
updateAnimation();
|
||||||
updateBonePosition();
|
updateBones(.0f);
|
||||||
updateAttachments();
|
updateAttachments();
|
||||||
setNodeLight(m_last_light);
|
setNodeLight(m_last_light);
|
||||||
updateMeshCulling();
|
updateMeshCulling();
|
||||||
@ -1246,7 +1247,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
|
|||||||
updatePositionRecursive(m_matrixnode);
|
updatePositionRecursive(m_matrixnode);
|
||||||
m_animated_meshnode->updateAbsolutePosition();
|
m_animated_meshnode->updateAbsolutePosition();
|
||||||
m_animated_meshnode->animateJoints();
|
m_animated_meshnode->animateJoints();
|
||||||
updateBonePosition();
|
updateBones(dtime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1527,19 +1528,28 @@ void GenericCAO::updateAnimationSpeed()
|
|||||||
m_animated_meshnode->setAnimationSpeed(m_animation_speed);
|
m_animated_meshnode->setAnimationSpeed(m_animation_speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericCAO::updateBonePosition()
|
void GenericCAO::updateBones(f32 dtime)
|
||||||
{
|
{
|
||||||
if (m_bone_position.empty() || !m_animated_meshnode)
|
if (!m_animated_meshnode)
|
||||||
return;
|
return;
|
||||||
|
if (m_bone_override.empty()) {
|
||||||
|
m_animated_meshnode->setJointMode(scene::EJUOR_NONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_animated_meshnode->setJointMode(scene::EJUOR_CONTROL); // To write positions to the mesh on render
|
m_animated_meshnode->setJointMode(scene::EJUOR_CONTROL); // To write positions to the mesh on render
|
||||||
for (auto &it : m_bone_position) {
|
for (auto &it : m_bone_override) {
|
||||||
std::string bone_name = it.first;
|
std::string bone_name = it.first;
|
||||||
scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
|
scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
|
||||||
if (bone) {
|
if (!bone)
|
||||||
bone->setPosition(it.second.X);
|
continue;
|
||||||
bone->setRotation(it.second.Y);
|
|
||||||
}
|
BoneOverride &props = it.second;
|
||||||
|
props.dtime_passed += dtime;
|
||||||
|
|
||||||
|
bone->setPosition(props.getPosition(bone->getPosition()));
|
||||||
|
bone->setRotation(props.getRotationEulerDeg(bone->getRotation()));
|
||||||
|
bone->setScale(props.getScale(bone->getScale()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// search through bones to find mistakenly rotated bones due to bug in Irrlicht
|
// search through bones to find mistakenly rotated bones due to bug in Irrlicht
|
||||||
@ -1550,7 +1560,7 @@ void GenericCAO::updateBonePosition()
|
|||||||
|
|
||||||
//If bone is manually positioned there is no need to perform the bug check
|
//If bone is manually positioned there is no need to perform the bug check
|
||||||
bool skip = false;
|
bool skip = false;
|
||||||
for (auto &it : m_bone_position) {
|
for (auto &it : m_bone_override) {
|
||||||
if (it.first == bone->getName()) {
|
if (it.first == bone->getName()) {
|
||||||
skip = true;
|
skip = true;
|
||||||
break;
|
break;
|
||||||
@ -1852,11 +1862,46 @@ void GenericCAO::processMessage(const std::string &data)
|
|||||||
updateAnimationSpeed();
|
updateAnimationSpeed();
|
||||||
} else if (cmd == AO_CMD_SET_BONE_POSITION) {
|
} else if (cmd == AO_CMD_SET_BONE_POSITION) {
|
||||||
std::string bone = deSerializeString16(is);
|
std::string bone = deSerializeString16(is);
|
||||||
v3f position = readV3F32(is);
|
auto it = m_bone_override.find(bone);
|
||||||
v3f rotation = readV3F32(is);
|
BoneOverride props;
|
||||||
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
|
if (it != m_bone_override.end()) {
|
||||||
|
props = it->second;
|
||||||
// updateBonePosition(); now called every step
|
// Reset timer
|
||||||
|
props.dtime_passed = 0;
|
||||||
|
// Save previous values for interpolation
|
||||||
|
props.position.previous = props.position.vector;
|
||||||
|
props.rotation.previous = props.rotation.next;
|
||||||
|
props.scale.previous = props.scale.vector;
|
||||||
|
} else {
|
||||||
|
// Disable interpolation
|
||||||
|
props.position.interp_timer = 0.0f;
|
||||||
|
props.rotation.interp_timer = 0.0f;
|
||||||
|
props.scale.interp_timer = 0.0f;
|
||||||
|
}
|
||||||
|
// Read new values
|
||||||
|
props.position.vector = readV3F32(is);
|
||||||
|
props.rotation.next = core::quaternion(readV3F32(is) * core::DEGTORAD);
|
||||||
|
props.scale.vector = readV3F32(is); // reads past end of string on older cmds
|
||||||
|
if (is.eof()) {
|
||||||
|
// Backwards compatibility
|
||||||
|
props.scale.vector = v3f(1, 1, 1); // restore the scale which was not sent
|
||||||
|
props.position.absolute = true;
|
||||||
|
props.rotation.absolute = true;
|
||||||
|
} else {
|
||||||
|
props.position.interp_timer = readF32(is);
|
||||||
|
props.rotation.interp_timer = readF32(is);
|
||||||
|
props.scale.interp_timer = readF32(is);
|
||||||
|
u8 absoluteFlag = readU8(is);
|
||||||
|
props.position.absolute = (absoluteFlag & 1) > 0;
|
||||||
|
props.rotation.absolute = (absoluteFlag & 2) > 0;
|
||||||
|
props.scale.absolute = (absoluteFlag & 4) > 0;
|
||||||
|
}
|
||||||
|
if (props.isIdentity()) {
|
||||||
|
m_bone_override.erase(bone);
|
||||||
|
} else {
|
||||||
|
m_bone_override[bone] = props;
|
||||||
|
}
|
||||||
|
// updateBones(); now called every step
|
||||||
} else if (cmd == AO_CMD_ATTACH_TO) {
|
} else if (cmd == AO_CMD_ATTACH_TO) {
|
||||||
u16 parent_id = readS16(is);
|
u16 parent_id = readS16(is);
|
||||||
std::string bone = deSerializeString16(is);
|
std::string bone = deSerializeString16(is);
|
||||||
|
@ -104,7 +104,7 @@ private:
|
|||||||
float m_animation_blend = 0.0f;
|
float m_animation_blend = 0.0f;
|
||||||
bool m_animation_loop = true;
|
bool m_animation_loop = true;
|
||||||
// stores position and rotation for each bone name
|
// stores position and rotation for each bone name
|
||||||
std::unordered_map<std::string, core::vector2d<v3f>> m_bone_position;
|
BoneOverrideMap m_bone_override;
|
||||||
|
|
||||||
int m_attachment_parent_id = 0;
|
int m_attachment_parent_id = 0;
|
||||||
std::unordered_set<int> m_attachment_child_ids;
|
std::unordered_set<int> m_attachment_child_ids;
|
||||||
@ -267,7 +267,7 @@ public:
|
|||||||
|
|
||||||
void updateAnimationSpeed();
|
void updateAnimationSpeed();
|
||||||
|
|
||||||
void updateBonePosition();
|
void updateBones(f32 dtime);
|
||||||
|
|
||||||
void processMessage(const std::string &data) override;
|
void processMessage(const std::string &data) override;
|
||||||
|
|
||||||
|
@ -404,6 +404,12 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
|
|||||||
CachedPixelShaderSetting<float> m_bloom_radius_pixel;
|
CachedPixelShaderSetting<float> m_bloom_radius_pixel;
|
||||||
float m_bloom_radius;
|
float m_bloom_radius;
|
||||||
CachedPixelShaderSetting<float> m_saturation_pixel;
|
CachedPixelShaderSetting<float> m_saturation_pixel;
|
||||||
|
bool m_volumetric_light_enabled;
|
||||||
|
CachedPixelShaderSetting<float, 3> m_sun_position_pixel;
|
||||||
|
CachedPixelShaderSetting<float> m_sun_brightness_pixel;
|
||||||
|
CachedPixelShaderSetting<float, 3> m_moon_position_pixel;
|
||||||
|
CachedPixelShaderSetting<float> m_moon_brightness_pixel;
|
||||||
|
CachedPixelShaderSetting<float> m_volumetric_light_strength_pixel;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void onSettingsChange(const std::string &name)
|
void onSettingsChange(const std::string &name)
|
||||||
@ -461,7 +467,12 @@ public:
|
|||||||
m_bloom_intensity_pixel("bloomIntensity"),
|
m_bloom_intensity_pixel("bloomIntensity"),
|
||||||
m_bloom_strength_pixel("bloomStrength"),
|
m_bloom_strength_pixel("bloomStrength"),
|
||||||
m_bloom_radius_pixel("bloomRadius"),
|
m_bloom_radius_pixel("bloomRadius"),
|
||||||
m_saturation_pixel("saturation")
|
m_saturation_pixel("saturation"),
|
||||||
|
m_sun_position_pixel("sunPositionScreen"),
|
||||||
|
m_sun_brightness_pixel("sunBrightness"),
|
||||||
|
m_moon_position_pixel("moonPositionScreen"),
|
||||||
|
m_moon_brightness_pixel("moonBrightness"),
|
||||||
|
m_volumetric_light_strength_pixel("volumetricLightStrength")
|
||||||
{
|
{
|
||||||
g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
|
g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
|
||||||
g_settings->registerChangedCallback("exposure_compensation", settingsCallback, this);
|
g_settings->registerChangedCallback("exposure_compensation", settingsCallback, this);
|
||||||
@ -475,6 +486,7 @@ public:
|
|||||||
m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
|
m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
|
||||||
m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f);
|
m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f);
|
||||||
m_bloom_radius = g_settings->getFloat("bloom_radius", 0.1f, 8.0f);
|
m_bloom_radius = g_settings->getFloat("bloom_radius", 0.1f, 8.0f);
|
||||||
|
m_volumetric_light_enabled = g_settings->getBool("enable_volumetric_lighting") && m_bloom_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
~GameGlobalShaderConstantSetter()
|
~GameGlobalShaderConstantSetter()
|
||||||
@ -579,6 +591,54 @@ public:
|
|||||||
}
|
}
|
||||||
float saturation = m_client->getEnv().getLocalPlayer()->getLighting().saturation;
|
float saturation = m_client->getEnv().getLocalPlayer()->getLighting().saturation;
|
||||||
m_saturation_pixel.set(&saturation, services);
|
m_saturation_pixel.set(&saturation, services);
|
||||||
|
|
||||||
|
if (m_volumetric_light_enabled) {
|
||||||
|
// Map directional light to screen space
|
||||||
|
auto camera_node = m_client->getCamera()->getCameraNode();
|
||||||
|
core::matrix4 transform = camera_node->getProjectionMatrix();
|
||||||
|
transform *= camera_node->getViewMatrix();
|
||||||
|
|
||||||
|
if (m_sky->getSunVisible()) {
|
||||||
|
v3f sun_position = camera_node->getAbsolutePosition() +
|
||||||
|
10000. * m_sky->getSunDirection();
|
||||||
|
transform.transformVect(sun_position);
|
||||||
|
sun_position.normalize();
|
||||||
|
|
||||||
|
float sun_position_array[3] = { sun_position.X, sun_position.Y, sun_position.Z};
|
||||||
|
m_sun_position_pixel.set(sun_position_array, services);
|
||||||
|
|
||||||
|
float sun_brightness = rangelim(107.143f * m_sky->getSunDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f);
|
||||||
|
m_sun_brightness_pixel.set(&sun_brightness, services);
|
||||||
|
} else {
|
||||||
|
float sun_position_array[3] = { 0.f, 0.f, -1.f };
|
||||||
|
m_sun_position_pixel.set(sun_position_array, services);
|
||||||
|
|
||||||
|
float sun_brightness = 0.f;
|
||||||
|
m_sun_brightness_pixel.set(&sun_brightness, services);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_sky->getMoonVisible()) {
|
||||||
|
v3f moon_position = camera_node->getAbsolutePosition() +
|
||||||
|
10000. * m_sky->getMoonDirection();
|
||||||
|
transform.transformVect(moon_position);
|
||||||
|
moon_position.normalize();
|
||||||
|
|
||||||
|
float moon_position_array[3] = { moon_position.X, moon_position.Y, moon_position.Z};
|
||||||
|
m_moon_position_pixel.set(moon_position_array, services);
|
||||||
|
|
||||||
|
float moon_brightness = rangelim(107.143f * m_sky->getMoonDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f);
|
||||||
|
m_moon_brightness_pixel.set(&moon_brightness, services);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float moon_position_array[3] = { 0.f, 0.f, -1.f };
|
||||||
|
m_moon_position_pixel.set(moon_position_array, services);
|
||||||
|
|
||||||
|
float moon_brightness = 0.f;
|
||||||
|
m_moon_brightness_pixel.set(&moon_brightness, services);
|
||||||
|
}
|
||||||
|
float volumetric_light_strength = m_client->getEnv().getLocalPlayer()->getLighting().volumetric_light_strength;
|
||||||
|
m_volumetric_light_strength_pixel.set(&volumetric_light_strength, services);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSetMaterial(const video::SMaterial &material) override
|
void onSetMaterial(const video::SMaterial &material) override
|
||||||
@ -821,6 +881,7 @@ protected:
|
|||||||
const CameraOrientation &cam);
|
const CameraOrientation &cam);
|
||||||
void updateClouds(float dtime);
|
void updateClouds(float dtime);
|
||||||
void updateShadows();
|
void updateShadows();
|
||||||
|
void drawScene(ProfilerGraph *graph, RunStats *stats);
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
void showOverlayMessage(const char *msg, float dtime, int percent,
|
void showOverlayMessage(const char *msg, float dtime, int percent,
|
||||||
@ -1243,8 +1304,8 @@ void Game::run()
|
|||||||
updatePauseState();
|
updatePauseState();
|
||||||
if (m_is_paused)
|
if (m_is_paused)
|
||||||
dtime = 0.0f;
|
dtime = 0.0f;
|
||||||
else
|
|
||||||
step(dtime);
|
step(dtime);
|
||||||
|
|
||||||
processClientEvents(&cam_view_target);
|
processClientEvents(&cam_view_target);
|
||||||
updateDebugState();
|
updateDebugState();
|
||||||
@ -1272,6 +1333,9 @@ void Game::shutdown()
|
|||||||
if (formspec)
|
if (formspec)
|
||||||
formspec->quitMenu();
|
formspec->quitMenu();
|
||||||
|
|
||||||
|
// Clear text when exiting.
|
||||||
|
m_game_ui->clearText();
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
#ifdef HAVE_TOUCHSCREENGUI
|
||||||
g_touchscreengui->hide();
|
g_touchscreengui->hide();
|
||||||
#endif
|
#endif
|
||||||
@ -1428,12 +1492,6 @@ bool Game::createClient(const GameStartData &start_data)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool could_connect, connect_aborted;
|
bool could_connect, connect_aborted;
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
if (g_touchscreengui) {
|
|
||||||
g_touchscreengui->init(texture_src);
|
|
||||||
g_touchscreengui->hide();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!connectToServer(start_data, &could_connect, &connect_aborted))
|
if (!connectToServer(start_data, &could_connect, &connect_aborted))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1542,10 +1600,8 @@ bool Game::initGui()
|
|||||||
-1, chat_backend, client, &g_menumgr);
|
-1, chat_backend, client, &g_menumgr);
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
#ifdef HAVE_TOUCHSCREENGUI
|
||||||
|
|
||||||
if (g_touchscreengui)
|
if (g_touchscreengui)
|
||||||
g_touchscreengui->show();
|
g_touchscreengui->init(texture_src);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1557,15 +1613,18 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||||||
*connect_ok = false; // Let's not be overly optimistic
|
*connect_ok = false; // Let's not be overly optimistic
|
||||||
*connection_aborted = false;
|
*connection_aborted = false;
|
||||||
bool local_server_mode = false;
|
bool local_server_mode = false;
|
||||||
|
const auto &address_name = start_data.address;
|
||||||
|
|
||||||
showOverlayMessage(N_("Resolving address..."), 0, 15);
|
showOverlayMessage(N_("Resolving address..."), 0, 15);
|
||||||
|
|
||||||
Address connect_address(0, 0, 0, 0, start_data.socket_port);
|
Address connect_address(0, 0, 0, 0, start_data.socket_port);
|
||||||
|
Address fallback_address;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
connect_address.Resolve(start_data.address.c_str());
|
connect_address.Resolve(address_name.c_str(), &fallback_address);
|
||||||
|
|
||||||
if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
|
if (connect_address.isAny()) {
|
||||||
|
// replace with localhost IP
|
||||||
if (connect_address.isIPv6()) {
|
if (connect_address.isIPv6()) {
|
||||||
IPv6AddressBytes addr_bytes;
|
IPv6AddressBytes addr_bytes;
|
||||||
addr_bytes.bytes[15] = 1;
|
addr_bytes.bytes[15] = 1;
|
||||||
@ -1582,45 +1641,58 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
|
// this shouldn't normally happen since Address::Resolve() checks for enable_ipv6
|
||||||
|
if (g_settings->getBool("enable_ipv6")) {
|
||||||
|
// empty
|
||||||
|
} else if (connect_address.isIPv6()) {
|
||||||
*error_message = fmtgettext("Unable to connect to %s because IPv6 is disabled", connect_address.serializeString().c_str());
|
*error_message = fmtgettext("Unable to connect to %s because IPv6 is disabled", connect_address.serializeString().c_str());
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
} else if (fallback_address.isIPv6()) {
|
||||||
|
fallback_address = Address();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fallback_address.setPort(connect_address.getPort());
|
||||||
|
if (fallback_address.isValid()) {
|
||||||
|
infostream << "Resolved two addresses for \"" << address_name
|
||||||
|
<< "\" isIPv6[0]=" << connect_address.isIPv6()
|
||||||
|
<< " isIPv6[1]=" << fallback_address.isIPv6() << std::endl;
|
||||||
|
} else {
|
||||||
|
infostream << "Resolved one address for \"" << address_name
|
||||||
|
<< "\" isIPv6=" << connect_address.isIPv6() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
client = new Client(start_data.name.c_str(),
|
client = new Client(start_data.name.c_str(),
|
||||||
start_data.password, start_data.address,
|
start_data.password,
|
||||||
*draw_control, texture_src, shader_src,
|
*draw_control, texture_src, shader_src,
|
||||||
itemdef_manager, nodedef_manager, sound_manager.get(), eventmgr,
|
itemdef_manager, nodedef_manager, sound_manager.get(), eventmgr,
|
||||||
m_rendering_engine, connect_address.isIPv6(), m_game_ui.get(),
|
m_rendering_engine, m_game_ui.get(),
|
||||||
start_data.allow_login_or_register);
|
start_data.allow_login_or_register);
|
||||||
client->migrateModStorage();
|
|
||||||
} catch (const BaseException &e) {
|
} catch (const BaseException &e) {
|
||||||
*error_message = fmtgettext("Error creating client: %s", e.what());
|
*error_message = fmtgettext("Error creating client: %s", e.what());
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client->migrateModStorage();
|
||||||
client->m_simple_singleplayer_mode = simple_singleplayer_mode;
|
client->m_simple_singleplayer_mode = simple_singleplayer_mode;
|
||||||
|
|
||||||
infostream << "Connecting to server at ";
|
|
||||||
connect_address.print(infostream);
|
|
||||||
infostream << std::endl;
|
|
||||||
|
|
||||||
client->connect(connect_address,
|
|
||||||
simple_singleplayer_mode || local_server_mode);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Wait for server to accept connection
|
Wait for server to accept connection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
client->connect(connect_address, address_name,
|
||||||
|
simple_singleplayer_mode || local_server_mode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
input->clear();
|
input->clear();
|
||||||
|
|
||||||
FpsControl fps_control;
|
FpsControl fps_control;
|
||||||
f32 dtime;
|
f32 dtime;
|
||||||
f32 wait_time = 0; // in seconds
|
f32 wait_time = 0; // in seconds
|
||||||
|
bool did_fallback = false;
|
||||||
|
|
||||||
fps_control.reset();
|
fps_control.reset();
|
||||||
|
|
||||||
@ -1629,10 +1701,7 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||||||
fps_control.limit(device, &dtime);
|
fps_control.limit(device, &dtime);
|
||||||
|
|
||||||
// Update client and server
|
// Update client and server
|
||||||
client->step(dtime);
|
step(dtime);
|
||||||
|
|
||||||
if (server != NULL)
|
|
||||||
server->step(dtime);
|
|
||||||
|
|
||||||
// End condition
|
// End condition
|
||||||
if (client->getState() == LC_Init) {
|
if (client->getState() == LC_Init) {
|
||||||
@ -1658,8 +1727,15 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
wait_time += dtime;
|
wait_time += dtime;
|
||||||
// Only time out if we aren't waiting for the server we started
|
if (local_server_mode) {
|
||||||
if (!start_data.address.empty() && wait_time > 10) {
|
// never time out
|
||||||
|
} else if (wait_time > GAME_FALLBACK_TIMEOUT && !did_fallback) {
|
||||||
|
if (!client->hasServerReplied() && fallback_address.isValid()) {
|
||||||
|
client->connect(fallback_address, address_name,
|
||||||
|
simple_singleplayer_mode || local_server_mode);
|
||||||
|
}
|
||||||
|
did_fallback = true;
|
||||||
|
} else if (wait_time > GAME_CONNECTION_TIMEOUT) {
|
||||||
*error_message = gettext("Connection timed out.");
|
*error_message = gettext("Connection timed out.");
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
break;
|
break;
|
||||||
@ -1669,8 +1745,7 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||||||
showOverlayMessage(N_("Connecting to server..."), dtime, 20);
|
showOverlayMessage(N_("Connecting to server..."), dtime, 20);
|
||||||
}
|
}
|
||||||
} catch (con::PeerNotFoundException &e) {
|
} catch (con::PeerNotFoundException &e) {
|
||||||
// TODO: Should something be done here? At least an info/error
|
warningstream << "This should not happen. Please report a bug." << std::endl;
|
||||||
// message?
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1691,10 +1766,7 @@ bool Game::getServerContent(bool *aborted)
|
|||||||
fps_control.limit(device, &dtime);
|
fps_control.limit(device, &dtime);
|
||||||
|
|
||||||
// Update client and server
|
// Update client and server
|
||||||
client->step(dtime);
|
step(dtime);
|
||||||
|
|
||||||
if (server != NULL)
|
|
||||||
server->step(dtime);
|
|
||||||
|
|
||||||
// End condition
|
// End condition
|
||||||
if (client->mediaReceived() && client->itemdefReceived() &&
|
if (client->mediaReceived() && client->itemdefReceived() &&
|
||||||
@ -2232,7 +2304,7 @@ void Game::openConsole(float scale, const wchar_t *line)
|
|||||||
assert(scale > 0.0f && scale <= 1.0f);
|
assert(scale > 0.0f && scale <= 1.0f);
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
porting::showInputDialog(gettext("ok"), "", "", 2);
|
porting::showTextInputDialog("", "", 2);
|
||||||
m_android_chat_open = true;
|
m_android_chat_open = true;
|
||||||
#else
|
#else
|
||||||
if (gui_chat_console->isOpenInhibited())
|
if (gui_chat_console->isOpenInhibited())
|
||||||
@ -2248,15 +2320,19 @@ void Game::openConsole(float scale, const wchar_t *line)
|
|||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
void Game::handleAndroidChatInput()
|
void Game::handleAndroidChatInput()
|
||||||
{
|
{
|
||||||
if (m_android_chat_open && porting::getInputDialogState() == 0) {
|
// It has to be a text input
|
||||||
std::string text = porting::getInputDialogValue();
|
if (m_android_chat_open && porting::getLastInputDialogType() == porting::TEXT_INPUT) {
|
||||||
client->typeChatMessage(utf8_to_wide(text));
|
porting::AndroidDialogState dialogState = porting::getInputDialogState();
|
||||||
m_android_chat_open = false;
|
if (dialogState == porting::DIALOG_INPUTTED) {
|
||||||
|
std::string text = porting::getInputDialogMessage();
|
||||||
|
client->typeChatMessage(utf8_to_wide(text));
|
||||||
|
}
|
||||||
|
if (dialogState != porting::DIALOG_SHOWN)
|
||||||
|
m_android_chat_open = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void Game::toggleFreeMove()
|
void Game::toggleFreeMove()
|
||||||
{
|
{
|
||||||
bool free_move = !g_settings->getBool("free_move");
|
bool free_move = !g_settings->getBool("free_move");
|
||||||
@ -2626,7 +2702,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
|
|||||||
#ifdef HAVE_TOUCHSCREENGUI
|
#ifdef HAVE_TOUCHSCREENGUI
|
||||||
if (g_touchscreengui) {
|
if (g_touchscreengui) {
|
||||||
cam->camera_yaw += g_touchscreengui->getYawChange();
|
cam->camera_yaw += g_touchscreengui->getYawChange();
|
||||||
cam->camera_pitch = g_touchscreengui->getPitch();
|
cam->camera_pitch += g_touchscreengui->getPitchChange();
|
||||||
} else {
|
} else {
|
||||||
#endif
|
#endif
|
||||||
v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
|
v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
|
||||||
@ -2722,10 +2798,23 @@ void Game::updatePauseState()
|
|||||||
|
|
||||||
inline void Game::step(f32 dtime)
|
inline void Game::step(f32 dtime)
|
||||||
{
|
{
|
||||||
if (server)
|
if (server) {
|
||||||
server->step(dtime);
|
float fps_max = (!device->isWindowFocused() || g_menumgr.pausesGame()) ?
|
||||||
|
g_settings->getFloat("fps_max_unfocused") :
|
||||||
|
g_settings->getFloat("fps_max");
|
||||||
|
fps_max = std::max(fps_max, 1.0f);
|
||||||
|
float steplen = 1.0f / fps_max;
|
||||||
|
|
||||||
client->step(dtime);
|
server->setStepSettings(Server::StepSettings{
|
||||||
|
steplen,
|
||||||
|
m_is_paused
|
||||||
|
});
|
||||||
|
|
||||||
|
server->step();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_is_paused)
|
||||||
|
client->step(dtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pauseNodeAnimation(PausedNodesList &paused, scene::ISceneNode *node) {
|
static void pauseNodeAnimation(PausedNodesList &paused, scene::ISceneNode *node) {
|
||||||
@ -3045,7 +3134,6 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
|
|||||||
else
|
else
|
||||||
sky->setFogStart(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
|
sky->setFogStart(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
|
||||||
|
|
||||||
|
|
||||||
delete event->set_sky;
|
delete event->set_sky;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4019,31 +4107,6 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
|
|||||||
*/
|
*/
|
||||||
client->getParticleManager()->step(dtime);
|
client->getParticleManager()->step(dtime);
|
||||||
|
|
||||||
/*
|
|
||||||
Fog
|
|
||||||
*/
|
|
||||||
if (m_cache_enable_fog) {
|
|
||||||
driver->setFog(
|
|
||||||
sky->getBgColor(),
|
|
||||||
video::EFT_FOG_LINEAR,
|
|
||||||
runData.fog_range * sky->getFogStart(),
|
|
||||||
runData.fog_range * 1.0,
|
|
||||||
0.01,
|
|
||||||
false, // pixel fog
|
|
||||||
true // range fog
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
driver->setFog(
|
|
||||||
sky->getBgColor(),
|
|
||||||
video::EFT_FOG_LINEAR,
|
|
||||||
100000 * BS,
|
|
||||||
110000 * BS,
|
|
||||||
0.01f,
|
|
||||||
false, // pixel fog
|
|
||||||
false // range fog
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Damage camera tilt
|
Damage camera tilt
|
||||||
*/
|
*/
|
||||||
@ -4143,52 +4206,18 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
|
|||||||
/*
|
/*
|
||||||
==================== Drawing begins ====================
|
==================== Drawing begins ====================
|
||||||
*/
|
*/
|
||||||
const video::SColor skycolor = sky->getSkyColor();
|
if (RenderingEngine::shouldRender())
|
||||||
|
drawScene(graph, stats);
|
||||||
TimeTaker tt_draw("Draw scene", nullptr, PRECISION_MICRO);
|
|
||||||
driver->beginScene(true, true, skycolor);
|
|
||||||
|
|
||||||
bool draw_wield_tool = (m_game_ui->m_flags.show_hud &&
|
|
||||||
(player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) &&
|
|
||||||
(camera->getCameraMode() == CAMERA_MODE_FIRST));
|
|
||||||
bool draw_crosshair = (
|
|
||||||
(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
|
|
||||||
(camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT));
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
if (isNoCrosshairAllowed())
|
|
||||||
draw_crosshair = false;
|
|
||||||
#endif
|
|
||||||
m_rendering_engine->draw_scene(skycolor, m_game_ui->m_flags.show_hud,
|
|
||||||
m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Profiler graph
|
|
||||||
*/
|
|
||||||
v2u32 screensize = driver->getScreenSize();
|
|
||||||
|
|
||||||
if (m_game_ui->m_flags.show_profiler_graph)
|
|
||||||
graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
|
|
||||||
|
|
||||||
/*
|
|
||||||
Damage flash
|
|
||||||
*/
|
|
||||||
if (runData.damage_flash > 0.0f) {
|
|
||||||
video::SColor color(runData.damage_flash, 180, 0, 0);
|
|
||||||
driver->draw2DRectangle(color,
|
|
||||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
runData.damage_flash -= 384.0f * dtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==================== End scene ====================
|
==================== End scene ====================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
driver->endScene();
|
// Damage flash is drawn in drawScene, but the timing update is done here to
|
||||||
|
// keep dtime out of the drawing code.
|
||||||
|
if (runData.damage_flash > 0.0f) {
|
||||||
|
runData.damage_flash -= 384.0f * dtime;
|
||||||
|
}
|
||||||
|
|
||||||
stats->drawtime = tt_draw.stop(true);
|
|
||||||
g_profiler->graphAdd("Draw scene [us]", stats->drawtime);
|
|
||||||
g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true));
|
g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4256,6 +4285,81 @@ void Game::updateShadows()
|
|||||||
shadow->getDirectionalLight().update_frustum(camera, client, m_camera_offset_changed);
|
shadow->getDirectionalLight().update_frustum(camera, client, m_camera_offset_changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
|
||||||
|
{
|
||||||
|
const video::SColor bg_color = this->sky->getBgColor();
|
||||||
|
const video::SColor sky_color = this->sky->getSkyColor();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fog
|
||||||
|
*/
|
||||||
|
if (this->m_cache_enable_fog) {
|
||||||
|
this->driver->setFog(
|
||||||
|
bg_color,
|
||||||
|
video::EFT_FOG_LINEAR,
|
||||||
|
this->runData.fog_range * this->sky->getFogStart(),
|
||||||
|
this->runData.fog_range * 1.0f,
|
||||||
|
0.01f,
|
||||||
|
false, // pixel fog
|
||||||
|
true // range fog
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this->driver->setFog(
|
||||||
|
bg_color,
|
||||||
|
video::EFT_FOG_LINEAR,
|
||||||
|
100000 * BS,
|
||||||
|
110000 * BS,
|
||||||
|
0.01f,
|
||||||
|
false, // pixel fog
|
||||||
|
false // range fog
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Drawing
|
||||||
|
*/
|
||||||
|
TimeTaker tt_draw("Draw scene", nullptr, PRECISION_MICRO);
|
||||||
|
this->driver->beginScene(true, true, sky_color);
|
||||||
|
|
||||||
|
const LocalPlayer *player = this->client->getEnv().getLocalPlayer();
|
||||||
|
bool draw_wield_tool = (this->m_game_ui->m_flags.show_hud &&
|
||||||
|
(player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) &&
|
||||||
|
(this->camera->getCameraMode() == CAMERA_MODE_FIRST));
|
||||||
|
bool draw_crosshair = (
|
||||||
|
(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
|
||||||
|
(this->camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT));
|
||||||
|
#ifdef HAVE_TOUCHSCREENGUI
|
||||||
|
if (this->isNoCrosshairAllowed())
|
||||||
|
draw_crosshair = false;
|
||||||
|
#endif
|
||||||
|
this->m_rendering_engine->draw_scene(sky_color, this->m_game_ui->m_flags.show_hud,
|
||||||
|
this->m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Profiler graph
|
||||||
|
*/
|
||||||
|
v2u32 screensize = this->driver->getScreenSize();
|
||||||
|
|
||||||
|
if (this->m_game_ui->m_flags.show_profiler_graph)
|
||||||
|
graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
|
||||||
|
|
||||||
|
/*
|
||||||
|
Damage flash
|
||||||
|
*/
|
||||||
|
if (this->runData.damage_flash > 0.0f) {
|
||||||
|
video::SColor color(this->runData.damage_flash, 180, 0, 0);
|
||||||
|
this->driver->draw2DRectangle(color,
|
||||||
|
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->driver->endScene();
|
||||||
|
|
||||||
|
stats->drawtime = tt_draw.stop(true);
|
||||||
|
g_profiler->graphAdd("Draw scene [us]", stats->drawtime);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
Misc
|
Misc
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -4389,41 +4493,6 @@ void Game::showPauseMenu()
|
|||||||
"- touch&drag, tap 2nd finger\n"
|
"- touch&drag, tap 2nd finger\n"
|
||||||
" --> place single item to slot\n"
|
" --> place single item to slot\n"
|
||||||
);
|
);
|
||||||
#else
|
|
||||||
static const std::string control_text_template = strgettext("Controls:\n"
|
|
||||||
"- %s: move forwards\n"
|
|
||||||
"- %s: move backwards\n"
|
|
||||||
"- %s: move left\n"
|
|
||||||
"- %s: move right\n"
|
|
||||||
"- %s: jump/climb up\n"
|
|
||||||
"- %s: dig/punch/use\n"
|
|
||||||
"- %s: place/use\n"
|
|
||||||
"- %s: sneak/climb down\n"
|
|
||||||
"- %s: drop item\n"
|
|
||||||
"- %s: inventory\n"
|
|
||||||
"- Mouse: turn/look\n"
|
|
||||||
"- Mouse wheel: select item\n"
|
|
||||||
"- %s: chat\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
char control_text_buf[600];
|
|
||||||
|
|
||||||
porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(),
|
|
||||||
GET_KEY_NAME(keymap_forward),
|
|
||||||
GET_KEY_NAME(keymap_backward),
|
|
||||||
GET_KEY_NAME(keymap_left),
|
|
||||||
GET_KEY_NAME(keymap_right),
|
|
||||||
GET_KEY_NAME(keymap_jump),
|
|
||||||
GET_KEY_NAME(keymap_dig),
|
|
||||||
GET_KEY_NAME(keymap_place),
|
|
||||||
GET_KEY_NAME(keymap_sneak),
|
|
||||||
GET_KEY_NAME(keymap_drop),
|
|
||||||
GET_KEY_NAME(keymap_inventory),
|
|
||||||
GET_KEY_NAME(keymap_chat)
|
|
||||||
);
|
|
||||||
|
|
||||||
std::string control_text = std::string(control_text_buf);
|
|
||||||
str_formspec_escape(control_text);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
|
float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
|
||||||
@ -4448,30 +4517,29 @@ void Game::showPauseMenu()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
|
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
|
||||||
<< strgettext("Change Keys") << "]";
|
<< strgettext("Controls") << "]";
|
||||||
#endif
|
#endif
|
||||||
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
|
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
|
||||||
<< strgettext("Exit to Menu") << "]";
|
<< strgettext("Exit to Menu") << "]";
|
||||||
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
|
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
|
||||||
<< strgettext("Exit to OS") << "]"
|
<< strgettext("Exit to OS") << "]";
|
||||||
<< "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
|
#ifdef HAVE_TOUCHSCREENGUI
|
||||||
<< "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
|
os << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]";
|
||||||
|
#endif
|
||||||
|
os << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< strgettext("Game info:") << "\n";
|
<< strgettext("Game info:") << "\n";
|
||||||
const std::string &address = client->getAddressName();
|
const std::string &address = client->getAddressName();
|
||||||
static const std::string mode = strgettext("- Mode: ");
|
os << strgettext("- Mode: ");
|
||||||
if (!simple_singleplayer_mode) {
|
if (!simple_singleplayer_mode) {
|
||||||
Address serverAddress = client->getServerAddress();
|
if (address.empty())
|
||||||
if (!address.empty()) {
|
os << strgettext("Hosting server");
|
||||||
os << mode << strgettext("Remote server") << "\n"
|
else
|
||||||
<< strgettext("- Address: ") << address;
|
os << strgettext("Remote server");
|
||||||
} else {
|
|
||||||
os << mode << strgettext("Hosting server");
|
|
||||||
}
|
|
||||||
os << "\n" << strgettext("- Port: ") << serverAddress.getPort() << "\n";
|
|
||||||
} else {
|
} else {
|
||||||
os << mode << strgettext("Singleplayer") << "\n";
|
os << strgettext("Singleplayer");
|
||||||
}
|
}
|
||||||
|
os << "\n";
|
||||||
if (simple_singleplayer_mode || address.empty()) {
|
if (simple_singleplayer_mode || address.empty()) {
|
||||||
static const std::string on = strgettext("On");
|
static const std::string on = strgettext("On");
|
||||||
static const std::string off = strgettext("Off");
|
static const std::string off = strgettext("Off");
|
||||||
|
@ -43,6 +43,8 @@ struct CameraOrientation {
|
|||||||
f32 camera_pitch; // "up/down"
|
f32 camera_pitch; // "up/down"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define GAME_FALLBACK_TIMEOUT 1.8f
|
||||||
|
#define GAME_CONNECTION_TIMEOUT 10.0f
|
||||||
|
|
||||||
void the_game(bool *kill,
|
void the_game(bool *kill,
|
||||||
InputHandler *input,
|
InputHandler *input,
|
||||||
|
@ -334,3 +334,36 @@ void GameUI::deleteFormspec()
|
|||||||
|
|
||||||
m_formname.clear();
|
m_formname.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameUI::clearText()
|
||||||
|
{
|
||||||
|
if (m_guitext_chat) {
|
||||||
|
m_guitext_chat->remove();
|
||||||
|
m_guitext_chat = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_guitext) {
|
||||||
|
m_guitext->remove();
|
||||||
|
m_guitext = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_guitext2) {
|
||||||
|
m_guitext2->remove();
|
||||||
|
m_guitext2 = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_guitext_info) {
|
||||||
|
m_guitext_info->remove();
|
||||||
|
m_guitext_info = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_guitext_status) {
|
||||||
|
m_guitext_status->remove();
|
||||||
|
m_guitext_status = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_guitext_profiler) {
|
||||||
|
m_guitext_profiler->remove();
|
||||||
|
m_guitext_profiler = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -106,6 +106,7 @@ public:
|
|||||||
const std::string &getFormspecName() { return m_formname; }
|
const std::string &getFormspecName() { return m_formname; }
|
||||||
GUIFormSpecMenu *&getFormspecGUI() { return m_formspec; }
|
GUIFormSpecMenu *&getFormspecGUI() { return m_formspec; }
|
||||||
void deleteFormspec();
|
void deleteFormspec();
|
||||||
|
void clearText();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Flags m_flags;
|
Flags m_flags;
|
||||||
|
@ -129,6 +129,11 @@ public:
|
|||||||
m_position = position;
|
m_position = position;
|
||||||
m_sneak_node_exists = false;
|
m_sneak_node_exists = false;
|
||||||
}
|
}
|
||||||
|
inline void addPosition(const v3f &added_pos)
|
||||||
|
{
|
||||||
|
m_position += added_pos;
|
||||||
|
m_sneak_node_exists = false;
|
||||||
|
}
|
||||||
|
|
||||||
v3f getPosition() const { return m_position; }
|
v3f getPosition() const { return m_position; }
|
||||||
|
|
||||||
|
@ -47,7 +47,6 @@ struct MeshCollector
|
|||||||
// offset: offset added to vertices
|
// offset: offset added to vertices
|
||||||
MeshCollector(const v3f center_pos, v3f offset = v3f()) : m_center_pos(center_pos), offset(offset) {}
|
MeshCollector(const v3f center_pos, v3f offset = v3f()) : m_center_pos(center_pos), offset(offset) {}
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
void append(const TileSpec &material,
|
void append(const TileSpec &material,
|
||||||
const video::S3DVertex *vertices, u32 numVertices,
|
const video::S3DVertex *vertices, u32 numVertices,
|
||||||
const u16 *indices, u32 numIndices);
|
const u16 *indices, u32 numIndices);
|
||||||
@ -55,10 +54,8 @@ struct MeshCollector
|
|||||||
const video::S3DVertex *vertices, u32 numVertices,
|
const video::S3DVertex *vertices, u32 numVertices,
|
||||||
const u16 *indices, u32 numIndices,
|
const u16 *indices, u32 numIndices,
|
||||||
v3f pos, video::SColor c, u8 light_source);
|
v3f pos, video::SColor c, u8 light_source);
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// clang-format off
|
|
||||||
void append(const TileLayer &material,
|
void append(const TileLayer &material,
|
||||||
const video::S3DVertex *vertices, u32 numVertices,
|
const video::S3DVertex *vertices, u32 numVertices,
|
||||||
const u16 *indices, u32 numIndices,
|
const u16 *indices, u32 numIndices,
|
||||||
@ -68,7 +65,6 @@ private:
|
|||||||
const u16 *indices, u32 numIndices,
|
const u16 *indices, u32 numIndices,
|
||||||
v3f pos, video::SColor c, u8 light_source,
|
v3f pos, video::SColor c, u8 light_source,
|
||||||
u8 layernum, bool use_scale = false);
|
u8 layernum, bool use_scale = false);
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
PreMeshBuffer &findBuffer(const TileLayer &layer, u8 layernum, u32 numVertices);
|
PreMeshBuffer &findBuffer(const TileLayer &layer, u8 layernum, u32 numVertices);
|
||||||
};
|
};
|
||||||
|
@ -120,8 +120,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
static const u8 TEXTURE_EXPOSURE_1 = 3;
|
static const u8 TEXTURE_EXPOSURE_1 = 3;
|
||||||
static const u8 TEXTURE_EXPOSURE_2 = 4;
|
static const u8 TEXTURE_EXPOSURE_2 = 4;
|
||||||
static const u8 TEXTURE_FXAA = 5;
|
static const u8 TEXTURE_FXAA = 5;
|
||||||
static const u8 TEXTURE_BLOOM_DOWN = 10;
|
static const u8 TEXTURE_VOLUME = 6;
|
||||||
static const u8 TEXTURE_BLOOM_UP = 20;
|
static const u8 TEXTURE_SCALE_DOWN = 10;
|
||||||
|
static const u8 TEXTURE_SCALE_UP = 20;
|
||||||
|
|
||||||
// Super-sampling is simply rendering into a larger texture.
|
// Super-sampling is simply rendering into a larger texture.
|
||||||
// Downscaling is done by the final step when rendering to the screen.
|
// Downscaling is done by the final step when rendering to the screen.
|
||||||
@ -130,6 +131,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure");
|
const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure");
|
||||||
const bool enable_ssaa = antialiasing == "ssaa";
|
const bool enable_ssaa = antialiasing == "ssaa";
|
||||||
const bool enable_fxaa = antialiasing == "fxaa";
|
const bool enable_fxaa = antialiasing == "fxaa";
|
||||||
|
const bool enable_volumetric_light = g_settings->getBool("enable_volumetric_lighting") && enable_bloom;
|
||||||
|
|
||||||
if (enable_ssaa) {
|
if (enable_ssaa) {
|
||||||
u16 ssaa_scale = MYMAX(2, g_settings->getU16("fsaa"));
|
u16 ssaa_scale = MYMAX(2, g_settings->getU16("fsaa"));
|
||||||
@ -160,9 +162,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
|
|
||||||
v2f downscale = scale * 0.5;
|
v2f downscale = scale * 0.5;
|
||||||
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
||||||
buffer->setTexture(TEXTURE_BLOOM_DOWN + i, downscale, std::string("downsample") + std::to_string(i), color_format);
|
buffer->setTexture(TEXTURE_SCALE_DOWN + i, downscale, std::string("downsample") + std::to_string(i), color_format);
|
||||||
if (enable_bloom)
|
if (enable_bloom)
|
||||||
buffer->setTexture(TEXTURE_BLOOM_UP + i, downscale, std::string("upsample") + std::to_string(i), color_format);
|
buffer->setTexture(TEXTURE_SCALE_UP + i, downscale, std::string("upsample") + std::to_string(i), color_format);
|
||||||
downscale *= 0.5;
|
downscale *= 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,20 +173,30 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
|
|
||||||
// get bright spots
|
// get bright spots
|
||||||
u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH);
|
u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
RenderStep *extract_bloom = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_EXPOSURE_1 });
|
RenderStep *extract_bloom = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source, TEXTURE_EXPOSURE_1 });
|
||||||
extract_bloom->setRenderSource(buffer);
|
extract_bloom->setRenderSource(buffer);
|
||||||
extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM));
|
extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM));
|
||||||
source = TEXTURE_BLOOM;
|
source = TEXTURE_BLOOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enable_volumetric_light) {
|
||||||
|
buffer->setTexture(TEXTURE_VOLUME, scale, "volume", color_format);
|
||||||
|
|
||||||
|
shader_id = client->getShaderSource()->getShader("volumetric_light", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
|
auto volume = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source, TEXTURE_DEPTH });
|
||||||
|
volume->setRenderSource(buffer);
|
||||||
|
volume->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_VOLUME));
|
||||||
|
source = TEXTURE_VOLUME;
|
||||||
|
}
|
||||||
|
|
||||||
// downsample
|
// downsample
|
||||||
shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
||||||
auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source });
|
auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source });
|
||||||
step->setRenderSource(buffer);
|
step->setRenderSource(buffer);
|
||||||
step->setBilinearFilter(0, true);
|
step->setBilinearFilter(0, true);
|
||||||
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM_DOWN + i));
|
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_SCALE_DOWN + i));
|
||||||
source = TEXTURE_BLOOM_DOWN + i;
|
source = TEXTURE_SCALE_DOWN + i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,19 +205,19 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
// upsample
|
// upsample
|
||||||
shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) {
|
for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) {
|
||||||
auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { u8(TEXTURE_BLOOM_DOWN + i - 1), source });
|
auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { u8(TEXTURE_SCALE_DOWN + i - 1), source });
|
||||||
step->setRenderSource(buffer);
|
step->setRenderSource(buffer);
|
||||||
step->setBilinearFilter(0, true);
|
step->setBilinearFilter(0, true);
|
||||||
step->setBilinearFilter(1, true);
|
step->setBilinearFilter(1, true);
|
||||||
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, u8(TEXTURE_BLOOM_UP + i - 1)));
|
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, u8(TEXTURE_SCALE_UP + i - 1)));
|
||||||
source = TEXTURE_BLOOM_UP + i - 1;
|
source = TEXTURE_SCALE_UP + i - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic Exposure pt2
|
// Dynamic Exposure pt2
|
||||||
if (enable_auto_exposure) {
|
if (enable_auto_exposure) {
|
||||||
shader_id = client->getShaderSource()->getShader("update_exposure", TILE_MATERIAL_PLAIN, NDT_MESH);
|
shader_id = client->getShaderSource()->getShader("update_exposure", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
auto update_exposure = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_EXPOSURE_1, u8(TEXTURE_BLOOM_DOWN + MIPMAP_LEVELS - 1) });
|
auto update_exposure = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_EXPOSURE_1, u8(TEXTURE_SCALE_DOWN + MIPMAP_LEVELS - 1) });
|
||||||
update_exposure->setBilinearFilter(1, true);
|
update_exposure->setBilinearFilter(1, true);
|
||||||
update_exposure->setRenderSource(buffer);
|
update_exposure->setRenderSource(buffer);
|
||||||
update_exposure->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_EXPOSURE_2));
|
update_exposure->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_EXPOSURE_2));
|
||||||
@ -228,7 +240,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
|
|
||||||
// final merge
|
// final merge
|
||||||
shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
|
shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { final_stage_source, TEXTURE_BLOOM_UP, TEXTURE_EXPOSURE_2 });
|
PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { final_stage_source, TEXTURE_SCALE_UP, TEXTURE_EXPOSURE_2 });
|
||||||
pipeline->addStep(effect);
|
pipeline->addStep(effect);
|
||||||
if (enable_ssaa)
|
if (enable_ssaa)
|
||||||
effect->setBilinearFilter(0, true);
|
effect->setBilinearFilter(0, true);
|
||||||
|
@ -249,8 +249,10 @@ void RenderingEngine::draw_load_screen(const std::wstring &text,
|
|||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
const core::dimension2d<u32> &img_size =
|
const core::dimension2d<u32> &img_size =
|
||||||
progress_img_bg->getSize();
|
progress_img_bg->getSize();
|
||||||
u32 imgW = rangelim(img_size.Width, 200, 600) * getDisplayDensity();
|
float density = g_settings->getFloat("gui_scaling", 0.5f, 20.0f) *
|
||||||
u32 imgH = rangelim(img_size.Height, 24, 72) * getDisplayDensity();
|
getDisplayDensity();
|
||||||
|
u32 imgW = rangelim(img_size.Width, 200, 600) * density;
|
||||||
|
u32 imgH = rangelim(img_size.Height, 24, 72) * density;
|
||||||
#else
|
#else
|
||||||
const core::dimension2d<u32> img_size(256, 48);
|
const core::dimension2d<u32> img_size(256, 48);
|
||||||
float imgRatio = (float)img_size.Height / img_size.Width;
|
float imgRatio = (float)img_size.Height / img_size.Width;
|
||||||
|
@ -138,6 +138,17 @@ public:
|
|||||||
const irr::core::dimension2d<u32> initial_screen_size,
|
const irr::core::dimension2d<u32> initial_screen_size,
|
||||||
const bool initial_window_maximized);
|
const bool initial_window_maximized);
|
||||||
|
|
||||||
|
static bool shouldRender()
|
||||||
|
{
|
||||||
|
// On Android, pause rendering while the app is in background (generally not visible).
|
||||||
|
// Don't do this on desktop because windows can be partially visible.
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
return get_raw_device()->isWindowActive();
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
v2u32 _getWindowSize() const;
|
v2u32 _getWindowSize() const;
|
||||||
|
|
||||||
|
@ -767,6 +767,13 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
|
|||||||
shaders_header << "#define SSAA_SCALE " << ssaa_scale << ".\n";
|
shaders_header << "#define SSAA_SCALE " << ssaa_scale << ".\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_settings->getBool("debanding"))
|
||||||
|
shaders_header << "#define ENABLE_DITHERING 1\n";
|
||||||
|
|
||||||
|
if (g_settings->getBool("enable_volumetric_lighting")) {
|
||||||
|
shaders_header << "#define VOLUMETRIC_LIGHT 1\n";
|
||||||
|
}
|
||||||
|
|
||||||
shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics
|
shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics
|
||||||
|
|
||||||
std::string common_header = shaders_header.str();
|
std::string common_header = shaders_header.str();
|
||||||
|
@ -120,6 +120,9 @@ public:
|
|||||||
void setFogStart(float fog_start) { m_sky_params.fog_start = fog_start; }
|
void setFogStart(float fog_start) { m_sky_params.fog_start = fog_start; }
|
||||||
float getFogStart() const { return m_sky_params.fog_start; }
|
float getFogStart() const { return m_sky_params.fog_start; }
|
||||||
|
|
||||||
|
void setVolumetricLightStrength(float volumetric_light_strength) { m_sky_params.volumetric_light_strength = volumetric_light_strength; }
|
||||||
|
float getVolumetricLightStrength() const { return m_sky_params.volumetric_light_strength; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
aabb3f m_box;
|
aabb3f m_box;
|
||||||
video::SMaterial m_materials[SKY_MATERIAL_COUNT];
|
video::SMaterial m_materials[SKY_MATERIAL_COUNT];
|
||||||
|
58
src/client/sound/al_extensions.cpp
Normal file
58
src/client/sound/al_extensions.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Minetest
|
||||||
|
Copyright (C) 2023 DS
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "al_extensions.h"
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace sound {
|
||||||
|
|
||||||
|
ALExtensions::ALExtensions(const ALCdevice *deviceHandle [[maybe_unused]])
|
||||||
|
{
|
||||||
|
auto blacklist_vec = str_split(g_settings->get("sound_extensions_blacklist"), ',');
|
||||||
|
for (auto &s : blacklist_vec) {
|
||||||
|
s = trim(s);
|
||||||
|
}
|
||||||
|
std::unordered_set<std::string> blacklist;
|
||||||
|
blacklist.insert(blacklist_vec.begin(), blacklist_vec.end());
|
||||||
|
|
||||||
|
{
|
||||||
|
constexpr const char *ext_name = "AL_SOFT_direct_channels_remix";
|
||||||
|
bool blacklisted = blacklist.find(ext_name) != blacklist.end();
|
||||||
|
if (blacklisted)
|
||||||
|
infostream << "ALExtensions: Blacklisted: " << ext_name << std::endl;
|
||||||
|
#ifndef AL_SOFT_direct_channels_remix
|
||||||
|
infostream << "ALExtensions: Not compiled with: " << ext_name << std::endl;
|
||||||
|
#else
|
||||||
|
bool found = alIsExtensionPresent(ext_name);
|
||||||
|
if (found)
|
||||||
|
infostream << "ALExtensions: Found: " << ext_name << std::endl;
|
||||||
|
else
|
||||||
|
infostream << "ALExtensions: Not found: " << ext_name << std::endl;
|
||||||
|
|
||||||
|
if (found && !blacklisted) {
|
||||||
|
have_ext_AL_SOFT_direct_channels_remix = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
src/client/sound/al_extensions.h
Normal file
38
src/client/sound/al_extensions.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
Minetest
|
||||||
|
Copyright (C) 2023 DS
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "al_helpers.h"
|
||||||
|
|
||||||
|
namespace sound {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct for AL and ALC extensions
|
||||||
|
*/
|
||||||
|
struct ALExtensions
|
||||||
|
{
|
||||||
|
explicit ALExtensions(const ALCdevice *deviceHandle [[maybe_unused]]);
|
||||||
|
|
||||||
|
#ifdef AL_SOFT_direct_channels_remix
|
||||||
|
bool have_ext_AL_SOFT_direct_channels_remix = false;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,12 +18,13 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "playing_sound.h"
|
#include "playing_sound.h"
|
||||||
|
|
||||||
|
#include "al_extensions.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@ -32,7 +33,8 @@ namespace sound {
|
|||||||
|
|
||||||
PlayingSound::PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> data,
|
PlayingSound::PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> data,
|
||||||
bool loop, f32 volume, f32 pitch, f32 start_time,
|
bool loop, f32 volume, f32 pitch, f32 start_time,
|
||||||
const std::optional<std::pair<v3f, v3f>> &pos_vel_opt)
|
const std::optional<std::pair<v3f, v3f>> &pos_vel_opt,
|
||||||
|
const ALExtensions &exts [[maybe_unused]])
|
||||||
: m_source_id(source_id), m_data(std::move(data)), m_looping(loop),
|
: m_source_id(source_id), m_data(std::move(data)), m_looping(loop),
|
||||||
m_is_positional(pos_vel_opt.has_value())
|
m_is_positional(pos_vel_opt.has_value())
|
||||||
{
|
{
|
||||||
@ -113,6 +115,15 @@ PlayingSound::PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> dat
|
|||||||
alSource3f(m_source_id, AL_POSITION, 0.0f, 0.0f, 0.0f);
|
alSource3f(m_source_id, AL_POSITION, 0.0f, 0.0f, 0.0f);
|
||||||
alSource3f(m_source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
alSource3f(m_source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||||
warn_if_al_error("PlayingSound::PlayingSound at making position-less");
|
warn_if_al_error("PlayingSound::PlayingSound at making position-less");
|
||||||
|
|
||||||
|
#ifdef AL_SOFT_direct_channels_remix
|
||||||
|
// Play directly on stereo output channels if possible. Improves sound quality.
|
||||||
|
if (exts.have_ext_AL_SOFT_direct_channels_remix
|
||||||
|
&& m_data->m_decode_info.is_stereo) {
|
||||||
|
alSourcei(m_source_id, AL_DIRECT_CHANNELS_SOFT, AL_REMIX_UNMATCHED_SOFT);
|
||||||
|
warn_if_al_error("PlayingSound::PlayingSound at setting AL_DIRECT_CHANNELS_SOFT");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
setGain(volume);
|
setGain(volume);
|
||||||
setPitch(pitch);
|
setPitch(pitch);
|
||||||
|
@ -18,13 +18,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "sound_data.h"
|
#include "sound_data.h"
|
||||||
|
namespace sound { struct ALExtensions; }
|
||||||
|
|
||||||
namespace sound {
|
namespace sound {
|
||||||
|
|
||||||
@ -51,7 +52,8 @@ class PlayingSound final
|
|||||||
public:
|
public:
|
||||||
PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> data, bool loop,
|
PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> data, bool loop,
|
||||||
f32 volume, f32 pitch, f32 start_time,
|
f32 volume, f32 pitch, f32 start_time,
|
||||||
const std::optional<std::pair<v3f, v3f>> &pos_vel_opt);
|
const std::optional<std::pair<v3f, v3f>> &pos_vel_opt,
|
||||||
|
const ALExtensions &exts [[maybe_unused]]);
|
||||||
|
|
||||||
~PlayingSound() noexcept
|
~PlayingSound() noexcept
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,13 +18,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "sound_data.h"
|
#include "sound_data.h"
|
||||||
|
|
||||||
#include "sound_constants.h"
|
#include "sound_constants.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace sound {
|
namespace sound {
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ std::shared_ptr<PlayingSound> OpenALSoundManager::createPlayingSound(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto sound = std::make_shared<PlayingSound>(source_id, std::move(lsnd), loop,
|
auto sound = std::make_shared<PlayingSound>(source_id, std::move(lsnd), loop,
|
||||||
volume, pitch, start_time, pos_vel_opt);
|
volume, pitch, start_time, pos_vel_opt, m_exts);
|
||||||
|
|
||||||
sound->play();
|
sound->play();
|
||||||
|
|
||||||
@ -271,7 +271,8 @@ OpenALSoundManager::OpenALSoundManager(SoundManagerSingleton *smg,
|
|||||||
Thread("OpenALSoundManager"),
|
Thread("OpenALSoundManager"),
|
||||||
m_fallback_path_provider(std::move(fallback_path_provider)),
|
m_fallback_path_provider(std::move(fallback_path_provider)),
|
||||||
m_device(smg->m_device.get()),
|
m_device(smg->m_device.get()),
|
||||||
m_context(smg->m_context.get())
|
m_context(smg->m_context.get()),
|
||||||
|
m_exts(m_device)
|
||||||
{
|
{
|
||||||
SANITY_CHECK(!!m_fallback_path_provider);
|
SANITY_CHECK(!!m_fallback_path_provider);
|
||||||
|
|
||||||
|
@ -18,13 +18,14 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "playing_sound.h"
|
#include "playing_sound.h"
|
||||||
|
#include "al_extensions.h"
|
||||||
#include "sound_constants.h"
|
#include "sound_constants.h"
|
||||||
#include "sound_manager_messages.h"
|
#include "sound_manager_messages.h"
|
||||||
#include "../sound.h"
|
#include "../sound.h"
|
||||||
@ -51,8 +52,10 @@ class OpenALSoundManager final : public Thread
|
|||||||
private:
|
private:
|
||||||
std::unique_ptr<SoundFallbackPathProvider> m_fallback_path_provider;
|
std::unique_ptr<SoundFallbackPathProvider> m_fallback_path_provider;
|
||||||
|
|
||||||
ALCdevice *m_device;
|
ALCdevice *const m_device;
|
||||||
ALCcontext *m_context;
|
ALCcontext *const m_context;
|
||||||
|
|
||||||
|
const ALExtensions m_exts;
|
||||||
|
|
||||||
// time in seconds until which removeDeadSounds will be called again
|
// time in seconds until which removeDeadSounds will be called again
|
||||||
f32 m_time_until_dead_removal = REMOVE_DEAD_SOUNDS_INTERVAL;
|
f32 m_time_until_dead_removal = REMOVE_DEAD_SOUNDS_INTERVAL;
|
||||||
|
@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
with this program; ifnot, write to the Free Software Foundation, Inc.,
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1598,6 +1598,13 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
|||||||
u32 frame_count = stoi(sf.next(":"));
|
u32 frame_count = stoi(sf.next(":"));
|
||||||
u32 frame_index = stoi(sf.next(":"));
|
u32 frame_index = stoi(sf.next(":"));
|
||||||
|
|
||||||
|
if (frame_count == 0){
|
||||||
|
errorstream << "generateImagePart(): invalid frame_count "
|
||||||
|
<< "for part_of_name=\"" << part_of_name
|
||||||
|
<< "\", using frame_count = 1 instead." << std::endl;
|
||||||
|
frame_count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (baseimg == NULL){
|
if (baseimg == NULL){
|
||||||
errorstream<<"generateImagePart(): baseimg != NULL "
|
errorstream<<"generateImagePart(): baseimg != NULL "
|
||||||
<<"for part_of_name=\""<<part_of_name
|
<<"for part_of_name=\""<<part_of_name
|
||||||
@ -1899,6 +1906,13 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
|||||||
u32 x0 = stoi(sf.next(","));
|
u32 x0 = stoi(sf.next(","));
|
||||||
u32 y0 = stoi(sf.next(":"));
|
u32 y0 = stoi(sf.next(":"));
|
||||||
|
|
||||||
|
if (w0 == 0 || h0 == 0) {
|
||||||
|
errorstream << "generateImagePart(): invalid width or height "
|
||||||
|
<< "for part_of_name=\"" << part_of_name
|
||||||
|
<< "\", cancelling." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
core::dimension2d<u32> img_dim = baseimg->getDimension();
|
core::dimension2d<u32> img_dim = baseimg->getDimension();
|
||||||
core::dimension2d<u32> tile_dim(v2u32(img_dim) / v2u32(w0, h0));
|
core::dimension2d<u32> tile_dim(v2u32(img_dim) / v2u32(w0, h0));
|
||||||
|
|
||||||
|
@ -33,11 +33,13 @@ public:
|
|||||||
f32 real_gui_scaling;
|
f32 real_gui_scaling;
|
||||||
f32 real_hud_scaling;
|
f32 real_hud_scaling;
|
||||||
v2f32 max_fs_size;
|
v2f32 max_fs_size;
|
||||||
|
bool touch_controls;
|
||||||
|
|
||||||
bool equal(const ClientDynamicInfo &other) const {
|
bool equal(const ClientDynamicInfo &other) const {
|
||||||
return render_target_size == other.render_target_size &&
|
return render_target_size == other.render_target_size &&
|
||||||
abs(real_gui_scaling - other.real_gui_scaling) < 0.001f &&
|
abs(real_gui_scaling - other.real_gui_scaling) < 0.001f &&
|
||||||
abs(real_hud_scaling - other.real_hud_scaling) < 0.001f;
|
abs(real_hud_scaling - other.real_hud_scaling) < 0.001f &&
|
||||||
|
touch_controls == other.touch_controls;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SERVER
|
#ifndef SERVER
|
||||||
@ -48,10 +50,16 @@ public:
|
|||||||
f32 hud_scaling = g_settings->getFloat("hud_scaling", 0.5f, 20.0f);
|
f32 hud_scaling = g_settings->getFloat("hud_scaling", 0.5f, 20.0f);
|
||||||
f32 real_gui_scaling = gui_scaling * density;
|
f32 real_gui_scaling = gui_scaling * density;
|
||||||
f32 real_hud_scaling = hud_scaling * density;
|
f32 real_hud_scaling = hud_scaling * density;
|
||||||
|
#ifdef HAVE_TOUCHSCREENGUI
|
||||||
|
bool touch_controls = true;
|
||||||
|
#else
|
||||||
|
bool touch_controls = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
return {
|
return {
|
||||||
screen_size, real_gui_scaling, real_hud_scaling,
|
screen_size, real_gui_scaling, real_hud_scaling,
|
||||||
ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling)
|
ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling),
|
||||||
|
touch_controls
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -59,6 +59,7 @@ RemoteClient::RemoteClient() :
|
|||||||
g_settings->getFloat("full_block_send_enable_min_time_from_building")),
|
g_settings->getFloat("full_block_send_enable_min_time_from_building")),
|
||||||
m_max_send_distance(g_settings->getS16("max_block_send_distance")),
|
m_max_send_distance(g_settings->getS16("max_block_send_distance")),
|
||||||
m_block_optimize_distance(g_settings->getS16("block_send_optimize_distance")),
|
m_block_optimize_distance(g_settings->getS16("block_send_optimize_distance")),
|
||||||
|
m_block_cull_optimize_distance(g_settings->getS16("block_cull_optimize_distance")),
|
||||||
m_max_gen_distance(g_settings->getS16("max_block_generate_distance")),
|
m_max_gen_distance(g_settings->getS16("max_block_generate_distance")),
|
||||||
m_occ_cull(g_settings->getBool("server_side_occlusion_culling"))
|
m_occ_cull(g_settings->getBool("server_side_occlusion_culling"))
|
||||||
{
|
{
|
||||||
@ -225,7 +226,10 @@ void RemoteClient::GetNextBlocks (
|
|||||||
wanted_range);
|
wanted_range);
|
||||||
const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, prop_zoom_fov),
|
const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, prop_zoom_fov),
|
||||||
wanted_range);
|
wanted_range);
|
||||||
const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE;
|
const s16 d_cull_opt = std::min(adjustDist(m_block_cull_optimize_distance, prop_zoom_fov),
|
||||||
|
wanted_range);
|
||||||
|
// f32 to prevent overflow, it is also what isBlockInSight(...) expects
|
||||||
|
const f32 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE;
|
||||||
|
|
||||||
s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov),
|
s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov),
|
||||||
wanted_range);
|
wanted_range);
|
||||||
@ -258,10 +262,9 @@ void RemoteClient::GetNextBlocks (
|
|||||||
Get the border/face dot coordinates of a "d-radiused"
|
Get the border/face dot coordinates of a "d-radiused"
|
||||||
box
|
box
|
||||||
*/
|
*/
|
||||||
std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
|
const auto &list = FacePositionCache::getFacePositions(d);
|
||||||
|
|
||||||
std::vector<v3s16>::iterator li;
|
for (auto li = list.begin(); li != list.end(); ++li) {
|
||||||
for (li = list.begin(); li != list.end(); ++li) {
|
|
||||||
v3s16 p = *li + center;
|
v3s16 p = *li + center;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -347,18 +350,21 @@ void RemoteClient::GetNextBlocks (
|
|||||||
if (!block->getIsUnderground() && !block->getDayNightDiff())
|
if (!block->getIsUnderground() && !block->getDayNightDiff())
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check occlusion cache first.
|
Check occlusion cache first.
|
||||||
*/
|
*/
|
||||||
if (m_blocks_occ.find(p) != m_blocks_occ.end())
|
if (m_blocks_occ.find(p) != m_blocks_occ.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (m_occ_cull && !block_not_found &&
|
/*
|
||||||
env->getMap().isBlockOccluded(block, cam_pos_nodes)) {
|
Note that we do this even before the block is loaded as this does not depend on its contents.
|
||||||
m_blocks_occ.insert(p);
|
*/
|
||||||
continue;
|
if (m_occ_cull &&
|
||||||
}
|
env->getMap().isBlockOccluded(p * MAP_BLOCKSIZE, cam_pos_nodes, d >= d_cull_opt)) {
|
||||||
|
m_blocks_occ.insert(p);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -397,6 +397,7 @@ private:
|
|||||||
const float m_min_time_from_building;
|
const float m_min_time_from_building;
|
||||||
const s16 m_max_send_distance;
|
const s16 m_max_send_distance;
|
||||||
const s16 m_block_optimize_distance;
|
const s16 m_block_optimize_distance;
|
||||||
|
const s16 m_block_cull_optimize_distance;
|
||||||
const s16 m_max_gen_distance;
|
const s16 m_max_gen_distance;
|
||||||
const bool m_occ_cull;
|
const bool m_occ_cull;
|
||||||
|
|
||||||
|
@ -108,10 +108,8 @@ bool parseModContents(ModSpec &spec)
|
|||||||
if (info.exists("depends")) {
|
if (info.exists("depends")) {
|
||||||
mod_conf_has_depends = true;
|
mod_conf_has_depends = true;
|
||||||
std::string dep = info.get("depends");
|
std::string dep = info.get("depends");
|
||||||
// clang-format off
|
|
||||||
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
||||||
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
||||||
// clang-format on
|
|
||||||
for (const auto &dependency : str_split(dep, ',')) {
|
for (const auto &dependency : str_split(dep, ',')) {
|
||||||
spec.depends.insert(dependency);
|
spec.depends.insert(dependency);
|
||||||
}
|
}
|
||||||
@ -120,10 +118,8 @@ bool parseModContents(ModSpec &spec)
|
|||||||
if (info.exists("optional_depends")) {
|
if (info.exists("optional_depends")) {
|
||||||
mod_conf_has_depends = true;
|
mod_conf_has_depends = true;
|
||||||
std::string dep = info.get("optional_depends");
|
std::string dep = info.get("optional_depends");
|
||||||
// clang-format off
|
|
||||||
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
||||||
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
||||||
// clang-format on
|
|
||||||
for (const auto &dependency : str_split(dep, ',')) {
|
for (const auto &dependency : str_split(dep, ',')) {
|
||||||
spec.optdepends.insert(dependency);
|
spec.optdepends.insert(dependency);
|
||||||
}
|
}
|
||||||
|
@ -942,8 +942,8 @@ void ModStorageDatabaseSQLite3::listMods(std::vector<std::string> *res)
|
|||||||
return 0;
|
return 0;
|
||||||
}, (void *) res, &errmsg);
|
}, (void *) res, &errmsg);
|
||||||
if (status != SQLITE_OK) {
|
if (status != SQLITE_OK) {
|
||||||
DatabaseException e(std::string("Error trying to list mods with metadata: ") + errmsg);
|
auto msg = std::string("Error trying to list mods with metadata: ") + errmsg;
|
||||||
sqlite3_free(errmsg);
|
sqlite3_free(errmsg);
|
||||||
throw e;
|
throw DatabaseException(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ void set_default_settings()
|
|||||||
settings->setDefault("sound_volume", "0.8");
|
settings->setDefault("sound_volume", "0.8");
|
||||||
settings->setDefault("sound_volume_unfocused", "0.3");
|
settings->setDefault("sound_volume_unfocused", "0.3");
|
||||||
settings->setDefault("mute_sound", "false");
|
settings->setDefault("mute_sound", "false");
|
||||||
|
settings->setDefault("sound_extensions_blacklist", "");
|
||||||
settings->setDefault("enable_mesh_cache", "false");
|
settings->setDefault("enable_mesh_cache", "false");
|
||||||
settings->setDefault("mesh_generation_interval", "0");
|
settings->setDefault("mesh_generation_interval", "0");
|
||||||
settings->setDefault("mesh_generation_threads", "0");
|
settings->setDefault("mesh_generation_threads", "0");
|
||||||
@ -268,12 +269,14 @@ void set_default_settings()
|
|||||||
settings->setDefault("enable_waving_plants", "false");
|
settings->setDefault("enable_waving_plants", "false");
|
||||||
settings->setDefault("exposure_compensation", "0.0");
|
settings->setDefault("exposure_compensation", "0.0");
|
||||||
settings->setDefault("enable_auto_exposure", "false");
|
settings->setDefault("enable_auto_exposure", "false");
|
||||||
|
settings->setDefault("debanding", "true");
|
||||||
settings->setDefault("antialiasing", "none");
|
settings->setDefault("antialiasing", "none");
|
||||||
settings->setDefault("enable_bloom", "false");
|
settings->setDefault("enable_bloom", "false");
|
||||||
settings->setDefault("enable_bloom_debug", "false");
|
settings->setDefault("enable_bloom_debug", "false");
|
||||||
settings->setDefault("bloom_strength_factor", "1.0");
|
settings->setDefault("bloom_strength_factor", "1.0");
|
||||||
settings->setDefault("bloom_intensity", "0.05");
|
settings->setDefault("bloom_intensity", "0.05");
|
||||||
settings->setDefault("bloom_radius", "1");
|
settings->setDefault("bloom_radius", "1");
|
||||||
|
settings->setDefault("enable_volumetric_lighting", "false");
|
||||||
|
|
||||||
// Effects Shadows
|
// Effects Shadows
|
||||||
settings->setDefault("enable_dynamic_shadows", "false");
|
settings->setDefault("enable_dynamic_shadows", "false");
|
||||||
@ -368,6 +371,7 @@ void set_default_settings()
|
|||||||
settings->setDefault("max_packets_per_iteration", "1024");
|
settings->setDefault("max_packets_per_iteration", "1024");
|
||||||
settings->setDefault("port", "30000");
|
settings->setDefault("port", "30000");
|
||||||
settings->setDefault("strict_protocol_version_checking", "false");
|
settings->setDefault("strict_protocol_version_checking", "false");
|
||||||
|
settings->setDefault("protocol_version_min", "1");
|
||||||
settings->setDefault("player_transfer_distance", "0");
|
settings->setDefault("player_transfer_distance", "0");
|
||||||
settings->setDefault("max_simultaneous_block_sends_per_client", "40");
|
settings->setDefault("max_simultaneous_block_sends_per_client", "40");
|
||||||
settings->setDefault("time_send_interval", "5");
|
settings->setDefault("time_send_interval", "5");
|
||||||
@ -397,6 +401,7 @@ void set_default_settings()
|
|||||||
// This causes frametime jitter on client side, or does it?
|
// This causes frametime jitter on client side, or does it?
|
||||||
settings->setDefault("max_block_send_distance", "12");
|
settings->setDefault("max_block_send_distance", "12");
|
||||||
settings->setDefault("block_send_optimize_distance", "4");
|
settings->setDefault("block_send_optimize_distance", "4");
|
||||||
|
settings->setDefault("block_cull_optimize_distance", "25");
|
||||||
settings->setDefault("server_side_occlusion_culling", "true");
|
settings->setDefault("server_side_occlusion_culling", "true");
|
||||||
settings->setDefault("csm_restriction_flags", "62");
|
settings->setDefault("csm_restriction_flags", "62");
|
||||||
settings->setDefault("csm_restriction_noderange", "0");
|
settings->setDefault("csm_restriction_noderange", "0");
|
||||||
@ -502,6 +507,7 @@ void set_default_settings()
|
|||||||
settings->setDefault("active_block_range", "2");
|
settings->setDefault("active_block_range", "2");
|
||||||
settings->setDefault("viewing_range", "50");
|
settings->setDefault("viewing_range", "50");
|
||||||
settings->setDefault("leaves_style", "simple");
|
settings->setDefault("leaves_style", "simple");
|
||||||
|
settings->setDefault("debanding", "false");
|
||||||
settings->setDefault("curl_verify_cert", "false");
|
settings->setDefault("curl_verify_cert", "false");
|
||||||
|
|
||||||
// Apply settings according to screen size
|
// Apply settings according to screen size
|
||||||
|
155
src/gettext.cpp
155
src/gettext.cpp
@ -25,6 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define setenv(n,v,o) _putenv_s(n,v)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if USE_GETTEXT && defined(_MSC_VER)
|
#if USE_GETTEXT && defined(_MSC_VER)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
@ -37,7 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
static std::map<std::wstring, std::wstring> glb_supported_locales;
|
static std::map<std::wstring, std::wstring> glb_supported_locales;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr)
|
static BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr)
|
||||||
{
|
{
|
||||||
char* endptr = 0;
|
char* endptr = 0;
|
||||||
int LOCALEID = strtol(pStr, &endptr,16);
|
int LOCALEID = strtol(pStr, &endptr,16);
|
||||||
@ -78,7 +82,8 @@ BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
const char* MSVC_LocaleLookup(const char* raw_shortname) {
|
static const char* MSVC_LocaleLookup(const char* raw_shortname)
|
||||||
|
{
|
||||||
|
|
||||||
/* NULL is used to read locale only so we need to return it too */
|
/* NULL is used to read locale only so we need to return it too */
|
||||||
if (raw_shortname == NULL) return NULL;
|
if (raw_shortname == NULL) return NULL;
|
||||||
@ -102,9 +107,9 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) {
|
|||||||
|
|
||||||
last_raw_value = shortname;
|
last_raw_value = shortname;
|
||||||
|
|
||||||
if (glb_supported_locales.find(utf8_to_wide(shortname)) != glb_supported_locales.end()) {
|
auto key = utf8_to_wide(shortname);
|
||||||
last_full_name = wide_to_utf8(
|
if (glb_supported_locales.find(key) != glb_supported_locales.end()) {
|
||||||
glb_supported_locales[utf8_to_wide(shortname)]);
|
last_full_name = wide_to_utf8(glb_supported_locales[key]);
|
||||||
return last_full_name.c_str();
|
return last_full_name.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +119,54 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void MSVC_LocaleWorkaround()
|
||||||
|
{
|
||||||
|
errorstream << "MSVC localization workaround active. "
|
||||||
|
"Restarting " PROJECT_NAME_C " in a new environment!" << std::endl;
|
||||||
|
|
||||||
|
std::string parameters;
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (i > 1)
|
||||||
|
parameters += ' ';
|
||||||
|
parameters += porting::QuoteArgv(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ptr_parameters = nullptr;
|
||||||
|
if (!parameters.empty())
|
||||||
|
ptr_parameters = ¶meters[0];
|
||||||
|
|
||||||
|
// Allow calling without an extension
|
||||||
|
std::string app_name = argv[0];
|
||||||
|
if (app_name.compare(app_name.size() - 4, 4, ".exe") != 0)
|
||||||
|
app_name += ".exe";
|
||||||
|
|
||||||
|
STARTUPINFO startup_info = {};
|
||||||
|
PROCESS_INFORMATION process_info = {};
|
||||||
|
|
||||||
|
bool success = CreateProcess(app_name.c_str(), ptr_parameters,
|
||||||
|
NULL, NULL, false, DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT,
|
||||||
|
NULL, NULL, &startup_info, &process_info);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
exit(0);
|
||||||
|
// NOTREACHED
|
||||||
|
} else {
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
||||||
|
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), buffer,
|
||||||
|
sizeof(buffer) - 1, NULL);
|
||||||
|
|
||||||
|
errorstream << "*******************************************************" << std::endl;
|
||||||
|
errorstream << "CMD: " << app_name << std::endl;
|
||||||
|
errorstream << "Failed to restart with current locale: " << std::endl;
|
||||||
|
errorstream << buffer;
|
||||||
|
errorstream << "Expect language to be broken!" << std::endl;
|
||||||
|
errorstream << "*******************************************************" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -123,72 +176,26 @@ void init_gettext(const char *path, const std::string &configured_language,
|
|||||||
#if USE_GETTEXT
|
#if USE_GETTEXT
|
||||||
// First, try to set user override environment
|
// First, try to set user override environment
|
||||||
if (!configured_language.empty()) {
|
if (!configured_language.empty()) {
|
||||||
#ifndef _WIN32
|
// Set LANGUAGE which overrides all others, see
|
||||||
// Add user specified locale to environment
|
// <https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html>
|
||||||
|
#ifndef _MSC_VER
|
||||||
setenv("LANGUAGE", configured_language.c_str(), 1);
|
setenv("LANGUAGE", configured_language.c_str(), 1);
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
setenv("LANG", configured_language.c_str(), 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Reload locale with changed environment
|
// Reload locale with changed environment
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
#elif defined(_MSC_VER)
|
#else
|
||||||
std::string current_language;
|
std::string current_language;
|
||||||
const char *env_lang = getenv("LANGUAGE");
|
const char *env_lang = getenv("LANGUAGE");
|
||||||
if (env_lang)
|
if (env_lang)
|
||||||
current_language = env_lang;
|
current_language = env_lang;
|
||||||
|
|
||||||
_putenv(("LANGUAGE=" + configured_language).c_str());
|
setenv("LANGUAGE", configured_language.c_str(), 1);
|
||||||
SetEnvironmentVariableA("LANGUAGE", configured_language.c_str());
|
SetEnvironmentVariableA("LANGUAGE", configured_language.c_str());
|
||||||
|
|
||||||
#ifndef SERVER
|
#ifndef SERVER
|
||||||
// Hack to force gettext to see the right environment
|
// Hack to force gettext to see the right environment
|
||||||
if (current_language != configured_language) {
|
if (current_language != configured_language)
|
||||||
errorstream << "MSVC localization workaround active. "
|
MSVC_LocaleWorkaround();
|
||||||
"Restarting " PROJECT_NAME_C " in a new environment!" << std::endl;
|
|
||||||
|
|
||||||
std::string parameters;
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
if (i > 1)
|
|
||||||
parameters += ' ';
|
|
||||||
parameters += porting::QuoteArgv(argv[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *ptr_parameters = nullptr;
|
|
||||||
if (!parameters.empty())
|
|
||||||
ptr_parameters = ¶meters[0];
|
|
||||||
|
|
||||||
// Allow calling without an extension
|
|
||||||
std::string app_name = argv[0];
|
|
||||||
if (app_name.compare(app_name.size() - 4, 4, ".exe") != 0)
|
|
||||||
app_name += ".exe";
|
|
||||||
|
|
||||||
STARTUPINFO startup_info = {};
|
|
||||||
PROCESS_INFORMATION process_info = {};
|
|
||||||
|
|
||||||
bool success = CreateProcess(app_name.c_str(), ptr_parameters,
|
|
||||||
NULL, NULL, false, DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT,
|
|
||||||
NULL, NULL, &startup_info, &process_info);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
exit(0);
|
|
||||||
// NOTREACHED
|
|
||||||
} else {
|
|
||||||
char buffer[1024];
|
|
||||||
|
|
||||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
|
||||||
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), buffer,
|
|
||||||
sizeof(buffer) - 1, NULL);
|
|
||||||
|
|
||||||
errorstream << "*******************************************************" << std::endl;
|
|
||||||
errorstream << "CMD: " << app_name << std::endl;
|
|
||||||
errorstream << "Failed to restart with current locale: " << std::endl;
|
|
||||||
errorstream << buffer;
|
|
||||||
errorstream << "Expect language to be broken!" << std::endl;
|
|
||||||
errorstream << "*******************************************************" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
errorstream << "*******************************************************" << std::endl;
|
errorstream << "*******************************************************" << std::endl;
|
||||||
errorstream << "Can't apply locale workaround for server!" << std::endl;
|
errorstream << "Can't apply locale workaround for server!" << std::endl;
|
||||||
@ -197,15 +204,8 @@ void init_gettext(const char *path, const std::string &configured_language,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
setlocale(LC_ALL, configured_language.c_str());
|
setlocale(LC_ALL, configured_language.c_str());
|
||||||
#else // Mingw
|
#endif // ifdef _MSC_VER
|
||||||
_putenv(("LANGUAGE=" + configured_language).c_str());
|
} else {
|
||||||
setlocale(LC_ALL, "");
|
|
||||||
#endif // ifndef _WIN32
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
setenv("LANG", porting::getLanguageAndroid().c_str(), 1);
|
|
||||||
#endif
|
|
||||||
/* set current system default locale */
|
/* set current system default locale */
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
}
|
}
|
||||||
@ -228,18 +228,13 @@ void init_gettext(const char *path, const std::string &configured_language,
|
|||||||
bindtextdomain(name.c_str(), path);
|
bindtextdomain(name.c_str(), path);
|
||||||
textdomain(name.c_str());
|
textdomain(name.c_str());
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#ifdef _WIN32
|
||||||
// Set character encoding for Win32
|
// set character encoding
|
||||||
char *tdomain = textdomain( (char *) NULL );
|
char *tdomain = textdomain(nullptr);
|
||||||
if( tdomain == NULL )
|
assert(tdomain);
|
||||||
{
|
if (tdomain)
|
||||||
errorstream << "Warning: domainname parameter is the null pointer" <<
|
bind_textdomain_codeset(tdomain, "UTF-8");
|
||||||
", default domain is not set" << std::endl;
|
#endif
|
||||||
tdomain = (char *) "messages";
|
|
||||||
}
|
|
||||||
/* char *codeset = */bind_textdomain_codeset( tdomain, "UTF-8" );
|
|
||||||
//errorstream << "Gettext debug: domainname = " << tdomain << "; codeset = "<< codeset << std::endl;
|
|
||||||
#endif // defined(_WIN32)
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/* set current system default locale */
|
/* set current system default locale */
|
||||||
@ -247,7 +242,7 @@ void init_gettext(const char *path, const std::string &configured_language,
|
|||||||
#endif // if USE_GETTEXT
|
#endif // if USE_GETTEXT
|
||||||
|
|
||||||
/* no matter what locale is used we need number format to be "C" */
|
/* no matter what locale is used we need number format to be "C" */
|
||||||
/* to ensure formspec parameters are evaluated correct! */
|
/* to ensure formspec parameters are evaluated correctly! */
|
||||||
|
|
||||||
setlocale(LC_NUMERIC, "C");
|
setlocale(LC_NUMERIC, "C");
|
||||||
infostream << "Message locale is now set to: "
|
infostream << "Message locale is now set to: "
|
||||||
|
@ -455,7 +455,6 @@ bool GUIEditBox::processKey(const SEvent &event)
|
|||||||
|
|
||||||
bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end)
|
bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end)
|
||||||
{
|
{
|
||||||
// clang-format off
|
|
||||||
if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
|
if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
|
||||||
s32 lineNo = getLineFromPos(m_cursor_pos);
|
s32 lineNo = getLineFromPos(m_cursor_pos);
|
||||||
s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
|
s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
|
||||||
@ -481,13 +480,11 @@ bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format on
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end)
|
bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end)
|
||||||
{
|
{
|
||||||
// clang-format off
|
|
||||||
if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
|
if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
|
||||||
s32 lineNo = getLineFromPos(m_cursor_pos);
|
s32 lineNo = getLineFromPos(m_cursor_pos);
|
||||||
s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
|
s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
|
||||||
@ -513,7 +510,6 @@ bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format on
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,35 +265,36 @@ void GUIEngine::run()
|
|||||||
f32 dtime = 0.0f;
|
f32 dtime = 0.0f;
|
||||||
|
|
||||||
while (m_rendering_engine->run() && (!m_startgame) && (!m_kill)) {
|
while (m_rendering_engine->run() && (!m_startgame) && (!m_kill)) {
|
||||||
|
if (RenderingEngine::shouldRender()) {
|
||||||
|
// check if we need to update the "upper left corner"-text
|
||||||
|
if (text_height != g_fontengine->getTextHeight()) {
|
||||||
|
updateTopLeftTextSize();
|
||||||
|
text_height = g_fontengine->getTextHeight();
|
||||||
|
}
|
||||||
|
|
||||||
//check if we need to update the "upper left corner"-text
|
driver->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR);
|
||||||
if (text_height != g_fontengine->getTextHeight()) {
|
|
||||||
updateTopLeftTextSize();
|
if (m_clouds_enabled)
|
||||||
text_height = g_fontengine->getTextHeight();
|
{
|
||||||
|
cloudPreProcess();
|
||||||
|
drawOverlay(driver);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
drawBackground(driver);
|
||||||
|
|
||||||
|
drawFooter(driver);
|
||||||
|
|
||||||
|
m_rendering_engine->get_gui_env()->drawAll();
|
||||||
|
|
||||||
|
// The header *must* be drawn after the menu because it uses
|
||||||
|
// GUIFormspecMenu::getAbsoluteRect().
|
||||||
|
// The header *can* be drawn after the menu because it never intersects
|
||||||
|
// the menu.
|
||||||
|
drawHeader(driver);
|
||||||
|
|
||||||
|
driver->endScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
driver->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR);
|
|
||||||
|
|
||||||
if (m_clouds_enabled)
|
|
||||||
{
|
|
||||||
cloudPreProcess();
|
|
||||||
drawOverlay(driver);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
drawBackground(driver);
|
|
||||||
|
|
||||||
drawFooter(driver);
|
|
||||||
|
|
||||||
m_rendering_engine->get_gui_env()->drawAll();
|
|
||||||
|
|
||||||
// The header *must* be drawn after the menu because it uses
|
|
||||||
// GUIFormspecMenu::getAbsoluteRect().
|
|
||||||
// The header *can* be drawn after the menu because it never intersects
|
|
||||||
// the menu.
|
|
||||||
drawHeader(driver);
|
|
||||||
|
|
||||||
driver->endScene();
|
|
||||||
|
|
||||||
IrrlichtDevice *device = m_rendering_engine->get_raw_device();
|
IrrlichtDevice *device = m_rendering_engine->get_raw_device();
|
||||||
|
|
||||||
u32 frametime_min = 1000 / (device->isWindowFocused()
|
u32 frametime_min = 1000 / (device->isWindowFocused()
|
||||||
|
@ -3497,46 +3497,58 @@ void GUIFormSpecMenu::legacySortElements(std::list<IGUIElement *>::iterator from
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
bool GUIFormSpecMenu::getAndroidUIInput()
|
void GUIFormSpecMenu::getAndroidUIInput()
|
||||||
{
|
{
|
||||||
if (!hasAndroidUIInput())
|
porting::AndroidDialogState dialogState = getAndroidUIInputState();
|
||||||
return false;
|
if (dialogState == porting::DIALOG_SHOWN) {
|
||||||
|
return;
|
||||||
|
} else if (dialogState == porting::DIALOG_CANCELED) {
|
||||||
|
m_jni_field_name.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// still waiting
|
porting::AndroidDialogType dialog_type = porting::getLastInputDialogType();
|
||||||
if (porting::getInputDialogState() == -1)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
std::string fieldname = m_jni_field_name;
|
std::string fieldname = m_jni_field_name;
|
||||||
m_jni_field_name.clear();
|
m_jni_field_name.clear();
|
||||||
|
|
||||||
for (const FieldSpec &field : m_fields) {
|
for (const FieldSpec &field : m_fields) {
|
||||||
if (field.fname != fieldname)
|
if (field.fname != fieldname)
|
||||||
continue;
|
continue; // Iterate until found
|
||||||
|
|
||||||
IGUIElement *element = getElementFromId(field.fid, true);
|
IGUIElement *element = getElementFromId(field.fid, true);
|
||||||
|
|
||||||
if (!element || element->getType() != irr::gui::EGUIET_EDIT_BOX)
|
if (!element)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
gui::IGUIEditBox *editbox = (gui::IGUIEditBox *)element;
|
auto element_type = element->getType();
|
||||||
std::string text = porting::getInputDialogValue();
|
if (dialog_type == porting::TEXT_INPUT && element_type == irr::gui::EGUIET_EDIT_BOX) {
|
||||||
editbox->setText(utf8_to_wide(text).c_str());
|
gui::IGUIEditBox *editbox = (gui::IGUIEditBox *)element;
|
||||||
|
std::string text = porting::getInputDialogMessage();
|
||||||
|
editbox->setText(utf8_to_wide(text).c_str());
|
||||||
|
|
||||||
bool enter_after_edit = false;
|
bool enter_after_edit = false;
|
||||||
auto iter = field_enter_after_edit.find(fieldname);
|
auto iter = field_enter_after_edit.find(fieldname);
|
||||||
if (iter != field_enter_after_edit.end()) {
|
if (iter != field_enter_after_edit.end()) {
|
||||||
enter_after_edit = iter->second;
|
enter_after_edit = iter->second;
|
||||||
}
|
}
|
||||||
if (enter_after_edit && editbox->getParent()) {
|
if (enter_after_edit && editbox->getParent()) {
|
||||||
SEvent enter;
|
SEvent enter;
|
||||||
enter.EventType = EET_GUI_EVENT;
|
enter.EventType = EET_GUI_EVENT;
|
||||||
enter.GUIEvent.Caller = editbox;
|
enter.GUIEvent.Caller = editbox;
|
||||||
enter.GUIEvent.Element = nullptr;
|
enter.GUIEvent.Element = nullptr;
|
||||||
enter.GUIEvent.EventType = gui::EGET_EDITBOX_ENTER;
|
enter.GUIEvent.EventType = gui::EGET_EDITBOX_ENTER;
|
||||||
editbox->getParent()->OnEvent(enter);
|
editbox->getParent()->OnEvent(enter);
|
||||||
|
}
|
||||||
|
} else if (dialog_type == porting::SELECTION_INPUT &&
|
||||||
|
element_type == irr::gui::EGUIET_COMBO_BOX) {
|
||||||
|
auto dropdown = (gui::IGUIComboBox *) element;
|
||||||
|
int selected = porting::getInputDialogSelection();
|
||||||
|
dropdown->setAndSendSelected(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return; // Early-return after found
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -3656,22 +3668,18 @@ void GUIFormSpecMenu::drawMenu()
|
|||||||
NULL, m_client, IT_ROT_HOVERED);
|
NULL, m_client, IT_ROT_HOVERED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On touchscreens, m_pointer is set by GUIModalMenu::preprocessEvent instead.
|
|
||||||
#ifndef HAVE_TOUCHSCREENGUI
|
|
||||||
m_pointer = RenderingEngine::get_raw_device()->getCursorControl()->getPosition();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Draw fields/buttons tooltips and update the mouse cursor
|
Draw fields/buttons tooltips and update the mouse cursor
|
||||||
*/
|
*/
|
||||||
gui::IGUIElement *hovered =
|
gui::IGUIElement *hovered =
|
||||||
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
|
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
|
||||||
|
|
||||||
#ifndef HAVE_TOUCHSCREENGUI
|
|
||||||
gui::ICursorControl *cursor_control = RenderingEngine::get_raw_device()->
|
gui::ICursorControl *cursor_control = RenderingEngine::get_raw_device()->
|
||||||
getCursorControl();
|
getCursorControl();
|
||||||
gui::ECURSOR_ICON current_cursor_icon = cursor_control->getActiveIcon();
|
gui::ECURSOR_ICON current_cursor_icon = gui::ECI_NORMAL;
|
||||||
#endif
|
if (cursor_control)
|
||||||
|
current_cursor_icon = cursor_control->getActiveIcon();
|
||||||
|
|
||||||
bool hovered_element_found = false;
|
bool hovered_element_found = false;
|
||||||
|
|
||||||
if (hovered) {
|
if (hovered) {
|
||||||
@ -3715,11 +3723,10 @@ void GUIFormSpecMenu::drawMenu()
|
|||||||
m_tooltips[field.fname].bgcolor);
|
m_tooltips[field.fname].bgcolor);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_TOUCHSCREENGUI
|
if (cursor_control &&
|
||||||
if (field.ftype != f_HyperText && // Handled directly in guiHyperText
|
field.ftype != f_HyperText && // Handled directly in guiHyperText
|
||||||
current_cursor_icon != field.fcursor_icon)
|
current_cursor_icon != field.fcursor_icon)
|
||||||
cursor_control->setActiveIcon(field.fcursor_icon);
|
cursor_control->setActiveIcon(field.fcursor_icon);
|
||||||
#endif
|
|
||||||
|
|
||||||
hovered_element_found = true;
|
hovered_element_found = true;
|
||||||
|
|
||||||
@ -3730,10 +3737,8 @@ void GUIFormSpecMenu::drawMenu()
|
|||||||
|
|
||||||
if (!hovered_element_found) {
|
if (!hovered_element_found) {
|
||||||
// no element is hovered
|
// no element is hovered
|
||||||
#ifndef HAVE_TOUCHSCREENGUI
|
if (cursor_control && current_cursor_icon != ECI_NORMAL)
|
||||||
if (current_cursor_icon != ECI_NORMAL)
|
|
||||||
cursor_control->setActiveIcon(ECI_NORMAL);
|
cursor_control->setActiveIcon(ECI_NORMAL);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_tooltip_element->draw();
|
m_tooltip_element->draw();
|
||||||
@ -3764,16 +3769,13 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text,
|
|||||||
v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
|
v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
|
||||||
int tooltip_offset_x = m_btn_height;
|
int tooltip_offset_x = m_btn_height;
|
||||||
int tooltip_offset_y = m_btn_height;
|
int tooltip_offset_y = m_btn_height;
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
tooltip_offset_x *= 3;
|
|
||||||
tooltip_offset_y = 0;
|
|
||||||
if (m_pointer.X > (s32)screenSize.X / 2)
|
|
||||||
tooltip_offset_x = -(tooltip_offset_x + tooltip_width);
|
|
||||||
|
|
||||||
// Hide tooltip after ETIE_LEFT_UP
|
if (m_pointer_type == PointerType::Touch) {
|
||||||
if (m_pointer.X == 0)
|
tooltip_offset_x *= 3;
|
||||||
return;
|
tooltip_offset_y = 0;
|
||||||
#endif
|
if (m_pointer.X > (s32)screenSize.X / 2)
|
||||||
|
tooltip_offset_x = -(tooltip_offset_x + tooltip_width);
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate and set the tooltip position
|
// Calculate and set the tooltip position
|
||||||
s32 tooltip_x = m_pointer.X + tooltip_offset_x;
|
s32 tooltip_x = m_pointer.X + tooltip_offset_x;
|
||||||
@ -4070,6 +4072,11 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode)
|
|||||||
|
|
||||||
bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
|
bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
|
||||||
{
|
{
|
||||||
|
// This must be done first so that GUIModalMenu can set m_pointer_type
|
||||||
|
// correctly.
|
||||||
|
if (GUIModalMenu::preprocessEvent(event))
|
||||||
|
return true;
|
||||||
|
|
||||||
// The IGUITabControl renders visually using the skin's selected
|
// The IGUITabControl renders visually using the skin's selected
|
||||||
// font, which we override for the duration of form drawing,
|
// font, which we override for the duration of form drawing,
|
||||||
// but computes tab hotspots based on how it would have rendered
|
// but computes tab hotspots based on how it would have rendered
|
||||||
@ -4147,7 +4154,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
|
|||||||
return handled;
|
return handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GUIModalMenu::preprocessEvent(event);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUIFormSpecMenu::tryClose()
|
void GUIFormSpecMenu::tryClose()
|
||||||
@ -4326,14 +4333,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
// The second touch (see GUIModalMenu::preprocessEvent() function)
|
// The second touch (see GUIModalMenu::preprocessEvent() function)
|
||||||
ButtonEventType touch = BET_OTHER;
|
ButtonEventType touch = BET_OTHER;
|
||||||
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
|
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
|
||||||
if (event.TouchInput.Event == ETIE_LEFT_UP)
|
if (event.TouchInput.Event == ETIE_LEFT_UP)
|
||||||
touch = BET_RIGHT;
|
touch = BET_RIGHT;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set this number to a positive value to generate a move action
|
// Set this number to a positive value to generate a move action
|
||||||
// from m_selected_item to s.
|
// from m_selected_item to s.
|
||||||
@ -4678,7 +4683,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
if (touch == BET_RIGHT && m_selected_item && !m_left_dragging) {
|
if (touch == BET_RIGHT && m_selected_item && !m_left_dragging) {
|
||||||
if (!s.isValid()) {
|
if (!s.isValid()) {
|
||||||
// Not a valid slot
|
// Not a valid slot
|
||||||
@ -4698,7 +4702,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Update left-dragged slots
|
// Update left-dragged slots
|
||||||
if (m_left_dragging && m_left_drag_stacks.size() > 1) {
|
if (m_left_dragging && m_left_drag_stacks.size() > 1) {
|
||||||
@ -5067,10 +5070,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
if (m_second_touch)
|
if (m_second_touch)
|
||||||
return true; // Stop propagating the event
|
return true; // Stop propagating the event
|
||||||
#endif
|
|
||||||
|
|
||||||
return Parent ? Parent->OnEvent(event) : false;
|
return Parent ? Parent->OnEvent(event) : false;
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ public:
|
|||||||
core::rect<s32> getAbsoluteRect();
|
core::rect<s32> getAbsoluteRect();
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
bool getAndroidUIInput();
|
void getAndroidUIInput();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -1052,14 +1052,10 @@ void GUIHyperText::checkHover(s32 X, s32 Y)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_TOUCHSCREENGUI
|
ICursorControl *cursor_control = RenderingEngine::get_raw_device()->getCursorControl();
|
||||||
if (m_drawer.m_hovertag)
|
|
||||||
RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon(
|
if (cursor_control)
|
||||||
gui::ECI_HAND);
|
cursor_control->setActiveIcon(m_drawer.m_hovertag ? gui::ECI_HAND : gui::ECI_NORMAL);
|
||||||
else
|
|
||||||
RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon(
|
|
||||||
gui::ECI_NORMAL);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GUIHyperText::OnEvent(const SEvent &event)
|
bool GUIHyperText::OnEvent(const SEvent &event)
|
||||||
@ -1075,12 +1071,11 @@ bool GUIHyperText::OnEvent(const SEvent &event)
|
|||||||
if (event.EventType == EET_GUI_EVENT &&
|
if (event.EventType == EET_GUI_EVENT &&
|
||||||
event.GUIEvent.EventType == EGET_ELEMENT_LEFT) {
|
event.GUIEvent.EventType == EGET_ELEMENT_LEFT) {
|
||||||
m_drawer.m_hovertag = nullptr;
|
m_drawer.m_hovertag = nullptr;
|
||||||
#ifndef HAVE_TOUCHSCREENGUI
|
|
||||||
gui::ICursorControl *cursor_control =
|
ICursorControl *cursor_control = RenderingEngine::get_raw_device()->getCursorControl();
|
||||||
RenderingEngine::get_raw_device()->getCursorControl();
|
|
||||||
if (cursor_control->isVisible())
|
if (cursor_control && cursor_control->isVisible())
|
||||||
cursor_control->setActiveIcon(gui::ECI_NORMAL);
|
cursor_control->setActiveIcon(gui::ECI_NORMAL);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.EventType == EET_MOUSE_INPUT_EVENT) {
|
if (event.EventType == EET_MOUSE_INPUT_EVENT) {
|
||||||
|
@ -152,10 +152,10 @@ void GUIInventoryList::draw()
|
|||||||
|
|
||||||
// Add hovering tooltip
|
// Add hovering tooltip
|
||||||
bool show_tooltip = !item.empty() && hovering && !selected_item;
|
bool show_tooltip = !item.empty() && hovering && !selected_item;
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
// Make it possible to see item tooltips on touchscreens
|
// Make it possible to see item tooltips on touchscreens
|
||||||
show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0;
|
if (m_fs_menu->getPointerType() == PointerType::Touch) {
|
||||||
#endif
|
show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0;
|
||||||
|
}
|
||||||
if (show_tooltip) {
|
if (show_tooltip) {
|
||||||
std::string tooltip = orig_item.getDescription(client->idef());
|
std::string tooltip = orig_item.getDescription(client->idef());
|
||||||
if (m_fs_menu->doTooltipAppendItemname())
|
if (m_fs_menu->doTooltipAppendItemname())
|
||||||
|
@ -199,14 +199,12 @@ bool GUIPasswordChange::processInput()
|
|||||||
bool GUIPasswordChange::OnEvent(const SEvent &event)
|
bool GUIPasswordChange::OnEvent(const SEvent &event)
|
||||||
{
|
{
|
||||||
if (event.EventType == EET_KEY_INPUT_EVENT) {
|
if (event.EventType == EET_KEY_INPUT_EVENT) {
|
||||||
// clang-format off
|
|
||||||
if ((event.KeyInput.Key == KEY_ESCAPE ||
|
if ((event.KeyInput.Key == KEY_ESCAPE ||
|
||||||
event.KeyInput.Key == KEY_CANCEL) &&
|
event.KeyInput.Key == KEY_CANCEL) &&
|
||||||
event.KeyInput.PressedDown) {
|
event.KeyInput.PressedDown) {
|
||||||
quitMenu();
|
quitMenu();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// clang-format on
|
|
||||||
if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
|
if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
|
||||||
acceptInput();
|
acceptInput();
|
||||||
if (processInput())
|
if (processInput())
|
||||||
@ -266,14 +264,19 @@ std::string GUIPasswordChange::getNameByID(s32 id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
bool GUIPasswordChange::getAndroidUIInput()
|
void GUIPasswordChange::getAndroidUIInput()
|
||||||
{
|
{
|
||||||
if (!hasAndroidUIInput())
|
porting::AndroidDialogState dialogState = getAndroidUIInputState();
|
||||||
return false;
|
if (dialogState == porting::DIALOG_SHOWN) {
|
||||||
|
return;
|
||||||
|
} else if (dialogState == porting::DIALOG_CANCELED) {
|
||||||
|
m_jni_field_name.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// still waiting
|
// It has to be a text input
|
||||||
if (porting::getInputDialogState() == -1)
|
if (porting::getLastInputDialogType() != porting::TEXT_INPUT)
|
||||||
return true;
|
return;
|
||||||
|
|
||||||
gui::IGUIElement *e = nullptr;
|
gui::IGUIElement *e = nullptr;
|
||||||
if (m_jni_field_name == "old_password")
|
if (m_jni_field_name == "old_password")
|
||||||
@ -285,10 +288,10 @@ bool GUIPasswordChange::getAndroidUIInput()
|
|||||||
m_jni_field_name.clear();
|
m_jni_field_name.clear();
|
||||||
|
|
||||||
if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX)
|
if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
std::string text = porting::getInputDialogValue();
|
std::string text = porting::getInputDialogMessage();
|
||||||
e->setText(utf8_to_wide(text).c_str());
|
e->setText(utf8_to_wide(text).c_str());
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -45,7 +45,7 @@ public:
|
|||||||
|
|
||||||
bool OnEvent(const SEvent &event);
|
bool OnEvent(const SEvent &event);
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
bool getAndroidUIInput();
|
void getAndroidUIInput();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -155,7 +155,6 @@ bool GUIScrollBar::OnEvent(const SEvent &event)
|
|||||||
if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
|
if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
|
||||||
is_dragging = false;
|
is_dragging = false;
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
if (!dragged_by_slider) {
|
if (!dragged_by_slider) {
|
||||||
if (is_inside) {
|
if (is_inside) {
|
||||||
dragged_by_slider = slider_rect.isPointInside(p);
|
dragged_by_slider = slider_rect.isPointInside(p);
|
||||||
@ -167,7 +166,6 @@ bool GUIScrollBar::OnEvent(const SEvent &event)
|
|||||||
return is_inside;
|
return is_inside;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
const s32 new_pos = getPosFromMousePos(p);
|
const s32 new_pos = getPosFromMousePos(p);
|
||||||
const s32 old_pos = scroll_pos;
|
const s32 old_pos = scroll_pos;
|
||||||
|
@ -60,7 +60,7 @@ GUITable::GUITable(gui::IGUIEnvironment *env,
|
|||||||
m_rowheight = MYMAX(m_rowheight, 1);
|
m_rowheight = MYMAX(m_rowheight, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
|
const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE) * 1.5f;
|
||||||
m_scrollbar = new GUIScrollBar(Environment, this, -1,
|
m_scrollbar = new GUIScrollBar(Environment, this, -1,
|
||||||
core::rect<s32>(RelativeRect.getWidth() - s,
|
core::rect<s32>(RelativeRect.getWidth() - s,
|
||||||
0,
|
0,
|
||||||
@ -77,18 +77,6 @@ GUITable::GUITable(gui::IGUIEnvironment *env,
|
|||||||
setTabStop(true);
|
setTabStop(true);
|
||||||
setTabOrder(-1);
|
setTabOrder(-1);
|
||||||
updateAbsolutePosition();
|
updateAbsolutePosition();
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
float density = 1; // dp scaling is applied by the skin
|
|
||||||
#else
|
|
||||||
float density = RenderingEngine::getDisplayDensity();
|
|
||||||
#endif
|
|
||||||
core::rect<s32> relative_rect = m_scrollbar->getRelativePosition();
|
|
||||||
s32 width = (relative_rect.getWidth() / (2.0 / 3.0)) * density *
|
|
||||||
g_settings->getFloat("gui_scaling", 0.5f, 20.0f);
|
|
||||||
m_scrollbar->setRelativePosition(core::rect<s32>(
|
|
||||||
relative_rect.LowerRightCorner.X-width,relative_rect.UpperLeftCorner.Y,
|
|
||||||
relative_rect.LowerRightCorner.X,relative_rect.LowerRightCorner.Y
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GUITable::~GUITable()
|
GUITable::~GUITable()
|
||||||
|
@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <IEventReceiver.h>
|
||||||
#include "client/renderingengine.h"
|
#include "client/renderingengine.h"
|
||||||
#include "modalMenu.h"
|
#include "modalMenu.h"
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
@ -29,7 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "touchscreengui.h"
|
#include "touchscreengui.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
||||||
s32 id, IMenuManager *menumgr, bool remap_dbl_click) :
|
s32 id, IMenuManager *menumgr, bool remap_dbl_click) :
|
||||||
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
|
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
|
||||||
@ -51,13 +51,9 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
|||||||
setVisible(true);
|
setVisible(true);
|
||||||
m_menumgr->createdMenu(this);
|
m_menumgr->createdMenu(this);
|
||||||
|
|
||||||
m_doubleclickdetect[0].time = 0;
|
m_last_touch.time = 0;
|
||||||
m_doubleclickdetect[1].time = 0;
|
m_last_touch.pos = v2s32(0, 0);
|
||||||
|
|
||||||
m_doubleclickdetect[0].pos = v2s32(0, 0);
|
|
||||||
m_doubleclickdetect[1].pos = v2s32(0, 0);
|
|
||||||
}
|
}
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
GUIModalMenu::~GUIModalMenu()
|
GUIModalMenu::~GUIModalMenu()
|
||||||
{
|
{
|
||||||
@ -105,13 +101,23 @@ void GUIModalMenu::quitMenu()
|
|||||||
m_menumgr->deletingMenu(this);
|
m_menumgr->deletingMenu(this);
|
||||||
this->remove();
|
this->remove();
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
#ifdef HAVE_TOUCHSCREENGUI
|
||||||
if (g_touchscreengui && m_touchscreen_visible)
|
if (g_touchscreengui)
|
||||||
g_touchscreengui->show();
|
g_touchscreengui->show();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
|
||||||
bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
|
{
|
||||||
|
while (tocheck) {
|
||||||
|
if (tocheck == parent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
tocheck = tocheck->getParent();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GUIModalMenu::remapDoubleClick(const SEvent &event)
|
||||||
{
|
{
|
||||||
/* The following code is for capturing double-clicks of the mouse button
|
/* The following code is for capturing double-clicks of the mouse button
|
||||||
* and translating the double-click into an EET_KEY_INPUT_EVENT event
|
* and translating the double-click into an EET_KEY_INPUT_EVENT event
|
||||||
@ -126,58 +132,37 @@ bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
|
|||||||
if (!m_remap_dbl_click)
|
if (!m_remap_dbl_click)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
if (event.EventType != EET_MOUSE_INPUT_EVENT ||
|
||||||
m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos;
|
event.MouseInput.Event != EMIE_LMOUSE_DOUBLE_CLICK)
|
||||||
m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
|
return false;
|
||||||
|
|
||||||
m_doubleclickdetect[1].pos = m_pointer;
|
// Only exit if the double-click happened outside the menu.
|
||||||
m_doubleclickdetect[1].time = porting::getTimeMs();
|
gui::IGUIElement *hovered =
|
||||||
} else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
|
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
|
||||||
u64 delta = porting::getDeltaMs(
|
if (isChild(hovered, this))
|
||||||
m_doubleclickdetect[0].time, porting::getTimeMs());
|
return false;
|
||||||
if (delta > 400)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
double squaredistance = m_doubleclickdetect[0].pos.
|
// Translate double-click to escape.
|
||||||
getDistanceFromSQ(m_doubleclickdetect[1].pos);
|
SEvent translated{};
|
||||||
|
translated.EventType = EET_KEY_INPUT_EVENT;
|
||||||
|
translated.KeyInput.Key = KEY_ESCAPE;
|
||||||
|
translated.KeyInput.Control = false;
|
||||||
|
translated.KeyInput.Shift = false;
|
||||||
|
translated.KeyInput.PressedDown = true;
|
||||||
|
translated.KeyInput.Char = 0;
|
||||||
|
OnEvent(translated);
|
||||||
|
|
||||||
if (squaredistance > (30 * 30)) {
|
return true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEvent translated{};
|
|
||||||
// translate doubleclick to escape
|
|
||||||
translated.EventType = EET_KEY_INPUT_EVENT;
|
|
||||||
translated.KeyInput.Key = KEY_ESCAPE;
|
|
||||||
translated.KeyInput.Control = false;
|
|
||||||
translated.KeyInput.Shift = false;
|
|
||||||
translated.KeyInput.PressedDown = true;
|
|
||||||
translated.KeyInput.Char = 0;
|
|
||||||
OnEvent(translated);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
|
|
||||||
{
|
|
||||||
while (tocheck) {
|
|
||||||
if (tocheck == parent) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
tocheck = tocheck->getParent();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try)
|
||||||
|
|
||||||
bool GUIModalMenu::simulateMouseEvent(
|
|
||||||
gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event)
|
|
||||||
{
|
{
|
||||||
|
IGUIElement *target;
|
||||||
|
if (!second_try)
|
||||||
|
target = Environment->getFocus();
|
||||||
|
else
|
||||||
|
target = m_touch_hovered.get();
|
||||||
|
|
||||||
SEvent mouse_event{}; // value-initialized, not unitialized
|
SEvent mouse_event{}; // value-initialized, not unitialized
|
||||||
mouse_event.EventType = EET_MOUSE_INPUT_EVENT;
|
mouse_event.EventType = EET_MOUSE_INPUT_EVENT;
|
||||||
mouse_event.MouseInput.X = m_pointer.X;
|
mouse_event.MouseInput.X = m_pointer.X;
|
||||||
@ -195,48 +180,65 @@ bool GUIModalMenu::simulateMouseEvent(
|
|||||||
mouse_event.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
|
mouse_event.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
|
||||||
mouse_event.MouseInput.ButtonStates = 0;
|
mouse_event.MouseInput.ButtonStates = 0;
|
||||||
break;
|
break;
|
||||||
|
case ETIE_COUNT:
|
||||||
|
// ETIE_COUNT is used for double-tap events.
|
||||||
|
mouse_event.MouseInput.Event = EMIE_LMOUSE_DOUBLE_CLICK;
|
||||||
|
mouse_event.MouseInput.ButtonStates = EMBSM_LEFT;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (preprocessEvent(mouse_event))
|
|
||||||
return true;
|
bool retval;
|
||||||
if (!target)
|
m_simulated_mouse = true;
|
||||||
return false;
|
do {
|
||||||
return target->OnEvent(mouse_event);
|
if (preprocessEvent(mouse_event)) {
|
||||||
|
retval = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!target) {
|
||||||
|
retval = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retval = target->OnEvent(mouse_event);
|
||||||
|
} while (false);
|
||||||
|
m_simulated_mouse = false;
|
||||||
|
|
||||||
|
if (!retval && !second_try)
|
||||||
|
return simulateMouseEvent(touch_event, true);
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUIModalMenu::enter(gui::IGUIElement *hovered)
|
void GUIModalMenu::enter(gui::IGUIElement *hovered)
|
||||||
{
|
{
|
||||||
if (!hovered)
|
if (!hovered)
|
||||||
return;
|
return;
|
||||||
sanity_check(!m_hovered);
|
sanity_check(!m_touch_hovered);
|
||||||
m_hovered.grab(hovered);
|
m_touch_hovered.grab(hovered);
|
||||||
SEvent gui_event{};
|
SEvent gui_event{};
|
||||||
gui_event.EventType = EET_GUI_EVENT;
|
gui_event.EventType = EET_GUI_EVENT;
|
||||||
gui_event.GUIEvent.Caller = m_hovered.get();
|
gui_event.GUIEvent.Caller = m_touch_hovered.get();
|
||||||
gui_event.GUIEvent.EventType = EGET_ELEMENT_HOVERED;
|
gui_event.GUIEvent.EventType = gui::EGET_ELEMENT_HOVERED;
|
||||||
gui_event.GUIEvent.Element = gui_event.GUIEvent.Caller;
|
gui_event.GUIEvent.Element = gui_event.GUIEvent.Caller;
|
||||||
m_hovered->OnEvent(gui_event);
|
m_touch_hovered->OnEvent(gui_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUIModalMenu::leave()
|
void GUIModalMenu::leave()
|
||||||
{
|
{
|
||||||
if (!m_hovered)
|
if (!m_touch_hovered)
|
||||||
return;
|
return;
|
||||||
SEvent gui_event{};
|
SEvent gui_event{};
|
||||||
gui_event.EventType = EET_GUI_EVENT;
|
gui_event.EventType = EET_GUI_EVENT;
|
||||||
gui_event.GUIEvent.Caller = m_hovered.get();
|
gui_event.GUIEvent.Caller = m_touch_hovered.get();
|
||||||
gui_event.GUIEvent.EventType = EGET_ELEMENT_LEFT;
|
gui_event.GUIEvent.EventType = gui::EGET_ELEMENT_LEFT;
|
||||||
m_hovered->OnEvent(gui_event);
|
m_touch_hovered->OnEvent(gui_event);
|
||||||
m_hovered.reset();
|
m_touch_hovered.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
||||||
{
|
{
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
// clang-format off
|
|
||||||
// display software keyboard when clicking edit boxes
|
// display software keyboard when clicking edit boxes
|
||||||
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
|
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
|
||||||
event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||||
@ -266,36 +268,76 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
|||||||
if (((gui::IGUIEditBox *)hovered)->isPasswordBox())
|
if (((gui::IGUIEditBox *)hovered)->isPasswordBox())
|
||||||
type = 3;
|
type = 3;
|
||||||
|
|
||||||
porting::showInputDialog(gettext("OK"), "",
|
porting::showTextInputDialog("",
|
||||||
wide_to_utf8(((gui::IGUIEditBox *)hovered)->getText()), type);
|
wide_to_utf8(((gui::IGUIEditBox *) hovered)->getText()), type);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.EventType == EET_GUI_EVENT) {
|
||||||
|
if (event.GUIEvent.EventType == gui::EGET_LISTBOX_OPENED) {
|
||||||
|
gui::IGUIComboBox *dropdown = (gui::IGUIComboBox *) event.GUIEvent.Caller;
|
||||||
|
|
||||||
|
std::string field_name = getNameByID(dropdown->getID());
|
||||||
|
if (field_name.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_jni_field_name = field_name;
|
||||||
|
|
||||||
|
s32 selected_idx = dropdown->getSelected();
|
||||||
|
s32 option_size = dropdown->getItemCount();
|
||||||
|
std::string list_of_options[option_size];
|
||||||
|
|
||||||
|
for (s32 i = 0; i < option_size; i++) {
|
||||||
|
list_of_options[i] = wide_to_utf8(dropdown->getItem(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
porting::showComboBoxDialog(list_of_options, option_size, selected_idx);
|
||||||
|
return true; // Prevent the Irrlicht dropdown from opening.
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
// Convert touch events into mouse events.
|
||||||
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
|
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
|
||||||
irr_ptr<GUIModalMenu> holder;
|
irr_ptr<GUIModalMenu> holder;
|
||||||
holder.grab(this); // keep this alive until return (it might be dropped downstream [?])
|
holder.grab(this); // keep this alive until return (it might be dropped downstream [?])
|
||||||
|
|
||||||
if (event.TouchInput.ID == 0) {
|
if (event.TouchInput.touchedCount == 1) {
|
||||||
if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED)
|
m_pointer_type = PointerType::Touch;
|
||||||
m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
|
m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
|
||||||
|
|
||||||
gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d<s32>(m_pointer));
|
gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d<s32>(m_pointer));
|
||||||
if (event.TouchInput.Event == ETIE_PRESSED_DOWN)
|
if (event.TouchInput.Event == ETIE_PRESSED_DOWN)
|
||||||
Environment->setFocus(hovered);
|
Environment->setFocus(hovered);
|
||||||
if (m_hovered != hovered) {
|
if (m_touch_hovered != hovered) {
|
||||||
leave();
|
leave();
|
||||||
enter(hovered);
|
enter(hovered);
|
||||||
}
|
}
|
||||||
gui::IGUIElement *focused = Environment->getFocus();
|
bool ret = simulateMouseEvent(event.TouchInput.Event);
|
||||||
bool ret = simulateMouseEvent(focused, event.TouchInput.Event);
|
|
||||||
if (!ret && m_hovered != focused)
|
|
||||||
ret = simulateMouseEvent(m_hovered.get(), event.TouchInput.Event);
|
|
||||||
if (event.TouchInput.Event == ETIE_LEFT_UP)
|
if (event.TouchInput.Event == ETIE_LEFT_UP)
|
||||||
leave();
|
leave();
|
||||||
|
|
||||||
|
// Detect double-taps and convert them into double-click events.
|
||||||
|
if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
|
||||||
|
u64 time_now = porting::getTimeMs();
|
||||||
|
u64 time_delta = porting::getDeltaMs(m_last_touch.time, time_now);
|
||||||
|
|
||||||
|
v2s32 pos_delta = m_pointer - m_last_touch.pos;
|
||||||
|
f32 distance_sq = (f32)pos_delta.X * pos_delta.X +
|
||||||
|
(f32)pos_delta.Y * pos_delta.Y;
|
||||||
|
|
||||||
|
if (time_delta < 400 && distance_sq < (30 * 30)) {
|
||||||
|
// ETIE_COUNT is used for double-tap events.
|
||||||
|
simulateMouseEvent(ETIE_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_last_touch.time = time_now;
|
||||||
|
m_last_touch.pos = m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
} else if (event.TouchInput.ID == 1) {
|
} else if (event.TouchInput.touchedCount == 2) {
|
||||||
if (event.TouchInput.Event != ETIE_LEFT_UP)
|
if (event.TouchInput.Event != ETIE_LEFT_UP)
|
||||||
return true; // ignore
|
return true; // ignore
|
||||||
auto focused = Environment->getFocus();
|
auto focused = Environment->getFocus();
|
||||||
@ -311,40 +353,29 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (event.EventType == EET_MOUSE_INPUT_EVENT) {
|
if (event.EventType == EET_MOUSE_INPUT_EVENT) {
|
||||||
s32 x = event.MouseInput.X;
|
if (!m_simulated_mouse) {
|
||||||
s32 y = event.MouseInput.Y;
|
// Only set the pointer type to mouse if this is a real mouse event.
|
||||||
gui::IGUIElement *hovered =
|
m_pointer_type = PointerType::Mouse;
|
||||||
Environment->getRootGUIElement()->getElementFromPoint(
|
m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y);
|
||||||
core::position2d<s32>(x, y));
|
m_touch_hovered.reset();
|
||||||
if (!isChild(hovered, this)) {
|
|
||||||
if (DoubleClickDetection(event)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (remapDoubleClick(event))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
bool GUIModalMenu::hasAndroidUIInput()
|
porting::AndroidDialogState GUIModalMenu::getAndroidUIInputState()
|
||||||
{
|
{
|
||||||
// no dialog shown
|
// No dialog is shown
|
||||||
if (m_jni_field_name.empty())
|
if (m_jni_field_name.empty())
|
||||||
return false;
|
return porting::DIALOG_CANCELED;
|
||||||
|
|
||||||
// still waiting
|
return porting::getInputDialogState();
|
||||||
if (porting::getInputDialogState() == -1)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// no value abort dialog processing
|
|
||||||
if (porting::getInputDialogState() != 0) {
|
|
||||||
m_jni_field_name.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -22,6 +22,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "irrlichttypes_extrabloated.h"
|
#include "irrlichttypes_extrabloated.h"
|
||||||
#include "irr_ptr.h"
|
#include "irr_ptr.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <porting_android.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum class PointerType {
|
||||||
|
Mouse,
|
||||||
|
Touch,
|
||||||
|
};
|
||||||
|
|
||||||
class GUIModalMenu;
|
class GUIModalMenu;
|
||||||
|
|
||||||
@ -54,42 +62,37 @@ public:
|
|||||||
virtual bool OnEvent(const SEvent &event) { return false; };
|
virtual bool OnEvent(const SEvent &event) { return false; };
|
||||||
virtual bool pausesGame() { return false; } // Used for pause menu
|
virtual bool pausesGame() { return false; } // Used for pause menu
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
virtual bool getAndroidUIInput() { return false; }
|
virtual void getAndroidUIInput() {};
|
||||||
bool hasAndroidUIInput();
|
porting::AndroidDialogState getAndroidUIInputState();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
PointerType getPointerType() { return m_pointer_type; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual std::wstring getLabelByID(s32 id) = 0;
|
virtual std::wstring getLabelByID(s32 id) = 0;
|
||||||
virtual std::string getNameByID(s32 id) = 0;
|
virtual std::string getNameByID(s32 id) = 0;
|
||||||
|
|
||||||
/**
|
// Stores the last known pointer type.
|
||||||
* check if event is part of a double click
|
PointerType m_pointer_type = PointerType::Mouse;
|
||||||
* @param event event to evaluate
|
// Stores the last known pointer position.
|
||||||
* @return true/false if a doubleclick was detected
|
// If the last input event was a mouse event, it's the cursor position.
|
||||||
*/
|
// If the last input event was a touch event, it's the finger position.
|
||||||
bool DoubleClickDetection(const SEvent &event);
|
|
||||||
|
|
||||||
v2s32 m_pointer;
|
v2s32 m_pointer;
|
||||||
v2s32 m_old_pointer; // Mouse position after previous mouse event
|
v2s32 m_old_pointer; // Mouse position after previous mouse event
|
||||||
|
|
||||||
v2u32 m_screensize_old;
|
v2u32 m_screensize_old;
|
||||||
float m_gui_scale;
|
float m_gui_scale;
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
std::string m_jni_field_name;
|
std::string m_jni_field_name;
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
|
||||||
// This is set to true if the menu is currently processing a second-touch event.
|
// This is set to true if the menu is currently processing a second-touch event.
|
||||||
bool m_second_touch = false;
|
bool m_second_touch = false;
|
||||||
bool m_touchscreen_visible = true;
|
// This is set to true if the menu is currently processing a mouse event
|
||||||
#endif
|
// that was synthesized by the menu itself from a touch event.
|
||||||
|
bool m_simulated_mouse = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct clickpos
|
|
||||||
{
|
|
||||||
v2s32 pos;
|
|
||||||
s64 time;
|
|
||||||
};
|
|
||||||
clickpos m_doubleclickdetect[2];
|
|
||||||
|
|
||||||
IMenuManager *m_menumgr;
|
IMenuManager *m_menumgr;
|
||||||
/* If true, remap a double-click (or double-tap) action to ESC. This is so
|
/* If true, remap a double-click (or double-tap) action to ESC. This is so
|
||||||
* that, for example, Android users can double-tap to close a formspec.
|
* that, for example, Android users can double-tap to close a formspec.
|
||||||
@ -98,15 +101,23 @@ private:
|
|||||||
* and the default value for the setting is true.
|
* and the default value for the setting is true.
|
||||||
*/
|
*/
|
||||||
bool m_remap_dbl_click;
|
bool m_remap_dbl_click;
|
||||||
|
bool remapDoubleClick(const SEvent &event);
|
||||||
|
|
||||||
// This might be necessary to expose to the implementation if it
|
// This might be necessary to expose to the implementation if it
|
||||||
// wants to launch other menus
|
// wants to launch other menus
|
||||||
bool m_allow_focus_removal = false;
|
bool m_allow_focus_removal = false;
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
// Stuff related to touchscreen input
|
||||||
irr_ptr<gui::IGUIElement> m_hovered;
|
|
||||||
|
|
||||||
bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event);
|
irr_ptr<gui::IGUIElement> m_touch_hovered;
|
||||||
|
|
||||||
|
bool simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try=false);
|
||||||
void enter(gui::IGUIElement *element);
|
void enter(gui::IGUIElement *element);
|
||||||
void leave();
|
void leave();
|
||||||
#endif
|
|
||||||
|
// Used to detect double-taps and convert them into double-click events.
|
||||||
|
struct {
|
||||||
|
v2s32 pos;
|
||||||
|
s64 time;
|
||||||
|
} m_last_touch;
|
||||||
};
|
};
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user