This commit is contained in:
Elias Fleckenstein 2021-09-19 20:56:13 +02:00
commit c8900e169a
365 changed files with 50693 additions and 12198 deletions

9
.editorconfig Executable file
View File

@ -0,0 +1,9 @@
[*]
end_of_line = lf
[*.{cpp,h,lua,txt,glsl,md,c,cmake,java,gradle}]
charset = utf8
indent_size = 4
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -10,13 +10,31 @@ Contributions are welcome! Here's how you can help:
## Code
1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository and [clone](https://help.github.com/articles/cloning-a-repository/) your fork.
1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository and
[clone](https://help.github.com/articles/cloning-a-repository/) your fork.
2. Before you start coding, consider opening an [issue at Github](https://github.com/minetest/minetest/issues) to discuss the suitability and implementation of your intended contribution with the core developers. If you are planning to start some very significant coding, you would benefit from first discussing on our IRC development channel [#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client is required to speak on this channel.
2. Before you start coding, consider opening an
[issue at Github](https://github.com/minetest/minetest/issues) to discuss the
suitability and implementation of your intended contribution with the core
developers.
Any Pull Request that isn't a bug fix and isn't covered by
[the roadmap](../doc/direction.md) will be closed within a week unless it
receives a concept approval from a Core Developer. For this reason, it is
recommended that you open an issue for any such pull requests before doing
the work, to avoid disappointment.
You may also benefit from discussing on our IRC development channel
[#minetest-dev](http://www.minetest.net/irc/). Note that a proper IRC client
is required to speak on this channel.
3. Start coding!
- Refer to the [Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt), [Developer Wiki](http://dev.minetest.net/Main_Page) and other [documentation](https://github.com/minetest/minetest/tree/master/doc).
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and [Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
- Refer to the
[Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt),
[Developer Wiki](http://dev.minetest.net/Main_Page) and other
[documentation](https://github.com/minetest/minetest/tree/master/doc).
- Follow the [C/C++](http://dev.minetest.net/Code_style_guidelines) and
[Lua](http://dev.minetest.net/Lua_code_style_guidelines) code style guidelines.
- Check your code works as expected and document any changes to the Lua API.
4. Commit & [push](https://help.github.com/articles/pushing-to-a-remote/) your changes to a new branch (not `master`, one change per branch)
@ -33,69 +51,115 @@ Contributions are welcome! Here's how you can help:
5. Once you are happy with your changes, submit a pull request.
- Open the [pull-request form](https://github.com/minetest/minetest/pull/new/master).
- Add a description explaining what you've done (or if it's a work-in-progress - what you need to do).
- Add a description explaining what you've done (or if it's a
work-in-progress - what you need to do).
- Make sure to fill out the pull request template.
### A pull-request is considered merge-able when:
1. It follows the roadmap in some way and fits the whole picture of the project: [roadmap introduction](http://c55.me/blog/?p=1491), [roadmap continued](https://forum.minetest.net/viewtopic.php?t=9177)
1. It follows [the roadmap](../doc/direction.md) in some way and fits the whole
picture of the project.
2. It works.
3. It follows the code style for [C/C++](http://dev.minetest.net/Code_style_guidelines) or [Lua](http://dev.minetest.net/Lua_code_style_guidelines).
4. The code's interfaces are well designed, regardless of other aspects that might need more work in the future.
3. It follows the code style for
[C/C++](http://dev.minetest.net/Code_style_guidelines) or
[Lua](http://dev.minetest.net/Lua_code_style_guidelines).
4. The code's interfaces are well designed, regardless of other aspects that
might need more work in the future.
5. It uses protocols and formats which include the required compatibility.
### Important note about automated GitHub checks
When you submit a pull request, GitHub automatically runs checks on the Minetest Engine combined with your changes. One of these checks is called 'cpp lint / clang format', which checks code formatting. Because formatting for readability requires human judgement this check often fails and often makes unsuitable formatting requests which make code readability worse.
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.
If this check fails, look at the details to check for any clear mistakes and
correct those. However, you should not apply everything ClangFormat requests.
Ignore requests that make code readability worse and any other clearly
unsuitable requests. Discuss in the pull request with a core developer about how
to progress.
## Issues
If you experience an issue, we would like to know the details - especially when a stable release is on the way.
If you experience an issue, we would like to know the details - especially when
a stable release is on the way.
1. Do a quick search on GitHub to check if the issue has already been reported.
2. Is it an issue with the Minetest *engine*? If not, report it [elsewhere](http://www.minetest.net/development/#reporting-issues).
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe the issue you are having - you could include:
2. Is it an issue with the Minetest *engine*? If not, report it
[elsewhere](http://www.minetest.net/development/#reporting-issues).
3. [Open an issue](https://github.com/minetest/minetest/issues/new) and describe
the issue you are having - you could include:
- Error logs (check the bottom of the `debug.txt` file).
- Screenshots.
- Ways you have tried to solve the issue, and whether they worked or not.
- Your Minetest version and the content (games, mods or texture packs) you have installed.
- Your platform (e.g. Windows 10 or Ubuntu 15.04 x64).
After reporting you should aim to answer questions or clarifications as this helps pinpoint the cause of the issue (if you don't do this your issue may be closed after 1 month).
After reporting you should aim to answer questions or clarifications as this
helps pinpoint the cause of the issue (if you don't do this your issue may be
closed after 1 month).
## Feature requests
Feature requests are welcome but take a moment to see if your idea follows the roadmap in some way and fits the whole picture of the project: [roadmap introduction](http://c55.me/blog/?p=1491), [roadmap continued](https://forum.minetest.net/viewtopic.php?t=9177). You should provide a clear explanation with as much detail as possible.
Feature requests are welcome but take a moment to see if your idea follows
[the roadmap](../doc/direction.md) in some way and fits the whole picture of
the project. You should provide a clear explanation with as much detail as
possible.
## Translations
The core translations of Minetest are performed using Weblate. You can access the project page with a list of current languages [here](https://hosted.weblate.org/projects/minetest/minetest/).
The core translations of Minetest are performed using Weblate. You can access
the project page with a list of current languages
[here](https://hosted.weblate.org/projects/minetest/minetest/).
Builtin (the component which contains things like server messages, chat command descriptions, privilege descriptions) is translated separately; it needs to be translated by editing a `.tr` text file. See [Translation](https://dev.minetest.net/Translation) for more information.
Builtin (the component which contains things like server messages, chat command
descriptions, privilege descriptions) is translated separately; it needs to be
translated by editing a `.tr` text file. See
[Translation](https://dev.minetest.net/Translation) for more information.
## Donations
If you'd like to monetarily support Minetest development, you can find donation methods on [our website](http://www.minetest.net/development/#donate).
If you'd like to monetarily support Minetest development, you can find donation
methods on [our website](http://www.minetest.net/development/#donate).
# Maintaining
*This is a concise version of the [Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
* This is a concise version of the
[Rules & Guidelines](http://dev.minetest.net/Category:Rules_and_Guidelines) on the developer wiki.*
These notes are for those who have push access Minetest (core developers / maintainers).
- See the [project organisation](http://dev.minetest.net/Organisation) for the people involved.
## Concept approvals and roadmaps
If a Pull Request is not a bug fix:
* If it matches a goal in [the roadmap](../doc/direction.md), then the PR should
be labelled as "Roadmap" and the goal stated by number in the description.
* If it doesn't match a goal, then it needs to receive a concept approval within
a week of being opened to remain open. This 1 week deadline does not apply to
PRs opened before the roadmap was adopted; instead, they may remain open or be
closed as needed. Use the "Concept Approved" label. Issues can be marked as
"Concept Approved" to give preapproval to future PRs.
## Reviewing pull requests
Pull requests should be reviewed and, if appropriate, checked if they achieve their intended purpose. You can show that you are in the process of, or will review the pull request by commenting *"Looks good"* or something similar.
Pull requests should be reviewed and, if appropriate, checked if they achieve
their intended purpose. You can show that you are in the process of, or will
review the pull request by commenting *"Looks good"* or something similar.
**If the pull-request is not [merge-able](#a-pull-request-is-considered-merge-able-when):**
Submit a comment explaining to the author what they need to change to make the pull-request merge-able.
Submit a comment explaining to the author what they need to change to make the
pull-request merge-able.
- If the author comments or makes changes to the pull-request, it can be reviewed again.
- If no response is made from the author within 1 month (when improvements are suggested or a question is asked), it can be closed.
- If the author comments or makes changes to the pull-request, it can be
reviewed again.
- If no response is made from the author within 1 month (when improvements are
suggested or a question is asked), it can be closed.
**If the pull-request is [merge-able](#a-pull-request-is-considered-merge-able-when):**

View File

@ -3,6 +3,7 @@ Add compact, short information about your PR for easier understanding:
- Goal of the PR
- How does the PR work?
- Does it resolve any reported issue?
- Does this relate to a goal in [the roadmap](../doc/direction.md)?
- If not a bug fix, why is this PR needed? What usecases does it solve?
## To do

View File

@ -8,7 +8,7 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- 'build/android/**'
- 'android/**'
- '.github/workflows/android.yml'
pull_request:
paths:
@ -16,7 +16,7 @@ on:
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- 'build/android/**'
- 'android/**'
- '.github/workflows/android.yml'
jobs:
@ -28,15 +28,17 @@ jobs:
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Install deps
run: sudo apt-get update; sudo apt-get install -y --no-install-recommends gettext
- name: Build with Gradle
run: cd build/android; ./gradlew assemblerelease
run: cd android; ./gradlew assemblerelease
- name: Save armeabi artifact
uses: actions/upload-artifact@v2
with:
name: Minetest-armeabi-v7a.apk
path: build/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
path: android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
- name: Save arm64 artifact
uses: actions/upload-artifact@v2
with:
name: Minetest-arm64-v8a.apk
path: build/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
path: android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk

View File

@ -180,7 +180,8 @@ jobs:
- uses: actions/checkout@v2
- name: Build docker image
run: |
docker build .
docker build . -t minetest:latest
docker run --rm minetest:latest /usr/local/bin/minetestserver --version
win32:
name: "MinGW cross-compiler (32-bit)"
@ -227,7 +228,7 @@ jobs:
env:
VCPKG_VERSION: 0bf3923f9fab4001c00f0f429682a0853b5749e0
# 2020.11
vcpkg_packages: irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit
vcpkg_packages: irrlicht zlib zstd curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit
strategy:
fail-fast: false
matrix:

66
.github/workflows/macos.yml vendored Normal file
View File

@ -0,0 +1,66 @@
name: macos
# build on c/cpp changes or workflow changes
on:
push:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- '.github/workflows/macos.yml'
pull_request:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- '.github/workflows/macos.yml'
env:
IRRLICHT_TAG: 1.9.0mt3
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
MINETEST_GAME_BRANCH: master
MINETEST_GAME_NAME: minetest_game
jobs:
build:
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2
- name: Install deps
run: |
pkgs=(cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd)
brew update
brew install ${pkgs[@]}
brew unlink $(brew ls --formula)
brew link ${pkgs[@]}
- name: Build
run: |
git clone -b $MINETEST_GAME_BRANCH $MINETEST_GAME_REPO games/$MINETEST_GAME_NAME
rm -rvf games/$MINETEST_GAME_NAME/.git
git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
mkdir cmakebuild
cd cmakebuild
cmake .. \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
-DCMAKE_FIND_FRAMEWORK=LAST \
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
-DRUN_IN_PLACE=FALSE \
-DENABLE_FREETYPE=TRUE -DENABLE_GETTEXT=TRUE
make -j2
make install
- name: Test
run: |
./build/macos/minetest.app/Contents/MacOS/minetest --run-unittests
- uses: actions/upload-artifact@v2
with:
name: minetest-macos
path: ./build/macos/

5
.gitignore vendored
View File

@ -76,6 +76,7 @@ doc/mkdocs/docs/*.md
doc/mkdocs/mkdocs.yml
## Build files
build/
CMakeFiles
Makefile
cmake_install.cmake
@ -86,6 +87,7 @@ src/test_config.h
src/cmake_config.h
src/cmake_config_githash.h
src/unittest/test_world/world.mt
games/devtest/mods/testnodes/textures/testnodes_generated_*.png
/locale/
.directory
*.cbp
@ -105,3 +107,6 @@ CMakeDoxy*
compile_commands.json
*.apk
*.zip
# Optional user provided library folder
lib/irrlichtmt

View File

@ -9,7 +9,7 @@ stages:
- deploy
variables:
IRRLICHT_TAG: "1.9.0mt1"
IRRLICHT_TAG: "1.9.0mt3"
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
@ -17,16 +17,12 @@ variables:
stage: build
before_script:
- apt-get update
- apt-get -y install build-essential git cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libleveldb-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
- DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential git cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libleveldb-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev
script:
- git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG
- cd irrlicht
- cmake . -DBUILD_SHARED_LIBS=OFF
- make -j2
- cd ..
- git clone https://github.com/minetest/irrlicht -b $IRRLICHT_TAG lib/irrlichtmt
- mkdir cmakebuild
- cd cmakebuild
- cmake -DIRRLICHT_LIBRARY=$PWD/../irrlicht/lib/Linux/libIrrlichtMt.a -DIRRLICHT_INCLUDE_DIR=$PWD/../irrlicht/include -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
- cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
- make -j2
- make install
artifacts:
@ -49,6 +45,7 @@ variables:
- sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control
- sed -i 's/JPEG_PLACEHOLDER/'$JPEG_PKG'/g' build/deb/minetest/DEBIAN/control
- sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control
- sed -i 's/JSONCPP_PLACEHOLDER/'$JSONCPP_PKG'/g' build/deb/minetest/DEBIAN/control
- cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../
artifacts:
expire_in: 90 day
@ -58,7 +55,7 @@ variables:
.debpkg_install:
stage: deploy
before_script:
- apt-get update
- apt-get update -qy
script:
- apt-get install -y ./*.deb
- minetest --version
@ -79,6 +76,7 @@ package:debian-9:
needs:
- build:debian-9
variables:
JSONCPP_PKG: libjsoncpp1
LEVELDB_PKG: libleveldb1v5
JPEG_PKG: libjpeg62-turbo
@ -100,6 +98,7 @@ package:debian-10:
needs:
- build:debian-10
variables:
JSONCPP_PKG: libjsoncpp1
LEVELDB_PKG: libleveldb1d
JPEG_PKG: libjpeg62-turbo
@ -109,31 +108,32 @@ deploy:debian-10:
needs:
- package:debian-10
# Bullseye
build:debian-11:
extends: .build_template
image: debian:11
package:debian-11:
extends: .debpkg_template
image: debian:11
needs:
- build:debian-11
variables:
JSONCPP_PKG: libjsoncpp24
LEVELDB_PKG: libleveldb1d
JPEG_PKG: libjpeg62-turbo
deploy:debian-11:
extends: .debpkg_install
image: debian:11
needs:
- package:debian-11
##
## Ubuntu
##
# Xenial
build:ubuntu-16.04:
extends: .build_template
image: ubuntu:xenial
package:ubuntu-16.04:
extends: .debpkg_template
image: ubuntu:xenial
needs:
- build:ubuntu-16.04
variables:
LEVELDB_PKG: libleveldb1v5
JPEG_PKG: libjpeg-turbo8
deploy:ubuntu-16.04:
extends: .debpkg_install
image: ubuntu:xenial
needs:
- package:ubuntu-16.04
# Bionic
build:ubuntu-18.04:
@ -146,6 +146,7 @@ package:ubuntu-18.04:
needs:
- build:ubuntu-18.04
variables:
JSONCPP_PKG: libjsoncpp1
LEVELDB_PKG: libleveldb1v5
JPEG_PKG: libjpeg-turbo8
@ -155,6 +156,28 @@ deploy:ubuntu-18.04:
needs:
- package:ubuntu-18.04
# Focal
build:ubuntu-20.04:
extends: .build_template
image: ubuntu:focal
package:ubuntu-20.04:
extends: .debpkg_template
image: ubuntu:focal
needs:
- build:ubuntu-20.04
variables:
JSONCPP_PKG: libjsoncpp1
LEVELDB_PKG: libleveldb1d
JPEG_PKG: libjpeg-turbo8
deploy:ubuntu-20.04:
extends: .debpkg_install
image: ubuntu:focal
needs:
- package:ubuntu-20.04
##
## Fedora
##
@ -164,7 +187,7 @@ build:fedora-28:
extends: .build_template
image: fedora:28
before_script:
- dnf -y install make git gcc gcc-c++ kernel-devel cmake libjpeg-devel libpng-devel libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel
- dnf -y install make git gcc gcc-c++ kernel-devel cmake libjpeg-devel libpng-devel libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel
##
## MinGW for Windows
@ -275,7 +298,7 @@ package:appimage-client:
- build:ubuntu-18.04
before_script:
- apt-get update -y
- apt-get install -y git wget
- apt-get install -y git
# Collect files
- mkdir AppDir
- cp -a artifact/minetest/usr/ AppDir/usr/

View File

@ -24,18 +24,21 @@ AppDir:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main universe
include:
- libirrlicht1.8
- libxxf86vm1
- libgl1-mesa-glx
- libsqlite3-0
- libogg0
- libvorbis0a
- libopenal1
- libc6
- libcurl3-gnutls
- libfreetype6
- zlib1g
- libgmp10
- libgl1
- libjpeg-turbo8
- libjsoncpp1
- libleveldb1v5
- libopenal1
- libpng16-16
- libsqlite3-0
- libstdc++6
- libvorbisfile3
- libx11-6
- libxxf86vm1
- zlib1g
files:
exclude:

View File

@ -1,5 +1,12 @@
cmake_minimum_required(VERSION 3.5)
# Set policies up to 3.9 since we want to enable the IPO option
if(${CMAKE_VERSION} VERSION_LESS 3.9)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.9)
endif()
# This can be read from ${PROJECT_NAME} after project() is called
project(minetest)
set(PROJECT_NAME_CAPITALIZED "Dragonfire")
@ -58,28 +65,44 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
# This is done here so that relative search paths are more reasonable
find_package(Irrlicht)
if(BUILD_CLIENT AND NOT IRRLICHT_FOUND)
message(FATAL_ERROR "IrrlichtMt is required to build the client, but it was not found.")
elseif(NOT IRRLICHT_INCLUDE_DIR)
message(FATAL_ERROR "Irrlicht or IrrlichtMt headers are required to build the server, but none found.")
endif()
include(CheckSymbolExists)
set(CMAKE_REQUIRED_INCLUDES ${IRRLICHT_INCLUDE_DIR})
unset(HAS_FORKED_IRRLICHT CACHE)
check_symbol_exists(IRRLICHT_VERSION_MT "IrrCompileConfig.h" HAS_FORKED_IRRLICHT)
if(NOT HAS_FORKED_IRRLICHT)
string(CONCAT EXPLANATION_MSG
"Irrlicht found, but it is not IrrlichtMt (Minetest's Irrlicht fork). "
"The Minetest team has forked Irrlicht to make their own customizations. "
"It can be found here: https://github.com/minetest/irrlicht")
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
if(BUILD_CLIENT)
message(FATAL_ERROR "${EXPLANATION_MSG}\n"
"Building the client with upstream Irrlicht is no longer possible.")
# tell IrrlichtMt to create a static library
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared library" FORCE)
add_subdirectory(lib/irrlichtmt EXCLUDE_FROM_ALL)
unset(BUILD_SHARED_LIBS CACHE)
if(NOT TARGET IrrlichtMt)
message(FATAL_ERROR "IrrlichtMt project is missing a CMake target?!")
endif()
else()
message(WARNING "${EXPLANATION_MSG}\n"
"The server can still be built with upstream Irrlicht but this is DISCOURAGED.")
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt/include")
endif()
else()
find_package(IrrlichtMt QUIET)
if(NOT TARGET IrrlichtMt::IrrlichtMt)
string(CONCAT explanation_msg
"The Minetest team has forked Irrlicht to make their own customizations. "
"It can be found here: https://github.com/minetest/irrlicht\n"
"For example use: git clone --depth=1 https://github.com/minetest/irrlicht lib/irrlichtmt\n")
if(BUILD_CLIENT)
message(FATAL_ERROR "IrrlichtMt is required to build the client, but it was not found.\n${explanation_msg}")
endif()
include(MinetestFindIrrlichtHeaders)
if(NOT IRRLICHT_INCLUDE_DIR)
message(FATAL_ERROR "Irrlicht or IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}")
endif()
message(STATUS "Found Irrlicht headers: ${IRRLICHT_INCLUDE_DIR}")
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
# Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${IRRLICHT_INCLUDE_DIR}")
else()
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
endif()
endif()

View File

@ -1,4 +1,5 @@
FROM alpine:3.13
ARG DOCKER_IMAGE=alpine:3.14
FROM $DOCKER_IMAGE AS builder
ENV MINETEST_GAME_VERSION master
ENV IRRLICHT_VERSION master
@ -19,8 +20,8 @@ COPY textures /usr/src/minetest/textures
WORKDIR /usr/src/minetest
RUN apk add --no-cache git build-base cmake sqlite-dev curl-dev zlib-dev \
gmp-dev jsoncpp-dev postgresql-dev luajit-dev ca-certificates && \
RUN apk add --no-cache git build-base cmake sqlite-dev curl-dev zlib-dev zstd-dev \
gmp-dev jsoncpp-dev postgresql-dev ninja luajit-dev ca-certificates && \
git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
rm -fr ./games/minetest_game/.git
@ -31,9 +32,10 @@ RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp/ && \
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_TESTING=0 && \
make -j2 && \
make install
-DENABLE_TESTING=0 \
-GNinja && \
ninja && \
ninja install
RUN git clone --depth=1 https://github.com/minetest/irrlicht/ -b ${IRRLICHT_VERSION} && \
cp -r irrlicht/include /usr/include/irrlichtmt
@ -47,21 +49,23 @@ RUN mkdir build && \
-DBUILD_SERVER=TRUE \
-DENABLE_PROMETHEUS=TRUE \
-DBUILD_UNITTESTS=FALSE \
-DBUILD_CLIENT=FALSE && \
make -j2 && \
make install
-DBUILD_CLIENT=FALSE \
-GNinja && \
ninja && \
ninja install
FROM alpine:3.13
ARG DOCKER_IMAGE=alpine:3.14
FROM $DOCKER_IMAGE AS runtime
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit jsoncpp && \
RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit jsoncpp zstd-libs && \
adduser -D minetest --uid 30000 -h /var/lib/minetest && \
chown -R minetest:minetest /var/lib/minetest
WORKDIR /var/lib/minetest
COPY --from=0 /usr/local/share/minetest /usr/local/share/minetest
COPY --from=0 /usr/local/bin/minetestserver /usr/local/bin/minetestserver
COPY --from=0 /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/minetest.conf
COPY --from=builder /usr/local/share/minetest /usr/local/share/minetest
COPY --from=builder /usr/local/bin/minetestserver /usr/local/bin/minetestserver
COPY --from=builder /usr/local/share/doc/minetest/minetest.conf.example /etc/minetest/minetest.conf
USER minetest:minetest

View File

@ -136,25 +136,26 @@ Compiling
| CMake | 3.5+ | |
| IrrlichtMt | - | Custom version of Irrlicht, see https://github.com/minetest/irrlicht |
| SQLite3 | 3.0+ | |
| Zstd | 1.0+ | |
| LuaJIT | 2.0+ | Bundled Lua 5.1 is used if not present |
| GMP | 5.0.0+ | Bundled mini-GMP is used if not present |
| JsonCPP | 1.0.0+ | Bundled JsonCPP is used if not present |
For Debian/Ubuntu users:
sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev
For Fedora users:
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel
sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel libzstd-devel
For Arch users:
sudo pacman -S base-devel libcurl-gnutls cmake libxxf86vm libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses
sudo pacman -S base-devel libcurl-gnutls cmake libxxf86vm libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd
For Alpine users:
sudo apk add build-base cmake libpng-dev jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev gmp-dev jsoncpp-dev luajit-dev
sudo apk add build-base cmake libpng-dev jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev gmp-dev jsoncpp-dev luajit-dev zstd-dev
#### Download
@ -177,6 +178,10 @@ Download minetest_game (otherwise only the "Development Test" game is available)
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
Download IrrlichtMt to `lib/irrlichtmt`, it will be used to satisfy the IrrlichtMt dependency that way:
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
Download source, without using Git:
wget https://github.com/minetest/minetest/archive/master.tar.gz
@ -191,6 +196,14 @@ Download minetest_game, without using Git:
mv minetest_game-master minetest_game
cd ..
Download IrrlichtMt, without using Git:
cd lib/
wget https://github.com/minetest/irrlicht/archive/master.tar.gz
tar xf master.tar.gz
mv irrlicht-master irrlichtmt
cd ..
#### Build
Build a version that runs directly from the source directory:
@ -211,6 +224,10 @@ Run it:
- Debug build is slower, but gives much more useful output in a debugger.
- If you build a bare server you don't need to have the Irrlicht or IrrlichtMt library installed.
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlicht/include`.
- IrrlichtMt can also be installed somewhere that is not a standard install path.
- In that case use `-DCMAKE_PREFIX_PATH=/path/to/install_prefix`
- The path must be set so that `$(CMAKE_PREFIX_PATH)/lib/cmake/IrrlichtMt` exists
or that `$(CMAKE_PREFIX_PATH)` is the path of an IrrlichtMt build folder.
### CMake options
@ -260,8 +277,7 @@ Library specific options:
GETTEXT_LIBRARY - Only when building with gettext on Windows; path to libintl.dll.a
GETTEXT_MSGFMT - Only when building with gettext; path to msgfmt/msgfmt.exe
IRRLICHT_DLL - Only on Windows; path to IrrlichtMt.dll
IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h
IRRLICHT_LIBRARY - Path to libIrrlichtMt.a/libIrrlichtMt.so/libIrrlichtMt.dll.a/IrrlichtMt.lib
IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h (usable for server build only)
LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h
LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a
LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll
@ -291,8 +307,11 @@ Library specific options:
ZLIB_DLL - Only on Windows; path to zlib1.dll
ZLIB_INCLUDE_DIR - Directory that contains zlib.h
ZLIB_LIBRARY - Path to libz.a/libz.so/zlib.lib
ZSTD_DLL - Only on Windows; path to libzstd.dll
ZSTD_INCLUDE_DIR - Directory that contains zstd.h
ZSTD_LIBRARY - Path to libzstd.a/libzstd.so/ztd.lib
### Compiling on Windows
### Compiling on Windows using MSVC
### Requirements
@ -305,14 +324,12 @@ Library specific options:
It is highly recommended to use vcpkg as package manager.
#### a) Using vcpkg to install dependencies
After you successfully built vcpkg you can easily install the required libraries:
```powershell
vcpkg install zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit gmp jsoncpp --triplet x64-windows
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit gmp jsoncpp --triplet x64-windows
```
- **Note that you currently need to build irrlicht on your own**
- **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt` as described in the Linux section.
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
- `freetype` is optional, it allows true-type font rendering.
@ -323,10 +340,6 @@ There are other optional libraries, but they are not tested if they can build an
Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-windows`.
#### b) Compile the dependencies on your own
This is outdated and not recommended. Follow the instructions on https://dev.minetest.net/Build_Win32_Minetest_including_all_required_libraries#VS2012_Build
### Compile Minetest
#### a) Using the vcpkg toolchain and CMake GUI
@ -355,12 +368,6 @@ cmake --build . --config Release
```
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
#### c) Using your own compiled libraries
**This is outdated and not recommended**
Follow the instructions on https://dev.minetest.net/Build_Win32_Minetest_including_all_required_libraries#VS2012_Build
### Windows Installer using WiX Toolset
Requirements:

View File

@ -52,7 +52,7 @@ android {
task prepareAssets() {
def assetsFolder = "build/assets"
def projRoot = "../../.."
def projRoot = "../.."
def gameToCopy = "minetest_game"
copy {
@ -76,10 +76,13 @@ task prepareAssets() {
copy {
from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}"
}
/*copy {
// ToDo: fix broken locales
from "${projRoot}/po" into "${assetsFolder}/po"
}*/
fileTree("${projRoot}/po").include("**/*.po").forEach { poFile ->
def moPath = "${assetsFolder}/locale/${poFile.parentFile.name}/LC_MESSAGES/"
file(moPath).mkdirs()
exec {
commandLine 'msgfmt', '-o', "${moPath}/minetest.mo", poFile
}
}
copy {
from "${projRoot}/textures" into "${assetsFolder}/textures"
}

View File

@ -30,7 +30,9 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import androidx.appcompat.app.AlertDialog;
@ -85,9 +87,19 @@ public class GameActivity extends NativeActivity {
private void showDialogUI(String hint, String current, int editType) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
EditText editText = new CustomEditText(this);
builder.setView(editText);
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
builder.setView(container);
AlertDialog alertDialog = builder.create();
EditText editText;
// For multi-line, do not close the dialog after pressing back button
if (editType == 1) {
editText = new EditText(this);
} else {
editText = new CustomEditText(this);
}
container.addView(editText);
editText.setMaxLines(8);
editText.requestFocus();
editText.setHint(hint);
editText.setText(current);
@ -103,8 +115,9 @@ public class GameActivity extends NativeActivity {
else
editText.setInputType(InputType.TYPE_CLASS_TEXT);
editText.setSelection(editText.getText().length());
editText.setOnKeyListener((view, KeyCode, event) -> {
if (KeyCode == KeyEvent.KEYCODE_ENTER) {
editText.setOnKeyListener((view, keyCode, event) -> {
// For multi-line, do not submit the text after pressing Enter key
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
messageReturnCode = 0;
messageReturnValue = editText.getText().toString();
@ -113,6 +126,18 @@ public class GameActivity extends NativeActivity {
}
return false;
});
// For multi-line, add Done button since Enter key does not submit text
if (editType == 1) {
Button doneButton = new Button(this);
container.addView(doneButton);
doneButton.setText(R.string.ime_dialog_done);
doneButton.setOnClickListener((view -> {
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
messageReturnCode = 0;
messageReturnValue = editText.getText().toString();
alertDialog.dismiss();
}));
}
alertDialog.show();
alertDialog.setOnCancelListener(dialog -> {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

View File

Before

Width:  |  Height:  |  Size: 83 B

After

Width:  |  Height:  |  Size: 83 B

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -6,5 +6,6 @@
<string name="not_granted">Required permission wasn\'t granted, Minetest can\'t run without it</string>
<string name="notification_title">Loading Minetest</string>
<string name="notification_description">Less than 1 minute&#8230;</string>
<string name="ime_dialog_done">Done</string>
</resources>

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,219 @@
LOCAL_PATH := $(call my-dir)/..
#LOCAL_ADDRESS_SANITIZER:=true
include $(CLEAR_VARS)
LOCAL_MODULE := Curl
LOCAL_SRC_FILES := deps/Android/Curl/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcurl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Freetype
LOCAL_SRC_FILES := deps/Android/Freetype/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libfreetype.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Irrlicht
LOCAL_SRC_FILES := deps/Android/Irrlicht/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libIrrlichtMt.a
include $(PREBUILT_STATIC_LIBRARY)
#include $(CLEAR_VARS)
#LOCAL_MODULE := LevelDB
#LOCAL_SRC_FILES := deps/Android/LevelDB/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libleveldb.a
#include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := LuaJIT
LOCAL_SRC_FILES := deps/Android/LuaJIT/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libluajit.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedTLS
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedtls.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedx509
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedx509.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedcrypto
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedcrypto.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := OpenAL
LOCAL_SRC_FILES := deps/Android/OpenAL-Soft/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libopenal.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := GetText
LOCAL_SRC_FILES := deps/Android/GetText/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libintl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Vorbis
LOCAL_SRC_FILES := deps/Android/Vorbis/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libvorbis.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Zstd
LOCAL_SRC_FILES := deps/Android/Zstd/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libzstd.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Minetest
LOCAL_CFLAGS += \
-DJSONCPP_NO_LOCALE_SUPPORT \
-DHAVE_TOUCHSCREENGUI \
-DENABLE_GLES=1 \
-DUSE_CURL=1 \
-DUSE_SOUND=1 \
-DUSE_FREETYPE=1 \
-DUSE_LEVELDB=0 \
-DUSE_LUAJIT=1 \
-DUSE_GETTEXT=1 \
-DVERSION_MAJOR=${versionMajor} \
-DVERSION_MINOR=${versionMinor} \
-DVERSION_PATCH=${versionPatch} \
-DVERSION_EXTRA=${versionExtra} \
$(GPROF_DEF)
ifdef NDEBUG
LOCAL_CFLAGS += -DNDEBUG=1
endif
ifdef GPROF
GPROF_DEF := -DGPROF
PROFILER_LIBS := android-ndk-profiler
LOCAL_CFLAGS += -pg
endif
LOCAL_C_INCLUDES := \
../../src \
../../src/script \
../../lib/gmp \
../../lib/jsoncpp \
deps/Android/Curl/include \
deps/Android/Freetype/include \
deps/Android/Irrlicht/include \
deps/Android/LevelDB/include \
deps/Android/GetText/include \
deps/Android/libiconv/include \
deps/Android/libiconv/libcharset/include \
deps/Android/LuaJIT/src \
deps/Android/OpenAL-Soft/include \
deps/Android/sqlite \
deps/Android/Vorbis/include \
deps/Android/Zstd/include
LOCAL_SRC_FILES := \
$(wildcard ../../src/client/*.cpp) \
$(wildcard ../../src/client/*/*.cpp) \
$(wildcard ../../src/content/*.cpp) \
../../src/database/database.cpp \
../../src/database/database-dummy.cpp \
../../src/database/database-files.cpp \
../../src/database/database-sqlite3.cpp \
$(wildcard ../../src/gui/*.cpp) \
$(wildcard ../../src/irrlicht_changes/*.cpp) \
$(wildcard ../../src/mapgen/*.cpp) \
$(wildcard ../../src/network/*.cpp) \
$(wildcard ../../src/script/*.cpp) \
$(wildcard ../../src/script/*/*.cpp) \
$(wildcard ../../src/server/*.cpp) \
$(wildcard ../../src/threading/*.cpp) \
$(wildcard ../../src/util/*.c) \
$(wildcard ../../src/util/*.cpp) \
../../src/ban.cpp \
../../src/chat.cpp \
../../src/clientiface.cpp \
../../src/collision.cpp \
../../src/content_mapnode.cpp \
../../src/content_nodemeta.cpp \
../../src/convert_json.cpp \
../../src/craftdef.cpp \
../../src/debug.cpp \
../../src/defaultsettings.cpp \
../../src/emerge.cpp \
../../src/environment.cpp \
../../src/face_position_cache.cpp \
../../src/filesys.cpp \
../../src/gettext.cpp \
../../src/httpfetch.cpp \
../../src/hud.cpp \
../../src/inventory.cpp \
../../src/inventorymanager.cpp \
../../src/itemdef.cpp \
../../src/itemstackmetadata.cpp \
../../src/light.cpp \
../../src/log.cpp \
../../src/main.cpp \
../../src/map.cpp \
../../src/map_settings_manager.cpp \
../../src/mapblock.cpp \
../../src/mapnode.cpp \
../../src/mapsector.cpp \
../../src/metadata.cpp \
../../src/modchannels.cpp \
../../src/nameidmapping.cpp \
../../src/nodedef.cpp \
../../src/nodemetadata.cpp \
../../src/nodetimer.cpp \
../../src/noise.cpp \
../../src/objdef.cpp \
../../src/object_properties.cpp \
../../src/particles.cpp \
../../src/pathfinder.cpp \
../../src/player.cpp \
../../src/porting.cpp \
../../src/porting_android.cpp \
../../src/profiler.cpp \
../../src/raycast.cpp \
../../src/reflowscan.cpp \
../../src/remoteplayer.cpp \
../../src/rollback.cpp \
../../src/rollback_interface.cpp \
../../src/serialization.cpp \
../../src/server.cpp \
../../src/serverenvironment.cpp \
../../src/serverlist.cpp \
../../src/settings.cpp \
../../src/staticobject.cpp \
../../src/texture_override.cpp \
../../src/tileanimation.cpp \
../../src/tool.cpp \
../../src/translation.cpp \
../../src/version.cpp \
../../src/voxel.cpp \
../../src/voxelalgorithms.cpp
# LevelDB backend is disabled
# ../../src/database/database-leveldb.cpp
# GMP
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
# JSONCPP
LOCAL_SRC_FILES += ../../lib/jsoncpp/jsoncpp.cpp
# iconv
LOCAL_SRC_FILES += \
deps/Android/libiconv/lib/iconv.c \
deps/Android/libiconv/libcharset/lib/localcharset.c
# SQLite3
LOCAL_SRC_FILES += deps/Android/sqlite/sqlite3.c
LOCAL_STATIC_LIBRARIES += Curl Freetype Irrlicht OpenAL mbedTLS mbedx509 mbedcrypto Vorbis LuaJIT GetText Zstd android_native_app_glue $(PROFILER_LIBS) #LevelDB
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
include $(BUILD_SHARED_LIBRARY)
ifdef GPROF
$(call import-module,android-ndk-profiler)
endif
$(call import-module,android/native_app_glue)

View File

@ -1,206 +0,0 @@
LOCAL_PATH := $(call my-dir)/..
#LOCAL_ADDRESS_SANITIZER:=true
include $(CLEAR_VARS)
LOCAL_MODULE := Curl
LOCAL_SRC_FILES := deps/Android/Curl/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libcurl.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Freetype
LOCAL_SRC_FILES := deps/Android/Freetype/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libfreetype.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Irrlicht
LOCAL_SRC_FILES := deps/Android/Irrlicht/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libIrrlichtMt.a
include $(PREBUILT_STATIC_LIBRARY)
#include $(CLEAR_VARS)
#LOCAL_MODULE := LevelDB
#LOCAL_SRC_FILES := deps/Android/LevelDB/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libleveldb.a
#include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := LuaJIT
LOCAL_SRC_FILES := deps/Android/LuaJIT/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libluajit.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedTLS
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedtls.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedx509
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedx509.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mbedcrypto
LOCAL_SRC_FILES := deps/Android/mbedTLS/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libmbedcrypto.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := OpenAL
LOCAL_SRC_FILES := deps/Android/OpenAL-Soft/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libopenal.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Vorbis
LOCAL_SRC_FILES := deps/Android/Vorbis/${NDK_TOOLCHAIN_VERSION}/$(APP_ABI)/libvorbis.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Minetest
LOCAL_CFLAGS += \
-DJSONCPP_NO_LOCALE_SUPPORT \
-DHAVE_TOUCHSCREENGUI \
-DENABLE_GLES=1 \
-DUSE_CURL=1 \
-DUSE_SOUND=1 \
-DUSE_FREETYPE=1 \
-DUSE_LEVELDB=0 \
-DUSE_LUAJIT=1 \
-DVERSION_MAJOR=${versionMajor} \
-DVERSION_MINOR=${versionMinor} \
-DVERSION_PATCH=${versionPatch} \
-DVERSION_EXTRA=${versionExtra} \
$(GPROF_DEF)
ifdef NDEBUG
LOCAL_CFLAGS += -DNDEBUG=1
endif
ifdef GPROF
GPROF_DEF := -DGPROF
PROFILER_LIBS := android-ndk-profiler
LOCAL_CFLAGS += -pg
endif
LOCAL_C_INCLUDES := \
../../../src \
../../../src/script \
../../../lib/gmp \
../../../lib/jsoncpp \
deps/Android/Curl/include \
deps/Android/Freetype/include \
deps/Android/Irrlicht/include \
deps/Android/LevelDB/include \
deps/Android/libiconv/include \
deps/Android/libiconv/libcharset/include \
deps/Android/LuaJIT/src \
deps/Android/OpenAL-Soft/include \
deps/Android/sqlite \
deps/Android/Vorbis/include
LOCAL_SRC_FILES := \
$(wildcard ../../../src/client/*.cpp) \
$(wildcard ../../../src/client/*/*.cpp) \
$(wildcard ../../../src/content/*.cpp) \
../../../src/database/database.cpp \
../../../src/database/database-dummy.cpp \
../../../src/database/database-files.cpp \
../../../src/database/database-sqlite3.cpp \
$(wildcard ../../../src/gui/*.cpp) \
$(wildcard ../../../src/irrlicht_changes/*.cpp) \
$(wildcard ../../../src/mapgen/*.cpp) \
$(wildcard ../../../src/network/*.cpp) \
$(wildcard ../../../src/script/*.cpp) \
$(wildcard ../../../src/script/*/*.cpp) \
$(wildcard ../../../src/server/*.cpp) \
$(wildcard ../../../src/threading/*.cpp) \
$(wildcard ../../../src/util/*.c) \
$(wildcard ../../../src/util/*.cpp) \
../../../src/ban.cpp \
../../../src/chat.cpp \
../../../src/clientiface.cpp \
../../../src/collision.cpp \
../../../src/content_mapnode.cpp \
../../../src/content_nodemeta.cpp \
../../../src/convert_json.cpp \
../../../src/craftdef.cpp \
../../../src/debug.cpp \
../../../src/defaultsettings.cpp \
../../../src/emerge.cpp \
../../../src/environment.cpp \
../../../src/face_position_cache.cpp \
../../../src/filesys.cpp \
../../../src/gettext.cpp \
../../../src/httpfetch.cpp \
../../../src/hud.cpp \
../../../src/inventory.cpp \
../../../src/inventorymanager.cpp \
../../../src/itemdef.cpp \
../../../src/itemstackmetadata.cpp \
../../../src/light.cpp \
../../../src/log.cpp \
../../../src/main.cpp \
../../../src/map.cpp \
../../../src/map_settings_manager.cpp \
../../../src/mapblock.cpp \
../../../src/mapnode.cpp \
../../../src/mapsector.cpp \
../../../src/metadata.cpp \
../../../src/modchannels.cpp \
../../../src/nameidmapping.cpp \
../../../src/nodedef.cpp \
../../../src/nodemetadata.cpp \
../../../src/nodetimer.cpp \
../../../src/noise.cpp \
../../../src/objdef.cpp \
../../../src/object_properties.cpp \
../../../src/particles.cpp \
../../../src/pathfinder.cpp \
../../../src/player.cpp \
../../../src/porting.cpp \
../../../src/porting_android.cpp \
../../../src/profiler.cpp \
../../../src/raycast.cpp \
../../../src/reflowscan.cpp \
../../../src/remoteplayer.cpp \
../../../src/rollback.cpp \
../../../src/rollback_interface.cpp \
../../../src/serialization.cpp \
../../../src/server.cpp \
../../../src/serverenvironment.cpp \
../../../src/serverlist.cpp \
../../../src/settings.cpp \
../../../src/staticobject.cpp \
../../../src/texture_override.cpp \
../../../src/tileanimation.cpp \
../../../src/tool.cpp \
../../../src/translation.cpp \
../../../src/version.cpp \
../../../src/voxel.cpp \
../../../src/voxelalgorithms.cpp
# LevelDB backend is disabled
# ../../../src/database/database-leveldb.cpp
# GMP
LOCAL_SRC_FILES += ../../../lib/gmp/mini-gmp.c
# JSONCPP
LOCAL_SRC_FILES += ../../../lib/jsoncpp/jsoncpp.cpp
# iconv
LOCAL_SRC_FILES += \
deps/Android/libiconv/lib/iconv.c \
deps/Android/libiconv/libcharset/lib/localcharset.c
# SQLite3
LOCAL_SRC_FILES += deps/Android/sqlite/sqlite3.c
LOCAL_STATIC_LIBRARIES += Curl Freetype Irrlicht OpenAL mbedTLS mbedx509 mbedcrypto Vorbis LuaJIT android_native_app_glue $(PROFILER_LIBS) #LevelDB
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
include $(BUILD_SHARED_LIBRARY)
ifdef GPROF
$(call import-module,android-ndk-profiler)
endif
$(call import-module,android/native_app_glue)

View File

@ -1,16 +1,10 @@
core.log("info", "Initializing Asynchronous environment")
function core.job_processor(serialized_func, serialized_param)
local func = loadstring(serialized_func)
function core.job_processor(func, serialized_param)
local param = core.deserialize(serialized_param)
local retval = nil
if type(func) == "function" then
retval = core.serialize(func(param))
else
core.log("error", "ASYNC WORKER: Unable to deserialize function")
end
local retval = core.serialize(func(param))
return retval or core.serialize(nil)
end

View File

@ -1,8 +1,8 @@
local death_formspec = ""
.. "size[11,5.5]"
.. "bgcolor[#320000b4;true]"
.. "label[4.85,1.35;" .. "You died" .. "]"
.. "button_exit[2,3;3,0.5;btn_respawn;" .. "Respawn" .. "]"
.. "label[4.85,1.35;" .. fgettext("You died") .. "]"
.. "button_exit[2,3;3,0.5;btn_respawn;" .. fgettext("Respawn") .. "]"
.. "button_exit[6,3;3,0.5;btn_ghost_mode;" .. "Ghost Mode" .. "]"
.. "set_focus[btn_respawn;true]"

View File

@ -209,14 +209,7 @@ end
--------------------------------------------------------------------------------
function math.hypot(x, y)
local t
x = math.abs(x)
y = math.abs(y)
t = math.min(x, y)
x = math.max(x, y)
if x == 0 then return 0 end
t = t / x
return x * math.sqrt(1 + t * t)
return math.sqrt(x * x + y * y)
end
--------------------------------------------------------------------------------
@ -432,21 +425,19 @@ function core.string_to_pos(value)
return nil
end
local p = {}
p.x, p.y, p.z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
if p.x and p.y and p.z then
p.x = tonumber(p.x)
p.y = tonumber(p.y)
p.z = tonumber(p.z)
return p
local x, y, z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
if x and y and z then
x = tonumber(x)
y = tonumber(y)
z = tonumber(z)
return vector.new(x, y, z)
end
p = {}
p.x, p.y, p.z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
if p.x and p.y and p.z then
p.x = tonumber(p.x)
p.y = tonumber(p.y)
p.z = tonumber(p.z)
return p
x, y, z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
if x and y and z then
x = tonumber(x)
y = tonumber(y)
z = tonumber(z)
return vector.new(x, y, z)
end
return nil
end

View File

@ -1,4 +1,5 @@
_G.core = {}
dofile("builtin/common/vector.lua")
dofile("builtin/common/misc_helpers.lua")
describe("string", function()
@ -55,8 +56,8 @@ end)
describe("pos", function()
it("from string", function()
assert.same({ x = 10, y = 5.1, z = -2}, core.string_to_pos("10.0, 5.1, -2"))
assert.same({ x = 10, y = 5.1, z = -2}, core.string_to_pos("( 10.0, 5.1, -2)"))
assert.equal(vector.new(10, 5.1, -2), core.string_to_pos("10.0, 5.1, -2"))
assert.equal(vector.new(10, 5.1, -2), core.string_to_pos("( 10.0, 5.1, -2)"))
assert.is_nil(core.string_to_pos("asd, 5, -2)"))
end)

View File

@ -3,6 +3,7 @@ _G.core = {}
_G.setfenv = require 'busted.compatibility'.setfenv
dofile("builtin/common/serialize.lua")
dofile("builtin/common/vector.lua")
describe("serialize", function()
it("works", function()
@ -53,4 +54,16 @@ describe("serialize", function()
assert.is_nil(test_out.func)
assert.equals(test_out.foo, "bar")
end)
it("vectors work", function()
local v = vector.new(1, 2, 3)
assert.same({{x = 1, y = 2, z = 3}}, core.deserialize(core.serialize({v})))
assert.same({x = 1, y = 2, z = 3}, core.deserialize(core.serialize(v)))
-- abuse
v = vector.new(1, 2, 3)
v.a = "bla"
assert.same({x = 1, y = 2, z = 3, a = "bla"},
core.deserialize(core.serialize(v)))
end)
end)

View File

@ -4,14 +4,20 @@ dofile("builtin/common/vector.lua")
describe("vector", function()
describe("new()", function()
it("constructs", function()
assert.same({ x = 0, y = 0, z = 0 }, vector.new())
assert.same({ x = 1, y = 2, z = 3 }, vector.new(1, 2, 3))
assert.same({ x = 3, y = 2, z = 1 }, vector.new({ x = 3, y = 2, z = 1 }))
assert.same({x = 0, y = 0, z = 0}, vector.new())
assert.same({x = 1, y = 2, z = 3}, vector.new(1, 2, 3))
assert.same({x = 3, y = 2, z = 1}, vector.new({x = 3, y = 2, z = 1}))
assert.is_true(vector.check(vector.new()))
assert.is_true(vector.check(vector.new(1, 2, 3)))
assert.is_true(vector.check(vector.new({x = 3, y = 2, z = 1})))
local input = vector.new({ x = 3, y = 2, z = 1 })
local output = vector.new(input)
assert.same(input, output)
assert.are_not.equal(input, output)
assert.equal(input, output)
assert.is_false(rawequal(input, output))
assert.equal(input, input:new())
end)
it("throws on invalid input", function()
@ -25,32 +31,271 @@ describe("vector", function()
end)
end)
it("equal()", function()
local function assertE(a, b)
assert.is_true(vector.equals(a, b))
end
local function assertNE(a, b)
assert.is_false(vector.equals(a, b))
end
assertE({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
assertE({x = -1, y = 0, z = 1}, {x = -1, y = 0, z = 1})
local a = { x = 2, y = 4, z = -10 }
assertE(a, a)
assertNE({x = -1, y = 0, z = 1}, a)
it("zero()", function()
assert.same({x = 0, y = 0, z = 0}, vector.zero())
assert.same(vector.new(), vector.zero())
assert.equal(vector.new(), vector.zero())
assert.is_true(vector.check(vector.zero()))
end)
it("add()", function()
assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 }))
it("copy()", function()
local v = vector.new(1, 2, 3)
assert.same(v, vector.copy(v))
assert.same(vector.new(v), vector.copy(v))
assert.equal(vector.new(v), vector.copy(v))
assert.is_true(vector.check(vector.copy(v)))
end)
it("indexes", function()
local some_vector = vector.new(24, 42, 13)
assert.equal(24, some_vector[1])
assert.equal(24, some_vector.x)
assert.equal(42, some_vector[2])
assert.equal(42, some_vector.y)
assert.equal(13, some_vector[3])
assert.equal(13, some_vector.z)
some_vector[1] = 100
assert.equal(100, some_vector.x)
some_vector.x = 101
assert.equal(101, some_vector[1])
some_vector[2] = 100
assert.equal(100, some_vector.y)
some_vector.y = 102
assert.equal(102, some_vector[2])
some_vector[3] = 100
assert.equal(100, some_vector.z)
some_vector.z = 103
assert.equal(103, some_vector[3])
end)
it("direction()", function()
local a = vector.new(1, 0, 0)
local b = vector.new(1, 42, 0)
assert.equal(vector.new(0, 1, 0), vector.direction(a, b))
assert.equal(vector.new(0, 1, 0), a:direction(b))
end)
it("distance()", function()
local a = vector.new(1, 0, 0)
local b = vector.new(3, 42, 9)
assert.is_true(math.abs(43 - vector.distance(a, b)) < 1.0e-12)
assert.is_true(math.abs(43 - a:distance(b)) < 1.0e-12)
assert.equal(0, vector.distance(a, a))
assert.equal(0, b:distance(b))
end)
it("length()", function()
local a = vector.new(0, 0, -23)
assert.equal(0, vector.length(vector.new()))
assert.equal(23, vector.length(a))
assert.equal(23, a:length())
end)
it("normalize()", function()
local a = vector.new(0, 0, -23)
assert.equal(vector.new(0, 0, -1), vector.normalize(a))
assert.equal(vector.new(0, 0, -1), a:normalize())
assert.equal(vector.new(), vector.normalize(vector.new()))
end)
it("floor()", function()
local a = vector.new(0.1, 0.9, -0.5)
assert.equal(vector.new(0, 0, -1), vector.floor(a))
assert.equal(vector.new(0, 0, -1), a:floor())
end)
it("round()", function()
local a = vector.new(0.1, 0.9, -0.5)
assert.equal(vector.new(0, 1, -1), vector.round(a))
assert.equal(vector.new(0, 1, -1), a:round())
end)
it("apply()", function()
local i = 0
local f = function(x)
i = i + 1
return x + i
end
local a = vector.new(0.1, 0.9, -0.5)
assert.equal(vector.new(1, 1, 0), vector.apply(a, math.ceil))
assert.equal(vector.new(1, 1, 0), a:apply(math.ceil))
assert.equal(vector.new(0.1, 0.9, 0.5), vector.apply(a, math.abs))
assert.equal(vector.new(0.1, 0.9, 0.5), a:apply(math.abs))
assert.equal(vector.new(1.1, 2.9, 2.5), vector.apply(a, f))
assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f))
end)
it("equals()", function()
local function assertE(a, b)
assert.is_true(vector.equals(a, b))
end
local function assertNE(a, b)
assert.is_false(vector.equals(a, b))
end
assertE({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
assertE({x = -1, y = 0, z = 1}, {x = -1, y = 0, z = 1})
assertE({x = -1, y = 0, z = 1}, vector.new(-1, 0, 1))
local a = {x = 2, y = 4, z = -10}
assertE(a, a)
assertNE({x = -1, y = 0, z = 1}, a)
assert.equal(vector.new(1, 2, 3), vector.new(1, 2, 3))
assert.is_true(vector.new(1, 2, 3):equals(vector.new(1, 2, 3)))
assert.not_equal(vector.new(1, 2, 3), vector.new(1, 2, 4))
assert.is_true(vector.new(1, 2, 3) == vector.new(1, 2, 3))
assert.is_false(vector.new(1, 2, 3) == vector.new(1, 3, 3))
end)
it("metatable is same", function()
local a = vector.new()
local b = vector.new(1, 2, 3)
assert.equal(true, vector.check(a))
assert.equal(true, vector.check(b))
assert.equal(vector.metatable, getmetatable(a))
assert.equal(vector.metatable, getmetatable(b))
assert.equal(vector.metatable, a.metatable)
end)
it("sort()", function()
local a = vector.new(1, 2, 3)
local b = vector.new(0.5, 232, -2)
local sorted = {vector.new(0.5, 2, -2), vector.new(1, 232, 3)}
assert.same(sorted, {vector.sort(a, b)})
assert.same(sorted, {a:sort(b)})
end)
it("angle()", function()
assert.equal(math.pi, vector.angle(vector.new(-1, -2, -3), vector.new(1, 2, 3)))
assert.equal(math.pi/2, vector.new(0, 1, 0):angle(vector.new(1, 0, 0)))
end)
it("dot()", function()
assert.equal(-14, vector.dot(vector.new(-1, -2, -3), vector.new(1, 2, 3)))
assert.equal(0, vector.new():dot(vector.new(1, 2, 3)))
end)
it("cross()", function()
local a = vector.new(-1, -2, 0)
local b = vector.new(1, 2, 3)
assert.equal(vector.new(-6, 3, 0), vector.cross(a, b))
assert.equal(vector.new(-6, 3, 0), a:cross(b))
end)
it("offset()", function()
assert.same({ x = 41, y = 52, z = 63 }, vector.offset(vector.new(1, 2, 3), 40, 50, 60))
assert.same({x = 41, y = 52, z = 63}, vector.offset(vector.new(1, 2, 3), 40, 50, 60))
assert.equal(vector.new(41, 52, 63), vector.offset(vector.new(1, 2, 3), 40, 50, 60))
assert.equal(vector.new(41, 52, 63), vector.new(1, 2, 3):offset(40, 50, 60))
end)
it("is()", function()
local some_table1 = {foo = 13, [42] = 1, "bar", 2}
local some_table2 = {1, 2, 3}
local some_table3 = {x = 1, 2, 3}
local some_table4 = {1, 2, z = 3}
local old = {x = 1, y = 2, z = 3}
local real = vector.new(1, 2, 3)
assert.is_false(vector.check(nil))
assert.is_false(vector.check(1))
assert.is_false(vector.check(true))
assert.is_false(vector.check("foo"))
assert.is_false(vector.check(some_table1))
assert.is_false(vector.check(some_table2))
assert.is_false(vector.check(some_table3))
assert.is_false(vector.check(some_table4))
assert.is_false(vector.check(old))
assert.is_true(vector.check(real))
assert.is_true(real:check())
end)
it("global pairs", function()
local out = {}
local vec = vector.new(10, 20, 30)
for k, v in pairs(vec) do
out[k] = v
end
assert.same({x = 10, y = 20, z = 30}, out)
end)
it("abusing works", function()
local v = vector.new(1, 2, 3)
v.a = 1
assert.equal(1, v.a)
local a_is_there = false
for key, value in pairs(v) do
if key == "a" then
a_is_there = true
assert.equal(value, 1)
break
end
end
assert.is_true(a_is_there)
end)
it("add()", function()
local a = vector.new(1, 2, 3)
local b = vector.new(1, 4, 3)
local c = vector.new(2, 6, 6)
assert.equal(c, vector.add(a, {x = 1, y = 4, z = 3}))
assert.equal(c, vector.add(a, b))
assert.equal(c, a:add(b))
assert.equal(c, a + b)
assert.equal(c, b + a)
end)
it("subtract()", function()
local a = vector.new(1, 2, 3)
local b = vector.new(2, 4, 3)
local c = vector.new(-1, -2, 0)
assert.equal(c, vector.subtract(a, {x = 2, y = 4, z = 3}))
assert.equal(c, vector.subtract(a, b))
assert.equal(c, a:subtract(b))
assert.equal(c, a - b)
assert.equal(c, -b + a)
end)
it("multiply()", function()
local a = vector.new(1, 2, 3)
local b = vector.new(2, 4, 3)
local c = vector.new(2, 8, 9)
local s = 2
local d = vector.new(2, 4, 6)
assert.equal(c, vector.multiply(a, {x = 2, y = 4, z = 3}))
assert.equal(c, vector.multiply(a, b))
assert.equal(d, vector.multiply(a, s))
assert.equal(d, a:multiply(s))
assert.equal(d, a * s)
assert.equal(d, s * a)
assert.equal(-a, -1 * a)
end)
it("divide()", function()
local a = vector.new(1, 2, 3)
local b = vector.new(2, 4, 3)
local c = vector.new(0.5, 0.5, 1)
local s = 2
local d = vector.new(0.5, 1, 1.5)
assert.equal(c, vector.divide(a, {x = 2, y = 4, z = 3}))
assert.equal(c, vector.divide(a, b))
assert.equal(d, vector.divide(a, s))
assert.equal(d, a:divide(s))
assert.equal(d, a / s)
assert.equal(d, 1/s * a)
assert.equal(-a, a / -1)
end)
it("to_string()", function()
local v = vector.new(1, 2, 3.14)
assert.same("(1, 2, 3.14)", vector.to_string(v))
assert.same("(1, 2, 3.14)", v:to_string())
assert.same("(1, 2, 3.14)", tostring(v))
end)
it("from_string()", function()

View File

@ -1,15 +1,55 @@
--[[
Vector helpers
Note: The vector.*-functions must be able to accept old vectors that had no metatables
]]
-- localize functions
local setmetatable = setmetatable
vector = {}
local metatable = {}
vector.metatable = metatable
local xyz = {"x", "y", "z"}
-- only called when rawget(v, key) returns nil
function metatable.__index(v, key)
return rawget(v, xyz[key]) or vector[key]
end
-- only called when rawget(v, key) returns nil
function metatable.__newindex(v, key, value)
rawset(v, xyz[key] or key, value)
end
-- constructors
local function fast_new(x, y, z)
return setmetatable({x = x, y = y, z = z}, metatable)
end
function vector.new(a, b, c)
if type(a) == "table" then
assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()")
return {x=a.x, y=a.y, z=a.z}
elseif a then
assert(b and c, "Invalid arguments for vector.new()")
return {x=a, y=b, z=c}
if a and b and c then
return fast_new(a, b, c)
end
return {x=0, y=0, z=0}
-- deprecated, use vector.copy and vector.zero directly
if type(a) == "table" then
return vector.copy(a)
else
assert(not a, "Invalid arguments for vector.new()")
return vector.zero()
end
end
function vector.zero()
return fast_new(0, 0, 0)
end
function vector.copy(v)
assert(v.x and v.y and v.z, "Invalid vector passed to vector.copy()")
return fast_new(v.x, v.y, v.z)
end
function vector.from_string(s, init)
@ -27,63 +67,60 @@ end
function vector.to_string(v)
return string.format("(%g, %g, %g)", v.x, v.y, v.z)
end
metatable.__tostring = vector.to_string
function vector.equals(a, b)
return a.x == b.x and
a.y == b.y and
a.z == b.z
end
metatable.__eq = vector.equals
-- unary operations
function vector.length(v)
return math.hypot(v.x, math.hypot(v.y, v.z))
return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
end
-- Note: we can not use __len because it is already used for primitive table length
function vector.normalize(v)
local len = vector.length(v)
if len == 0 then
return {x=0, y=0, z=0}
return fast_new(0, 0, 0)
else
return vector.divide(v, len)
end
end
function vector.floor(v)
return {
x = math.floor(v.x),
y = math.floor(v.y),
z = math.floor(v.z)
}
return vector.apply(v, math.floor)
end
function vector.round(v)
return {
x = math.round(v.x),
y = math.round(v.y),
z = math.round(v.z)
}
return fast_new(
math.round(v.x),
math.round(v.y),
math.round(v.z)
)
end
function vector.apply(v, func)
return {
x = func(v.x),
y = func(v.y),
z = func(v.z)
}
return fast_new(
func(v.x),
func(v.y),
func(v.z)
)
end
function vector.distance(a, b)
local x = a.x - b.x
local y = a.y - b.y
local z = a.z - b.z
return math.hypot(x, math.hypot(y, z))
return math.sqrt(x * x + y * y + z * z)
end
function vector.direction(pos1, pos2)
return vector.normalize({
x = pos2.x - pos1.x,
y = pos2.y - pos1.y,
z = pos2.z - pos1.z
})
return vector.subtract(pos2, pos1):normalize()
end
function vector.angle(a, b)
@ -98,70 +135,137 @@ function vector.dot(a, b)
end
function vector.cross(a, b)
return {
x = a.y * b.z - a.z * b.y,
y = a.z * b.x - a.x * b.z,
z = a.x * b.y - a.y * b.x
}
return fast_new(
a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x
)
end
function metatable.__unm(v)
return fast_new(-v.x, -v.y, -v.z)
end
-- add, sub, mul, div operations
function vector.add(a, b)
if type(b) == "table" then
return {x = a.x + b.x,
y = a.y + b.y,
z = a.z + b.z}
return fast_new(
a.x + b.x,
a.y + b.y,
a.z + b.z
)
else
return {x = a.x + b,
y = a.y + b,
z = a.z + b}
return fast_new(
a.x + b,
a.y + b,
a.z + b
)
end
end
function metatable.__add(a, b)
return fast_new(
a.x + b.x,
a.y + b.y,
a.z + b.z
)
end
function vector.subtract(a, b)
if type(b) == "table" then
return {x = a.x - b.x,
y = a.y - b.y,
z = a.z - b.z}
return fast_new(
a.x - b.x,
a.y - b.y,
a.z - b.z
)
else
return {x = a.x - b,
y = a.y - b,
z = a.z - b}
return fast_new(
a.x - b,
a.y - b,
a.z - b
)
end
end
function metatable.__sub(a, b)
return fast_new(
a.x - b.x,
a.y - b.y,
a.z - b.z
)
end
function vector.multiply(a, b)
if type(b) == "table" then
return {x = a.x * b.x,
y = a.y * b.y,
z = a.z * b.z}
return fast_new(
a.x * b.x,
a.y * b.y,
a.z * b.z
)
else
return {x = a.x * b,
y = a.y * b,
z = a.z * b}
return fast_new(
a.x * b,
a.y * b,
a.z * b
)
end
end
function metatable.__mul(a, b)
if type(a) == "table" then
return fast_new(
a.x * b,
a.y * b,
a.z * b
)
else
return fast_new(
a * b.x,
a * b.y,
a * b.z
)
end
end
function vector.divide(a, b)
if type(b) == "table" then
return {x = a.x / b.x,
y = a.y / b.y,
z = a.z / b.z}
return fast_new(
a.x / b.x,
a.y / b.y,
a.z / b.z
)
else
return {x = a.x / b,
y = a.y / b,
z = a.z / b}
return fast_new(
a.x / b,
a.y / b,
a.z / b
)
end
end
function metatable.__div(a, b)
-- scalar/vector makes no sense
return fast_new(
a.x / b,
a.y / b,
a.z / b
)
end
-- misc stuff
function vector.offset(v, x, y, z)
return {x = v.x + x,
y = v.y + y,
z = v.z + z}
return fast_new(
v.x + x,
v.y + y,
v.z + z
)
end
function vector.sort(a, b)
return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)},
{x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)}
return fast_new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z)),
fast_new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z))
end
function vector.check(v)
return getmetatable(v) == metatable
end
local function sin(x)
@ -229,7 +333,7 @@ end
function vector.dir_to_rotation(forward, up)
forward = vector.normalize(forward)
local rot = {x = math.asin(forward.y), y = -math.atan2(forward.x, forward.z), z = 0}
local rot = vector.new(math.asin(forward.y), -math.atan2(forward.x, forward.z), 0)
if not up then
return rot
end
@ -237,7 +341,7 @@ function vector.dir_to_rotation(forward, up)
"Invalid vectors passed to vector.dir_to_rotation().")
up = vector.normalize(up)
-- Calculate vector pointing up with roll = 0, just based on forward vector.
local forwup = vector.rotate({x = 0, y = 1, z = 0}, rot)
local forwup = vector.rotate(vector.new(0, 1, 0), rot)
-- 'forwup' and 'up' are now in a plane with 'forward' as normal.
-- The angle between them is the absolute of the roll value we're looking for.
rot.z = vector.angle(forwup, up)

View File

@ -87,6 +87,10 @@ core.builtin_auth_handler = {
core.settings:get("default_password")))
end
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
@ -100,9 +104,6 @@ core.builtin_auth_handler = {
core.run_priv_callbacks(name, priv, nil, "revoke")
end
end
auth_entry.privileges = privileges
core_auth.save(auth_entry)
core.notify_authentication_modified(name)
end,
reload = function()

View File

@ -212,9 +212,14 @@ core.register_chatcommand("haspriv", {
table.insert(players_with_priv, player_name)
end
end
return true, S("Players online with the \"@1\" privilege: @2",
param,
table.concat(players_with_priv, ", "))
if #players_with_priv == 0 then
return true, S("No online player has the \"@1\" privilege.",
param)
else
return true, S("Players online with the \"@1\" privilege: @2",
param,
table.concat(players_with_priv, ", "))
end
end
})
@ -250,11 +255,11 @@ local function handle_grant_command(caller, grantname, grantprivstr)
if privs_unknown ~= "" then
return false, privs_unknown
end
core.set_player_privs(grantname, privs)
for priv, _ in pairs(grantprivs) do
-- call the on_grant callbacks
core.run_priv_callbacks(grantname, priv, caller, "grant")
end
core.set_player_privs(grantname, privs)
core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
if grantname ~= caller then
core.chat_send_player(grantname,
@ -354,13 +359,13 @@ local function handle_revoke_command(caller, revokename, revokeprivstr)
end
local revokecount = 0
core.set_player_privs(revokename, privs)
for priv, _ in pairs(revokeprivs) do
-- call the on_revoke callbacks
core.run_priv_callbacks(revokename, priv, caller, "revoke")
revokecount = revokecount + 1
end
core.set_player_privs(revokename, privs)
local new_privs = core.get_player_privs(revokename)
if revokecount == 0 then
@ -499,10 +504,10 @@ core.register_chatcommand("remove_player", {
-- pos may be a non-integer position
local function find_free_position_near(pos)
local tries = {
{x=1, y=0, z=0},
{x=-1, y=0, z=0},
{x=0, y=0, z=1},
{x=0, y=0, z=-1},
vector.new( 1, 0, 0),
vector.new(-1, 0, 0),
vector.new( 0, 0, 1),
vector.new( 0, 0, -1),
}
for _, d in ipairs(tries) do
local p = vector.add(pos, d)
@ -737,7 +742,12 @@ core.register_chatcommand("mods", {
description = S("List mods installed on the server"),
privs = {},
func = function(name, param)
return true, table.concat(core.get_modnames(), ", ")
local mods = core.get_modnames()
if #mods == 0 then
return true, S("No mods installed.")
else
return true, table.concat(core.get_modnames(), ", ")
end
end,
})
@ -1053,24 +1063,58 @@ core.register_chatcommand("days", {
end
})
local function parse_shutdown_param(param)
local delay, reconnect, message
local one, two, three
one, two, three = param:match("^(%S+) +(%-r) +(.*)")
if one and two and three then
-- 3 arguments: delay, reconnect and message
return one, two, three
end
-- 2 arguments
one, two = param:match("^(%S+) +(.*)")
if one and two then
if tonumber(one) then
delay = one
if two == "-r" then
reconnect = two
else
message = two
end
elseif one == "-r" then
reconnect, message = one, two
end
return delay, reconnect, message
end
-- 1 argument
one = param:match("(.*)")
if tonumber(one) then
delay = one
elseif one == "-r" then
reconnect = one
else
message = one
end
return delay, reconnect, message
end
core.register_chatcommand("shutdown", {
params = S("[<delay_in_seconds> | -1] [reconnect] [<message>]"),
description = S("Shutdown server (-1 cancels a delayed shutdown)"),
params = S("[<delay_in_seconds> | -1] [-r] [<message>]"),
description = S("Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)"),
privs = {server=true},
func = function(name, param)
local delay, reconnect, message
delay, param = param:match("^%s*(%S+)(.*)")
if param then
reconnect, param = param:match("^%s*(%S+)(.*)")
local delay, reconnect, message = parse_shutdown_param(param)
local bool_reconnect = reconnect == "-r"
if not message then
message = ""
end
message = param and param:match("^%s*(.+)") or ""
delay = tonumber(delay) or 0
if delay == 0 then
core.log("action", name .. " shuts down server")
core.chat_send_all("*** "..S("Server shutting down (operator request)."))
end
core.request_shutdown(message:trim(), core.is_yes(reconnect), delay)
core.request_shutdown(message:trim(), bool_reconnect, delay)
return true
end,
})

View File

@ -39,7 +39,7 @@ local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
core.register_entity(":__builtin:falling_node", {
initial_properties = {
visual = "item",
visual_size = {x = SCALE, y = SCALE, z = SCALE},
visual_size = vector.new(SCALE, SCALE, SCALE),
textures = {},
physical = true,
is_visible = false,
@ -96,7 +96,7 @@ core.register_entity(":__builtin:falling_node", {
local vsize
if def.visual_scale then
local s = def.visual_scale
vsize = {x = s, y = s, z = s}
vsize = vector.new(s, s, s)
end
self.object:set_properties({
is_visible = true,
@ -111,15 +111,21 @@ core.register_entity(":__builtin:falling_node", {
itemstring = core.itemstring_with_palette(itemstring, node.param2)
end
-- FIXME: solution needed for paramtype2 == "leveled"
local vsize
if def.visual_scale then
local s = def.visual_scale * SCALE
vsize = {x = s, y = s, z = s}
-- Calculate size of falling node
local s = {}
s.x = (def.visual_scale or 1) * SCALE
s.y = s.x
s.z = s.x
-- Compensate for wield_scale
if def.wield_scale then
s.x = s.x / def.wield_scale.x
s.y = s.y / def.wield_scale.y
s.z = s.z / def.wield_scale.z
end
self.object:set_properties({
is_visible = true,
wield_item = itemstring,
visual_size = vsize,
visual_size = s,
glow = def.light_source,
})
end
@ -158,7 +164,8 @@ core.register_entity(":__builtin:falling_node", {
if euler then
self.object:set_rotation(euler)
end
elseif (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" or def.drawtype == "signlike") then
elseif (def.drawtype ~= "plantlike" and def.drawtype ~= "plantlike_rooted" and
(def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" or def.drawtype == "signlike")) then
local rot = node.param2 % 8
if (def.drawtype == "signlike" and def.paramtype2 ~= "wallmounted" and def.paramtype2 ~= "colorwallmounted") then
-- Change rotation to "floor" by default for non-wallmounted paramtype2
@ -227,7 +234,7 @@ core.register_entity(":__builtin:falling_node", {
on_activate = function(self, staticdata)
self.object:set_armor_groups({immortal = 1})
self.object:set_acceleration({x = 0, y = -gravity, z = 0})
self.object:set_acceleration(vector.new(0, -gravity, 0))
local ds = core.deserialize(staticdata)
if ds and ds.node then
@ -303,7 +310,7 @@ core.register_entity(":__builtin:falling_node", {
if self.floats then
local pos = self.object:get_pos()
local bcp = vector.round({x = pos.x, y = pos.y - 0.7, z = pos.z})
local bcp = pos:offset(0, -0.7, 0):round()
local bcn = core.get_node(bcp)
local bcd = core.registered_nodes[bcn.name]
@ -344,13 +351,12 @@ core.register_entity(":__builtin:falling_node", {
-- TODO: this hack could be avoided in the future if objects
-- could choose who to collide with
local vel = self.object:get_velocity()
self.object:set_velocity({
x = vel.x,
y = player_collision.old_velocity.y,
z = vel.z
})
self.object:set_pos(vector.add(self.object:get_pos(),
{x = 0, y = -0.5, z = 0}))
self.object:set_velocity(vector.new(
vel.x,
player_collision.old_velocity.y,
vel.z
))
self.object:set_pos(self.object:get_pos():offset(0, -0.5, 0))
end
return
elseif bcn.name == "ignore" then
@ -430,7 +436,7 @@ local function drop_attached_node(p)
if def and def.preserve_metadata then
local oldmeta = core.get_meta(p):to_table().fields
-- Copy pos and node because the callback can modify them.
local pos_copy = {x=p.x, y=p.y, z=p.z}
local pos_copy = vector.new(p)
local node_copy = {name=n.name, param1=n.param1, param2=n.param2}
local drop_stacks = {}
for k, v in pairs(drops) do
@ -455,14 +461,14 @@ end
function builtin_shared.check_attached_node(p, n)
local def = core.registered_nodes[n.name]
local d = {x = 0, y = 0, z = 0}
local d = vector.new()
if def.paramtype2 == "wallmounted" or
def.paramtype2 == "colorwallmounted" then
-- The fallback vector here is in case 'wallmounted to dir' is nil due
-- to voxelmanip placing a wallmounted node without resetting a
-- pre-existing param2 value that is out-of-range for wallmounted.
-- The fallback vector corresponds to param2 = 0.
d = core.wallmounted_to_dir(n.param2) or {x = 0, y = 1, z = 0}
d = core.wallmounted_to_dir(n.param2) or vector.new(0, 1, 0)
else
d.y = -1
end
@ -482,7 +488,7 @@ end
function core.check_single_for_falling(p)
local n = core.get_node(p)
if core.get_item_group(n.name, "falling_node") ~= 0 then
local p_bottom = {x = p.x, y = p.y - 1, z = p.z}
local p_bottom = vector.offset(p, 0, -1, 0)
-- Only spawn falling node if node below is loaded
local n_bottom = core.get_node_or_nil(p_bottom)
local d_bottom = n_bottom and core.registered_nodes[n_bottom.name]
@ -521,17 +527,17 @@ end
-- Down first as likely case, but always before self. The same with sides.
-- Up must come last, so that things above self will also fall all at once.
local check_for_falling_neighbors = {
{x = -1, y = -1, z = 0},
{x = 1, y = -1, z = 0},
{x = 0, y = -1, z = -1},
{x = 0, y = -1, z = 1},
{x = 0, y = -1, z = 0},
{x = -1, y = 0, z = 0},
{x = 1, y = 0, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z = -1},
{x = 0, y = 0, z = 0},
{x = 0, y = 1, z = 0},
vector.new(-1, -1, 0),
vector.new( 1, -1, 0),
vector.new( 0, -1, -1),
vector.new( 0, -1, 1),
vector.new( 0, -1, 0),
vector.new(-1, 0, 0),
vector.new( 1, 0, 0),
vector.new( 0, 0, 1),
vector.new( 0, 0, -1),
vector.new( 0, 0, 0),
vector.new( 0, 1, 0),
}
function core.check_for_falling(p)

View File

@ -20,6 +20,8 @@ core.features = {
direct_velocity_on_players = true,
use_texture_alpha_string_modes = true,
degrotate_240_steps = true,
abm_min_max_y = true,
dynamic_add_media_table = true,
}
function core.has_feature(arg)

View File

@ -86,12 +86,6 @@ local function read_file(filename)
return core.deserialize(t) or {}
end
local function write_file(filename, table)
local f = io.open(filename, "w")
f:write(core.serialize(table))
f:close()
end
blocks_forceloaded = read_file(wpath.."/force_loaded.txt")
for _, __ in pairs(blocks_forceloaded) do
total_forceloaded = total_forceloaded + 1
@ -106,7 +100,8 @@ end)
-- persists the currently forceloaded blocks to disk
local function persist_forceloaded_blocks()
write_file(wpath.."/force_loaded.txt", blocks_forceloaded)
local data = core.serialize(blocks_forceloaded)
core.safe_file_write(wpath.."/force_loaded.txt", data)
end
-- periodical forceload persistence

View File

@ -7,8 +7,6 @@ local gamepath = scriptpath .. "game".. DIR_DELIM
-- not exposed to outer context
local builtin_shared = {}
dofile(commonpath .. "vector.lua")
dofile(gamepath .. "constants.lua")
assert(loadfile(gamepath .. "item.lua"))(builtin_shared)
dofile(gamepath .. "register.lua")

View File

@ -70,12 +70,12 @@ end
-- Table of possible dirs
local facedir_to_dir = {
{x= 0, y=0, z= 1},
{x= 1, y=0, z= 0},
{x= 0, y=0, z=-1},
{x=-1, y=0, z= 0},
{x= 0, y=-1, z= 0},
{x= 0, y=1, z= 0},
vector.new( 0, 0, 1),
vector.new( 1, 0, 0),
vector.new( 0, 0, -1),
vector.new(-1, 0, 0),
vector.new( 0, -1, 0),
vector.new( 0, 1, 0),
}
-- Mapping from facedir value to index in facedir_to_dir.
local facedir_to_dir_map = {
@ -114,12 +114,12 @@ end
-- table of dirs in wallmounted order
local wallmounted_to_dir = {
[0] = {x = 0, y = 1, z = 0},
{x = 0, y = -1, z = 0},
{x = 1, y = 0, z = 0},
{x = -1, y = 0, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z = -1},
[0] = vector.new( 0, 1, 0),
vector.new( 0, -1, 0),
vector.new( 1, 0, 0),
vector.new(-1, 0, 0),
vector.new( 0, 0, 1),
vector.new( 0, 0, -1),
}
function core.wallmounted_to_dir(wallmounted)
return wallmounted_to_dir[wallmounted % 8]
@ -130,7 +130,7 @@ function core.dir_to_yaw(dir)
end
function core.yaw_to_dir(yaw)
return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)}
return vector.new(-math.sin(yaw), 0, math.cos(yaw))
end
function core.is_colored_paramtype(ptype)
@ -153,6 +153,18 @@ function core.strip_param2_color(param2, paramtype2)
return param2
end
local function has_all_groups(tbl, required_groups)
if type(required_groups) == "string" then
return (tbl[required_groups] or 0) ~= 0
end
for _, group in ipairs(required_groups) do
if (tbl[group] or 0) == 0 then
return false
end
end
return true
end
function core.get_node_drops(node, toolname)
-- Compatibility, if node is string
local nodename = node
@ -192,7 +204,7 @@ function core.get_node_drops(node, toolname)
if item.rarity ~= nil then
good_rarity = item.rarity < 1 or math.random(item.rarity) == 1
end
if item.tools ~= nil then
if item.tools ~= nil or item.tool_groups ~= nil then
good_tool = false
end
if item.tools ~= nil and toolname then
@ -207,6 +219,27 @@ function core.get_node_drops(node, toolname)
end
end
end
if item.tool_groups ~= nil and toolname then
local tooldef = core.registered_items[toolname]
if tooldef ~= nil and type(tooldef.groups) == "table" then
if type(item.tool_groups) == "string" then
-- tool_groups can be a string which specifies the required group
good_tool = core.get_item_group(toolname, item.tool_groups) ~= 0
else
-- tool_groups can be a list of sufficient requirements.
-- i.e. if any item in the list can be satisfied then the tool is good
assert(type(item.tool_groups) == "table")
for _, required_groups in ipairs(item.tool_groups) do
-- required_groups can be either a string (a single group),
-- or an array of strings where all must be in tooldef.groups
good_tool = has_all_groups(tooldef.groups, required_groups)
if good_tool then
break
end
end
end
end
end
if good_rarity and good_tool then
got_count = got_count + 1
for _, add_item in ipairs(item.items) do
@ -268,12 +301,12 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
end
-- Place above pointed node
local place_to = {x = above.x, y = above.y, z = above.z}
local place_to = vector.new(above)
-- If node under is buildable_to, place into it instead (eg. snow)
if olddef_under.buildable_to then
log("info", "node under is buildable to")
place_to = {x = under.x, y = under.y, z = under.z}
place_to = vector.new(under)
end
if core.is_protected(place_to, playername) then
@ -293,22 +326,14 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
newnode.param2 = def.place_param2
elseif (def.paramtype2 == "wallmounted" or
def.paramtype2 == "colorwallmounted") and not param2 then
local dir = {
x = under.x - above.x,
y = under.y - above.y,
z = under.z - above.z
}
local dir = vector.subtract(under, above)
newnode.param2 = core.dir_to_wallmounted(dir)
-- Calculate the direction for furnaces and chests and stuff
elseif (def.paramtype2 == "facedir" or
def.paramtype2 == "colorfacedir") and not param2 then
local placer_pos = placer and placer:get_pos()
if placer_pos then
local dir = {
x = above.x - placer_pos.x,
y = above.y - placer_pos.y,
z = above.z - placer_pos.z
}
local dir = vector.subtract(above, placer_pos)
newnode.param2 = core.dir_to_facedir(dir)
log("info", "facedir: " .. newnode.param2)
end
@ -362,7 +387,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
-- Run callback
if def.after_place_node and not prevent_after_place then
-- Deepcopy place_to and pointed_thing because callback can modify it
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
local place_to_copy = vector.new(place_to)
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
if def.after_place_node(place_to_copy, placer, itemstack,
pointed_thing_copy) then
@ -373,7 +398,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
-- Run script hook
for _, callback in ipairs(core.registered_on_placenodes) do
-- Deepcopy pos, node and pointed_thing because callback can modify them
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
local place_to_copy = vector.new(place_to)
local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
@ -519,11 +544,11 @@ function core.handle_node_drops(pos, drops, digger)
for _, dropped_item in pairs(drops) do
local left = give_item(dropped_item)
if not left:is_empty() then
local p = {
x = pos.x + math.random()/2-0.25,
y = pos.y + math.random()/2-0.25,
z = pos.z + math.random()/2-0.25,
}
local p = vector.offset(pos,
math.random()/2-0.25,
math.random()/2-0.25,
math.random()/2-0.25
)
core.add_item(p, left)
end
end
@ -582,7 +607,7 @@ function core.node_dig(pos, node, digger)
if def and def.preserve_metadata then
local oldmeta = core.get_meta(pos):to_table().fields
-- Copy pos and node because the callback can modify them.
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local pos_copy = vector.new(pos)
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
local drop_stacks = {}
for k, v in pairs(drops) do
@ -614,7 +639,7 @@ function core.node_dig(pos, node, digger)
-- Run callback
if def and def.after_dig_node then
-- Copy pos and node because callback can modify them
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local pos_copy = vector.new(pos)
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
def.after_dig_node(pos_copy, node_copy, oldmetadata, digger)
end
@ -627,7 +652,7 @@ function core.node_dig(pos, node, digger)
end
-- Copy pos and node because callback can modify them
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local pos_copy = vector.new(pos)
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
callback(pos_copy, node_copy, digger)
end
@ -670,7 +695,7 @@ core.nodedef_default = {
groups = {},
inventory_image = "",
wield_image = "",
wield_scale = {x=1,y=1,z=1},
wield_scale = vector.new(1, 1, 1),
stack_max = default_stack_max,
usable = false,
liquids_pointable = false,
@ -729,7 +754,7 @@ core.craftitemdef_default = {
groups = {},
inventory_image = "",
wield_image = "",
wield_scale = {x=1,y=1,z=1},
wield_scale = vector.new(1, 1, 1),
stack_max = default_stack_max,
liquids_pointable = false,
tool_capabilities = nil,
@ -748,7 +773,7 @@ core.tooldef_default = {
groups = {},
inventory_image = "",
wield_image = "",
wield_scale = {x=1,y=1,z=1},
wield_scale = vector.new(1, 1, 1),
stack_max = 1,
liquids_pointable = false,
tool_capabilities = nil,
@ -767,7 +792,7 @@ core.noneitemdef_default = { -- This is used for the hand and unknown items
groups = {},
inventory_image = "",
wield_image = "",
wield_scale = {x=1,y=1,z=1},
wield_scale = vector.new(1, 1, 1),
stack_max = default_stack_max,
liquids_pointable = false,
tool_capabilities = nil,

View File

@ -119,13 +119,12 @@ end
function core.get_position_from_hash(hash)
local pos = {}
pos.x = (hash % 65536) - 32768
local x = (hash % 65536) - 32768
hash = math.floor(hash / 65536)
pos.y = (hash % 65536) - 32768
local y = (hash % 65536) - 32768
hash = math.floor(hash / 65536)
pos.z = (hash % 65536) - 32768
return pos
local z = (hash % 65536) - 32768
return vector.new(x, y, z)
end
@ -215,7 +214,7 @@ function core.is_area_protected(minp, maxp, player_name, interval)
local y = math.floor(yf + 0.5)
for xf = minp.x, maxp.x, d.x do
local x = math.floor(xf + 0.5)
local pos = {x = x, y = y, z = z}
local pos = vector.new(x, y, z)
if core.is_protected(pos, player_name) then
return pos
end
@ -270,24 +269,44 @@ function core.cancel_shutdown_requests()
end
-- Callback handling for dynamic_add_media
-- Used for callback handling with dynamic_add_media
core.dynamic_media_callbacks = {}
local dynamic_add_media_raw = core.dynamic_add_media_raw
core.dynamic_add_media_raw = nil
function core.dynamic_add_media(filepath, callback)
local ret = dynamic_add_media_raw(filepath)
if ret == false then
return ret
-- PNG encoder safety wrapper
local o_encode_png = core.encode_png
function core.encode_png(width, height, data, compression)
if type(width) ~= "number" then
error("Incorrect type for 'width', expected number, got " .. type(width))
end
if callback == nil then
core.log("deprecated", "Calling minetest.dynamic_add_media without "..
"a callback is deprecated and will stop working in future versions.")
else
-- At the moment async loading is not actually implemented, so we
-- immediately call the callback ourselves
for _, name in ipairs(ret) do
callback(name)
if type(height) ~= "number" then
error("Incorrect type for 'height', expected number, got " .. type(height))
end
local expected_byte_count = width * height * 4
if type(data) ~= "table" and type(data) ~= "string" then
error("Incorrect type for 'height', expected table or string, got " .. type(height))
end
local data_length = type(data) == "table" and #data * 4 or string.len(data)
if data_length ~= expected_byte_count then
error(string.format(
"Incorrect length of 'data', width and height imply %d bytes but %d were provided",
expected_byte_count,
data_length
))
end
if type(data) == "table" then
local dataBuf = {}
for i = 1, #data do
dataBuf[i] = core.colorspec_to_bytes(data[i])
end
data = table.concat(dataBuf)
end
return true
return o_encode_png(width, height, data, compression or 6)
end

View File

@ -97,10 +97,13 @@ core.register_privilege("rollback", {
description = S("Can use the rollback functionality"),
give_to_singleplayer = false,
})
core.register_privilege("debug", {
description = S("Allows enabling various debug options that may affect gameplay"),
core.register_privilege("basic_debug", {
description = S("Can view more debug info that might give a gameplay advantage"),
give_to_singleplayer = false,
})
core.register_privilege("debug", {
description = S("Can enable wireframe"),
give_to_singleplayer = false,
give_to_admin = true,
})
core.register_can_bypass_userlimit(function(name, ip)

View File

@ -610,6 +610,7 @@ core.registered_on_modchannel_message, core.register_on_modchannel_message = mak
core.registered_on_player_inventory_actions, core.register_on_player_inventory_action = make_registration()
core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration()
core.registered_on_rightclickplayers, core.register_on_rightclickplayer = make_registration()
core.registered_on_liquid_transformed, core.register_on_liquid_transformed = make_registration()
--
-- Compatibility for on_mapgen_init()

View File

@ -1,6 +1,6 @@
VoxelArea = {
MinEdge = {x=1, y=1, z=1},
MaxEdge = {x=0, y=0, z=0},
MinEdge = vector.new(1, 1, 1),
MaxEdge = vector.new(0, 0, 0),
ystride = 0,
zstride = 0,
}
@ -19,11 +19,11 @@ end
function VoxelArea:getExtent()
local MaxEdge, MinEdge = self.MaxEdge, self.MinEdge
return {
x = MaxEdge.x - MinEdge.x + 1,
y = MaxEdge.y - MinEdge.y + 1,
z = MaxEdge.z - MinEdge.z + 1,
}
return vector.new(
MaxEdge.x - MinEdge.x + 1,
MaxEdge.y - MinEdge.y + 1,
MaxEdge.z - MinEdge.z + 1
)
end
function VoxelArea:getVolume()

View File

@ -30,6 +30,7 @@ local clientpath = scriptdir .. "client" .. DIR_DELIM
local commonpath = scriptdir .. "common" .. DIR_DELIM
local asyncpath = scriptdir .. "async" .. DIR_DELIM
dofile(commonpath .. "vector.lua")
dofile(commonpath .. "strict.lua")
dofile(commonpath .. "serialize.lua")
dofile(commonpath .. "misc_helpers.lua")

View File

@ -21,6 +21,7 @@ Player @1 does not exist.=Spieler @1 existiert nicht.
Return list of all online players with privilege=Liste aller Spieler mit einem Privileg ausgeben
Invalid parameters (see /help haspriv).=Ungültige Parameter (siehe „/help haspriv“).
Unknown privilege!=Unbekanntes Privileg!
No online player has the "@1" privilege.=Kein online spielender Spieler hat das „@1“-Privileg.
Players online with the "@1" privilege: @2=Derzeit online spielende Spieler mit dem „@1“-Privileg: @2
Your privileges are insufficient.=Ihre Privilegien sind unzureichend.
Your privileges are insufficient. '@1' only allows you to grant: @2=Ihre Privilegien sind unzureichend. Mit „@1“ können Sie nur folgendes gewähren: @2
@ -87,6 +88,7 @@ Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in
Successfully reset light in the area ranging from @1 to @2.=Das Licht im Gebiet zwischen @1 und @2 wurde erfolgreich zurückgesetzt.
Failed to load one or more blocks in area.=Fehlgeschlagen: Ein oder mehrere Kartenblöcke im Gebiet konnten nicht geladen werden.
List mods installed on the server=Installierte Mods auf dem Server auflisten
No mods installed.=Es sind keine Mods installiert.
Cannot give an empty item.=Ein leerer Gegenstand kann nicht gegeben werden.
Cannot give an unknown item.=Ein unbekannter Gegenstand kann nicht gegeben werden.
Giving 'ignore' is not allowed.=„ignore“ darf nicht gegeben werden.
@ -143,8 +145,8 @@ Invalid hour (must be between 0 and 23 inclusive).=Ungültige Stunde (muss zwisc
Invalid minute (must be between 0 and 59 inclusive).=Ungültige Minute (muss zwischen 0 und 59 inklusive liegen).
Show day count since world creation=Anzahl Tage seit der Erschaffung der Welt anzeigen
Current day is @1.=Aktueller Tag ist @1.
[<delay_in_seconds> | -1] [reconnect] [<message>]=[<Verzögerung_in_Sekunden> | -1] [reconnect] [<Nachricht>]
Shutdown server (-1 cancels a delayed shutdown)=Server herunterfahren (-1 bricht einen verzögerten Abschaltvorgang ab)
[<delay_in_seconds> | -1] [-r] [<message>]=[<Verzögerung_in_Sekunden> | -1] [-r] [<Nachricht>]
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=Server herunterfahren (-1 bricht einen verzögerten Abschaltvorgang ab, -r erlaubt Spielern, sich wiederzuverbinden)
Server shutting down (operator request).=Server wird heruntergefahren (Betreiberanfrage).
Ban the IP of a player or show the ban list=Die IP eines Spielers verbannen oder die Bannliste anzeigen
The ban list is empty.=Die Bannliste ist leer.
@ -191,6 +193,7 @@ Available commands:=Verfügbare Befehle:
Command not available: @1=Befehl nicht verfügbar: @1
[all | privs | <cmd>]=[all | privs | <Befehl>]
Get help for commands or list privileges=Hilfe für Befehle erhalten oder Privilegien auflisten
Available privileges:=Verfügbare Privilegien:
Command=Befehl
Parameters=Parameter
For more information, click on any entry in the list.=Für mehr Informationen klicken Sie auf einen beliebigen Eintrag in der Liste.
@ -200,7 +203,6 @@ Available commands: (see also: /help <cmd>)=Verfügbare Befehle: (siehe auch: /h
Close=Schließen
Privilege=Privileg
Description=Beschreibung
Available privileges:=Verfügbare Privilegien:
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<Filter>] | dump [<Filter>] | save [<Format> [<Filter>]]
Handle the profiler and profiling data=Den Profiler und Profilingdaten verwalten
Statistics written to action log.=Statistiken zum Aktionsprotokoll geschrieben.

View File

@ -21,6 +21,7 @@ Player @1 does not exist.=Il giocatore @1 non esiste.
Return list of all online players with privilege=Ritorna una lista di tutti i giocatori connessi col tale privilegio
Invalid parameters (see /help haspriv).=Parametri non validi (vedi /help haspriv).
Unknown privilege!=Privilegio sconosciuto!
No online player has the "@1" privilege.=
Players online with the "@1" privilege: @2=Giocatori connessi con il privilegio "@1": @2
Your privileges are insufficient.=I tuoi privilegi sono insufficienti.
Your privileges are insufficient. '@1' only allows you to grant: @2=
@ -87,6 +88,7 @@ Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in
Successfully reset light in the area ranging from @1 to @2.=Luce nell'area tra @1 e @2 reimpostata con successo.
Failed to load one or more blocks in area.=Errore nel caricare uno o più blocchi mappa nell'area.
List mods installed on the server=Elenca le mod installate nel server
No mods installed.=
Cannot give an empty item.=Impossibile dare un oggetto vuoto.
Cannot give an unknown item.=Impossibile dare un oggetto sconosciuto.
Giving 'ignore' is not allowed.=Non è permesso dare 'ignore'.
@ -143,8 +145,8 @@ Invalid hour (must be between 0 and 23 inclusive).=Ora non valida (deve essere t
Invalid minute (must be between 0 and 59 inclusive).=Minuto non valido (deve essere tra 0 e 59 inclusi)
Show day count since world creation=Mostra il conteggio dei giorni da quando il mondo è stato creato
Current day is @1.=Giorno attuale: @1.
[<delay_in_seconds> | -1] [reconnect] [<message>]=[<ritardo_in_secondi> | -1] [reconnect] [<messaggio>]
Shutdown server (-1 cancels a delayed shutdown)=Arresta il server (-1 annulla un arresto programmato)
[<delay_in_seconds> | -1] [-r] [<message>]=
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=
Server shutting down (operator request).=Arresto del server in corso (per richiesta dell'operatore)
Ban the IP of a player or show the ban list=Bandisce l'IP del giocatore o mostra la lista di quelli banditi
The ban list is empty.=La lista banditi è vuota.
@ -191,6 +193,7 @@ Available commands:=Comandi disponibili:
Command not available: @1=Comando non disponibile: @1
[all | privs | <cmd>]=[all | privs | <comando>]
Get help for commands or list privileges=Richiama la finestra d'aiuto dei comandi o dei privilegi
Available privileges:=Privilegi disponibili:
Command=Comando
Parameters=Parametri
For more information, click on any entry in the list.=Per più informazioni, clicca su una qualsiasi voce dell'elenco.
@ -200,7 +203,6 @@ Available commands: (see also: /help <cmd>)=Comandi disponibili: (vedi anche /he
Close=Chiudi
Privilege=Privilegio
Description=Descrizione
Available privileges:=Privilegi disponibili:
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filtro>] | dump [<filtro>] | save [<formato> [<filtro>]] | reset
Handle the profiler and profiling data=Gestisce il profiler e i dati da esso elaborati
Statistics written to action log.=Statistiche scritte nel log delle azioni.
@ -242,6 +244,8 @@ Profile saved to @1=
##### not used anymore #####
[<delay_in_seconds> | -1] [reconnect] [<message>]=[<ritardo_in_secondi> | -1] [reconnect] [<messaggio>]
Shutdown server (-1 cancels a delayed shutdown)=Arresta il server (-1 annulla un arresto programmato)
<name> (<privilege> | all)=<nome> (<privilegio> | all)
<privilege> | all=<privilegio> | all
Can modify 'shout' and 'interact' privileges=Si possono modificare i privilegi 'shout' e 'interact'

View File

@ -21,6 +21,7 @@ Player @1 does not exist.=
Return list of all online players with privilege=
Invalid parameters (see /help haspriv).=
Unknown privilege!=
No online player has the "@1" privilege.=
Players online with the "@1" privilege: @2=
Your privileges are insufficient.=
Your privileges are insufficient. '@1' only allows you to grant: @2=
@ -87,6 +88,7 @@ Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in
Successfully reset light in the area ranging from @1 to @2.=
Failed to load one or more blocks in area.=
List mods installed on the server=
No mods installed.=
Cannot give an empty item.=
Cannot give an unknown item.=
Giving 'ignore' is not allowed.=
@ -143,8 +145,8 @@ Invalid hour (must be between 0 and 23 inclusive).=
Invalid minute (must be between 0 and 59 inclusive).=
Show day count since world creation=
Current day is @1.=
[<delay_in_seconds> | -1] [reconnect] [<message>]=
Shutdown server (-1 cancels a delayed shutdown)=
[<delay_in_seconds> | -1] [-r] [<message>]=
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=
Server shutting down (operator request).=
Ban the IP of a player or show the ban list=
The ban list is empty.=
@ -191,6 +193,7 @@ Available commands:=
Command not available: @1=
[all | privs | <cmd>]=
Get help for commands or list privileges=
Available privileges:=
Command=
Parameters=
For more information, click on any entry in the list.=
@ -200,7 +203,6 @@ Available commands: (see also: /help <cmd>)=
Close=
Privilege=
Description=
Available privileges:=
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=
Handle the profiler and profiling data=
Statistics written to action log.=

View File

@ -57,24 +57,39 @@ local filter_types_type = {
"txp",
}
local REASON_NEW = "new"
local REASON_UPDATE = "update"
local REASON_DEPENDENCY = "dependency"
local function get_download_url(package, reason)
local base_url = core.settings:get("contentdb_url")
local ret = base_url .. ("/packages/%s/%s/releases/%d/download/"):format(package.author, package.name, package.release)
if reason then
ret = ret .. "?reason=" .. reason
end
return ret
end
local function download_package(param)
if core.download_file(param.package.url, param.filename) then
if core.download_file(param.url, param.filename) then
return {
filename = param.filename,
successful = true,
}
else
core.log("error", "downloading " .. dump(param.package.url) .. " failed")
core.log("error", "downloading " .. dump(param.url) .. " failed")
return {
successful = false,
}
end
end
local function start_install(package)
local function start_install(package, reason)
local params = {
package = package,
url = get_download_url(package, reason),
filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip",
}
@ -135,7 +150,7 @@ local function start_install(package)
if next then
table.remove(download_queue, 1)
start_install(next)
start_install(next.package, next.reason)
end
ui.update()
@ -151,12 +166,12 @@ local function start_install(package)
end
end
local function queue_download(package)
local function queue_download(package, reason)
local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads"))
if number_downloading < max_concurrent_downloads then
start_install(package)
start_install(package, reason)
else
table.insert(download_queue, package)
table.insert(download_queue, { package = package, reason = reason })
package.queued = true
end
end
@ -407,12 +422,12 @@ function install_dialog.handle_submit(this, fields)
end
if fields.install_all then
queue_download(install_dialog.package)
queue_download(install_dialog.package, REASON_NEW)
if install_dialog.will_install_deps then
for _, dep in pairs(install_dialog.dependencies) do
if not dep.is_optional and not dep.installed and dep.package then
queue_download(dep.package)
queue_download(dep.package, REASON_DEPENDENCY)
end
end
end
@ -560,18 +575,25 @@ function store.load()
end
store.packages_full = core.parse_json(response.data) or {}
store.aliases = {}
for _, package in pairs(store.packages_full) do
package.url = base_url .. "/packages/" ..
package.author .. "/" .. package.name ..
"/releases/" .. package.release .. "/download/"
local name_len = #package.name
if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then
package.id = package.author:lower() .. "/" .. package.name:sub(1, name_len - 5)
else
package.id = package.author:lower() .. "/" .. package.name
end
if package.aliases then
for _, alias in ipairs(package.aliases) do
-- We currently don't support name changing
local suffix = "/" .. package.name
if alias:sub(-#suffix) == suffix then
store.aliases[alias:lower()] = package.id
end
end
end
end
store.packages_full_unordered = store.packages_full
@ -584,7 +606,8 @@ function store.update_paths()
pkgmgr.refresh_globals()
for _, mod in pairs(pkgmgr.clientmods:get_list()) do
if mod.author and mod.release > 0 then
mod_hash[mod.author:lower() .. "/" .. mod.name] = mod
local id = mod.author:lower() .. "/" .. mod.name
mod_hash[store.aliases[id] or id] = mod
end
end
@ -592,14 +615,16 @@ function store.update_paths()
pkgmgr.update_gamelist()
for _, game in pairs(pkgmgr.games) do
if game.author ~= "" and game.release > 0 then
game_hash[game.author:lower() .. "/" .. game.id] = game
local id = game.author:lower() .. "/" .. game.id
game_hash[store.aliases[id] or id] = game
end
end
local txp_hash = {}
for _, txp in pairs(pkgmgr.get_texture_packs()) do
if txp.author and txp.release > 0 then
txp_hash[txp.author:lower() .. "/" .. txp.name] = txp
local id = txp.author:lower() .. "/" .. txp.name
txp_hash[store.aliases[id] or id] = txp
end
end
@ -915,7 +940,7 @@ function store.handle_submit(this, fields)
local package = store.packages_full[i]
if package.path and package.installed_release < package.release and
not (package.downloading or package.queued) then
queue_download(package)
queue_download(package, REASON_UPDATE)
end
end
return true
@ -948,7 +973,7 @@ function store.handle_submit(this, fields)
this:hide()
dlg:show()
else
queue_download(package)
queue_download(package, package.path and REASON_UPDATE or REASON_NEW)
end
end

View File

@ -31,6 +31,10 @@ end
-- returns error message, or nil
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
-- strip carriage returns (CR, /r)
line = line:gsub("\r", "")
-- comment
local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
if comment then
@ -650,7 +654,7 @@ local function create_change_setting_formspec(dialogdata)
-- Third row
add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
add_field(3.6, "te_persist", fgettext("Persistance"), t[8])
add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
height = height + 1.1

View File

@ -18,8 +18,14 @@
local enable_gamebar = PLATFORM ~= "Android"
local current_game, singleplayer_refresh_gamebar
local valid_disabled_settings = {
["enable_damage"]=true,
["creative_mode"]=true,
["enable_server"]=true,
}
if enable_gamebar then
-- Currently chosen game in gamebar for theming and filtering
function current_game()
local last_game_id = core.settings:get("menu_last_game")
local game = pkgmgr.find_by_gameid(last_game_id)
@ -102,37 +108,87 @@ if enable_gamebar then
btnbar:add_button("game_open_cdb", "", plus_image, fgettext("Install games from ContentDB"))
end
else
-- Currently chosen game in gamebar: no gamebar -> no "current" game
function current_game()
return nil
end
end
local function get_disabled_settings(game)
if not game then
return {}
end
local gameconfig = Settings(game.path .. "/game.conf")
local disabled_settings = {}
if gameconfig then
local disabled_settings_str = (gameconfig:get("disabled_settings") or ""):split()
for _, value in pairs(disabled_settings_str) do
local state = false
value = value:trim()
if string.sub(value, 1, 1) == "!" then
state = true
value = string.sub(value, 2)
end
if valid_disabled_settings[value] then
disabled_settings[value] = state
else
core.log("error", "Invalid disabled setting in game.conf: "..tostring(value))
end
end
end
return disabled_settings
end
local function get_formspec(tabview, name, tabdata)
local retval = ""
local index = filterlist.get_current_index(menudata.worldlist,
tonumber(core.settings:get("mainmenu_last_selected_world"))
)
tonumber(core.settings:get("mainmenu_last_selected_world")))
local list = menudata.worldlist:get_list()
local world = list and index and list[index]
local gameid = world and world.gameid
local game = gameid and pkgmgr.find_by_gameid(gameid)
local disabled_settings = get_disabled_settings(game)
local creative, damage, host = "", "", ""
-- Y offsets for game settings checkboxes
local y = -0.2
local yo = 0.45
if disabled_settings["creative_mode"] == nil then
creative = "checkbox[0,"..y..";cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
dump(core.settings:get_bool("creative_mode")) .. "]"
y = y + yo
end
if disabled_settings["enable_damage"] == nil then
damage = "checkbox[0,"..y..";cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
dump(core.settings:get_bool("enable_damage")) .. "]"
y = y + yo
end
if disabled_settings["enable_server"] == nil then
host = "checkbox[0,"..y..";cb_server;".. fgettext("Host Server") ..";" ..
dump(core.settings:get_bool("enable_server")) .. "]"
y = y + yo
end
retval = retval ..
"button[3.9,3.8;2.8,1;world_delete;".. fgettext("Delete") .. "]" ..
"button[6.55,3.8;2.8,1;world_configure;".. fgettext("Select Mods") .. "]" ..
"button[9.2,3.8;2.8,1;world_create;".. fgettext("New") .. "]" ..
"label[3.9,-0.05;".. fgettext("Select World:") .. "]"..
"checkbox[0,-0.20;cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
dump(core.settings:get_bool("creative_mode")) .. "]"..
"checkbox[0,0.25;cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
dump(core.settings:get_bool("enable_damage")) .. "]"..
"checkbox[0,0.7;cb_server;".. fgettext("Host Server") ..";" ..
dump(core.settings:get_bool("enable_server")) .. "]" ..
creative ..
damage ..
host ..
"textlist[3.9,0.4;7.9,3.45;sp_worlds;" ..
menu_render_worldlist() ..
";" .. index .. "]"
if core.settings:get_bool("enable_server") then
if core.settings:get_bool("enable_server") and disabled_settings["enable_server"] == nil then
retval = retval ..
"button[7.9,4.75;4.1,1;play;".. fgettext("Host Game") .. "]" ..
"checkbox[0,1.15;cb_server_announce;" .. fgettext("Announce Server") .. ";" ..
"checkbox[0,"..y..";cb_server_announce;" .. fgettext("Announce Server") .. ";" ..
dump(core.settings:get_bool("server_announce")) .. "]" ..
"field[0.3,2.85;3.8,0.5;te_playername;" .. fgettext("Name") .. ";" ..
core.formspec_escape(core.settings:get("name")) .. "]" ..
@ -227,9 +283,21 @@ local function main_button_handler(this, fields, name, tabdata)
-- Update last game
local world = menudata.worldlist:get_raw_element(gamedata.selected_world)
local game_obj
if world then
local game = pkgmgr.find_by_gameid(world.gameid)
core.settings:set("menu_last_game", game.id)
game_obj = pkgmgr.find_by_gameid(world.gameid)
core.settings:set("menu_last_game", game_obj.id)
end
local disabled_settings = get_disabled_settings(game_obj)
for k, _ in pairs(valid_disabled_settings) do
local v = disabled_settings[k]
if v ~= nil then
if k == "enable_server" and v == true then
error("Setting 'enable_server' cannot be force-enabled! The game.conf needs to be fixed.")
end
core.settings:set_bool(k, disabled_settings[k])
end
end
if core.settings:get_bool("enable_server") then

View File

@ -43,6 +43,14 @@ local labels = {
fgettext("2x"),
fgettext("4x"),
fgettext("8x")
},
shadow_levels = {
fgettext("Disabled"),
fgettext("Very Low"),
fgettext("Low"),
fgettext("Medium"),
fgettext("High"),
fgettext("Ultra High")
}
}
@ -66,6 +74,10 @@ local dd_options = {
antialiasing = {
table.concat(labels.antialiasing, ","),
{"0", "2", "4", "8"}
},
shadow_levels = {
table.concat(labels.shadow_levels, ","),
{ "0", "1", "2", "3", "4", "5" }
}
}
@ -110,6 +122,15 @@ local getSettingIndex = {
end
end
return 1
end,
ShadowMapping = function()
local shadow_setting = core.settings:get("shadow_levels")
for i = 1, #dd_options.shadow_levels[2] do
if shadow_setting == dd_options.shadow_levels[2][i] then
return i
end
end
return 1
end
}
@ -197,7 +218,10 @@ local function formspec(tabview, name, tabdata)
"checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
.. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
"checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"..
"label[8.25,3.0;" .. fgettext("Dynamic shadows: ") .. "]" ..
"dropdown[8.25,3.5;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";"
.. getSettingIndex.ShadowMapping() .. "]"
else
tab_string = tab_string ..
"label[8.38,0.7;" .. core.colorize("#888888",
@ -207,7 +231,9 @@ local function formspec(tabview, name, tabdata)
"label[8.38,1.7;" .. core.colorize("#888888",
fgettext("Waving Leaves")) .. "]" ..
"label[8.38,2.2;" .. core.colorize("#888888",
fgettext("Waving Plants")) .. "]"
fgettext("Waving Plants")) .. "]"..
"label[8.38,2.7;" .. core.colorize("#888888",
fgettext("Dynamic shadows")) .. "]"
end
return tab_string
@ -333,6 +359,34 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
ddhandled = true
end
for i = 1, #labels.shadow_levels do
if fields["dd_shadows"] == labels.shadow_levels[i] then
core.settings:set("shadow_levels", dd_options.shadow_levels[2][i])
ddhandled = true
end
end
if fields["dd_shadows"] == labels.shadow_levels[1] then
core.settings:set("enable_dynamic_shadows", "false")
else
local shadow_presets = {
[2] = { 80, 512, "true", 0, "false" },
[3] = { 120, 1024, "true", 1, "false" },
[4] = { 350, 2048, "true", 1, "false" },
[5] = { 350, 2048, "true", 2, "true" },
[6] = { 450, 4096, "true", 2, "true" },
}
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
if s then
core.settings:set("enable_dynamic_shadows", "true")
core.settings:set("shadow_map_max_distance", s[1])
core.settings:set("shadow_map_texture_size", s[2])
core.settings:set("shadow_map_texture_32bit", s[3])
core.settings:set("shadow_filters", s[4])
core.settings:set("shadow_map_color", s[5])
end
end
return ddhandled
end

View File

@ -2,6 +2,7 @@ _G.core = {}
_G.unpack = table.unpack
_G.serverlistmgr = {}
dofile("builtin/common/vector.lua")
dofile("builtin/common/misc_helpers.lua")
dofile("builtin/mainmenu/serverlistmgr.lua")

View File

@ -525,7 +525,7 @@ texture_clean_transparent (Clean transparent textures) bool false
# can be blurred, so automatically upscale them with nearest-neighbor
# interpolation to preserve crisp pixels. This sets the minimum texture size
# for the upscaled textures; higher values look sharper, but require more
# memory. Powers of 2 are recommended. This setting is ONLY applies if
# memory. Powers of 2 are recommended. This setting is ONLY applied if
# bilinear/trilinear/anisotropic filtering is enabled.
# This is also used as the base node texture size for world-aligned
# texture autoscaling.
@ -594,6 +594,58 @@ enable_waving_leaves (Waving leaves) bool false
# Requires shaders to be enabled.
enable_waving_plants (Waving plants) bool false
[***Dynamic shadows]
# Set to true to enable Shadow Mapping.
# Requires shaders to be enabled.
enable_dynamic_shadows (Dynamic shadows) bool false
# Set the shadow strength.
# Lower value means lighter shadows, higher value means darker shadows.
shadow_strength (Shadow strength) float 0.2 0.05 1.0
# Maximum distance to render shadows.
shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 120.0 10.0 1000.0
# Texture size to render the shadow map on.
# This must be a power of two.
# Bigger numbers create better shadows but it is also more expensive.
shadow_map_texture_size (Shadow map texture size) int 1024 128 8192
# Sets shadow texture quality to 32 bits.
# On false, 16 bits texture will be used.
# This can cause much more artifacts in the shadow.
shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true
# Enable Poisson disk filtering.
# On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering.
shadow_poisson_filter (Poisson filtering) bool true
# Define shadow filtering quality
# This simulates the soft shadows effect by applying a PCF or Poisson disk
# but also uses more resources.
shadow_filters (Shadow filter quality) enum 1 0,1,2
# Enable colored shadows.
# On true translucent nodes cast colored shadows. This is expensive.
shadow_map_color (Colored shadows) bool false
# Spread a complete update of shadow map over given amount of frames.
# Higher values might make shadows laggy, lower values
# will consume more resources.
# Minimum value: 1; maximum value: 16
shadow_update_frames (Map shadows update frames) int 8 1 16
# Set the soft shadow radius size.
# Lower values mean sharper shadows, bigger values mean softer shadows.
# Minimum value: 1.0; maxiumum value: 10.0
shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0
# Set the tilt of Sun/Moon orbit in degrees
# Value of 0 means no tilt / vertical orbit.
# Minimum value: 0.0; maximum value: 60.0
shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 0.0 60.0
[**Advanced]
# Arm inertia, gives a more realistic movement of
@ -620,10 +672,10 @@ viewing_range (Viewing range) int 190 20 4000
# 0.1 = Default, 0.25 = Good value for weaker tablets.
near_plane (Near plane) float 0.1 0 0.25
# Width component of the initial window size.
# Width component of the initial window size. Ignored in fullscreen mode.
screen_w (Screen width) int 1024 1
# Height component of the initial window size.
# Height component of the initial window size. Ignored in fullscreen mode.
screen_h (Screen height) int 600 1
# Save window size automatically when modified.
@ -632,9 +684,6 @@ autosave_screensize (Autosave screen size) bool true
# Fullscreen mode.
fullscreen (Full screen) bool false
# Bits per pixel (aka color depth) in fullscreen mode.
fullscreen_bpp (Full screen BPP) int 24
# Vertical screen synchronization.
vsync (VSync) bool false
@ -678,7 +727,7 @@ texture_path (Texture path) path
# Note: On Android, stick with OGLES1 if unsure! App may fail to start otherwise.
# On other platforms, OpenGL is recommended.
# Shaders are supported by OpenGL (desktop only) and OGLES2 (experimental)
video_driver (Video driver) enum opengl null,software,burningsvideo,direct3d8,direct3d9,opengl,ogles1,ogles2
video_driver (Video driver) enum opengl opengl,ogles1,ogles2
# Radius of cloud area stated in number of 64 node cloud squares.
# Values larger than 26 will start to produce sharp cutoffs at cloud area corners.
@ -739,7 +788,7 @@ selectionbox_width (Selection box width) int 2 1 5
crosshair_color (Crosshair color) string (255,255,255)
# Crosshair alpha (opaqueness, between 0 and 255).
# Also controls the object crosshair color
# This also applies to the object crosshair.
crosshair_alpha (Crosshair alpha) int 255 0 255
# Maximum number of recent chat messages to show
@ -936,6 +985,12 @@ mute_sound (Mute sound) bool false
[Client]
# Clickable weblinks (middle-click or ctrl-left-click) enabled in chat console output.
clickable_chat_weblinks (Chat weblinks) bool false
# Optional override for chat weblink color.
chat_weblink_color (Weblink color) string
[*Network]
# Address to connect to.
@ -948,9 +1003,9 @@ address (Server address) string
remote_port (Remote port) int 30000 1 65535
# Prometheus listener address.
# If minetest is compiled with ENABLE_PROMETHEUS option enabled,
# If Minetest is compiled with ENABLE_PROMETHEUS option enabled,
# enable metrics listener for Prometheus on that address.
# Metrics can be fetch on http://127.0.0.1:30000/metrics
# Metrics can be fetched on http://127.0.0.1:30000/metrics
prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000
# Save the map received by the client on disk.
@ -1057,11 +1112,10 @@ full_block_send_enable_min_time_from_building (Delay in sending blocks after bui
# client number.
max_packets_per_iteration (Max. packets per iteration) int 1024
# ZLib compression level to use when sending mapblocks to the client.
# -1 - Zlib's default compression level
# 0 - no compresson, fastest
# Compression level to use when sending mapblocks to the client.
# -1 - use default compression level
# 0 - least compresson, fastest
# 9 - best compression, slowest
# (levels 1-3 use Zlib's "fast" method, 4-9 use the normal method)
map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9
[*Game]
@ -1260,12 +1314,11 @@ max_objects_per_block (Maximum objects per block) int 64
# See https://www.sqlite.org/pragma.html#pragma_synchronous
sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2
# ZLib compression level to use when saving mapblocks to disk.
# -1 - Zlib's default compression level
# 0 - no compresson, fastest
# Compression level to use when saving mapblocks to disk.
# -1 - use default compression level
# 0 - least compresson, fastest
# 9 - best compression, slowest
# (levels 1-3 use Zlib's "fast" method, 4-9 use the normal method)
map_compression_level_disk (Map Compression Level for Disk Storage) int 3 -1 9
map_compression_level_disk (Map Compression Level for Disk Storage) int -1 -1 9
# Length of a server tick and the interval at which objects are generally updated over
# network.
@ -1437,9 +1490,6 @@ curl_parallel_limit (cURL parallel limit) int 8
# Maximum time a file download (e.g. a mod download) may take, stated in milliseconds.
curl_file_download_timeout (cURL file download timeout) int 300000
# Makes DirectX work with LuaJIT. Disable if it causes troubles.
high_precision_fpu (High-precision FPU) bool true
# Replaces the default main menu with a custom one.
main_menu_script (Main menu script) string
@ -2178,15 +2228,15 @@ chunksize (Chunk size) int 5
enable_mapgen_debug_info (Mapgen debug) bool false
# Maximum number of blocks that can be queued for loading.
emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 1024
emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 1024 1 1000000
# Maximum number of blocks to be queued that are to be loaded from file.
# This limit is enforced per player.
emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 128
emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 128 1 1000000
# Maximum number of blocks to be queued that are to be generated.
# This limit is enforced per player.
emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 128
emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 128 1 1000000
# Number of emerge threads to use.
# Value 0:

View File

@ -7,7 +7,22 @@ uniform vec3 eyePosition;
// The cameraOffset is the current center of the visible world.
uniform vec3 cameraOffset;
uniform float animationTimer;
#ifdef ENABLE_DYNAMIC_SHADOWS
// shadow texture
uniform sampler2D ShadowMapSampler;
// shadow uniforms
uniform vec3 v_LightDirection;
uniform float f_textureresolution;
uniform mat4 m_ShadowViewProj;
uniform float f_shadowfar;
varying float normalOffsetScale;
varying float adj_shadow_strength;
varying float cosLight;
varying float f_normal_length;
#endif
varying vec3 vNormal;
varying vec3 vPosition;
// World position in the visible world (i.e. relative to the cameraOffset.)
// This can be used for many shader effects without loss of precision.
@ -22,10 +37,392 @@ varying mediump vec2 varTexCoord;
centroid varying vec2 varTexCoord;
#endif
varying vec3 eyeVec;
varying float nightRatio;
const float fogStart = FOG_START;
const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
#ifdef ENABLE_DYNAMIC_SHADOWS
const float bias0 = 0.9;
const float zPersFactor = 0.5;
const float bias1 = 1.0 - bias0 + 1e-6;
vec4 getPerspectiveFactor(in vec4 shadowPosition)
{
float pDistance = length(shadowPosition.xy);
float pFactor = pDistance * bias0 + bias1;
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
return shadowPosition;
}
// assuming near is always 1.0
float getLinearDepth()
{
return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0));
}
vec3 getLightSpacePosition()
{
vec4 pLightSpace;
// some drawtypes have zero normals, so we need to handle it :(
#if DRAW_TYPE == NDT_PLANTLIKE
pLightSpace = m_ShadowViewProj * vec4(worldPosition, 1.0);
#else
float offsetScale = (0.0057 * getLinearDepth() + normalOffsetScale);
pLightSpace = m_ShadowViewProj * vec4(worldPosition + offsetScale * normalize(vNormal), 1.0);
#endif
pLightSpace = getPerspectiveFactor(pLightSpace);
return pLightSpace.xyz * 0.5 + 0.5;
}
// custom smoothstep implementation because it's not defined in glsl1.2
// https://docs.gl/sl4/smoothstep
float mtsmoothstep(in float edge0, in float edge1, in float x)
{
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
#ifdef COLORED_SHADOWS
// c_precision of 128 fits within 7 base-10 digits
const float c_precision = 128.0;
const float c_precisionp1 = c_precision + 1.0;
float packColor(vec3 color)
{
return floor(color.b * c_precision + 0.5)
+ floor(color.g * c_precision + 0.5) * c_precisionp1
+ floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
}
vec3 unpackColor(float value)
{
vec3 color;
color.b = mod(value, c_precisionp1) / c_precision;
color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision;
color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision;
return color;
}
vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
float visibility = step(0.0, realDistance - texDepth.r);
vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
if (visibility < 0.1) {
visibility = step(0.0, realDistance - texDepth.b);
result = vec4(visibility, unpackColor(texDepth.a));
}
return result;
}
#else
float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
float visibility = step(0.0, realDistance - texDepth);
return visibility;
}
#endif
#if SHADOW_FILTER == 2
#define PCFBOUND 3.5
#define PCFSAMPLES 64.0
#elif SHADOW_FILTER == 1
#define PCFBOUND 1.5
#if defined(POISSON_FILTER)
#define PCFSAMPLES 32.0
#else
#define PCFSAMPLES 16.0
#endif
#else
#define PCFBOUND 0.0
#if defined(POISSON_FILTER)
#define PCFSAMPLES 4.0
#else
#define PCFSAMPLES 1.0
#endif
#endif
#ifdef COLORED_SHADOWS
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy);
float depth = max(realDistance - texDepth.r, realDistance - texDepth.b);
return depth;
}
#else
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
float depth = realDistance - texDepth;
return depth;
}
#endif
float getBaseLength(vec2 smTexCoord)
{
float l = length(2.0 * smTexCoord.xy - 1.0); // length in texture coords
return bias1 / (1.0 / l - bias0); // return to undistorted coords
}
float getDeltaPerspectiveFactor(float l)
{
return 0.1 / (bias0 * l + bias1); // original distortion factor, divided by 10
}
float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier)
{
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
// Return fast if sharp shadows are requested
if (SOFTSHADOWRADIUS <= 1.0) {
perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS);
}
vec2 clampedpos;
float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5);
float y, x;
float depth = 0.0;
float pointDepth;
float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND);
int n = 0;
for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y);
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius);
clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy;
pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance);
if (pointDepth > -0.01) {
depth += pointDepth;
n += 1;
}
}
depth = depth / n;
depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius);
}
#ifdef POISSON_FILTER
const vec2[64] poissonDisk = vec2[64](
vec2(0.170019, -0.040254),
vec2(-0.299417, 0.791925),
vec2(0.645680, 0.493210),
vec2(-0.651784, 0.717887),
vec2(0.421003, 0.027070),
vec2(-0.817194, -0.271096),
vec2(-0.705374, -0.668203),
vec2(0.977050, -0.108615),
vec2(0.063326, 0.142369),
vec2(0.203528, 0.214331),
vec2(-0.667531, 0.326090),
vec2(-0.098422, -0.295755),
vec2(-0.885922, 0.215369),
vec2(0.566637, 0.605213),
vec2(0.039766, -0.396100),
vec2(0.751946, 0.453352),
vec2(0.078707, -0.715323),
vec2(-0.075838, -0.529344),
vec2(0.724479, -0.580798),
vec2(0.222999, -0.215125),
vec2(-0.467574, -0.405438),
vec2(-0.248268, -0.814753),
vec2(0.354411, -0.887570),
vec2(0.175817, 0.382366),
vec2(0.487472, -0.063082),
vec2(0.355476, 0.025357),
vec2(-0.084078, 0.898312),
vec2(0.488876, -0.783441),
vec2(0.470016, 0.217933),
vec2(-0.696890, -0.549791),
vec2(-0.149693, 0.605762),
vec2(0.034211, 0.979980),
vec2(0.503098, -0.308878),
vec2(-0.016205, -0.872921),
vec2(0.385784, -0.393902),
vec2(-0.146886, -0.859249),
vec2(0.643361, 0.164098),
vec2(0.634388, -0.049471),
vec2(-0.688894, 0.007843),
vec2(0.464034, -0.188818),
vec2(-0.440840, 0.137486),
vec2(0.364483, 0.511704),
vec2(0.034028, 0.325968),
vec2(0.099094, -0.308023),
vec2(0.693960, -0.366253),
vec2(0.678884, -0.204688),
vec2(0.001801, 0.780328),
vec2(0.145177, -0.898984),
vec2(0.062655, -0.611866),
vec2(0.315226, -0.604297),
vec2(-0.780145, 0.486251),
vec2(-0.371868, 0.882138),
vec2(0.200476, 0.494430),
vec2(-0.494552, -0.711051),
vec2(0.612476, 0.705252),
vec2(-0.578845, -0.768792),
vec2(-0.772454, -0.090976),
vec2(0.504440, 0.372295),
vec2(0.155736, 0.065157),
vec2(0.391522, 0.849605),
vec2(-0.620106, -0.328104),
vec2(0.789239, -0.419965),
vec2(-0.545396, 0.538133),
vec2(-0.178564, -0.596057)
);
#ifdef COLORED_SHADOWS
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
}
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
int end_offset = int(samples) + init_offset;
for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x];
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / samples;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
}
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
int end_offset = int(samples) + init_offset;
for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x];
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / samples;
}
#endif
#else
/* poisson filter disabled */
#ifdef COLORED_SHADOWS
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
}
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float texture_size = 1.0 / (f_textureresolution * 0.5);
float y, x;
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
int n = 0;
// basic PCF filter
for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y); // screen offset
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
n += 1;
}
return visibility / n;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
}
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float texture_size = 1.0 / (f_textureresolution * 0.5);
float y, x;
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
int n = 0;
// basic PCF filter
for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y); // screen offset
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
n += 1;
}
return visibility / n;
}
#endif
#endif
#endif
#if ENABLE_TONE_MAPPING
/* Hable's UC2 Tone mapping parameters
@ -58,25 +455,62 @@ vec4 applyToneMapping(vec4 color)
}
#endif
void main(void)
{
vec3 color;
vec2 uv = varTexCoord.st;
vec4 base = texture2D(baseTexture, uv).rgba;
#ifdef USE_DISCARD
// If alpha is zero, we can just discard the pixel. This fixes transparency
// on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
// and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
if (base.a == 0.0) {
#ifdef USE_DISCARD
if (base.a == 0.0)
discard;
#endif
#ifdef USE_DISCARD_REF
if (base.a < 0.5)
discard;
}
#endif
color = base.rgb;
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
#ifdef ENABLE_DYNAMIC_SHADOWS
float shadow_int = 0.0;
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
vec3 posLightSpace = getLightSpacePosition();
float distance_rate = (1 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 20.0));
float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1, posLightSpace.z ),0.0);
if (distance_rate > 1e-7) {
#ifdef COLORED_SHADOWS
vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
shadow_int = visibility.r;
shadow_color = visibility.gba;
#else
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
#endif
shadow_int *= distance_rate;
shadow_int *= 1.0 - nightRatio;
}
if (f_normal_length != 0 && cosLight < 0.035) {
shadow_int = max(shadow_int, min(clamp(1.0-nightRatio, 0.0, 1.0), 1 - clamp(cosLight, 0.0, 0.035)/0.035));
}
shadow_int = 1.0 - (shadow_int * f_adj_shadow_strength);
col.rgb = mix(shadow_color,col.rgb,shadow_int)*shadow_int;
// col.r = 0.5 * clamp(getPenumbraRadius(ShadowMapSampler, posLightSpace.xy, posLightSpace.z, 1.0) / SOFTSHADOWRADIUS, 0.0, 1.0) + 0.5 * col.r;
#endif
#if ENABLE_TONE_MAPPING
col = applyToneMapping(col);
#endif
@ -94,6 +528,6 @@ void main(void)
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
col = mix(skyBgColor, col, clarity);
col = vec4(col.rgb, base.a);
gl_FragColor = col;
}

View File

@ -1,5 +1,4 @@
uniform mat4 mWorld;
// Color of the light emitted by the sun.
uniform vec3 dayLight;
uniform vec3 eyePosition;
@ -8,6 +7,7 @@ uniform vec3 eyePosition;
uniform vec3 cameraOffset;
uniform float animationTimer;
varying vec3 vNormal;
varying vec3 vPosition;
// World position in the visible world (i.e. relative to the cameraOffset.)
// This can be used for many shader effects without loss of precision.
@ -24,13 +24,38 @@ varying mediump vec2 varTexCoord;
#else
centroid varying vec2 varTexCoord;
#endif
varying vec3 eyeVec;
#ifdef ENABLE_DYNAMIC_SHADOWS
// shadow uniforms
uniform vec3 v_LightDirection;
uniform float f_textureresolution;
uniform mat4 m_ShadowViewProj;
uniform float f_shadowfar;
uniform float f_shadow_strength;
uniform float f_timeofday;
varying float cosLight;
varying float normalOffsetScale;
varying float adj_shadow_strength;
varying float f_normal_length;
#endif
varying vec3 eyeVec;
varying float nightRatio;
// Color of the light emitted by the light sources.
const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
const float e = 2.718281828459;
const float BS = 10.0;
#ifdef ENABLE_DYNAMIC_SHADOWS
// custom smoothstep implementation because it's not defined in glsl1.2
// https://docs.gl/sl4/smoothstep
float mtsmoothstep(in float edge0, in float edge1, in float x)
{
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
#endif
float smoothCurve(float x)
{
@ -86,6 +111,9 @@ float snoise(vec3 p)
#endif
void main(void)
{
varTexCoord = inTexCoord0.st;
@ -136,10 +164,9 @@ void main(void)
gl_Position = mWorldViewProj * inVertexPosition;
#endif
vPosition = gl_Position.xyz;
eyeVec = -(mWorldView * inVertexPosition).xyz;
vNormal = inVertexNormal;
// Calculate color.
// Red, green and blue components are pre-multiplied with
@ -152,7 +179,7 @@ void main(void)
vec4 color = inVertexColor;
#endif
// The alpha gives the ratio of sunlight in the incoming light.
float nightRatio = 1.0 - color.a;
nightRatio = 1.0 - color.a;
color.rgb = color.rgb * (color.a * dayLight.rgb +
nightRatio * artificialLight.rgb) * 2.0;
color.a = 1.0;
@ -164,4 +191,26 @@ void main(void)
0.07 * brightness);
varColor = clamp(color, 0.0, 1.0);
#ifdef ENABLE_DYNAMIC_SHADOWS
vec3 nNormal = normalize(vNormal);
cosLight = dot(nNormal, -v_LightDirection);
float texelSize = 767.0 / f_textureresolution;
float slopeScale = clamp(1.0 - abs(cosLight), 0.0, 1.0);
normalOffsetScale = texelSize * slopeScale;
if (f_timeofday < 0.2) {
adj_shadow_strength = f_shadow_strength * 0.5 *
(1.0 - mtsmoothstep(0.18, 0.2, f_timeofday));
} else if (f_timeofday >= 0.8) {
adj_shadow_strength = f_shadow_strength * 0.5 *
mtsmoothstep(0.8, 0.83, f_timeofday);
} else {
adj_shadow_strength = f_shadow_strength *
mtsmoothstep(0.20, 0.25, f_timeofday) *
(1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
}
f_normal_length = length(vNormal);
#endif
}

View File

@ -23,8 +23,22 @@ const float BS = 10.0;
const float fogStart = FOG_START;
const float fogShadingParameter = 1.0 / (1.0 - fogStart);
#if ENABLE_TONE_MAPPING
#ifdef ENABLE_DYNAMIC_SHADOWS
// shadow texture
uniform sampler2D ShadowMapSampler;
// shadow uniforms
uniform vec3 v_LightDirection;
uniform float f_textureresolution;
uniform mat4 m_ShadowViewProj;
uniform float f_shadowfar;
uniform float f_timeofday;
varying float normalOffsetScale;
varying float adj_shadow_strength;
varying float cosLight;
varying float f_normal_length;
#endif
#if ENABLE_TONE_MAPPING
/* Hable's UC2 Tone mapping parameters
A = 0.22;
B = 0.30;
@ -55,30 +69,306 @@ vec4 applyToneMapping(vec4 color)
}
#endif
#ifdef ENABLE_DYNAMIC_SHADOWS
const float bias0 = 0.9;
const float zPersFactor = 0.5;
const float bias1 = 1.0 - bias0;
vec4 getPerspectiveFactor(in vec4 shadowPosition)
{
float pDistance = length(shadowPosition.xy);
float pFactor = pDistance * bias0 + bias1;
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
return shadowPosition;
}
// assuming near is always 1.0
float getLinearDepth()
{
return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0));
}
vec3 getLightSpacePosition()
{
vec4 pLightSpace;
float normalBias = 0.0005 * getLinearDepth() * cosLight + normalOffsetScale;
pLightSpace = m_ShadowViewProj * vec4(worldPosition + normalBias * normalize(vNormal), 1.0);
pLightSpace = getPerspectiveFactor(pLightSpace);
return pLightSpace.xyz * 0.5 + 0.5;
}
#ifdef COLORED_SHADOWS
// c_precision of 128 fits within 7 base-10 digits
const float c_precision = 128.0;
const float c_precisionp1 = c_precision + 1.0;
float packColor(vec3 color)
{
return floor(color.b * c_precision + 0.5)
+ floor(color.g * c_precision + 0.5) * c_precisionp1
+ floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
}
vec3 unpackColor(float value)
{
vec3 color;
color.b = mod(value, c_precisionp1) / c_precision;
color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision;
color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision;
return color;
}
vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
float visibility = step(0.0, (realDistance-2e-5) - texDepth.r);
vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
if (visibility < 0.1) {
visibility = step(0.0, (realDistance-2e-5) - texDepth.r);
result = vec4(visibility, unpackColor(texDepth.a));
}
return result;
}
#else
float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
float visibility = step(0.0, (realDistance-2e-5) - texDepth);
return visibility;
}
#endif
#if SHADOW_FILTER == 2
#define PCFBOUND 3.5
#define PCFSAMPLES 64.0
#elif SHADOW_FILTER == 1
#define PCFBOUND 1.5
#if defined(POISSON_FILTER)
#define PCFSAMPLES 32.0
#else
#define PCFSAMPLES 16.0
#endif
#else
#define PCFBOUND 0.0
#if defined(POISSON_FILTER)
#define PCFSAMPLES 4.0
#else
#define PCFSAMPLES 1.0
#endif
#endif
#ifdef POISSON_FILTER
const vec2[64] poissonDisk = vec2[64](
vec2(0.170019, -0.040254),
vec2(-0.299417, 0.791925),
vec2(0.645680, 0.493210),
vec2(-0.651784, 0.717887),
vec2(0.421003, 0.027070),
vec2(-0.817194, -0.271096),
vec2(-0.705374, -0.668203),
vec2(0.977050, -0.108615),
vec2(0.063326, 0.142369),
vec2(0.203528, 0.214331),
vec2(-0.667531, 0.326090),
vec2(-0.098422, -0.295755),
vec2(-0.885922, 0.215369),
vec2(0.566637, 0.605213),
vec2(0.039766, -0.396100),
vec2(0.751946, 0.453352),
vec2(0.078707, -0.715323),
vec2(-0.075838, -0.529344),
vec2(0.724479, -0.580798),
vec2(0.222999, -0.215125),
vec2(-0.467574, -0.405438),
vec2(-0.248268, -0.814753),
vec2(0.354411, -0.887570),
vec2(0.175817, 0.382366),
vec2(0.487472, -0.063082),
vec2(0.355476, 0.025357),
vec2(-0.084078, 0.898312),
vec2(0.488876, -0.783441),
vec2(0.470016, 0.217933),
vec2(-0.696890, -0.549791),
vec2(-0.149693, 0.605762),
vec2(0.034211, 0.979980),
vec2(0.503098, -0.308878),
vec2(-0.016205, -0.872921),
vec2(0.385784, -0.393902),
vec2(-0.146886, -0.859249),
vec2(0.643361, 0.164098),
vec2(0.634388, -0.049471),
vec2(-0.688894, 0.007843),
vec2(0.464034, -0.188818),
vec2(-0.440840, 0.137486),
vec2(0.364483, 0.511704),
vec2(0.034028, 0.325968),
vec2(0.099094, -0.308023),
vec2(0.693960, -0.366253),
vec2(0.678884, -0.204688),
vec2(0.001801, 0.780328),
vec2(0.145177, -0.898984),
vec2(0.062655, -0.611866),
vec2(0.315226, -0.604297),
vec2(-0.780145, 0.486251),
vec2(-0.371868, 0.882138),
vec2(0.200476, 0.494430),
vec2(-0.494552, -0.711051),
vec2(0.612476, 0.705252),
vec2(-0.578845, -0.768792),
vec2(-0.772454, -0.090976),
vec2(0.504440, 0.372295),
vec2(0.155736, 0.065157),
vec2(0.391522, 0.849605),
vec2(-0.620106, -0.328104),
vec2(0.789239, -0.419965),
vec2(-0.545396, 0.538133),
vec2(-0.178564, -0.596057)
);
#ifdef COLORED_SHADOWS
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float texture_size = 1.0 / (f_textureresolution * 0.5);
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
int end_offset = int(PCFSAMPLES) + init_offset;
for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / PCFSAMPLES;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
int end_offset = int(PCFSAMPLES) + init_offset;
for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / PCFSAMPLES;
}
#endif
#else
/* poisson filter disabled */
#ifdef COLORED_SHADOWS
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float sradius=0.0;
if( PCFBOUND>0)
sradius = SOFTSHADOWRADIUS / PCFBOUND;
float texture_size = 1.0 / (f_textureresolution * 0.5);
float y, x;
// basic PCF filter
for (y = -PCFBOUND; y <= PCFBOUND; y += 1.0)
for (x = -PCFBOUND; x <= PCFBOUND; x += 1.0) {
clampedpos = vec2(x,y) * texture_size* sradius + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / PCFSAMPLES;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float sradius=0.0;
if( PCFBOUND>0)
sradius = SOFTSHADOWRADIUS / PCFBOUND;
float texture_size = 1.0 / (f_textureresolution * 0.5);
float y, x;
// basic PCF filter
for (y = -PCFBOUND; y <= PCFBOUND; y += 1.0)
for (x = -PCFBOUND; x <= PCFBOUND; x += 1.0) {
clampedpos = vec2(x,y) * texture_size * sradius + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / PCFSAMPLES;
}
#endif
#endif
#endif
void main(void)
{
vec3 color;
vec2 uv = varTexCoord.st;
vec4 base = texture2D(baseTexture, uv).rgba;
#ifdef USE_DISCARD
// If alpha is zero, we can just discard the pixel. This fixes transparency
// on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
// and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
if (base.a == 0.0) {
#ifdef USE_DISCARD
if (base.a == 0.0)
discard;
#endif
#ifdef USE_DISCARD_REF
if (base.a < 0.5)
discard;
}
#endif
color = base.rgb;
vec4 col = vec4(color.rgb, base.a);
col.rgb *= varColor.rgb;
col.rgb *= emissiveColor.rgb * vIDiff;
#ifdef ENABLE_DYNAMIC_SHADOWS
float shadow_int = 0.0;
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
vec3 posLightSpace = getLightSpacePosition();
#ifdef COLORED_SHADOWS
vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
shadow_int = visibility.r;
shadow_color = visibility.gba;
#else
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
#endif
if (f_normal_length != 0 && cosLight <= 0.001) {
shadow_int = clamp(shadow_int + 0.5 * abs(cosLight), 0.0, 1.0);
}
shadow_int = 1.0 - (shadow_int * adj_shadow_strength);
col.rgb = mix(shadow_color, col.rgb, shadow_int) * shadow_int;
#endif
#if ENABLE_TONE_MAPPING
col = applyToneMapping(col);
#endif

View File

@ -13,12 +13,37 @@ varying mediump vec2 varTexCoord;
centroid varying vec2 varTexCoord;
#endif
#ifdef ENABLE_DYNAMIC_SHADOWS
// shadow uniforms
uniform vec3 v_LightDirection;
uniform float f_textureresolution;
uniform mat4 m_ShadowViewProj;
uniform float f_shadowfar;
uniform float f_shadow_strength;
uniform float f_timeofday;
varying float cosLight;
varying float normalOffsetScale;
varying float adj_shadow_strength;
varying float f_normal_length;
#endif
varying vec3 eyeVec;
varying float vIDiff;
const float e = 2.718281828459;
const float BS = 10.0;
#ifdef ENABLE_DYNAMIC_SHADOWS
// custom smoothstep implementation because it's not defined in glsl1.2
// https://docs.gl/sl4/smoothstep
float mtsmoothstep(in float edge0, in float edge1, in float x)
{
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
#endif
float directional_ambient(vec3 normal)
{
vec3 v = normal * normal;
@ -54,4 +79,25 @@ void main(void)
#else
varColor = inVertexColor;
#endif
#ifdef ENABLE_DYNAMIC_SHADOWS
cosLight = max(0.0, dot(vNormal, -v_LightDirection));
float texelSize = 0.51;
float slopeScale = clamp(1.0 - cosLight, 0.0, 1.0);
normalOffsetScale = texelSize * slopeScale;
if (f_timeofday < 0.2) {
adj_shadow_strength = f_shadow_strength * 0.5 *
(1.0 - mtsmoothstep(0.18, 0.2, f_timeofday));
} else if (f_timeofday >= 0.8) {
adj_shadow_strength = f_shadow_strength * 0.5 *
mtsmoothstep(0.8, 0.83, f_timeofday);
} else {
adj_shadow_strength = f_shadow_strength *
mtsmoothstep(0.20, 0.25, f_timeofday) *
(1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
}
f_normal_length = length(vNormal);
#endif
}

View File

@ -0,0 +1,13 @@
uniform sampler2D ColorMapSampler;
varying vec4 tPos;
void main()
{
vec4 col = texture2D(ColorMapSampler, gl_TexCoord[0].st);
if (col.a < 0.70)
discard;
float depth = 0.5 + tPos.z * 0.5;
gl_FragColor = vec4(depth, 0.0, 0.0, 1.0);
}

View File

@ -0,0 +1,38 @@
uniform sampler2D ColorMapSampler;
varying vec4 tPos;
#ifdef COLORED_SHADOWS
// c_precision of 128 fits within 7 base-10 digits
const float c_precision = 128.0;
const float c_precisionp1 = c_precision + 1.0;
float packColor(vec3 color)
{
return floor(color.b * c_precision + 0.5)
+ floor(color.g * c_precision + 0.5) * c_precisionp1
+ floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
}
const vec3 black = vec3(0.0);
#endif
void main()
{
vec4 col = texture2D(ColorMapSampler, gl_TexCoord[0].st);
#ifndef COLORED_SHADOWS
if (col.a < 0.5)
discard;
#endif
float depth = 0.5 + tPos.z * 0.5;
// ToDo: Liso: Apply movement on waving plants
// depth in [0, 1] for texture
//col.rgb = col.a == 1.0 ? vec3(1.0) : col.rgb;
#ifdef COLORED_SHADOWS
float packedColor = packColor(mix(col.rgb, black, col.a));
gl_FragColor = vec4(depth, packedColor, 0.0,1.0);
#else
gl_FragColor = vec4(depth, 0.0, 0.0, 1.0);
#endif
}

View File

@ -0,0 +1,26 @@
uniform mat4 LightMVP; // world matrix
varying vec4 tPos;
const float bias0 = 0.9;
const float zPersFactor = 0.5;
const float bias1 = 1.0 - bias0 + 1e-6;
vec4 getPerspectiveFactor(in vec4 shadowPosition)
{
float pDistance = length(shadowPosition.xy);
float pFactor = pDistance * bias0 + bias1;
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
return shadowPosition;
}
void main()
{
vec4 pos = LightMVP * gl_Vertex;
tPos = getPerspectiveFactor(LightMVP * gl_Vertex);
gl_Position = vec4(tPos.xyz, 1.0);
gl_TexCoord[0].st = gl_MultiTexCoord0.st;
}

View File

@ -0,0 +1,26 @@
uniform mat4 LightMVP; // world matrix
varying vec4 tPos;
const float bias0 = 0.9;
const float zPersFactor = 0.5;
const float bias1 = 1.0 - bias0 + 1e-6;
vec4 getPerspectiveFactor(in vec4 shadowPosition)
{
float pDistance = length(shadowPosition.xy);
float pFactor = pDistance * bias0 + bias1;
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
return shadowPosition;
}
void main()
{
vec4 pos = LightMVP * gl_Vertex;
tPos = getPerspectiveFactor(pos);
gl_Position = vec4(tPos.xyz, 1.0);
gl_TexCoord[0].st = gl_MultiTexCoord0.st;
}

View File

@ -0,0 +1,23 @@
uniform sampler2D ShadowMapClientMap;
#ifdef COLORED_SHADOWS
uniform sampler2D ShadowMapClientMapTraslucent;
#endif
uniform sampler2D ShadowMapSamplerdynamic;
void main() {
#ifdef COLORED_SHADOWS
vec2 first_depth = texture2D(ShadowMapClientMap, gl_TexCoord[0].st).rg;
vec2 depth_splitdynamics = vec2(texture2D(ShadowMapSamplerdynamic, gl_TexCoord[2].st).r, 0.0);
if (first_depth.r > depth_splitdynamics.r)
first_depth = depth_splitdynamics;
vec2 depth_color = texture2D(ShadowMapClientMapTraslucent, gl_TexCoord[1].st).rg;
gl_FragColor = vec4(first_depth.r, first_depth.g, depth_color.r, depth_color.g);
#else
float first_depth = texture2D(ShadowMapClientMap, gl_TexCoord[0].st).r;
float depth_splitdynamics = texture2D(ShadowMapSamplerdynamic, gl_TexCoord[2].st).r;
first_depth = min(first_depth, depth_splitdynamics);
gl_FragColor = vec4(first_depth, 0.0, 0.0, 1.0);
#endif
}

View File

@ -0,0 +1,9 @@
void main()
{
vec4 uv = vec4(gl_Vertex.xyz, 1.0) * 0.5 + 0.5;
gl_TexCoord[0] = uv;
gl_TexCoord[1] = uv;
gl_TexCoord[2] = uv;
gl_Position = vec4(gl_Vertex.xyz, 1.0);
}

View File

@ -1,61 +0,0 @@
mark_as_advanced(IRRLICHT_DLL)
# Find include directory and libraries
# find our fork first, then upstream (TODO: remove this?)
foreach(libname IN ITEMS IrrlichtMt Irrlicht)
string(TOLOWER "${libname}" libname2)
find_path(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h
DOC "Path to the directory with IrrlichtMt includes"
PATHS
/usr/local/include/${libname2}
/usr/include/${libname2}
/system/develop/headers/${libname2} #Haiku
PATH_SUFFIXES "include/${libname2}"
)
find_library(IRRLICHT_LIBRARY NAMES lib${libname} ${libname}
DOC "Path to the IrrlichtMt library file"
PATHS
/usr/local/lib
/usr/lib
/system/develop/lib # Haiku
)
if(IRRLICHT_INCLUDE_DIR OR IRRLICHT_LIBRARY)
break()
endif()
endforeach()
# Handholding for users
if(IRRLICHT_INCLUDE_DIR AND (NOT IS_DIRECTORY "${IRRLICHT_INCLUDE_DIR}" OR
NOT EXISTS "${IRRLICHT_INCLUDE_DIR}/irrlicht.h"))
message(WARNING "IRRLICHT_INCLUDE_DIR was set to ${IRRLICHT_INCLUDE_DIR} "
"but irrlicht.h does not exist inside. The path will not be used.")
unset(IRRLICHT_INCLUDE_DIR CACHE)
endif()
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR APPLE)
# (only on systems where we're sure how a valid library looks like)
if(IRRLICHT_LIBRARY AND (IS_DIRECTORY "${IRRLICHT_LIBRARY}" OR
NOT IRRLICHT_LIBRARY MATCHES "\\.(a|so|dylib|lib)([.0-9]+)?$"))
message(WARNING "IRRLICHT_LIBRARY was set to ${IRRLICHT_LIBRARY} "
"but is not a valid library file. The path will not be used.")
unset(IRRLICHT_LIBRARY CACHE)
endif()
endif()
# On Windows, find the DLL for installation
if(WIN32)
# If VCPKG_APPLOCAL_DEPS is ON, dll's are automatically handled by VCPKG
if(NOT VCPKG_APPLOCAL_DEPS)
find_file(IRRLICHT_DLL NAMES IrrlichtMt.dll
DOC "Path of the IrrlichtMt dll (for installation)"
)
endif()
endif(WIN32)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Irrlicht DEFAULT_MSG IRRLICHT_LIBRARY IRRLICHT_INCLUDE_DIR)

Some files were not shown because too many files have changed in this diff Show More