forked from Mirrorlandia_minetest/minetest
master #6
@ -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.
|
||||
5. It uses protocols and formats which include the required compatibility.
|
||||
|
||||
### Important note about automated GitHub checks
|
||||
|
||||
When you submit a pull request, GitHub automatically runs checks on the Minetest
|
||||
Engine combined with your changes. One of these checks is called 'cpp lint /
|
||||
clang format', which checks code formatting. Because formatting for readability
|
||||
requires human judgement this check often fails and often makes unsuitable
|
||||
formatting requests which make code readability worse.
|
||||
|
||||
If this check fails, look at the details to check for any clear mistakes and
|
||||
correct those. However, you should not apply everything ClangFormat requests.
|
||||
Ignore requests that make code readability worse and any other clearly
|
||||
unsuitable requests. Discuss in the pull request with a core developer about how
|
||||
to progress.
|
||||
|
||||
## Issues
|
||||
|
||||
If you experience an issue, we would like to know the details - especially when
|
||||
|
4
.github/workflows/android.yml
vendored
4
.github/workflows/android.yml
vendored
@ -8,6 +8,8 @@ on:
|
||||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- '**/CMakeLists.txt'
|
||||
- 'cmake/Modules/**'
|
||||
- 'android/**'
|
||||
- '.github/workflows/android.yml'
|
||||
pull_request:
|
||||
@ -16,6 +18,8 @@ on:
|
||||
- 'lib/**.cpp'
|
||||
- 'src/**.[ch]'
|
||||
- 'src/**.cpp'
|
||||
- '**/CMakeLists.txt'
|
||||
- 'cmake/Modules/**'
|
||||
- 'android/**'
|
||||
- '.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/**'
|
||||
- '.github/workflows/**.yml'
|
||||
|
||||
env:
|
||||
CLANG_TIDY: clang-tidy-15
|
||||
|
||||
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:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install deps
|
||||
run: |
|
||||
source ./util/ci/common.sh
|
||||
install_linux_deps clang-tidy-9
|
||||
install_linux_deps $CLANG_TIDY
|
||||
|
||||
- name: Run clang-tidy
|
||||
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)
|
||||
|
||||
We provide Minetest server Docker images using the GitLab mirror registry.
|
||||
|
||||
Images are built on each commit and available using the following tag scheme:
|
||||
|
||||
* `registry.gitlab.com/minetest/minetest/server:latest` (latest build)
|
||||
* `registry.gitlab.com/minetest/minetest/server:<branch/tag>` (current branch or current tag)
|
||||
* `registry.gitlab.com/minetest/minetest/server:<commit-id>` (current commit id)
|
||||
|
||||
If you want to test it on a Docker server you can easily run:
|
||||
|
||||
sudo docker run registry.gitlab.com/minetest/minetest/server:<docker tag>
|
||||
|
||||
If you want to use it in a production environment you should use volumes bound to the Docker host
|
||||
to persist data and modify the configuration:
|
||||
|
||||
sudo docker create -v /home/minetest/data/:/var/lib/minetest/ -v /home/minetest/conf/:/etc/minetest/ registry.gitlab.com/minetest/minetest/server:master
|
||||
|
||||
Data will be written to `/home/minetest/data` on the host, and configuration will be read from `/home/minetest/conf/minetest.conf`.
|
||||
|
||||
**Note:** If you don't understand the previous commands please read the official Docker documentation before use.
|
||||
|
||||
You can also host your Minetest server inside a Kubernetes cluster. See our example implementation in [`misc/kubernetes.yml`](misc/kubernetes.yml).
|
||||
We provide a Dockerfile that can be used to build the server.
|
||||
|
||||
|
||||
Version scheme
|
||||
|
@ -51,8 +51,13 @@ public class GameActivity extends NativeActivity {
|
||||
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 int selectionReturnValue = 0;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@ -85,11 +90,17 @@ public class GameActivity extends NativeActivity {
|
||||
// Ignore the back press so Minetest can handle it
|
||||
}
|
||||
|
||||
public void showDialog(String acceptButton, String hint, String current, int editType) {
|
||||
runOnUiThread(() -> showDialogUI(hint, current, editType));
|
||||
public void showTextInputDialog(String hint, String current, int 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);
|
||||
LinearLayout container = new LinearLayout(this);
|
||||
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
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
messageReturnCode = 0;
|
||||
inputDialogState = DialogState.DIALOG_INPUTTED;
|
||||
messageReturnValue = editText.getText().toString();
|
||||
alertDialog.dismiss();
|
||||
return true;
|
||||
@ -128,29 +139,55 @@ public class GameActivity extends NativeActivity {
|
||||
doneButton.setText(R.string.ime_dialog_done);
|
||||
doneButton.setOnClickListener((view -> {
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
messageReturnCode = 0;
|
||||
inputDialogState = DialogState.DIALOG_INPUTTED;
|
||||
messageReturnValue = editText.getText().toString();
|
||||
alertDialog.dismiss();
|
||||
}));
|
||||
}
|
||||
alertDialog.setOnCancelListener(dialog -> {
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||
messageReturnValue = current;
|
||||
messageReturnCode = -1;
|
||||
});
|
||||
alertDialog.show();
|
||||
editText.requestFocusTryShow();
|
||||
}
|
||||
|
||||
public int getDialogState() {
|
||||
return messageReturnCode;
|
||||
public void showSelectionInputDialogUI(String[] optionList, int selectedIdx) {
|
||||
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() {
|
||||
messageReturnCode = -1;
|
||||
public int getLastDialogType() {
|
||||
return lastDialogType.ordinal();
|
||||
}
|
||||
|
||||
public int getInputDialogState() {
|
||||
return inputDialogState.ordinal();
|
||||
}
|
||||
|
||||
public String getDialogMessage() {
|
||||
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||
return messageReturnValue;
|
||||
}
|
||||
|
||||
public int getDialogSelection() {
|
||||
inputDialogState = DialogState.DIALOG_CANCELED;
|
||||
return selectionReturnValue;
|
||||
}
|
||||
|
||||
public float getDensity() {
|
||||
return getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
@ -87,19 +87,20 @@ core.builtin_auth_handler = {
|
||||
core.settings:get("default_password")))
|
||||
end
|
||||
|
||||
local prev_privs = auth_entry.privileges
|
||||
auth_entry.privileges = privileges
|
||||
|
||||
core_auth.save(auth_entry)
|
||||
|
||||
-- Run grant callbacks
|
||||
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")
|
||||
end
|
||||
end
|
||||
|
||||
-- Run revoke callbacks
|
||||
for priv, _ in pairs(auth_entry.privileges) do
|
||||
for priv, _ in pairs(prev_privs) do
|
||||
if not privileges[priv] then
|
||||
core.run_priv_callbacks(name, priv, nil, "revoke")
|
||||
end
|
||||
|
@ -29,6 +29,7 @@ core.features = {
|
||||
compress_zstd = true,
|
||||
sound_params_start_time = true,
|
||||
physics_overrides_v2 = true,
|
||||
hud_def_type_field = true,
|
||||
}
|
||||
|
||||
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))
|
||||
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
|
||||
|
||||
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 = {
|
||||
hp = {
|
||||
hud_elem_type = "statbar",
|
||||
type = "statbar",
|
||||
position = {x = 0.5, y = 1},
|
||||
text = "heart.png",
|
||||
text2 = "heart_gone.png",
|
||||
@ -14,7 +14,7 @@ local bar_definitions = {
|
||||
offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)},
|
||||
},
|
||||
breath = {
|
||||
hud_elem_type = "statbar",
|
||||
type = "statbar",
|
||||
position = {x = 0.5, y = 1},
|
||||
text = "bubble.png",
|
||||
text2 = "bubble_gone.png",
|
||||
@ -139,7 +139,7 @@ end
|
||||
|
||||
function core.hud_replace_builtin(hud_name, definition)
|
||||
if type(definition) ~= "table" or
|
||||
definition.hud_elem_type ~= "statbar" then
|
||||
(definition.type or definition.hud_elem_type) ~= "statbar" then
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -28,6 +28,13 @@ end
|
||||
|
||||
local has_fetched = false
|
||||
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()
|
||||
@ -89,8 +96,9 @@ local function fetch()
|
||||
has_fetched = false
|
||||
return
|
||||
end
|
||||
|
||||
latest_releases = lowercase_keys(releases)
|
||||
core.set_once("cdb_latest_releases", core.serialize(latest_releases))
|
||||
|
||||
if update_detector.get_count() > 0 then
|
||||
local maintab = ui.find_by_name("maintab")
|
||||
if not maintab.hidden then
|
||||
|
@ -47,13 +47,13 @@ end
|
||||
|
||||
|
||||
local change_keys = {
|
||||
query_text = "Change Keys",
|
||||
query_text = "Controls",
|
||||
requires = {
|
||||
keyboard_mouse = true,
|
||||
},
|
||||
get_formspec = function(self, avail_w)
|
||||
local btn_w = math.min(avail_w, 3)
|
||||
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Change Keys")), 0.8
|
||||
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
|
||||
end,
|
||||
on_submit = function(self, fields)
|
||||
if fields.btn_change_keys then
|
||||
|
@ -46,11 +46,18 @@ local register_functions = {
|
||||
register_on_mapblocks_changed = 0,
|
||||
}
|
||||
|
||||
local function regex_escape(s)
|
||||
return s:gsub("(%W)", "%%%1")
|
||||
end
|
||||
|
||||
---
|
||||
-- Create an unique instrument name.
|
||||
-- Generate a missing label with a running index number.
|
||||
--
|
||||
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 class, label, func_name = def.class, def.label, def.func_name
|
||||
if label then
|
||||
@ -65,7 +72,16 @@ local function generate_name(def)
|
||||
local index_id = def.mod .. (class or func_name)
|
||||
local index = counts[index_id] or 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
|
||||
|
||||
---
|
||||
|
@ -77,7 +77,7 @@ local Formatter = {
|
||||
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 HR = {}
|
||||
|
@ -90,7 +90,7 @@
|
||||
# Smooths rotation of camera, also called look or mouse smoothing. 0 to disable.
|
||||
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
|
||||
cinematic_camera_smoothing (Camera smoothing in cinematic mode) float 0.7 0.0 0.99
|
||||
@ -580,6 +580,17 @@ enable_auto_exposure (Enable Automatic Exposure) bool false
|
||||
# Requires: shaders, enable_auto_exposure
|
||||
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]
|
||||
|
||||
# Set to true to enable bloom effect.
|
||||
@ -616,6 +627,10 @@ bloom_strength_factor (Bloom Strength Factor) float 1.0 0.1 10.0
|
||||
# Requires: shaders, enable_bloom
|
||||
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]
|
||||
|
||||
@ -799,6 +814,14 @@ bind_address (Bind address) string
|
||||
# to new servers, but they may not support all new features that you are expecting.
|
||||
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.
|
||||
# $filename should be accessible from $remote_media$filename via cURL
|
||||
# (obviously, remote_media should end with a slash).
|
||||
@ -1847,6 +1870,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.
|
||||
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_bold (Font bold by default) bool false
|
||||
@ -2061,8 +2089,8 @@ liquid_update (Liquid update tick) float 1.0 0.001
|
||||
# as well as sometimes on land).
|
||||
# Setting this to a value greater than max_block_send_distance disables this
|
||||
# optimization.
|
||||
# Stated in mapblocks (16 nodes).
|
||||
block_send_optimize_distance (Block send optimize distance) int 4 2 32767
|
||||
# Stated in MapBlocks (16 nodes).
|
||||
block_send_optimize_distance (Block send optimize distance) int 4 2 2047
|
||||
|
||||
# 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
|
||||
@ -2070,6 +2098,13 @@ block_send_optimize_distance (Block send optimize distance) int 4 2 32767
|
||||
# invisible blocks, so that the utility of noclip mode is reduced.
|
||||
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]
|
||||
|
||||
# Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).
|
||||
|
@ -1,6 +1,13 @@
|
||||
#define rendered texture0
|
||||
#define bloom texture1
|
||||
|
||||
#ifdef GL_ES
|
||||
// Dithering requires sufficient floating-point precision
|
||||
#ifndef GL_FRAGMENT_PRECISION_HIGH
|
||||
#undef ENABLE_DITHERING
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct ExposureParams {
|
||||
float compensationFactor;
|
||||
};
|
||||
@ -61,14 +68,17 @@ vec3 uncharted2Tonemap(vec3 x)
|
||||
|
||||
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);
|
||||
// Precalculated white_scale from
|
||||
//vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
|
||||
vec3 whiteScale = vec3(1.036015346);
|
||||
color.rgb *= whiteScale;
|
||||
return color;
|
||||
return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
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));
|
||||
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
|
||||
|
||||
void main(void)
|
||||
@ -105,25 +128,31 @@ void main(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_BLOOM
|
||||
color = applyBloom(color, uv);
|
||||
#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
|
||||
if (uv.x > 0.5 || uv.y > 0.5)
|
||||
#endif
|
||||
{
|
||||
#if ENABLE_TONE_MAPPING
|
||||
color = applyToneMapping(color);
|
||||
color.rgb = applySaturation(color.rgb, saturation);
|
||||
#endif
|
||||
|
||||
color.rgb = applySaturation(color.rgb, saturation);
|
||||
}
|
||||
|
||||
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_DITHERING
|
||||
// Apply dithering just before quantisation
|
||||
color.rgb += screen_space_dither(gl_FragCoord.xy);
|
||||
#endif
|
||||
|
||||
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()
|
||||
print("armor: " .. dump(core.localplayer:get_armor_groups()))
|
||||
id = core.localplayer:hud_add({
|
||||
hud_elem_type = "text",
|
||||
type = "text",
|
||||
name = "example",
|
||||
number = 0xff0000,
|
||||
position = {x=0, y=1},
|
||||
|
@ -1332,8 +1332,10 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
|
||||
|
||||
```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"
|
||||
hud_elem_type = "image",
|
||||
-- ^ Deprecated, same as `type`. In case both are specified `type` will be used.
|
||||
position = {x=0.5, y=0.5},
|
||||
-- ^ Left corner position of element, default `{x=0,y=0}`.
|
||||
name = "<name>", -- default ""
|
||||
|
@ -30,7 +30,7 @@ For openSUSE 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:
|
||||
|
||||
|
@ -33,13 +33,15 @@ mkdir build
|
||||
cd build
|
||||
|
||||
cmake .. \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
|
||||
-DCMAKE_FIND_FRAMEWORK=LAST \
|
||||
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
|
||||
-DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE
|
||||
|
||||
make -j$(sysctl -n hw.logicalcpu)
|
||||
make install
|
||||
|
||||
# M1 Macs w/ MacOS >= BigSur
|
||||
codesign --force --deep -s - macos/minetest.app
|
||||
```
|
||||
|
||||
## Run
|
||||
|
@ -5278,6 +5278,8 @@ Utilities
|
||||
-- liquid_fluidity, liquid_fluidity_smooth, liquid_sink,
|
||||
-- acceleration_default, acceleration_air (5.8.0)
|
||||
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
|
||||
-- Equal to the setting `hud_scaling` multiplied by `dpi / 96`
|
||||
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.
|
||||
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.
|
||||
Please note that it's not safe to use string.char to generate raw data,
|
||||
use `colorspec_to_bytes` to generate raw RGBA values in a predictable way.
|
||||
The resulting PNG image is always 32-bit. Palettes are not supported at the moment.
|
||||
You can use `colorspec_to_bytes` to generate raw RGBA values.
|
||||
Palettes are not supported at the moment.
|
||||
You may use this to procedurally generate textures during server init.
|
||||
* `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a
|
||||
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.
|
||||
* No-op if object is attached.
|
||||
* `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.
|
||||
* `add_velocity(vel)`
|
||||
* Changes velocity by adding to the current velocity.
|
||||
* `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
|
||||
set_velocity, add_velocity is supposed to avoid synchronization problems.
|
||||
Additionally, players also do not support set_velocity.
|
||||
* In comparison to using `get_velocity`, adding the velocity and then using
|
||||
`set_velocity`, `add_velocity` is supposed to avoid synchronization problems.
|
||||
Additionally, players also do not support `set_velocity`.
|
||||
* 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,
|
||||
increasing e.g. Y velocity beyond what would usually be achieved
|
||||
(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.
|
||||
* `set_detach()`: Detaches object. No-op if object was not attached.
|
||||
* `set_bone_position([bone, position, rotation])`
|
||||
* `bone`: string. Default is `""`, the root bone
|
||||
* `position`: `{x=num, y=num, z=num}`, relative, `default {x=0, y=0, z=0}`
|
||||
* `rotation`: `{x=num, y=num, z=num}`, default `{x=0, y=0, z=0}`
|
||||
* `get_bone_position(bone)`:
|
||||
* returns bone parameters previously set by `set_bone_position`
|
||||
* returns `position, rotation` of the specified bone (as vectors)
|
||||
* note: position is relative to the object
|
||||
* `set_properties(object property table)`:
|
||||
* set a number of object properties in the given table
|
||||
* only properties listed in the table will be changed
|
||||
* see the 'Object properties' section for details
|
||||
* Shorthand for `set_bone_override(bone, {position = position, rotation = rotation:apply(math.rad)})` using absolute values.
|
||||
* **Note:** Rotation is in degrees, not radians.
|
||||
* **Deprecated:** Use `set_bone_override` instead.
|
||||
* `get_bone_position(bone)`: returns the previously set position and rotation of the bone
|
||||
* Shorthand for `get_bone_override(bone).position.vec, get_bone_override(bone).rotation.vec:apply(math.deg)`.
|
||||
* **Note:** Returned rotation is in degrees, not radians.
|
||||
* **Deprecated:** Use `get_bone_override` instead.
|
||||
* `set_bone_override(bone, override)`
|
||||
* `bone`: string
|
||||
* `override`: `{ position = property, rotation = property, scale = property }` or `nil`
|
||||
* `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
|
||||
* `is_player()`: returns true for players, false otherwise
|
||||
* `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
|
||||
element.
|
||||
* `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_set_flags(flags)`: sets specified HUD flags of player.
|
||||
* `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.
|
||||
* `light_definition` is a table with the following optional fields:
|
||||
* `saturation` sets the saturation (vividness; default: `1.0`).
|
||||
values > 1 increase the saturation
|
||||
values in [0,1) decrease the saturation
|
||||
* This value has no effect on clients who have the "Tone Mapping" shader disabled.
|
||||
* values > 1 increase the saturation
|
||||
* values in [0,1] decrease the saturation
|
||||
* `shadows` is a table that controls ambient shadows
|
||||
* `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.
|
||||
@ -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_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`)
|
||||
* `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.
|
||||
* 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
|
||||
{
|
||||
hud_elem_type = "image",
|
||||
type = "image",
|
||||
-- Type of element, can be "image", "text", "statbar", "inventory",
|
||||
-- "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},
|
||||
-- Top left corner position of element
|
||||
|
@ -38,7 +38,9 @@ Functions
|
||||
---------
|
||||
|
||||
* `core.start()`
|
||||
* start game session
|
||||
* `core.close()`
|
||||
* exit engine
|
||||
* `core.get_min_supp_proto()`
|
||||
* returns the minimum supported network protocol version
|
||||
* `core.get_max_supp_proto()`
|
||||
@ -53,6 +55,10 @@ Functions
|
||||
* Android only. Shares file using the share popup
|
||||
* `core.get_version()` (possible in async calls)
|
||||
* 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
|
||||
-- Equal to the setting `hud_scaling` multiplied by `dpi / 96`
|
||||
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
|
||||
|
||||
* `down_arrow.png`
|
||||
* `left_arrow.png`
|
||||
* `right_arrow.png`
|
||||
* `up_arrow.png`
|
||||
|
||||
* `drop_btn.png`
|
||||
* `fast_btn.png`
|
||||
* `fly_btn.png`
|
||||
|
@ -66,7 +66,7 @@ minetest.register_globalstep(function()
|
||||
local ent = pointed_thing.ref:get_luaentity()
|
||||
if ent and ent.name == "testentities:selectionbox" then
|
||||
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},
|
||||
text = "X",
|
||||
number = 0xFF0000,
|
||||
|
@ -7,6 +7,8 @@ local function show_fullscreen_fs(name)
|
||||
print(dump(window))
|
||||
|
||||
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 = {
|
||||
"formspec_version[4]",
|
||||
("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(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))
|
||||
|
@ -12,7 +12,7 @@ local font_states = {
|
||||
|
||||
|
||||
local font_default_def = {
|
||||
hud_elem_type = "text",
|
||||
type = "text",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
scale = {x = 2, y = 2},
|
||||
alignment = { x = 0, y = 0 },
|
||||
@ -102,14 +102,14 @@ minetest.register_chatcommand("hudwaypoints", {
|
||||
return false
|
||||
end
|
||||
local regular = player:hud_add {
|
||||
hud_elem_type = "waypoint",
|
||||
type = "waypoint",
|
||||
name = "regular waypoint",
|
||||
text = "m",
|
||||
number = 0xFFFFFF,
|
||||
world_pos = vector.add(player:get_pos(), {x = 0, y = 1.5, z = 0})
|
||||
}
|
||||
local reduced_precision = player:hud_add {
|
||||
hud_elem_type = "waypoint",
|
||||
type = "waypoint",
|
||||
name = "imprecise waypoint",
|
||||
text = "m (0.1 steps, 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})
|
||||
}
|
||||
local hidden_distance = player:hud_add {
|
||||
hud_elem_type = "waypoint",
|
||||
type = "waypoint",
|
||||
name = "waypoint with hidden distance",
|
||||
text = "this text is hidden as well (precision = 0)",
|
||||
precision = 0,
|
||||
@ -149,7 +149,7 @@ minetest.register_chatcommand("hudwaypoints", {
|
||||
minetest.after(0.5, change, player)
|
||||
end
|
||||
local image_waypoint = player:hud_add {
|
||||
hud_elem_type = "image_waypoint",
|
||||
type = "image_waypoint",
|
||||
text = "testhud_waypoint.png",
|
||||
world_pos = player:get_pos(),
|
||||
scale = {x = 3, y = 3},
|
||||
|
@ -105,6 +105,19 @@ local function gen_checkers(w, h, tile)
|
||||
return r
|
||||
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 frac_emb = mandelbrot(64, 64, 64)
|
||||
local checker = gen_checkers(512, 512, 32)
|
||||
@ -129,17 +142,21 @@ for i=1, #fractal do
|
||||
b = floor(abs(1 - fractal[i]) * 255),
|
||||
a = 255,
|
||||
}
|
||||
data_ck[i] = checker[i] > 0 and "#F80" or "#000"
|
||||
data_ck[i] = checker[i] > 0 and "#888" or "#000"
|
||||
end
|
||||
|
||||
fractal = nil
|
||||
frac_emb = nil
|
||||
checker = nil
|
||||
|
||||
local textures_path = minetest.get_modpath( minetest.get_current_modname() ) .. "/textures/"
|
||||
minetest.safe_file_write(
|
||||
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(
|
||||
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", {
|
||||
@ -155,7 +172,8 @@ minetest.register_node("testnodes:generated_png_ck", {
|
||||
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", {
|
||||
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 },
|
||||
})
|
||||
|
||||
data_emb = nil
|
||||
data_mb = nil
|
||||
data_ck = nil
|
||||
|
||||
--[[
|
||||
|
||||
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))
|
||||
end
|
||||
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})
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
# type: float min: 0 max: 0.99
|
||||
# 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
|
||||
# cinematic_camera_smoothing = 0.7
|
||||
|
||||
@ -760,6 +760,15 @@
|
||||
# type: bool
|
||||
# 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.
|
||||
# $filename should be accessible from $remote_media$filename via cURL
|
||||
# (obviously, remote_media should end with a slash).
|
||||
@ -3627,4 +3636,3 @@
|
||||
# See https://github.com/minetest/irrlicht/blob/master/include/Keycodes.h
|
||||
# type: key
|
||||
# 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>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
|
||||
<heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
|
@ -314,6 +314,18 @@ else()
|
||||
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)
|
||||
|
||||
configure_file(
|
||||
@ -761,7 +773,9 @@ else()
|
||||
endif()
|
||||
|
||||
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")
|
||||
endif()
|
||||
|
||||
@ -801,7 +815,7 @@ else()
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mwindows")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-mwindows")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -21,7 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "irr_aabb3d.h"
|
||||
#include "irr_v3d.h"
|
||||
#include <quaternion.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
enum ActiveObjectType {
|
||||
@ -72,6 +74,78 @@ enum ActiveObjectCommand {
|
||||
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
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@ set (BENCHMARK_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapblock.cpp
|
||||
PARENT_SCOPE)
|
||||
|
||||
set (BENCHMARK_CLIENT_SRCS
|
||||
|
@ -23,10 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "benchmark_setup.h"
|
||||
|
||||
int run_benchmarks()
|
||||
bool run_benchmarks(const char *arg)
|
||||
{
|
||||
int argc = 1;
|
||||
const char *argv[] = { "MinetestBenchmark", NULL };
|
||||
const char *const argv[] = {
|
||||
"MinetestBenchmark", arg, nullptr
|
||||
};
|
||||
const int argc = arg ? 2 : 1;
|
||||
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"
|
||||
|
||||
#if BUILD_BENCHMARKS
|
||||
extern int run_benchmarks();
|
||||
extern bool run_benchmarks(const char *arg = nullptr);
|
||||
#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)
|
||||
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/ogg_file.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)
|
||||
{
|
||||
assert(obj); // Pre-condition
|
||||
@ -93,7 +92,6 @@ void ActiveObjectMgr::removeObject(u16 id)
|
||||
obj->removeFromScene(true);
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
void ActiveObjectMgr::getActiveObjects(const v3f &origin, f32 max_d,
|
||||
std::vector<DistanceSortedActiveObject> &dest)
|
||||
{
|
||||
|
@ -97,7 +97,6 @@ void PacketCounter::print(std::ostream &o) const
|
||||
Client::Client(
|
||||
const char *playername,
|
||||
const std::string &password,
|
||||
const std::string &address_name,
|
||||
MapDrawControl &control,
|
||||
IWritableTextureSource *tsrc,
|
||||
IWritableShaderSource *shsrc,
|
||||
@ -106,7 +105,6 @@ Client::Client(
|
||||
ISoundManager *sound,
|
||||
MtEventManager *event,
|
||||
RenderingEngine *rendering_engine,
|
||||
bool ipv6,
|
||||
GameUI *game_ui,
|
||||
ELoginRegister allow_login_or_register
|
||||
):
|
||||
@ -123,8 +121,6 @@ Client::Client(
|
||||
tsrc, this
|
||||
),
|
||||
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_server_ser_ver(SER_FMT_VER_INVALID),
|
||||
m_last_chat_message_sent(time(NULL)),
|
||||
@ -338,6 +334,7 @@ bool Client::isShutdown()
|
||||
Client::~Client()
|
||||
{
|
||||
m_shutdown = true;
|
||||
if (m_con)
|
||||
m_con->Disconnect();
|
||||
|
||||
deleteAuthData();
|
||||
@ -381,13 +378,32 @@ Client::~Client()
|
||||
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
|
||||
m_con->SetTimeoutMs(0);
|
||||
m_con->Connect(address);
|
||||
|
||||
initLocalMapSaving(address, m_address_name, is_local_server);
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
if (m_localdb) {
|
||||
infostream << "Local map saving already running" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string world_path;
|
||||
#define set_world_path(hostname) \
|
||||
@ -935,6 +955,8 @@ void Client::ReceiveAll()
|
||||
NetworkPacket pkt;
|
||||
u64 start_ms = porting::getTimeMs();
|
||||
const u64 budget = 10;
|
||||
|
||||
FATAL_ERROR_IF(!m_con, "Networking not initialized");
|
||||
for(;;) {
|
||||
// Limit time even if there would be huge amounts of data to
|
||||
// process
|
||||
@ -1439,6 +1461,7 @@ void Client::sendUpdateClientInfo(const ClientDynamicInfo& info)
|
||||
pkt << info.real_gui_scaling;
|
||||
pkt << info.real_hud_scaling;
|
||||
pkt << (f32)info.max_fs_size.X << (f32)info.max_fs_size.Y;
|
||||
pkt << info.touch_controls;
|
||||
|
||||
Send(&pkt);
|
||||
}
|
||||
@ -1766,7 +1789,7 @@ ClientEvent *Client::getClientEvent()
|
||||
|
||||
const Address Client::getServerAddress()
|
||||
{
|
||||
return m_con->GetPeerAddress(PEER_ID_SERVER);
|
||||
return m_con ? m_con->GetPeerAddress(PEER_ID_SERVER) : Address();
|
||||
}
|
||||
|
||||
float Client::mediaReceiveProgress()
|
||||
@ -1872,11 +1895,13 @@ void Client::afterContentReceived()
|
||||
|
||||
float Client::getRTT()
|
||||
{
|
||||
assert(m_con);
|
||||
return m_con->getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
|
||||
}
|
||||
|
||||
float Client::getCurRate()
|
||||
{
|
||||
assert(m_con);
|
||||
return (m_con->getLocalStat(con::CUR_INC_RATE) +
|
||||
m_con->getLocalStat(con::CUR_DL_RATE));
|
||||
}
|
||||
|
@ -122,7 +122,6 @@ public:
|
||||
Client(
|
||||
const char *playername,
|
||||
const std::string &password,
|
||||
const std::string &address_name,
|
||||
MapDrawControl &control,
|
||||
IWritableTextureSource *tsrc,
|
||||
IWritableShaderSource *shsrc,
|
||||
@ -131,7 +130,6 @@ public:
|
||||
ISoundManager *sound,
|
||||
MtEventManager *event,
|
||||
RenderingEngine *rendering_engine,
|
||||
bool ipv6,
|
||||
GameUI *game_ui,
|
||||
ELoginRegister allow_login_or_register
|
||||
);
|
||||
@ -155,11 +153,8 @@ public:
|
||||
|
||||
bool isShutdown();
|
||||
|
||||
/*
|
||||
The name of the local player should already be set when
|
||||
calling this, as it is sent in the initialization.
|
||||
*/
|
||||
void connect(Address address, bool is_local_server);
|
||||
void connect(const Address &address, const std::string &address_name,
|
||||
bool is_local_server);
|
||||
|
||||
/*
|
||||
Stuff that references the environment is valid only as
|
||||
@ -196,6 +191,7 @@ public:
|
||||
void handleCommand_HP(NetworkPacket* pkt);
|
||||
void handleCommand_Breath(NetworkPacket* pkt);
|
||||
void handleCommand_MovePlayer(NetworkPacket* pkt);
|
||||
void handleCommand_MovePlayerRel(NetworkPacket* pkt);
|
||||
void handleCommand_DeathScreen(NetworkPacket* pkt);
|
||||
void handleCommand_AnnounceMedia(NetworkPacket* pkt);
|
||||
void handleCommand_Media(NetworkPacket* pkt);
|
||||
@ -350,7 +346,7 @@ public:
|
||||
bool activeObjectsReceived() const
|
||||
{ return m_activeobjects_received; }
|
||||
|
||||
u16 getProtoVersion()
|
||||
u16 getProtoVersion() const
|
||||
{ return m_proto_ver; }
|
||||
|
||||
bool m_simple_singleplayer_mode;
|
||||
@ -362,6 +358,10 @@ public:
|
||||
|
||||
float getRTT();
|
||||
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; }
|
||||
void setCamera(Camera* camera) { m_camera = camera; }
|
||||
@ -414,8 +414,10 @@ public:
|
||||
|
||||
void showMinimap(bool show = true);
|
||||
|
||||
// IP and port we're connected to
|
||||
const Address getServerAddress();
|
||||
|
||||
// Hostname of the connected server (but can also be a numerical IP)
|
||||
const std::string &getAddressName() const
|
||||
{
|
||||
return m_address_name;
|
||||
|
@ -34,33 +34,43 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include <queue>
|
||||
|
||||
// struct MeshBufListList
|
||||
void MeshBufListList::clear()
|
||||
namespace {
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
using MeshBufListMap = std::unordered_map<
|
||||
video::SMaterial,
|
||||
std::vector<std::pair<v3s16, scene::IMeshBuffer *>>,
|
||||
MaterialHash>;
|
||||
|
||||
std::array<MeshBufListMap, MAX_TILE_LAYERS> maps;
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (auto &map : maps)
|
||||
map.clear();
|
||||
}
|
||||
|
||||
void MeshBufListList::add(scene::IMeshBuffer *buf, v3s16 position, u8 layer)
|
||||
void add(scene::IMeshBuffer *buf, v3s16 position, u8 layer)
|
||||
{
|
||||
assert(layer < MAX_TILE_LAYERS);
|
||||
|
||||
// Append to the correct layer
|
||||
std::vector<MeshBufList> &list = lists[layer];
|
||||
auto &map = maps[layer];
|
||||
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) {
|
||||
l.bufs.emplace_back(position, buf);
|
||||
return;
|
||||
auto &bufs = map[m]; // default constructs if non-existent
|
||||
bufs.emplace_back(position, buf);
|
||||
}
|
||||
}
|
||||
MeshBufList l;
|
||||
l.m = m;
|
||||
l.bufs.emplace_back(position, buf);
|
||||
list.emplace_back(l);
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
MeshBufListList grouped_buffers;
|
||||
MeshBufListMaps grouped_buffers;
|
||||
std::vector<DrawDescriptor> draw_order;
|
||||
video::SMaterial previous_material;
|
||||
|
||||
@ -793,7 +803,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
||||
}
|
||||
else {
|
||||
// otherwise, group buffers across meshes
|
||||
// using MeshBufListList
|
||||
// using MeshBufListMaps
|
||||
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
|
||||
scene::IMesh *mesh = block_mesh->getMesh(layer);
|
||||
assert(mesh);
|
||||
@ -819,11 +829,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
||||
}
|
||||
|
||||
// Capture draw order for all solid meshes
|
||||
for (auto &lists : grouped_buffers.lists) {
|
||||
for (MeshBufList &list : lists) {
|
||||
for (auto &map : grouped_buffers.maps) {
|
||||
for (auto &list : map) {
|
||||
// iterate in reverse to draw closest blocks first
|
||||
for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it) {
|
||||
draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
|
||||
for (auto it = list.second.rbegin(); it != list.second.rend(); ++it) {
|
||||
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 vertex_count = 0;
|
||||
|
||||
MeshBufListList grouped_buffers;
|
||||
MeshBufListMaps grouped_buffers;
|
||||
std::vector<DrawDescriptor> draw_order;
|
||||
|
||||
|
||||
@ -1144,7 +1154,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
||||
}
|
||||
else {
|
||||
// otherwise, group buffers across meshes
|
||||
// using MeshBufListList
|
||||
// using MeshBufListMaps
|
||||
MapBlockMesh *mapBlockMesh = block->mesh;
|
||||
assert(mapBlockMesh);
|
||||
|
||||
@ -1167,18 +1177,18 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
||||
}
|
||||
|
||||
u32 buffer_count = 0;
|
||||
for (auto &lists : grouped_buffers.lists)
|
||||
for (MeshBufList &list : lists)
|
||||
buffer_count += list.bufs.size();
|
||||
for (auto &map : grouped_buffers.maps)
|
||||
for (auto &list : map)
|
||||
buffer_count += list.second.size();
|
||||
|
||||
draw_order.reserve(draw_order.size() + buffer_count);
|
||||
|
||||
// Capture draw order for all solid meshes
|
||||
for (auto &lists : grouped_buffers.lists) {
|
||||
for (MeshBufList &list : lists) {
|
||||
for (auto &map : grouped_buffers.maps) {
|
||||
for (auto &list : map) {
|
||||
// iterate in reverse to draw closest blocks first
|
||||
for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it)
|
||||
draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
|
||||
for (auto it = list.second.rbegin(); it != list.second.rend(); ++it)
|
||||
draw_order.emplace_back(it->first, it->second, it != list.second.rbegin());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,25 +37,6 @@ struct MapDrawControl
|
||||
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 ITextureSource;
|
||||
class PartialMeshBuffer;
|
||||
|
@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <cmath>
|
||||
#include "client/shader.h"
|
||||
#include "client/minimap.h"
|
||||
#include <quaternion.h>
|
||||
|
||||
class Settings;
|
||||
struct ToolCapabilities;
|
||||
@ -828,7 +829,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
|
||||
updateMarker();
|
||||
updateNodePos();
|
||||
updateAnimation();
|
||||
updateBonePosition();
|
||||
updateBones(.0f);
|
||||
updateAttachments();
|
||||
setNodeLight(m_last_light);
|
||||
updateMeshCulling();
|
||||
@ -1246,7 +1247,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
|
||||
updatePositionRecursive(m_matrixnode);
|
||||
m_animated_meshnode->updateAbsolutePosition();
|
||||
m_animated_meshnode->animateJoints();
|
||||
updateBonePosition();
|
||||
updateBones(dtime);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1527,19 +1528,28 @@ void GenericCAO::updateAnimationSpeed()
|
||||
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;
|
||||
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
|
||||
for (auto &it : m_bone_position) {
|
||||
for (auto &it : m_bone_override) {
|
||||
std::string bone_name = it.first;
|
||||
scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
|
||||
if (bone) {
|
||||
bone->setPosition(it.second.X);
|
||||
bone->setRotation(it.second.Y);
|
||||
}
|
||||
if (!bone)
|
||||
continue;
|
||||
|
||||
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
|
||||
@ -1550,7 +1560,7 @@ void GenericCAO::updateBonePosition()
|
||||
|
||||
//If bone is manually positioned there is no need to perform the bug check
|
||||
bool skip = false;
|
||||
for (auto &it : m_bone_position) {
|
||||
for (auto &it : m_bone_override) {
|
||||
if (it.first == bone->getName()) {
|
||||
skip = true;
|
||||
break;
|
||||
@ -1852,11 +1862,46 @@ void GenericCAO::processMessage(const std::string &data)
|
||||
updateAnimationSpeed();
|
||||
} else if (cmd == AO_CMD_SET_BONE_POSITION) {
|
||||
std::string bone = deSerializeString16(is);
|
||||
v3f position = readV3F32(is);
|
||||
v3f rotation = readV3F32(is);
|
||||
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
|
||||
|
||||
// updateBonePosition(); now called every step
|
||||
auto it = m_bone_override.find(bone);
|
||||
BoneOverride props;
|
||||
if (it != m_bone_override.end()) {
|
||||
props = it->second;
|
||||
// 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) {
|
||||
u16 parent_id = readS16(is);
|
||||
std::string bone = deSerializeString16(is);
|
||||
|
@ -104,7 +104,7 @@ private:
|
||||
float m_animation_blend = 0.0f;
|
||||
bool m_animation_loop = true;
|
||||
// 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;
|
||||
std::unordered_set<int> m_attachment_child_ids;
|
||||
@ -267,7 +267,7 @@ public:
|
||||
|
||||
void updateAnimationSpeed();
|
||||
|
||||
void updateBonePosition();
|
||||
void updateBones(f32 dtime);
|
||||
|
||||
void processMessage(const std::string &data) override;
|
||||
|
||||
|
@ -404,6 +404,12 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
|
||||
CachedPixelShaderSetting<float> m_bloom_radius_pixel;
|
||||
float m_bloom_radius;
|
||||
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:
|
||||
void onSettingsChange(const std::string &name)
|
||||
@ -461,7 +467,12 @@ public:
|
||||
m_bloom_intensity_pixel("bloomIntensity"),
|
||||
m_bloom_strength_pixel("bloomStrength"),
|
||||
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("exposure_compensation", settingsCallback, this);
|
||||
@ -475,6 +486,7 @@ public:
|
||||
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_radius = g_settings->getFloat("bloom_radius", 0.1f, 8.0f);
|
||||
m_volumetric_light_enabled = g_settings->getBool("enable_volumetric_lighting") && m_bloom_enabled;
|
||||
}
|
||||
|
||||
~GameGlobalShaderConstantSetter()
|
||||
@ -579,6 +591,54 @@ public:
|
||||
}
|
||||
float saturation = m_client->getEnv().getLocalPlayer()->getLighting().saturation;
|
||||
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
|
||||
@ -821,6 +881,7 @@ protected:
|
||||
const CameraOrientation &cam);
|
||||
void updateClouds(float dtime);
|
||||
void updateShadows();
|
||||
void drawScene(ProfilerGraph *graph, RunStats *stats);
|
||||
|
||||
// Misc
|
||||
void showOverlayMessage(const char *msg, float dtime, int percent,
|
||||
@ -1243,7 +1304,7 @@ void Game::run()
|
||||
updatePauseState();
|
||||
if (m_is_paused)
|
||||
dtime = 0.0f;
|
||||
else
|
||||
|
||||
step(dtime);
|
||||
|
||||
processClientEvents(&cam_view_target);
|
||||
@ -1272,6 +1333,9 @@ void Game::shutdown()
|
||||
if (formspec)
|
||||
formspec->quitMenu();
|
||||
|
||||
// Clear text when exiting.
|
||||
m_game_ui->clearText();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
g_touchscreengui->hide();
|
||||
#endif
|
||||
@ -1428,12 +1492,6 @@ bool Game::createClient(const GameStartData &start_data)
|
||||
return false;
|
||||
|
||||
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))
|
||||
return false;
|
||||
|
||||
@ -1542,10 +1600,8 @@ bool Game::initGui()
|
||||
-1, chat_backend, client, &g_menumgr);
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->show();
|
||||
|
||||
g_touchscreengui->init(texture_src);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@ -1557,15 +1613,18 @@ bool Game::connectToServer(const GameStartData &start_data,
|
||||
*connect_ok = false; // Let's not be overly optimistic
|
||||
*connection_aborted = false;
|
||||
bool local_server_mode = false;
|
||||
const auto &address_name = start_data.address;
|
||||
|
||||
showOverlayMessage(N_("Resolving address..."), 0, 15);
|
||||
|
||||
Address connect_address(0, 0, 0, 0, start_data.socket_port);
|
||||
Address fallback_address;
|
||||
|
||||
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()) {
|
||||
IPv6AddressBytes addr_bytes;
|
||||
addr_bytes.bytes[15] = 1;
|
||||
@ -1582,45 +1641,58 @@ bool Game::connectToServer(const GameStartData &start_data,
|
||||
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());
|
||||
errorstream << *error_message << std::endl;
|
||||
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 {
|
||||
client = new Client(start_data.name.c_str(),
|
||||
start_data.password, start_data.address,
|
||||
start_data.password,
|
||||
*draw_control, texture_src, shader_src,
|
||||
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);
|
||||
client->migrateModStorage();
|
||||
} catch (const BaseException &e) {
|
||||
*error_message = fmtgettext("Error creating client: %s", e.what());
|
||||
errorstream << *error_message << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
client->migrateModStorage();
|
||||
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
|
||||
*/
|
||||
|
||||
client->connect(connect_address, address_name,
|
||||
simple_singleplayer_mode || local_server_mode);
|
||||
|
||||
try {
|
||||
input->clear();
|
||||
|
||||
FpsControl fps_control;
|
||||
f32 dtime;
|
||||
f32 wait_time = 0; // in seconds
|
||||
bool did_fallback = false;
|
||||
|
||||
fps_control.reset();
|
||||
|
||||
@ -1629,10 +1701,7 @@ bool Game::connectToServer(const GameStartData &start_data,
|
||||
fps_control.limit(device, &dtime);
|
||||
|
||||
// Update client and server
|
||||
client->step(dtime);
|
||||
|
||||
if (server != NULL)
|
||||
server->step(dtime);
|
||||
step(dtime);
|
||||
|
||||
// End condition
|
||||
if (client->getState() == LC_Init) {
|
||||
@ -1658,8 +1727,15 @@ bool Game::connectToServer(const GameStartData &start_data,
|
||||
}
|
||||
|
||||
wait_time += dtime;
|
||||
// Only time out if we aren't waiting for the server we started
|
||||
if (!start_data.address.empty() && wait_time > 10) {
|
||||
if (local_server_mode) {
|
||||
// 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.");
|
||||
errorstream << *error_message << std::endl;
|
||||
break;
|
||||
@ -1669,8 +1745,7 @@ bool Game::connectToServer(const GameStartData &start_data,
|
||||
showOverlayMessage(N_("Connecting to server..."), dtime, 20);
|
||||
}
|
||||
} catch (con::PeerNotFoundException &e) {
|
||||
// TODO: Should something be done here? At least an info/error
|
||||
// message?
|
||||
warningstream << "This should not happen. Please report a bug." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1691,10 +1766,7 @@ bool Game::getServerContent(bool *aborted)
|
||||
fps_control.limit(device, &dtime);
|
||||
|
||||
// Update client and server
|
||||
client->step(dtime);
|
||||
|
||||
if (server != NULL)
|
||||
server->step(dtime);
|
||||
step(dtime);
|
||||
|
||||
// End condition
|
||||
if (client->mediaReceived() && client->itemdefReceived() &&
|
||||
@ -2227,7 +2299,7 @@ void Game::openConsole(float scale, const wchar_t *line)
|
||||
assert(scale > 0.0f && scale <= 1.0f);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
porting::showInputDialog(gettext("ok"), "", "", 2);
|
||||
porting::showTextInputDialog("", "", 2);
|
||||
m_android_chat_open = true;
|
||||
#else
|
||||
if (gui_chat_console->isOpenInhibited())
|
||||
@ -2243,15 +2315,19 @@ void Game::openConsole(float scale, const wchar_t *line)
|
||||
#ifdef __ANDROID__
|
||||
void Game::handleAndroidChatInput()
|
||||
{
|
||||
if (m_android_chat_open && porting::getInputDialogState() == 0) {
|
||||
std::string text = porting::getInputDialogValue();
|
||||
// It has to be a text input
|
||||
if (m_android_chat_open && porting::getLastInputDialogType() == porting::TEXT_INPUT) {
|
||||
porting::AndroidDialogState dialogState = porting::getInputDialogState();
|
||||
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
|
||||
|
||||
|
||||
void Game::toggleFreeMove()
|
||||
{
|
||||
bool free_move = !g_settings->getBool("free_move");
|
||||
@ -2616,7 +2692,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
cam->camera_yaw += g_touchscreengui->getYawChange();
|
||||
cam->camera_pitch = g_touchscreengui->getPitch();
|
||||
cam->camera_pitch += g_touchscreengui->getPitchChange();
|
||||
} else {
|
||||
#endif
|
||||
v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
|
||||
@ -2712,9 +2788,22 @@ void Game::updatePauseState()
|
||||
|
||||
inline void Game::step(f32 dtime)
|
||||
{
|
||||
if (server)
|
||||
server->step(dtime);
|
||||
if (server) {
|
||||
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;
|
||||
|
||||
server->setStepSettings(Server::StepSettings{
|
||||
steplen,
|
||||
m_is_paused
|
||||
});
|
||||
|
||||
server->step();
|
||||
}
|
||||
|
||||
if (!m_is_paused)
|
||||
client->step(dtime);
|
||||
}
|
||||
|
||||
@ -3035,7 +3124,6 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
|
||||
else
|
||||
sky->setFogStart(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
|
||||
|
||||
|
||||
delete event->set_sky;
|
||||
}
|
||||
|
||||
@ -4009,31 +4097,6 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 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
|
||||
*/
|
||||
@ -4133,52 +4196,18 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
|
||||
/*
|
||||
==================== Drawing begins ====================
|
||||
*/
|
||||
const video::SColor skycolor = sky->getSkyColor();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (RenderingEngine::shouldRender())
|
||||
drawScene(graph, stats);
|
||||
/*
|
||||
==================== 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));
|
||||
}
|
||||
|
||||
@ -4246,6 +4275,81 @@ void Game::updateShadows()
|
||||
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
|
||||
****************************************************************************/
|
||||
@ -4379,41 +4483,6 @@ void Game::showPauseMenu()
|
||||
"- touch&drag, tap 2nd finger\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
|
||||
|
||||
float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
|
||||
@ -4438,30 +4507,29 @@ void Game::showPauseMenu()
|
||||
}
|
||||
#endif
|
||||
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
|
||||
<< strgettext("Change Keys") << "]";
|
||||
<< strgettext("Controls") << "]";
|
||||
#endif
|
||||
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
|
||||
<< strgettext("Exit to Menu") << "]";
|
||||
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
|
||||
<< strgettext("Exit to OS") << "]"
|
||||
<< "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
|
||||
<< "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
|
||||
<< strgettext("Exit to OS") << "]";
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
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"
|
||||
<< strgettext("Game info:") << "\n";
|
||||
const std::string &address = client->getAddressName();
|
||||
static const std::string mode = strgettext("- Mode: ");
|
||||
os << strgettext("- Mode: ");
|
||||
if (!simple_singleplayer_mode) {
|
||||
Address serverAddress = client->getServerAddress();
|
||||
if (!address.empty()) {
|
||||
os << mode << strgettext("Remote server") << "\n"
|
||||
<< strgettext("- Address: ") << address;
|
||||
if (address.empty())
|
||||
os << strgettext("Hosting server");
|
||||
else
|
||||
os << strgettext("Remote server");
|
||||
} else {
|
||||
os << mode << strgettext("Hosting server");
|
||||
}
|
||||
os << "\n" << strgettext("- Port: ") << serverAddress.getPort() << "\n";
|
||||
} else {
|
||||
os << mode << strgettext("Singleplayer") << "\n";
|
||||
os << strgettext("Singleplayer");
|
||||
}
|
||||
os << "\n";
|
||||
if (simple_singleplayer_mode || address.empty()) {
|
||||
static const std::string on = strgettext("On");
|
||||
static const std::string off = strgettext("Off");
|
||||
|
@ -43,6 +43,8 @@ struct CameraOrientation {
|
||||
f32 camera_pitch; // "up/down"
|
||||
};
|
||||
|
||||
#define GAME_FALLBACK_TIMEOUT 1.8f
|
||||
#define GAME_CONNECTION_TIMEOUT 10.0f
|
||||
|
||||
void the_game(bool *kill,
|
||||
InputHandler *input,
|
||||
|
@ -334,3 +334,36 @@ void GameUI::deleteFormspec()
|
||||
|
||||
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; }
|
||||
GUIFormSpecMenu *&getFormspecGUI() { return m_formspec; }
|
||||
void deleteFormspec();
|
||||
void clearText();
|
||||
|
||||
private:
|
||||
Flags m_flags;
|
||||
|
@ -129,6 +129,11 @@ public:
|
||||
m_position = position;
|
||||
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; }
|
||||
|
||||
|
@ -47,7 +47,6 @@ struct MeshCollector
|
||||
// offset: offset added to vertices
|
||||
MeshCollector(const v3f center_pos, v3f offset = v3f()) : m_center_pos(center_pos), offset(offset) {}
|
||||
|
||||
// clang-format off
|
||||
void append(const TileSpec &material,
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices);
|
||||
@ -55,10 +54,8 @@ struct MeshCollector
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices,
|
||||
v3f pos, video::SColor c, u8 light_source);
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
// clang-format off
|
||||
void append(const TileLayer &material,
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices,
|
||||
@ -68,7 +65,6 @@ private:
|
||||
const u16 *indices, u32 numIndices,
|
||||
v3f pos, video::SColor c, u8 light_source,
|
||||
u8 layernum, bool use_scale = false);
|
||||
// clang-format on
|
||||
|
||||
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_2 = 4;
|
||||
static const u8 TEXTURE_FXAA = 5;
|
||||
static const u8 TEXTURE_BLOOM_DOWN = 10;
|
||||
static const u8 TEXTURE_BLOOM_UP = 20;
|
||||
static const u8 TEXTURE_VOLUME = 6;
|
||||
static const u8 TEXTURE_SCALE_DOWN = 10;
|
||||
static const u8 TEXTURE_SCALE_UP = 20;
|
||||
|
||||
// Super-sampling is simply rendering into a larger texture.
|
||||
// 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_ssaa = antialiasing == "ssaa";
|
||||
const bool enable_fxaa = antialiasing == "fxaa";
|
||||
const bool enable_volumetric_light = g_settings->getBool("enable_volumetric_lighting") && enable_bloom;
|
||||
|
||||
if (enable_ssaa) {
|
||||
u16 ssaa_scale = MYMAX(2, g_settings->getU16("fsaa"));
|
||||
@ -160,9 +162,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
||||
|
||||
v2f downscale = scale * 0.5;
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -171,20 +173,30 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
||||
|
||||
// get bright spots
|
||||
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->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, 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
|
||||
shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
||||
auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source });
|
||||
step->setRenderSource(buffer);
|
||||
step->setBilinearFilter(0, true);
|
||||
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM_DOWN + i));
|
||||
source = TEXTURE_BLOOM_DOWN + i;
|
||||
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_SCALE_DOWN + i));
|
||||
source = TEXTURE_SCALE_DOWN + i;
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,19 +205,19 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
||||
// upsample
|
||||
shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||
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->setBilinearFilter(0, true);
|
||||
step->setBilinearFilter(1, true);
|
||||
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, u8(TEXTURE_BLOOM_UP + i - 1)));
|
||||
source = TEXTURE_BLOOM_UP + i - 1;
|
||||
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, u8(TEXTURE_SCALE_UP + i - 1)));
|
||||
source = TEXTURE_SCALE_UP + i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic Exposure pt2
|
||||
if (enable_auto_exposure) {
|
||||
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->setRenderSource(buffer);
|
||||
update_exposure->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_EXPOSURE_2));
|
||||
@ -228,7 +240,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
||||
|
||||
// final merge
|
||||
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);
|
||||
if (enable_ssaa)
|
||||
effect->setBilinearFilter(0, true);
|
||||
|
@ -249,8 +249,10 @@ void RenderingEngine::draw_load_screen(const std::wstring &text,
|
||||
#ifndef __ANDROID__
|
||||
const core::dimension2d<u32> &img_size =
|
||||
progress_img_bg->getSize();
|
||||
u32 imgW = rangelim(img_size.Width, 200, 600) * getDisplayDensity();
|
||||
u32 imgH = rangelim(img_size.Height, 24, 72) * getDisplayDensity();
|
||||
float density = g_settings->getFloat("gui_scaling", 0.5f, 20.0f) *
|
||||
getDisplayDensity();
|
||||
u32 imgW = rangelim(img_size.Width, 200, 600) * density;
|
||||
u32 imgH = rangelim(img_size.Height, 24, 72) * density;
|
||||
#else
|
||||
const core::dimension2d<u32> img_size(256, 48);
|
||||
float imgRatio = (float)img_size.Height / img_size.Width;
|
||||
|
@ -138,6 +138,17 @@ public:
|
||||
const irr::core::dimension2d<u32> initial_screen_size,
|
||||
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:
|
||||
v2u32 _getWindowSize() const;
|
||||
|
||||
|
@ -767,6 +767,13 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
|
||||
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
|
||||
|
||||
std::string common_header = shaders_header.str();
|
||||
|
@ -120,6 +120,9 @@ public:
|
||||
void setFogStart(float fog_start) { m_sky_params.fog_start = 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:
|
||||
aabb3f m_box;
|
||||
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
|
||||
};
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "playing_sound.h"
|
||||
|
||||
#include "al_extensions.h"
|
||||
#include "debug.h"
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
@ -32,7 +33,8 @@ namespace sound {
|
||||
|
||||
PlayingSound::PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> data,
|
||||
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_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_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
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);
|
||||
setPitch(pitch);
|
||||
|
@ -25,6 +25,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
|
||||
#pragma once
|
||||
|
||||
#include "sound_data.h"
|
||||
namespace sound { struct ALExtensions; }
|
||||
|
||||
namespace sound {
|
||||
|
||||
@ -51,7 +52,8 @@ class PlayingSound final
|
||||
public:
|
||||
PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> data, 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]]);
|
||||
|
||||
~PlayingSound() noexcept
|
||||
{
|
||||
|
@ -25,6 +25,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
|
||||
#include "sound_data.h"
|
||||
|
||||
#include "sound_constants.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace sound {
|
||||
|
||||
|
@ -181,7 +181,7 @@ std::shared_ptr<PlayingSound> OpenALSoundManager::createPlayingSound(
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@ -271,7 +271,8 @@ OpenALSoundManager::OpenALSoundManager(SoundManagerSingleton *smg,
|
||||
Thread("OpenALSoundManager"),
|
||||
m_fallback_path_provider(std::move(fallback_path_provider)),
|
||||
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);
|
||||
|
||||
|
@ -25,6 +25,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
|
||||
#pragma once
|
||||
|
||||
#include "playing_sound.h"
|
||||
#include "al_extensions.h"
|
||||
#include "sound_constants.h"
|
||||
#include "sound_manager_messages.h"
|
||||
#include "../sound.h"
|
||||
@ -51,8 +52,10 @@ class OpenALSoundManager final : public Thread
|
||||
private:
|
||||
std::unique_ptr<SoundFallbackPathProvider> m_fallback_path_provider;
|
||||
|
||||
ALCdevice *m_device;
|
||||
ALCcontext *m_context;
|
||||
ALCdevice *const m_device;
|
||||
ALCcontext *const m_context;
|
||||
|
||||
const ALExtensions m_exts;
|
||||
|
||||
// time in seconds until which removeDeadSounds will be called again
|
||||
f32 m_time_until_dead_removal = REMOVE_DEAD_SOUNDS_INTERVAL;
|
||||
|
@ -1598,6 +1598,13 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
||||
u32 frame_count = 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){
|
||||
errorstream<<"generateImagePart(): baseimg != NULL "
|
||||
<<"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 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> tile_dim(v2u32(img_dim) / v2u32(w0, h0));
|
||||
|
||||
|
@ -33,11 +33,13 @@ public:
|
||||
f32 real_gui_scaling;
|
||||
f32 real_hud_scaling;
|
||||
v2f32 max_fs_size;
|
||||
bool touch_controls;
|
||||
|
||||
bool equal(const ClientDynamicInfo &other) const {
|
||||
return render_target_size == other.render_target_size &&
|
||||
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
|
||||
@ -48,10 +50,16 @@ public:
|
||||
f32 hud_scaling = g_settings->getFloat("hud_scaling", 0.5f, 20.0f);
|
||||
f32 real_gui_scaling = gui_scaling * density;
|
||||
f32 real_hud_scaling = hud_scaling * density;
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
bool touch_controls = true;
|
||||
#else
|
||||
bool touch_controls = false;
|
||||
#endif
|
||||
|
||||
return {
|
||||
screen_size, real_gui_scaling, real_hud_scaling,
|
||||
ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling)
|
||||
ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling),
|
||||
touch_controls
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -59,6 +59,7 @@ RemoteClient::RemoteClient() :
|
||||
g_settings->getFloat("full_block_send_enable_min_time_from_building")),
|
||||
m_max_send_distance(g_settings->getS16("max_block_send_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_occ_cull(g_settings->getBool("server_side_occlusion_culling"))
|
||||
{
|
||||
@ -225,7 +226,10 @@ void RemoteClient::GetNextBlocks (
|
||||
wanted_range);
|
||||
const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, prop_zoom_fov),
|
||||
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),
|
||||
wanted_range);
|
||||
@ -258,10 +262,9 @@ void RemoteClient::GetNextBlocks (
|
||||
Get the border/face dot coordinates of a "d-radiused"
|
||||
box
|
||||
*/
|
||||
std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
|
||||
const auto &list = FacePositionCache::getFacePositions(d);
|
||||
|
||||
std::vector<v3s16>::iterator li;
|
||||
for (li = list.begin(); li != list.end(); ++li) {
|
||||
for (auto li = list.begin(); li != list.end(); ++li) {
|
||||
v3s16 p = *li + center;
|
||||
|
||||
/*
|
||||
@ -347,6 +350,7 @@ void RemoteClient::GetNextBlocks (
|
||||
if (!block->getIsUnderground() && !block->getDayNightDiff())
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Check occlusion cache first.
|
||||
@ -354,12 +358,14 @@ void RemoteClient::GetNextBlocks (
|
||||
if (m_blocks_occ.find(p) != m_blocks_occ.end())
|
||||
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.
|
||||
*/
|
||||
if (m_occ_cull &&
|
||||
env->getMap().isBlockOccluded(p * MAP_BLOCKSIZE, cam_pos_nodes, d >= d_cull_opt)) {
|
||||
m_blocks_occ.insert(p);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If block has been marked to not exist on disk (dummy) or is
|
||||
|
@ -397,6 +397,7 @@ private:
|
||||
const float m_min_time_from_building;
|
||||
const s16 m_max_send_distance;
|
||||
const s16 m_block_optimize_distance;
|
||||
const s16 m_block_cull_optimize_distance;
|
||||
const s16 m_max_gen_distance;
|
||||
const bool m_occ_cull;
|
||||
|
||||
|
@ -108,10 +108,8 @@ bool parseModContents(ModSpec &spec)
|
||||
if (info.exists("depends")) {
|
||||
mod_conf_has_depends = true;
|
||||
std::string dep = info.get("depends");
|
||||
// clang-format off
|
||||
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
||||
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
||||
// clang-format on
|
||||
for (const auto &dependency : str_split(dep, ',')) {
|
||||
spec.depends.insert(dependency);
|
||||
}
|
||||
@ -120,10 +118,8 @@ bool parseModContents(ModSpec &spec)
|
||||
if (info.exists("optional_depends")) {
|
||||
mod_conf_has_depends = true;
|
||||
std::string dep = info.get("optional_depends");
|
||||
// clang-format off
|
||||
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
||||
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
||||
// clang-format on
|
||||
for (const auto &dependency : str_split(dep, ',')) {
|
||||
spec.optdepends.insert(dependency);
|
||||
}
|
||||
|
@ -942,8 +942,8 @@ void ModStorageDatabaseSQLite3::listMods(std::vector<std::string> *res)
|
||||
return 0;
|
||||
}, (void *) res, &errmsg);
|
||||
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);
|
||||
throw e;
|
||||
throw DatabaseException(msg);
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ void set_default_settings()
|
||||
settings->setDefault("sound_volume", "0.8");
|
||||
settings->setDefault("sound_volume_unfocused", "0.3");
|
||||
settings->setDefault("mute_sound", "false");
|
||||
settings->setDefault("sound_extensions_blacklist", "");
|
||||
settings->setDefault("enable_mesh_cache", "false");
|
||||
settings->setDefault("mesh_generation_interval", "0");
|
||||
settings->setDefault("mesh_generation_threads", "0");
|
||||
@ -264,12 +265,14 @@ void set_default_settings()
|
||||
settings->setDefault("enable_waving_plants", "false");
|
||||
settings->setDefault("exposure_compensation", "0.0");
|
||||
settings->setDefault("enable_auto_exposure", "false");
|
||||
settings->setDefault("debanding", "true");
|
||||
settings->setDefault("antialiasing", "none");
|
||||
settings->setDefault("enable_bloom", "false");
|
||||
settings->setDefault("enable_bloom_debug", "false");
|
||||
settings->setDefault("bloom_strength_factor", "1.0");
|
||||
settings->setDefault("bloom_intensity", "0.05");
|
||||
settings->setDefault("bloom_radius", "1");
|
||||
settings->setDefault("enable_volumetric_lighting", "false");
|
||||
|
||||
// Effects Shadows
|
||||
settings->setDefault("enable_dynamic_shadows", "false");
|
||||
@ -364,6 +367,7 @@ void set_default_settings()
|
||||
settings->setDefault("max_packets_per_iteration", "1024");
|
||||
settings->setDefault("port", "30000");
|
||||
settings->setDefault("strict_protocol_version_checking", "false");
|
||||
settings->setDefault("protocol_version_min", "1");
|
||||
settings->setDefault("player_transfer_distance", "0");
|
||||
settings->setDefault("max_simultaneous_block_sends_per_client", "40");
|
||||
settings->setDefault("time_send_interval", "5");
|
||||
@ -393,6 +397,7 @@ void set_default_settings()
|
||||
// This causes frametime jitter on client side, or does it?
|
||||
settings->setDefault("max_block_send_distance", "12");
|
||||
settings->setDefault("block_send_optimize_distance", "4");
|
||||
settings->setDefault("block_cull_optimize_distance", "25");
|
||||
settings->setDefault("server_side_occlusion_culling", "true");
|
||||
settings->setDefault("csm_restriction_flags", "62");
|
||||
settings->setDefault("csm_restriction_noderange", "0");
|
||||
@ -498,6 +503,7 @@ void set_default_settings()
|
||||
settings->setDefault("active_block_range", "2");
|
||||
settings->setDefault("viewing_range", "50");
|
||||
settings->setDefault("leaves_style", "simple");
|
||||
settings->setDefault("debanding", "false");
|
||||
settings->setDefault("curl_verify_cert", "false");
|
||||
|
||||
// Apply settings according to screen size
|
||||
|
109
src/gettext.cpp
109
src/gettext.cpp
@ -25,6 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "util/string.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define setenv(n,v,o) _putenv_s(n,v)
|
||||
#endif
|
||||
|
||||
#if USE_GETTEXT && defined(_MSC_VER)
|
||||
#include <windows.h>
|
||||
#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;
|
||||
|
||||
/******************************************************************************/
|
||||
BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr)
|
||||
static BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr)
|
||||
{
|
||||
char* endptr = 0;
|
||||
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 */
|
||||
if (raw_shortname == NULL) return NULL;
|
||||
@ -102,9 +107,9 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) {
|
||||
|
||||
last_raw_value = shortname;
|
||||
|
||||
if (glb_supported_locales.find(utf8_to_wide(shortname)) != glb_supported_locales.end()) {
|
||||
last_full_name = wide_to_utf8(
|
||||
glb_supported_locales[utf8_to_wide(shortname)]);
|
||||
auto key = utf8_to_wide(shortname);
|
||||
if (glb_supported_locales.find(key) != glb_supported_locales.end()) {
|
||||
last_full_name = wide_to_utf8(glb_supported_locales[key]);
|
||||
return last_full_name.c_str();
|
||||
}
|
||||
|
||||
@ -114,37 +119,8 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) {
|
||||
return "";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
void init_gettext(const char *path, const std::string &configured_language,
|
||||
int argc, char *argv[])
|
||||
static void MSVC_LocaleWorkaround()
|
||||
{
|
||||
#if USE_GETTEXT
|
||||
// First, try to set user override environment
|
||||
if (!configured_language.empty()) {
|
||||
#ifndef _WIN32
|
||||
// Add user specified locale to environment
|
||||
setenv("LANGUAGE", configured_language.c_str(), 1);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
setenv("LANG", configured_language.c_str(), 1);
|
||||
#endif
|
||||
|
||||
// Reload locale with changed environment
|
||||
setlocale(LC_ALL, "");
|
||||
#elif defined(_MSC_VER)
|
||||
std::string current_language;
|
||||
const char *env_lang = getenv("LANGUAGE");
|
||||
if (env_lang)
|
||||
current_language = env_lang;
|
||||
|
||||
_putenv(("LANGUAGE=" + configured_language).c_str());
|
||||
SetEnvironmentVariableA("LANGUAGE", configured_language.c_str());
|
||||
|
||||
#ifndef SERVER
|
||||
// Hack to force gettext to see the right environment
|
||||
if (current_language != configured_language) {
|
||||
errorstream << "MSVC localization workaround active. "
|
||||
"Restarting " PROJECT_NAME_C " in a new environment!" << std::endl;
|
||||
|
||||
@ -189,6 +165,37 @@ void init_gettext(const char *path, const std::string &configured_language,
|
||||
errorstream << "*******************************************************" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
void init_gettext(const char *path, const std::string &configured_language,
|
||||
int argc, char *argv[])
|
||||
{
|
||||
#if USE_GETTEXT
|
||||
// First, try to set user override environment
|
||||
if (!configured_language.empty()) {
|
||||
// Set LANGUAGE which overrides all others, see
|
||||
// <https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html>
|
||||
#ifndef _MSC_VER
|
||||
setenv("LANGUAGE", configured_language.c_str(), 1);
|
||||
|
||||
// Reload locale with changed environment
|
||||
setlocale(LC_ALL, "");
|
||||
#else
|
||||
std::string current_language;
|
||||
const char *env_lang = getenv("LANGUAGE");
|
||||
if (env_lang)
|
||||
current_language = env_lang;
|
||||
|
||||
setenv("LANGUAGE", configured_language.c_str(), 1);
|
||||
SetEnvironmentVariableA("LANGUAGE", configured_language.c_str());
|
||||
|
||||
#ifndef SERVER
|
||||
// Hack to force gettext to see the right environment
|
||||
if (current_language != configured_language)
|
||||
MSVC_LocaleWorkaround();
|
||||
#else
|
||||
errorstream << "*******************************************************" << 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
|
||||
|
||||
setlocale(LC_ALL, configured_language.c_str());
|
||||
#else // Mingw
|
||||
_putenv(("LANGUAGE=" + configured_language).c_str());
|
||||
setlocale(LC_ALL, "");
|
||||
#endif // ifndef _WIN32
|
||||
}
|
||||
else {
|
||||
#ifdef __ANDROID__
|
||||
setenv("LANG", porting::getLanguageAndroid().c_str(), 1);
|
||||
#endif
|
||||
#endif // ifdef _MSC_VER
|
||||
} else {
|
||||
/* set current system default locale */
|
||||
setlocale(LC_ALL, "");
|
||||
}
|
||||
@ -228,18 +228,13 @@ void init_gettext(const char *path, const std::string &configured_language,
|
||||
bindtextdomain(name.c_str(), path);
|
||||
textdomain(name.c_str());
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Set character encoding for Win32
|
||||
char *tdomain = textdomain( (char *) NULL );
|
||||
if( tdomain == NULL )
|
||||
{
|
||||
errorstream << "Warning: domainname parameter is the null pointer" <<
|
||||
", default domain is not set" << std::endl;
|
||||
tdomain = (char *) "messages";
|
||||
}
|
||||
/* char *codeset = */bind_textdomain_codeset( tdomain, "UTF-8" );
|
||||
//errorstream << "Gettext debug: domainname = " << tdomain << "; codeset = "<< codeset << std::endl;
|
||||
#endif // defined(_WIN32)
|
||||
#ifdef _WIN32
|
||||
// set character encoding
|
||||
char *tdomain = textdomain(nullptr);
|
||||
assert(tdomain);
|
||||
if (tdomain)
|
||||
bind_textdomain_codeset(tdomain, "UTF-8");
|
||||
#endif
|
||||
|
||||
#else
|
||||
/* set current system default locale */
|
||||
@ -247,7 +242,7 @@ void init_gettext(const char *path, const std::string &configured_language,
|
||||
#endif // if USE_GETTEXT
|
||||
|
||||
/* 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");
|
||||
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)
|
||||
{
|
||||
// clang-format off
|
||||
if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
|
||||
s32 lineNo = getLineFromPos(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;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
return false;
|
||||
}
|
||||
|
||||
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)) {
|
||||
s32 lineNo = getLineFromPos(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;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -265,7 +265,7 @@ void GUIEngine::run()
|
||||
f32 dtime = 0.0f;
|
||||
|
||||
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();
|
||||
@ -293,6 +293,7 @@ void GUIEngine::run()
|
||||
drawHeader(driver);
|
||||
|
||||
driver->endScene();
|
||||
}
|
||||
|
||||
IrrlichtDevice *device = m_rendering_engine->get_raw_device();
|
||||
|
||||
|
@ -3497,29 +3497,34 @@ void GUIFormSpecMenu::legacySortElements(std::list<IGUIElement *>::iterator from
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
bool GUIFormSpecMenu::getAndroidUIInput()
|
||||
void GUIFormSpecMenu::getAndroidUIInput()
|
||||
{
|
||||
if (!hasAndroidUIInput())
|
||||
return false;
|
||||
porting::AndroidDialogState dialogState = getAndroidUIInputState();
|
||||
if (dialogState == porting::DIALOG_SHOWN) {
|
||||
return;
|
||||
} else if (dialogState == porting::DIALOG_CANCELED) {
|
||||
m_jni_field_name.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// still waiting
|
||||
if (porting::getInputDialogState() == -1)
|
||||
return true;
|
||||
porting::AndroidDialogType dialog_type = porting::getLastInputDialogType();
|
||||
|
||||
std::string fieldname = m_jni_field_name;
|
||||
m_jni_field_name.clear();
|
||||
|
||||
for (const FieldSpec &field : m_fields) {
|
||||
if (field.fname != fieldname)
|
||||
continue;
|
||||
continue; // Iterate until found
|
||||
|
||||
IGUIElement *element = getElementFromId(field.fid, true);
|
||||
|
||||
if (!element || element->getType() != irr::gui::EGUIET_EDIT_BOX)
|
||||
return false;
|
||||
if (!element)
|
||||
return;
|
||||
|
||||
auto element_type = element->getType();
|
||||
if (dialog_type == porting::TEXT_INPUT && element_type == irr::gui::EGUIET_EDIT_BOX) {
|
||||
gui::IGUIEditBox *editbox = (gui::IGUIEditBox *)element;
|
||||
std::string text = porting::getInputDialogValue();
|
||||
std::string text = porting::getInputDialogMessage();
|
||||
editbox->setText(utf8_to_wide(text).c_str());
|
||||
|
||||
bool enter_after_edit = false;
|
||||
@ -3535,8 +3540,15 @@ bool GUIFormSpecMenu::getAndroidUIInput()
|
||||
enter.GUIEvent.EventType = gui::EGET_EDITBOX_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
|
||||
|
||||
@ -3656,22 +3668,18 @@ void GUIFormSpecMenu::drawMenu()
|
||||
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
|
||||
*/
|
||||
gui::IGUIElement *hovered =
|
||||
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
|
||||
|
||||
#ifndef HAVE_TOUCHSCREENGUI
|
||||
gui::ICursorControl *cursor_control = RenderingEngine::get_raw_device()->
|
||||
getCursorControl();
|
||||
gui::ECURSOR_ICON current_cursor_icon = cursor_control->getActiveIcon();
|
||||
#endif
|
||||
gui::ECURSOR_ICON current_cursor_icon = gui::ECI_NORMAL;
|
||||
if (cursor_control)
|
||||
current_cursor_icon = cursor_control->getActiveIcon();
|
||||
|
||||
bool hovered_element_found = false;
|
||||
|
||||
if (hovered) {
|
||||
@ -3715,11 +3723,10 @@ void GUIFormSpecMenu::drawMenu()
|
||||
m_tooltips[field.fname].bgcolor);
|
||||
}
|
||||
|
||||
#ifndef HAVE_TOUCHSCREENGUI
|
||||
if (field.ftype != f_HyperText && // Handled directly in guiHyperText
|
||||
if (cursor_control &&
|
||||
field.ftype != f_HyperText && // Handled directly in guiHyperText
|
||||
current_cursor_icon != field.fcursor_icon)
|
||||
cursor_control->setActiveIcon(field.fcursor_icon);
|
||||
#endif
|
||||
|
||||
hovered_element_found = true;
|
||||
|
||||
@ -3730,10 +3737,8 @@ void GUIFormSpecMenu::drawMenu()
|
||||
|
||||
if (!hovered_element_found) {
|
||||
// no element is hovered
|
||||
#ifndef HAVE_TOUCHSCREENGUI
|
||||
if (current_cursor_icon != ECI_NORMAL)
|
||||
if (cursor_control && current_cursor_icon != ECI_NORMAL)
|
||||
cursor_control->setActiveIcon(ECI_NORMAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_tooltip_element->draw();
|
||||
@ -3764,16 +3769,13 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text,
|
||||
v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
|
||||
int tooltip_offset_x = m_btn_height;
|
||||
int tooltip_offset_y = m_btn_height;
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
|
||||
if (m_pointer_type == PointerType::Touch) {
|
||||
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.X == 0)
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Calculate and set the tooltip position
|
||||
s32 tooltip_x = m_pointer.X + tooltip_offset_x;
|
||||
@ -4070,6 +4072,11 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode)
|
||||
|
||||
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
|
||||
// font, which we override for the duration of form drawing,
|
||||
// but computes tab hotspots based on how it would have rendered
|
||||
@ -4147,7 +4154,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
|
||||
return handled;
|
||||
}
|
||||
|
||||
return GUIModalMenu::preprocessEvent(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void GUIFormSpecMenu::tryClose()
|
||||
@ -4326,14 +4333,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
// The second touch (see GUIModalMenu::preprocessEvent() function)
|
||||
ButtonEventType touch = BET_OTHER;
|
||||
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
|
||||
if (event.TouchInput.Event == ETIE_LEFT_UP)
|
||||
touch = BET_RIGHT;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set this number to a positive value to generate a move action
|
||||
// from m_selected_item to s.
|
||||
@ -4678,7 +4683,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (touch == BET_RIGHT && m_selected_item && !m_left_dragging) {
|
||||
if (!s.isValid()) {
|
||||
// Not a valid slot
|
||||
@ -4698,7 +4702,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update left-dragged slots
|
||||
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)
|
||||
return true; // Stop propagating the event
|
||||
#endif
|
||||
|
||||
return Parent ? Parent->OnEvent(event) : false;
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ public:
|
||||
core::rect<s32> getAbsoluteRect();
|
||||
|
||||
#ifdef __ANDROID__
|
||||
bool getAndroidUIInput();
|
||||
void getAndroidUIInput();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
@ -1052,14 +1052,10 @@ void GUIHyperText::checkHover(s32 X, s32 Y)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_TOUCHSCREENGUI
|
||||
if (m_drawer.m_hovertag)
|
||||
RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon(
|
||||
gui::ECI_HAND);
|
||||
else
|
||||
RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon(
|
||||
gui::ECI_NORMAL);
|
||||
#endif
|
||||
ICursorControl *cursor_control = RenderingEngine::get_raw_device()->getCursorControl();
|
||||
|
||||
if (cursor_control)
|
||||
cursor_control->setActiveIcon(m_drawer.m_hovertag ? gui::ECI_HAND : gui::ECI_NORMAL);
|
||||
}
|
||||
|
||||
bool GUIHyperText::OnEvent(const SEvent &event)
|
||||
@ -1075,12 +1071,11 @@ bool GUIHyperText::OnEvent(const SEvent &event)
|
||||
if (event.EventType == EET_GUI_EVENT &&
|
||||
event.GUIEvent.EventType == EGET_ELEMENT_LEFT) {
|
||||
m_drawer.m_hovertag = nullptr;
|
||||
#ifndef HAVE_TOUCHSCREENGUI
|
||||
gui::ICursorControl *cursor_control =
|
||||
RenderingEngine::get_raw_device()->getCursorControl();
|
||||
if (cursor_control->isVisible())
|
||||
|
||||
ICursorControl *cursor_control = RenderingEngine::get_raw_device()->getCursorControl();
|
||||
|
||||
if (cursor_control && cursor_control->isVisible())
|
||||
cursor_control->setActiveIcon(gui::ECI_NORMAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (event.EventType == EET_MOUSE_INPUT_EVENT) {
|
||||
|
@ -152,10 +152,10 @@ void GUIInventoryList::draw()
|
||||
|
||||
// Add hovering tooltip
|
||||
bool show_tooltip = !item.empty() && hovering && !selected_item;
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
// Make it possible to see item tooltips on touchscreens
|
||||
if (m_fs_menu->getPointerType() == PointerType::Touch) {
|
||||
show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0;
|
||||
#endif
|
||||
}
|
||||
if (show_tooltip) {
|
||||
std::string tooltip = orig_item.getDescription(client->idef());
|
||||
if (m_fs_menu->doTooltipAppendItemname())
|
||||
|
@ -199,14 +199,12 @@ bool GUIPasswordChange::processInput()
|
||||
bool GUIPasswordChange::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (event.EventType == EET_KEY_INPUT_EVENT) {
|
||||
// clang-format off
|
||||
if ((event.KeyInput.Key == KEY_ESCAPE ||
|
||||
event.KeyInput.Key == KEY_CANCEL) &&
|
||||
event.KeyInput.PressedDown) {
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
// clang-format on
|
||||
if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
|
||||
acceptInput();
|
||||
if (processInput())
|
||||
@ -266,14 +264,19 @@ std::string GUIPasswordChange::getNameByID(s32 id)
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
bool GUIPasswordChange::getAndroidUIInput()
|
||||
void GUIPasswordChange::getAndroidUIInput()
|
||||
{
|
||||
if (!hasAndroidUIInput())
|
||||
return false;
|
||||
porting::AndroidDialogState dialogState = getAndroidUIInputState();
|
||||
if (dialogState == porting::DIALOG_SHOWN) {
|
||||
return;
|
||||
} else if (dialogState == porting::DIALOG_CANCELED) {
|
||||
m_jni_field_name.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// still waiting
|
||||
if (porting::getInputDialogState() == -1)
|
||||
return true;
|
||||
// It has to be a text input
|
||||
if (porting::getLastInputDialogType() != porting::TEXT_INPUT)
|
||||
return;
|
||||
|
||||
gui::IGUIElement *e = nullptr;
|
||||
if (m_jni_field_name == "old_password")
|
||||
@ -285,10 +288,10 @@ bool GUIPasswordChange::getAndroidUIInput()
|
||||
m_jni_field_name.clear();
|
||||
|
||||
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());
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
@ -45,7 +45,7 @@ public:
|
||||
|
||||
bool OnEvent(const SEvent &event);
|
||||
#ifdef __ANDROID__
|
||||
bool getAndroidUIInput();
|
||||
void getAndroidUIInput();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
@ -155,7 +155,6 @@ bool GUIScrollBar::OnEvent(const SEvent &event)
|
||||
if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
|
||||
is_dragging = false;
|
||||
|
||||
// clang-format off
|
||||
if (!dragged_by_slider) {
|
||||
if (is_inside) {
|
||||
dragged_by_slider = slider_rect.isPointInside(p);
|
||||
@ -167,7 +166,6 @@ bool GUIScrollBar::OnEvent(const SEvent &event)
|
||||
return is_inside;
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
const s32 new_pos = getPosFromMousePos(p);
|
||||
const s32 old_pos = scroll_pos;
|
||||
|
@ -60,7 +60,7 @@ GUITable::GUITable(gui::IGUIEnvironment *env,
|
||||
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,
|
||||
core::rect<s32>(RelativeRect.getWidth() - s,
|
||||
0,
|
||||
@ -77,18 +77,6 @@ GUITable::GUITable(gui::IGUIEnvironment *env,
|
||||
setTabStop(true);
|
||||
setTabOrder(-1);
|
||||
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()
|
||||
|
@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <IEventReceiver.h>
|
||||
#include "client/renderingengine.h"
|
||||
#include "modalMenu.h"
|
||||
#include "gettext.h"
|
||||
@ -29,7 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "touchscreengui.h"
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
||||
s32 id, IMenuManager *menumgr, bool remap_dbl_click) :
|
||||
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
|
||||
@ -51,13 +51,9 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
||||
setVisible(true);
|
||||
m_menumgr->createdMenu(this);
|
||||
|
||||
m_doubleclickdetect[0].time = 0;
|
||||
m_doubleclickdetect[1].time = 0;
|
||||
|
||||
m_doubleclickdetect[0].pos = v2s32(0, 0);
|
||||
m_doubleclickdetect[1].pos = v2s32(0, 0);
|
||||
m_last_touch.time = 0;
|
||||
m_last_touch.pos = v2s32(0, 0);
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
GUIModalMenu::~GUIModalMenu()
|
||||
{
|
||||
@ -105,13 +101,23 @@ void GUIModalMenu::quitMenu()
|
||||
m_menumgr->deletingMenu(this);
|
||||
this->remove();
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui && m_touchscreen_visible)
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->show();
|
||||
#endif
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
|
||||
static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
|
||||
{
|
||||
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
|
||||
* and translating the double-click into an EET_KEY_INPUT_EVENT event
|
||||
@ -126,27 +132,18 @@ bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
|
||||
if (!m_remap_dbl_click)
|
||||
return false;
|
||||
|
||||
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||
m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos;
|
||||
m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
|
||||
|
||||
m_doubleclickdetect[1].pos = m_pointer;
|
||||
m_doubleclickdetect[1].time = porting::getTimeMs();
|
||||
} else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
|
||||
u64 delta = porting::getDeltaMs(
|
||||
m_doubleclickdetect[0].time, porting::getTimeMs());
|
||||
if (delta > 400)
|
||||
if (event.EventType != EET_MOUSE_INPUT_EVENT ||
|
||||
event.MouseInput.Event != EMIE_LMOUSE_DOUBLE_CLICK)
|
||||
return false;
|
||||
|
||||
double squaredistance = m_doubleclickdetect[0].pos.
|
||||
getDistanceFromSQ(m_doubleclickdetect[1].pos);
|
||||
|
||||
if (squaredistance > (30 * 30)) {
|
||||
// Only exit if the double-click happened outside the menu.
|
||||
gui::IGUIElement *hovered =
|
||||
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
|
||||
if (isChild(hovered, this))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Translate double-click to escape.
|
||||
SEvent translated{};
|
||||
// translate doubleclick to escape
|
||||
translated.EventType = EET_KEY_INPUT_EVENT;
|
||||
translated.KeyInput.Key = KEY_ESCAPE;
|
||||
translated.KeyInput.Control = false;
|
||||
@ -158,26 +155,14 @@ bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
|
||||
bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try)
|
||||
{
|
||||
while (tocheck) {
|
||||
if (tocheck == parent) {
|
||||
return true;
|
||||
}
|
||||
tocheck = tocheck->getParent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
IGUIElement *target;
|
||||
if (!second_try)
|
||||
target = Environment->getFocus();
|
||||
else
|
||||
target = m_touch_hovered.get();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
|
||||
bool GUIModalMenu::simulateMouseEvent(
|
||||
gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event)
|
||||
{
|
||||
SEvent mouse_event{}; // value-initialized, not unitialized
|
||||
mouse_event.EventType = EET_MOUSE_INPUT_EVENT;
|
||||
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.ButtonStates = 0;
|
||||
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:
|
||||
return false;
|
||||
}
|
||||
if (preprocessEvent(mouse_event))
|
||||
return true;
|
||||
if (!target)
|
||||
return false;
|
||||
return target->OnEvent(mouse_event);
|
||||
|
||||
bool retval;
|
||||
m_simulated_mouse = true;
|
||||
do {
|
||||
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)
|
||||
{
|
||||
if (!hovered)
|
||||
return;
|
||||
sanity_check(!m_hovered);
|
||||
m_hovered.grab(hovered);
|
||||
sanity_check(!m_touch_hovered);
|
||||
m_touch_hovered.grab(hovered);
|
||||
SEvent gui_event{};
|
||||
gui_event.EventType = EET_GUI_EVENT;
|
||||
gui_event.GUIEvent.Caller = m_hovered.get();
|
||||
gui_event.GUIEvent.EventType = EGET_ELEMENT_HOVERED;
|
||||
gui_event.GUIEvent.Caller = m_touch_hovered.get();
|
||||
gui_event.GUIEvent.EventType = gui::EGET_ELEMENT_HOVERED;
|
||||
gui_event.GUIEvent.Element = gui_event.GUIEvent.Caller;
|
||||
m_hovered->OnEvent(gui_event);
|
||||
m_touch_hovered->OnEvent(gui_event);
|
||||
}
|
||||
|
||||
void GUIModalMenu::leave()
|
||||
{
|
||||
if (!m_hovered)
|
||||
if (!m_touch_hovered)
|
||||
return;
|
||||
SEvent gui_event{};
|
||||
gui_event.EventType = EET_GUI_EVENT;
|
||||
gui_event.GUIEvent.Caller = m_hovered.get();
|
||||
gui_event.GUIEvent.EventType = EGET_ELEMENT_LEFT;
|
||||
m_hovered->OnEvent(gui_event);
|
||||
m_hovered.reset();
|
||||
gui_event.GUIEvent.Caller = m_touch_hovered.get();
|
||||
gui_event.GUIEvent.EventType = gui::EGET_ELEMENT_LEFT;
|
||||
m_touch_hovered->OnEvent(gui_event);
|
||||
m_touch_hovered.reset();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
// clang-format off
|
||||
// display software keyboard when clicking edit boxes
|
||||
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
|
||||
event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||
@ -266,36 +268,76 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
||||
if (((gui::IGUIEditBox *)hovered)->isPasswordBox())
|
||||
type = 3;
|
||||
|
||||
porting::showInputDialog(gettext("OK"), "",
|
||||
porting::showTextInputDialog("",
|
||||
wide_to_utf8(((gui::IGUIEditBox *) hovered)->getText()), type);
|
||||
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
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
// Convert touch events into mouse events.
|
||||
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
|
||||
irr_ptr<GUIModalMenu> holder;
|
||||
holder.grab(this); // keep this alive until return (it might be dropped downstream [?])
|
||||
|
||||
if (event.TouchInput.ID == 0) {
|
||||
if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED)
|
||||
if (event.TouchInput.touchedCount == 1) {
|
||||
m_pointer_type = PointerType::Touch;
|
||||
m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
|
||||
|
||||
gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d<s32>(m_pointer));
|
||||
if (event.TouchInput.Event == ETIE_PRESSED_DOWN)
|
||||
Environment->setFocus(hovered);
|
||||
if (m_hovered != hovered) {
|
||||
if (m_touch_hovered != hovered) {
|
||||
leave();
|
||||
enter(hovered);
|
||||
}
|
||||
gui::IGUIElement *focused = Environment->getFocus();
|
||||
bool ret = simulateMouseEvent(focused, event.TouchInput.Event);
|
||||
if (!ret && m_hovered != focused)
|
||||
ret = simulateMouseEvent(m_hovered.get(), event.TouchInput.Event);
|
||||
bool ret = simulateMouseEvent(event.TouchInput.Event);
|
||||
if (event.TouchInput.Event == ETIE_LEFT_UP)
|
||||
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;
|
||||
} else if (event.TouchInput.ID == 1) {
|
||||
} else if (event.TouchInput.touchedCount == 2) {
|
||||
if (event.TouchInput.Event != ETIE_LEFT_UP)
|
||||
return true; // ignore
|
||||
auto focused = Environment->getFocus();
|
||||
@ -311,40 +353,29 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (event.EventType == EET_MOUSE_INPUT_EVENT) {
|
||||
s32 x = event.MouseInput.X;
|
||||
s32 y = event.MouseInput.Y;
|
||||
gui::IGUIElement *hovered =
|
||||
Environment->getRootGUIElement()->getElementFromPoint(
|
||||
core::position2d<s32>(x, y));
|
||||
if (!isChild(hovered, this)) {
|
||||
if (DoubleClickDetection(event)) {
|
||||
if (!m_simulated_mouse) {
|
||||
// Only set the pointer type to mouse if this is a real mouse event.
|
||||
m_pointer_type = PointerType::Mouse;
|
||||
m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y);
|
||||
m_touch_hovered.reset();
|
||||
}
|
||||
|
||||
if (remapDoubleClick(event))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
bool GUIModalMenu::hasAndroidUIInput()
|
||||
porting::AndroidDialogState GUIModalMenu::getAndroidUIInputState()
|
||||
{
|
||||
// no dialog shown
|
||||
// No dialog is shown
|
||||
if (m_jni_field_name.empty())
|
||||
return false;
|
||||
return porting::DIALOG_CANCELED;
|
||||
|
||||
// still waiting
|
||||
if (porting::getInputDialogState() == -1)
|
||||
return true;
|
||||
|
||||
// no value abort dialog processing
|
||||
if (porting::getInputDialogState() != 0) {
|
||||
m_jni_field_name.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return porting::getInputDialogState();
|
||||
}
|
||||
#endif
|
||||
|
@ -22,6 +22,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "irr_ptr.h"
|
||||
#include "util/string.h"
|
||||
#ifdef __ANDROID__
|
||||
#include <porting_android.h>
|
||||
#endif
|
||||
|
||||
enum class PointerType {
|
||||
Mouse,
|
||||
Touch,
|
||||
};
|
||||
|
||||
class GUIModalMenu;
|
||||
|
||||
@ -54,42 +62,37 @@ public:
|
||||
virtual bool OnEvent(const SEvent &event) { return false; };
|
||||
virtual bool pausesGame() { return false; } // Used for pause menu
|
||||
#ifdef __ANDROID__
|
||||
virtual bool getAndroidUIInput() { return false; }
|
||||
bool hasAndroidUIInput();
|
||||
virtual void getAndroidUIInput() {};
|
||||
porting::AndroidDialogState getAndroidUIInputState();
|
||||
#endif
|
||||
|
||||
PointerType getPointerType() { return m_pointer_type; };
|
||||
|
||||
protected:
|
||||
virtual std::wstring getLabelByID(s32 id) = 0;
|
||||
virtual std::string getNameByID(s32 id) = 0;
|
||||
|
||||
/**
|
||||
* check if event is part of a double click
|
||||
* @param event event to evaluate
|
||||
* @return true/false if a doubleclick was detected
|
||||
*/
|
||||
bool DoubleClickDetection(const SEvent &event);
|
||||
|
||||
// Stores the last known pointer type.
|
||||
PointerType m_pointer_type = PointerType::Mouse;
|
||||
// Stores the last known pointer position.
|
||||
// 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.
|
||||
v2s32 m_pointer;
|
||||
v2s32 m_old_pointer; // Mouse position after previous mouse event
|
||||
|
||||
v2u32 m_screensize_old;
|
||||
float m_gui_scale;
|
||||
#ifdef __ANDROID__
|
||||
std::string m_jni_field_name;
|
||||
#endif
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
|
||||
// This is set to true if the menu is currently processing a second-touch event.
|
||||
bool m_second_touch = false;
|
||||
bool m_touchscreen_visible = true;
|
||||
#endif
|
||||
// This is set to true if the menu is currently processing a mouse event
|
||||
// that was synthesized by the menu itself from a touch event.
|
||||
bool m_simulated_mouse = false;
|
||||
|
||||
private:
|
||||
struct clickpos
|
||||
{
|
||||
v2s32 pos;
|
||||
s64 time;
|
||||
};
|
||||
clickpos m_doubleclickdetect[2];
|
||||
|
||||
IMenuManager *m_menumgr;
|
||||
/* 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.
|
||||
@ -98,15 +101,23 @@ private:
|
||||
* and the default value for the setting is true.
|
||||
*/
|
||||
bool m_remap_dbl_click;
|
||||
bool remapDoubleClick(const SEvent &event);
|
||||
|
||||
// This might be necessary to expose to the implementation if it
|
||||
// wants to launch other menus
|
||||
bool m_allow_focus_removal = false;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
irr_ptr<gui::IGUIElement> m_hovered;
|
||||
// Stuff related to touchscreen input
|
||||
|
||||
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 leave();
|
||||
#endif
|
||||
|
||||
// Used to detect double-taps and convert them into double-click events.
|
||||
struct {
|
||||
v2s32 pos;
|
||||
s64 time;
|
||||
} m_last_touch;
|
||||
};
|
||||
|
@ -832,7 +832,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
|
||||
const double d = g_settings->getFloat("touchscreen_sensitivity", 0.001f, 10.0f) * 3.0f;
|
||||
|
||||
m_camera_yaw_change -= dir_free.X * d;
|
||||
m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dir_free.Y * d), -180.0f), 180.0f);
|
||||
m_camera_pitch_change += dir_free.Y * d;
|
||||
|
||||
// update shootline
|
||||
// no need to update (X, Y) when using crosshair since the shootline is not used
|
||||
|
@ -171,7 +171,11 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
double getPitch() { return m_camera_pitch; }
|
||||
double getPitchChange() {
|
||||
double res = m_camera_pitch_change;
|
||||
m_camera_pitch_change = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a line which describes what the player is pointing at.
|
||||
@ -213,7 +217,7 @@ private:
|
||||
|
||||
// value in degree
|
||||
double m_camera_yaw_change = 0.0;
|
||||
double m_camera_pitch = 0.0;
|
||||
double m_camera_pitch_change = 0.0;
|
||||
|
||||
/**
|
||||
* A line starting at the camera and pointing towards the selected object.
|
||||
|
@ -392,7 +392,7 @@ bool ItemStack::itemFits(ItemStack newitem,
|
||||
return newitem.empty();
|
||||
}
|
||||
|
||||
bool ItemStack::stacksWith(ItemStack other) const
|
||||
bool ItemStack::stacksWith(const ItemStack &other) const
|
||||
{
|
||||
return (this->name == other.name &&
|
||||
this->wear == other.wear &&
|
||||
@ -777,20 +777,18 @@ u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
|
||||
return 0;
|
||||
|
||||
// Try to add the item to destination list
|
||||
u32 oldcount = item1.count;
|
||||
count = item1.count;
|
||||
item1 = dest->addItem(dest_i, item1);
|
||||
|
||||
// If something is returned, the item was not fully added
|
||||
if (!item1.empty()) {
|
||||
// If olditem is returned, nothing was added.
|
||||
bool nothing_added = (item1.count == oldcount);
|
||||
bool nothing_added = (item1.count == count);
|
||||
|
||||
// If something else is returned, part of the item was left unadded.
|
||||
// Add the other part back to the source item
|
||||
addItem(i, item1);
|
||||
// Add any leftover stack back to the source stack.
|
||||
item1.add(getItem(i).count); // leftover + source count
|
||||
changeItem(i, item1); // do NOT use addItem to allow oversized stacks!
|
||||
|
||||
// If olditem is returned, nothing was added.
|
||||
// Swap the items
|
||||
// Swap if no items could be moved
|
||||
if (nothing_added && swap_if_needed) {
|
||||
// Tell that we swapped
|
||||
if (did_swap != NULL) {
|
||||
@ -802,9 +800,10 @@ u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
|
||||
ItemStack item2 = dest->changeItem(dest_i, item1);
|
||||
// Put item from destination list to the source list
|
||||
changeItem(i, item2);
|
||||
item1.clear(); // no leftover
|
||||
}
|
||||
}
|
||||
return (oldcount - item1.count);
|
||||
return (count - item1.count);
|
||||
}
|
||||
|
||||
void InventoryList::checkResizeLock()
|
||||
|
@ -164,7 +164,7 @@ struct ItemStack
|
||||
|
||||
// Checks if another itemstack would stack with this one.
|
||||
// Does not check if the item actually fits in the stack.
|
||||
bool stacksWith(ItemStack other) const;
|
||||
bool stacksWith(const ItemStack &other) const;
|
||||
|
||||
// Takes some items.
|
||||
// If there are not enough, takes as many as it can.
|
||||
|
@ -144,9 +144,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
// ^ dislikes long lines
|
||||
|
||||
/** Constructs a shared pointer as a *secondary* reference to an object
|
||||
*
|
||||
* This function is intended to make a temporary reference to an object which
|
||||
@ -205,5 +202,3 @@ irr_ptr<T> make_irr(Args&&... args)
|
||||
{
|
||||
return irr_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
@ -32,8 +32,6 @@
|
||||
|
||||
#include <irrlicht.h>
|
||||
#include <iostream>
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include "CGUITTFont.h"
|
||||
|
||||
namespace irr
|
||||
@ -1251,21 +1249,31 @@ std::u32string CGUITTFont::convertWCharToU32String(const wchar_t* const charArra
|
||||
{
|
||||
static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "unexpected wchar size");
|
||||
|
||||
if (sizeof(wchar_t) == 4) // Systems where wchar_t is UTF-32
|
||||
if (sizeof(wchar_t) == 4) // wchar_t is UTF-32
|
||||
return std::u32string(reinterpret_cast<const char32_t*>(charArray));
|
||||
|
||||
// Systems where wchar_t is UTF-16:
|
||||
// First, convert to UTF-8
|
||||
std::u16string utf16String(reinterpret_cast<const char16_t*>(charArray));
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter1;
|
||||
std::string utf8String = converter1.to_bytes(utf16String);
|
||||
// wchar_t is UTF-16 and we need to convert.
|
||||
// std::codecvt could do this for us but aside from being deprecated,
|
||||
// it turns out that it's laughably slow on MSVC. Thanks Microsoft.
|
||||
|
||||
// Next, convert to UTF-32
|
||||
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter2;
|
||||
return converter2.from_bytes(utf8String);
|
||||
|
||||
// This is inefficient, but importantly it is _correct_, rather than a hand-rolled UTF-16 to
|
||||
// UTF-32 converter which may or may not be correct.
|
||||
std::u32string ret;
|
||||
ret.reserve(wcslen(charArray));
|
||||
const wchar_t *p = charArray;
|
||||
while (*p) {
|
||||
char32_t c = *p;
|
||||
if (c >= 0xD800 && c < 0xDC00) {
|
||||
p++;
|
||||
char32_t c2 = *p;
|
||||
if (!c2)
|
||||
break;
|
||||
else if (c2 < 0xDC00 || c2 > 0xDFFF)
|
||||
continue; // can't find low surrogate, skip
|
||||
c = 0x10000 + ( ((c & 0x3ff) << 10) | (c2 & 0x3ff) );
|
||||
}
|
||||
ret.push_back(c);
|
||||
p++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -53,4 +53,5 @@ struct Lighting
|
||||
AutoExposure exposure;
|
||||
float shadow_intensity {0.0f};
|
||||
float saturation {1.0f};
|
||||
float volumetric_light_strength {0.0f};
|
||||
};
|
||||
|
54
src/main.cpp
54
src/main.cpp
@ -95,7 +95,7 @@ static void set_allowed_options(OptionList *allowed_options);
|
||||
|
||||
static void print_help(const OptionList &allowed_options);
|
||||
static void print_allowed_options(const OptionList &allowed_options);
|
||||
static void print_version();
|
||||
static void print_version(std::ostream &os);
|
||||
static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
|
||||
std::ostream &os, bool print_name = true, bool print_path = true);
|
||||
static void print_modified_quicktune_values();
|
||||
@ -143,6 +143,8 @@ int main(int argc, char *argv[])
|
||||
g_logger.registerThread("Main");
|
||||
g_logger.addOutputMaxLevel(&stderr_output, LL_ACTION);
|
||||
|
||||
porting::osSpecificInit();
|
||||
|
||||
Settings cmd_args;
|
||||
get_env_opts(cmd_args);
|
||||
bool cmd_args_ok = get_cmdline_opts(argc, argv, &cmd_args);
|
||||
@ -158,10 +160,13 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (cmd_args.getFlag("version")) {
|
||||
porting::attachOrCreateConsole();
|
||||
print_version();
|
||||
print_version(std::cout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Debug handler
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
if (!setup_log_params(cmd_args))
|
||||
return 1;
|
||||
|
||||
@ -171,22 +176,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
porting::signal_handler_init();
|
||||
|
||||
#ifdef __ANDROID__
|
||||
porting::initAndroid();
|
||||
porting::initializePathsAndroid();
|
||||
#else
|
||||
porting::initializePaths();
|
||||
#endif
|
||||
|
||||
if (!create_userdata_path()) {
|
||||
errorstream << "Cannot create user data directory" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Debug handler
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
// List gameids if requested
|
||||
if (cmd_args.exists("gameid") && cmd_args.get("gameid") == "list") {
|
||||
list_game_ids();
|
||||
@ -215,9 +211,9 @@ int main(int argc, char *argv[])
|
||||
if (g_settings->getBool("enable_console"))
|
||||
porting::attachOrCreateConsole();
|
||||
|
||||
#ifndef __ANDROID__
|
||||
// Run unit tests
|
||||
if (cmd_args.getFlag("run-unittests")) {
|
||||
porting::attachOrCreateConsole();
|
||||
#if BUILD_UNITTESTS
|
||||
if (cmd_args.exists("test-module"))
|
||||
return run_tests(cmd_args.get("test-module")) ? 0 : 1;
|
||||
@ -233,8 +229,12 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Run benchmarks
|
||||
if (cmd_args.getFlag("run-benchmarks")) {
|
||||
porting::attachOrCreateConsole();
|
||||
#if BUILD_BENCHMARKS
|
||||
return run_benchmarks();
|
||||
if (cmd_args.exists("test-module"))
|
||||
return run_benchmarks(cmd_args.get("test-module").c_str()) ? 0 : 1;
|
||||
else
|
||||
return run_benchmarks() ? 0 : 1;
|
||||
#else
|
||||
errorstream << "Benchmark support is not enabled in this binary. "
|
||||
<< "If you want to enable it, compile project with BUILD_BENCHMARKS=1 flag."
|
||||
@ -242,7 +242,6 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
#endif // __ANDROID__
|
||||
|
||||
GameStartData game_params;
|
||||
#ifdef SERVER
|
||||
@ -340,7 +339,7 @@ static void set_allowed_options(OptionList *allowed_options)
|
||||
allowed_options->insert(std::make_pair("run-benchmarks", ValueSpec(VALUETYPE_FLAG,
|
||||
_("Run the benchmarks and exit"))));
|
||||
allowed_options->insert(std::make_pair("test-module", ValueSpec(VALUETYPE_STRING,
|
||||
_("Only run the specified test module"))));
|
||||
_("Only run the specified test module or benchmark"))));
|
||||
allowed_options->insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING,
|
||||
_("Same as --world (deprecated)"))));
|
||||
allowed_options->insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING,
|
||||
@ -427,19 +426,20 @@ static void print_allowed_options(const OptionList &allowed_options)
|
||||
}
|
||||
}
|
||||
|
||||
static void print_version()
|
||||
static void print_version(std::ostream &os)
|
||||
{
|
||||
std::cout << PROJECT_NAME_C " " << g_version_hash
|
||||
os << PROJECT_NAME_C " " << g_version_hash
|
||||
<< " (" << porting::getPlatformName() << ")" << std::endl;
|
||||
#ifndef SERVER
|
||||
std::cout << "Using Irrlicht " IRRLICHT_SDK_VERSION << std::endl;
|
||||
os << "Using Irrlicht " IRRLICHT_SDK_VERSION << std::endl;
|
||||
#endif
|
||||
#if USE_LUAJIT
|
||||
std::cout << "Using " << LUAJIT_VERSION << std::endl;
|
||||
os << "Using " << LUAJIT_VERSION << std::endl;
|
||||
#else
|
||||
std::cout << "Using " << LUA_RELEASE << std::endl;
|
||||
os << "Using " << LUA_RELEASE << std::endl;
|
||||
#endif
|
||||
std::cout << g_build_info << std::endl;
|
||||
os << "Running on " << porting::get_sysinfo() << std::endl;
|
||||
os << g_build_info << std::endl;
|
||||
}
|
||||
|
||||
static void list_game_ids()
|
||||
@ -637,6 +637,7 @@ static bool use_debugger(int argc, char *argv[])
|
||||
continue;
|
||||
new_args.push_back(argv[i]);
|
||||
}
|
||||
new_args.push_back("--console");
|
||||
new_args.push_back(nullptr);
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -715,10 +716,11 @@ static void uninit_common()
|
||||
|
||||
static void startup_message()
|
||||
{
|
||||
infostream << PROJECT_NAME_C << " " << g_version_hash
|
||||
<< "\nwith SER_FMT_VER_HIGHEST_READ="
|
||||
<< (int)SER_FMT_VER_HIGHEST_READ << ", "
|
||||
<< g_build_info << std::endl;
|
||||
print_version(infostream);
|
||||
infostream << "SER_FMT_VER_HIGHEST_READ=" <<
|
||||
TOSTRING(SER_FMT_VER_HIGHEST_READ) <<
|
||||
" LATEST_PROTOCOL_VERSION=" << TOSTRING(LATEST_PROTOCOL_VERSION)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
static bool read_config_file(const Settings &cmd_args)
|
||||
@ -1269,7 +1271,7 @@ static bool recompress_map_database(const GameParams &game_params, const Setting
|
||||
iss.clear();
|
||||
|
||||
{
|
||||
MapBlock mb(nullptr, v3s16(0,0,0), &server);
|
||||
MapBlock mb(v3s16(0,0,0), &server);
|
||||
u8 ver = readU8(iss);
|
||||
mb.deSerialize(iss, ver, true);
|
||||
|
||||
|
25
src/map.cpp
25
src/map.cpp
@ -164,11 +164,11 @@ MapNode Map::getNode(v3s16 p, bool *is_valid_position)
|
||||
return node;
|
||||
}
|
||||
|
||||
static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n)
|
||||
static void set_node_in_block(const NodeDefManager *nodedef, MapBlock *block,
|
||||
v3s16 relpos, MapNode n)
|
||||
{
|
||||
// Never allow placing CONTENT_IGNORE, it causes problems
|
||||
if(n.getContent() == CONTENT_IGNORE){
|
||||
const NodeDefManager *nodedef = block->getParent()->getNodeDefManager();
|
||||
v3s16 blockpos = block->getPos();
|
||||
v3s16 p = blockpos * MAP_BLOCKSIZE + relpos;
|
||||
errorstream<<"Not allowing to place CONTENT_IGNORE"
|
||||
@ -186,7 +186,7 @@ void Map::setNode(v3s16 p, MapNode n)
|
||||
v3s16 blockpos = getNodeBlockPos(p);
|
||||
MapBlock *block = getBlockNoCreate(blockpos);
|
||||
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
|
||||
set_node_in_block(block, relpos, n);
|
||||
set_node_in_block(m_gamedef->ndef(), block, relpos, n);
|
||||
}
|
||||
|
||||
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
||||
@ -215,14 +215,14 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
||||
// No light update needed, just copy over the old light.
|
||||
n.setLight(LIGHTBANK_DAY, oldnode.getLightRaw(LIGHTBANK_DAY, oldf), f);
|
||||
n.setLight(LIGHTBANK_NIGHT, oldnode.getLightRaw(LIGHTBANK_NIGHT, oldf), f);
|
||||
set_node_in_block(block, relpos, n);
|
||||
set_node_in_block(m_gamedef->ndef(), block, relpos, n);
|
||||
|
||||
modified_blocks[blockpos] = block;
|
||||
} else {
|
||||
// Ignore light (because calling voxalgo::update_lighting_nodes)
|
||||
n.setLight(LIGHTBANK_DAY, 0, f);
|
||||
n.setLight(LIGHTBANK_NIGHT, 0, f);
|
||||
set_node_in_block(block, relpos, n);
|
||||
set_node_in_block(m_gamedef->ndef(), block, relpos, n);
|
||||
|
||||
// Update lighting
|
||||
std::vector<std::pair<v3s16, MapNode> > oldnodes;
|
||||
@ -1155,7 +1155,7 @@ bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
|
||||
bool Map::isBlockOccluded(v3s16 pos_relative, v3s16 cam_pos_nodes, bool simple_check)
|
||||
{
|
||||
// Check occlusion for center and all 8 corners of the mapblock
|
||||
// Overshoot a little for less flickering
|
||||
@ -1172,7 +1172,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
|
||||
v3s16(-1, -1, -1) * bs2,
|
||||
};
|
||||
|
||||
v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
|
||||
v3s16 pos_blockcenter = pos_relative + (MAP_BLOCKSIZE / 2);
|
||||
|
||||
// Starting step size, value between 1m and sqrt(3)m
|
||||
float step = BS * 1.2f;
|
||||
@ -1193,9 +1193,18 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
|
||||
// this is a HACK, we should think of a more precise algorithm
|
||||
u32 needed_count = 2;
|
||||
|
||||
// This should be only used in server occlusion cullung.
|
||||
// The client recalculates the complete drawlist periodically,
|
||||
// and random sampling could lead to visible flicker.
|
||||
if (simple_check) {
|
||||
v3s16 random_point(myrand_range(-bs2, bs2), myrand_range(-bs2, bs2), myrand_range(-bs2, bs2));
|
||||
return isOccluded(cam_pos_nodes, pos_blockcenter + random_point, step, stepfac,
|
||||
start_offset, end_offset, 1);
|
||||
}
|
||||
|
||||
// Additional occlusion check, see comments in that function
|
||||
v3s16 check;
|
||||
if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
|
||||
if (determineAdditionalOcclusionCheck(cam_pos_nodes, MapBlock::getBox(pos_relative), check)) {
|
||||
// node is always on a side facing the camera, end_offset can be lower
|
||||
if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
|
||||
-1.0f, needed_count))
|
||||
|
@ -305,7 +305,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes);
|
||||
bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
|
||||
{
|
||||
return isBlockOccluded(block->getPosRelative(), cam_pos_nodes, false);
|
||||
}
|
||||
bool isBlockOccluded(v3s16 pos_relative, v3s16 cam_pos_nodes, bool simple_check = false);
|
||||
|
||||
protected:
|
||||
IGameDef *m_gamedef;
|
||||
|
||||
|
@ -66,10 +66,10 @@ static const char *modified_reason_strings[] = {
|
||||
MapBlock
|
||||
*/
|
||||
|
||||
MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef):
|
||||
m_parent(parent),
|
||||
MapBlock::MapBlock(v3s16 pos, IGameDef *gamedef):
|
||||
m_pos(pos),
|
||||
m_pos_relative(pos * MAP_BLOCKSIZE),
|
||||
data(new MapNode[nodecount]),
|
||||
m_gamedef(gamedef)
|
||||
{
|
||||
reallocate();
|
||||
@ -83,6 +83,8 @@ MapBlock::~MapBlock()
|
||||
mesh = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
bool MapBlock::onObjectsActivation()
|
||||
@ -143,25 +145,6 @@ void MapBlock::step(float dtime, const std::function<bool(v3s16, MapNode, f32)>
|
||||
}
|
||||
}
|
||||
|
||||
bool MapBlock::isValidPositionParent(v3s16 p)
|
||||
{
|
||||
if (isValidPosition(p)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_parent->isValidPosition(getPosRelative() + p);
|
||||
}
|
||||
|
||||
MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position)
|
||||
{
|
||||
if (!isValidPosition(p))
|
||||
return m_parent->getNode(getPosRelative() + p, is_valid_position);
|
||||
|
||||
if (is_valid_position)
|
||||
*is_valid_position = true;
|
||||
return data[p.Z * zstride + p.Y * ystride + p.X];
|
||||
}
|
||||
|
||||
std::string MapBlock::getModifiedReasonString()
|
||||
{
|
||||
std::string reason;
|
||||
|
167
src/mapblock.h
167
src/mapblock.h
@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include "irr_v3d.h"
|
||||
#include "mapnode.h"
|
||||
#include "exceptions.h"
|
||||
@ -72,30 +72,20 @@ class VoxelManipulator;
|
||||
class MapBlock
|
||||
{
|
||||
public:
|
||||
MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef);
|
||||
MapBlock(v3s16 pos, IGameDef *gamedef);
|
||||
~MapBlock();
|
||||
|
||||
/*virtual u16 nodeContainerId() const
|
||||
{
|
||||
return NODECONTAINER_ID_MAPBLOCK;
|
||||
}*/
|
||||
|
||||
Map *getParent()
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
// Any server-modding code can "delete" arbitrary blocks (i.e. with
|
||||
// core.delete_area), which makes them orphan. Avoid using orphan blocks for
|
||||
// anything.
|
||||
bool isOrphan() const
|
||||
{
|
||||
return !m_parent;
|
||||
return m_orphan;
|
||||
}
|
||||
|
||||
void makeOrphan()
|
||||
{
|
||||
m_parent = nullptr;
|
||||
m_orphan = true;
|
||||
}
|
||||
|
||||
void reallocate()
|
||||
@ -124,7 +114,7 @@ public:
|
||||
m_modified_reason |= reason;
|
||||
}
|
||||
if (mod == MOD_STATE_WRITE_NEEDED)
|
||||
contents_cached = false;
|
||||
contents.clear();
|
||||
}
|
||||
|
||||
inline u32 getModified()
|
||||
@ -226,10 +216,14 @@ public:
|
||||
return m_pos_relative;
|
||||
}
|
||||
|
||||
inline core::aabbox3d<s16> getBox()
|
||||
inline core::aabbox3d<s16> getBox() {
|
||||
return getBox(getPosRelative());
|
||||
}
|
||||
|
||||
static inline core::aabbox3d<s16> getBox(const v3s16 &pos_relative)
|
||||
{
|
||||
return core::aabbox3d<s16>(getPosRelative(),
|
||||
getPosRelative()
|
||||
return core::aabbox3d<s16>(pos_relative,
|
||||
pos_relative
|
||||
+ v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
|
||||
- v3s16(1,1,1));
|
||||
}
|
||||
@ -310,11 +304,6 @@ public:
|
||||
setNodeNoCheck(p.X, p.Y, p.Z, n);
|
||||
}
|
||||
|
||||
// These functions consult the parent container if the position
|
||||
// is not valid on this MapBlock.
|
||||
bool isValidPositionParent(v3s16 p);
|
||||
MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL);
|
||||
|
||||
// Copies data to VoxelManipulator to getPosRelative()
|
||||
void copyTo(VoxelManipulator &dst);
|
||||
|
||||
@ -394,15 +383,17 @@ public:
|
||||
|
||||
inline void refGrab()
|
||||
{
|
||||
assert(m_refcount < SHRT_MAX);
|
||||
m_refcount++;
|
||||
}
|
||||
|
||||
inline void refDrop()
|
||||
{
|
||||
assert(m_refcount > 0);
|
||||
m_refcount--;
|
||||
}
|
||||
|
||||
inline int refGet()
|
||||
inline short refGet()
|
||||
{
|
||||
return m_refcount;
|
||||
}
|
||||
@ -450,6 +441,11 @@ public:
|
||||
// clearObject and return removed objects count
|
||||
u32 clearObjects();
|
||||
|
||||
static const u32 ystride = MAP_BLOCKSIZE;
|
||||
static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
|
||||
|
||||
static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
|
||||
|
||||
private:
|
||||
/*
|
||||
Private methods
|
||||
@ -457,70 +453,88 @@ private:
|
||||
|
||||
void deSerialize_pre22(std::istream &is, u8 version, bool disk);
|
||||
|
||||
public:
|
||||
/*
|
||||
Public member variables
|
||||
* PLEASE NOTE: When adding something here be mindful of position and size
|
||||
* of member variables! This is also the reason for the weird public-private
|
||||
* interleaving.
|
||||
* If in doubt consult `pahole` to see the effects.
|
||||
*/
|
||||
|
||||
public:
|
||||
#ifndef SERVER // Only on client
|
||||
MapBlockMesh *mesh = nullptr;
|
||||
|
||||
// marks the sides which are opaque: 00+Z-Z+Y-Y+X-X
|
||||
u8 solid_sides = 0;
|
||||
#endif
|
||||
|
||||
NodeMetadataList m_node_metadata;
|
||||
StaticObjectList m_static_objects;
|
||||
|
||||
static const u32 ystride = MAP_BLOCKSIZE;
|
||||
static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
|
||||
|
||||
static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
|
||||
|
||||
//// ABM optimizations ////
|
||||
// Cache of content types
|
||||
std::unordered_set<content_t> contents;
|
||||
// True if content types are cached
|
||||
bool contents_cached = false;
|
||||
// True if we never want to cache content types for this block
|
||||
bool do_not_cache_contents = false;
|
||||
// marks the sides which are opaque: 00+Z-Z+Y-Y+X-X
|
||||
u8 solid_sides {0};
|
||||
|
||||
private:
|
||||
/*
|
||||
Private member variables
|
||||
*/
|
||||
// see isOrphan()
|
||||
bool m_orphan = false;
|
||||
|
||||
// NOTE: Lots of things rely on this being the Map
|
||||
Map *m_parent;
|
||||
// Position in blocks on parent
|
||||
v3s16 m_pos;
|
||||
|
||||
/* This is the precalculated m_pos_relative value
|
||||
/* Precalculated m_pos_relative value
|
||||
* This caches the value, improving performance by removing 3 s16 multiplications
|
||||
* at runtime on each getPosRelative call
|
||||
* For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications
|
||||
* The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins
|
||||
* at runtime on each getPosRelative call.
|
||||
* For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications.
|
||||
* The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins.
|
||||
*/
|
||||
v3s16 m_pos_relative;
|
||||
|
||||
/*
|
||||
Reference count; currently used for determining if this block is in
|
||||
the list of blocks to be drawn.
|
||||
*/
|
||||
short m_refcount = 0;
|
||||
|
||||
/*
|
||||
* Note that this is not an inline array because that has implications for
|
||||
* heap fragmentation (the array is exactly 16K), CPU caches and/or
|
||||
* optimizability of algorithms working on this array.
|
||||
*/
|
||||
MapNode *const data; // of `nodecount` elements
|
||||
|
||||
// provides the item and node definitions
|
||||
IGameDef *m_gamedef;
|
||||
|
||||
/*
|
||||
When the block is accessed, this is set to 0.
|
||||
Map will unload the block when this reaches a timeout.
|
||||
*/
|
||||
float m_usage_timer = 0;
|
||||
|
||||
public:
|
||||
//// ABM optimizations ////
|
||||
// True if we never want to cache content types for this block
|
||||
bool do_not_cache_contents = false;
|
||||
// Cache of content types
|
||||
// This is actually a set but for the small sizes we have a vector should be
|
||||
// more efficient.
|
||||
// Can be empty, in which case nothing was cached yet.
|
||||
std::vector<content_t> contents;
|
||||
|
||||
private:
|
||||
// Whether day and night lighting differs
|
||||
bool m_day_night_differs = false;
|
||||
bool m_day_night_differs_expired = true;
|
||||
|
||||
/*
|
||||
- On the server, this is used for telling whether the
|
||||
block has been modified from the one on disk.
|
||||
- On the client, this is used for nothing.
|
||||
*/
|
||||
u32 m_modified = MOD_STATE_WRITE_NEEDED;
|
||||
u16 m_modified = MOD_STATE_WRITE_NEEDED;
|
||||
u32 m_modified_reason = MOD_REASON_INITIAL;
|
||||
|
||||
/*
|
||||
When propagating sunlight and the above block doesn't exist,
|
||||
sunlight is assumed if this is false.
|
||||
|
||||
In practice this is set to true if the block is completely
|
||||
undeground with nothing visible above the ground except
|
||||
caves.
|
||||
When block is removed from active blocks, this is set to gametime.
|
||||
Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
|
||||
*/
|
||||
bool is_underground = false;
|
||||
u32 m_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
|
||||
// The on-disk (or to-be on-disk) timestamp value
|
||||
u32 m_disk_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
|
||||
|
||||
/*!
|
||||
* Each bit indicates if light spreading was finished
|
||||
@ -532,33 +546,24 @@ private:
|
||||
*/
|
||||
u16 m_lighting_complete = 0xFFFF;
|
||||
|
||||
// Whether day and night lighting differs
|
||||
bool m_day_night_differs = false;
|
||||
bool m_day_night_differs_expired = true;
|
||||
|
||||
// Whether mapgen has generated the content of this block (persisted)
|
||||
bool m_generated = false;
|
||||
|
||||
/*
|
||||
When block is removed from active blocks, this is set to gametime.
|
||||
Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
|
||||
*/
|
||||
u32 m_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
|
||||
// The on-disk (or to-be on-disk) timestamp value
|
||||
u32 m_disk_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
|
||||
When propagating sunlight and the above block doesn't exist,
|
||||
sunlight is assumed if this is false.
|
||||
|
||||
/*
|
||||
When the block is accessed, this is set to 0.
|
||||
Map will unload the block when this reaches a timeout.
|
||||
In practice this is set to true if the block is completely
|
||||
undeground with nothing visible above the ground except
|
||||
caves.
|
||||
*/
|
||||
float m_usage_timer = 0;
|
||||
bool is_underground = false;
|
||||
|
||||
/*
|
||||
Reference count; currently used for determining if this block is in
|
||||
the list of blocks to be drawn.
|
||||
*/
|
||||
int m_refcount = 0;
|
||||
public:
|
||||
NodeMetadataList m_node_metadata;
|
||||
StaticObjectList m_static_objects;
|
||||
|
||||
MapNode data[nodecount];
|
||||
private:
|
||||
NodeTimerList m_node_timers;
|
||||
};
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user