Compare commits

...

72 Commits

Author SHA1 Message Date
Cora de la Mouche 6df1590803
Add list setting -> chatcmd def for external access (#62)
* Add list setting -> chatcmd def for external access

* Document the _list_setting field

... in both the chatcmd def and minetest.register_list_command

* doc: add missing 'by'

* remove underscore (_list_setting -> list_setting)

---------

Co-authored-by: Lizzy Fleckenstein <eliasfleckenstein@web.de>
2023-06-23 18:26:33 +02:00
Cora de la Mouche 2c050a42d7
Merge pull request #61 from dragonfireclient/serverinfo_seed
Add mapseed to get_server_info table
2023-06-20 02:43:39 +02:00
cora 64e7dda46e Add server info seed field to documentation 2023-06-15 20:15:17 +02:00
cora 7a59bcda75 Add mapseed to get_server_info table 2023-06-15 19:40:02 +02:00
Cora de la Mouche f8fd5c11b6
Merge pull request #59 from PrairieAstronomer/readme_irrlicht_change
Add exact irrlichtmt version to the clone command
2023-04-11 00:34:58 +02:00
PrairieWind c66ae6717a Add exact irrlichtmt version to the clone command 2023-03-11 13:20:00 -07:00
Elias Fleckenstein 296cce39d3
Fix upstream merge issues 2022-06-07 03:31:06 +02:00
Elias Fleckenstein b11c0a6721
Merge branch 'master' of https://github.com/minetest/minetest 2022-06-07 03:19:05 +02:00
Lars Müller edc7df5480
core.formspec_escape: Restore backwards compat
Support numbers as arguments by using `string.gsub(text, ...)` instead of `text:gsub(...)` which will coerce `text` to a string
2022-06-06 18:32:28 +02:00
paradust7 951604e29f Remove invalid fps_max on Mac 2022-06-06 12:03:28 +02:00
Zughy 381f84ee27
Bug report template: ask contributors to provide a code snippet (#12405) 2022-06-05 16:12:39 -04:00
sfan5 14c283a623 Fix crash in commit a69b7abe00 2022-06-05 19:00:14 +02:00
rubenwardy 4baf56520d
Android: Add support for sharing debug.txt (#12370) 2022-06-05 17:42:09 +01:00
sfan5 a69b7abe00 Improve LBMManager::applyLBMs() code
Fixes a possible bug for lbms on content ID zero and removes unsafe casts.
2022-06-05 17:48:51 +02:00
rubenwardy 03d86ea0b4
Add register dialog to separate login/register (#12185)
New users find Minetest's account system confusing.
This change moves username/password to a new dialog,
with login and register buttons added to the Join Game tab.

The old registration confirmation dialog is removed in
favour of the new dialog.

Fixes #8138
2022-06-05 17:47:38 +02:00
ROllerozxa 21323ef1ff
Hide "Autosave Screen Size" on Android 2022-06-05 15:20:39 +02:00
savilli 1f39948bc3
Fix BSD iconv declaration 2022-06-05 15:20:29 +02:00
Lars Mueller e82985c0a1 Document itemstrings with metadata 2022-06-05 15:20:13 +02:00
JosiahWI 8e5bd82c4d
fix integer overflow in mapgen (#11641)
* fix integer overflow in mapgen

Some calculations involving the magic seed had overflow because the result of an intermediate arithmetic step could not fit in an s32. By making the magic seed unsigned, the other operand in the equation will be cast to unsigned, and possibly other operands or intermediate operands. This will result in unexpected behavior if an operand is negative, which is technically possible, but logically should not happen.

* comment noise2d bitshift

While working through the code I was momentarily concerned that the right bitshift in noise2d could fill ones in some cases. It turns out that with signed integers, this is indeed true, but this one is shifting an unsigned integer, so the behavior is as expected. I put a comment here to clarify this, in case someone else wonders the same thing down the line.

* noise2d and noise3d unittests

I have added 3 tests each for noise2d and noise3d, testing all zero inputs, a very large seed (case which caused UB in the old implementation) and some fun primes I picked for no particular reason. This should be sufficient to demonstrate that the behavior of the new implementation has not changed. I used uniform initialization because it is a good feature of C++11. Please do not explode.

* uncomment the noise2d bitshift

This reverts commit 583b77ee9f1ad6bb77340ebb5ba51eb9a88ff51c. It's a
well-defined language semantic; it doesn't need to be commented.

* code cleanliness
2022-06-03 20:51:58 -04:00
sfan5 575caa8015 Properly keep noclip state in Game and ClientMap 2022-06-03 21:48:52 +02:00
sfan5 5f3af7d18b Remove obsolete eye_height related workaround
This was added a long time ago in 42bbd5c9ae
and meant to fix prevent the view becoming black when jumping into a
ceiling, this no longer happens today.
2022-06-03 21:48:52 +02:00
Wuzzy 6a6b579c54
Add helper functions to make tool usable n times (#12047) 2022-06-03 21:47:04 +02:00
ROllerozxa 6d163b72dc
Rework main menu confirmation dialogs (#12356) 2022-06-03 21:46:37 +02:00
Zughy b72932b445 Docs: remove unimplemented `preserve` field in crafting recipes 2022-06-03 21:46:26 +02:00
Elias Fleckenstein 827b9f8d70
Merge branch 'master' of https://github.com/minetest/minetest 2022-06-02 20:54:02 +02:00
sfan5 9fc018ded1 Fix use-after-free in node meta cleanup
bug introduced in 8908a91016
2022-05-29 16:30:13 +02:00
sfan5 a9a207685a Reject registering node with empty name
fixes #10769
2022-05-29 14:00:19 +02:00
sfan5 c1d03695d4 Minor code improvements around active block keeping 2022-05-29 14:00:19 +02:00
sfan5 ea74680df4 Immediately activate blocks when a player joins
issue: #10884
This makes it possible for objects to immediately be activated,
but doesn't guarantee it since blocks may still need be emerged.
2022-05-29 14:00:19 +02:00
stefan bb671c3089 Remove debug.get/setmetatable from security whitelist
fixes #12216
2022-05-29 14:00:19 +02:00
sfan5 1b68fb7683 Don't allow banning in singleplayer
fixes #11819
2022-05-29 14:00:19 +02:00
sfan5 303329f2d6 Handle lua entity HP changes correctly (like punches)
fixes #11975
2022-05-29 14:00:19 +02:00
sfan5 85c824ed13 Make sure real disconnect reason isn't overwritten
bug introduced in 2f32044273
2022-05-29 14:00:19 +02:00
sfan5 998e4820c9 Fix linking with Postgres libs on older cmake versions
closes #12149
2022-05-29 14:00:19 +02:00
sfan5 5cd7b0c6e4 Remove remains of video mode querying 2022-05-29 14:00:19 +02:00
sfan5 8908a91016 Get rid of node metadata when it becomes empty
fixes #8943
2022-05-29 14:00:19 +02:00
sfan5 261a8db9dd Optimize Server::sendMetadataChanged a bit
The distance check also never worked as intended, now fixed.
2022-05-29 14:00:19 +02:00
sfan5 f195db2d14 Add API function to invoke player respawn
closes #12272
2022-05-29 14:00:19 +02:00
sfan5 da71e86633 Protect a few more settings from being set from mods
Of those settings main_menu_script has concrete security impact, the rest are added out of abundance of caution.
2022-05-29 14:00:19 +02:00
sfan5 bccaf5fc2d Map opaque waving leaves to allfaces drawtype
fixes #9842
2022-05-29 14:00:19 +02:00
sfan5 0c6a029413 Improve a translation string
fixes #11442
2022-05-29 14:00:19 +02:00
x2048 25ba9d848d
Default settings, presets and configuration for dynamic shadows (#12359) 2022-05-29 13:58:57 +02:00
Lars Müller 76000e676b
Lua workflow: Use Leafo's Luarocks action
instead of installing outdated packages which lead to failing workflows
2022-05-28 11:31:56 +02:00
Lars Müller e8b2954586
Builtin: Optimize misc helpers (#12377)
Also add formspec_escape unit test
2022-05-27 21:40:38 +02:00
Wuzzy fe299e24d6
DevTest: Add nodes and items for testing overlays (#12304) 2022-05-27 21:39:36 +02:00
x2048 ef22c0206f
Force-update shadows when the world is changed (#12364) 2022-05-26 22:28:34 +02:00
sfan5 8b74257bf3 Reduce size of ContentFeatures structure
On my system this is a reduction from 4664 to 3704 bytes.
This is not for the sake of saving RAM but ensuring
commonly used structures fit into caches better.
2022-05-26 15:49:12 +02:00
sfan5 9a01581cdd Get rid of global buffer that would ruin concurrent MapBlock serialization 2022-05-26 15:49:12 +02:00
sfan5 5d26ac0088 Improve code in mapblock_mesh.cpp a bit 2022-05-26 15:49:12 +02:00
x2048 ed26ed5a1f
Quantize light frustum calculations (#12357)
* Quantize light frustum calculations

Reduces shadow flicker

* Fix function name to match conventions
2022-05-23 23:45:18 +02:00
sfan5 16a30556df Formally drop support for building with upstream Irrlicht
It stopped working with (at least) the last commit.
2022-05-23 22:50:58 +02:00
sfan5 5daafc9d33 Fix hash implementation for SerializedBlockCache 2022-05-23 22:50:58 +02:00
Richard Try e16a470d59
Use unordered_map instead of map for MapSectors 2022-05-23 22:50:49 +02:00
paradust7 367a2d4b29
Add missing concurrency protection in logger (#12325) 2022-05-23 22:50:25 +02:00
Wuzzy 0f9c78c3eb
Fix no_texture.png for unknown nodes with ID < 125 (#12329) 2022-05-23 22:50:10 +02:00
Zughy c660218e43
Docs: clarify spawn_by for decorations 2022-05-23 22:49:48 +02:00
Wuzzy fa682270a9
Add missing comma in example in lua_api.txt (#12339) 2022-05-22 15:23:04 -04:00
Wuzzy ac5e8176b9
Add relative numbers for commands by prepending ~ (#9588)
* Add relative numbers for commands by prepending ~

* Some builtin code cleanup

* Disallow nan and inf in minetest.string_to_area

* Remove unused local variable teleportee (makes Luacheck happy)

* Clean up core.string_to_pos

* Make area parsing less permissive

* Rewrite tests as busted tests

* /time: Fix negative minutes not working

Co-authored-by: Lars Mueller <appgurulars@gmx.de>
2022-05-22 10:28:24 -04:00
Elias Fleckenstein 3ff3103e98
Merge branch 'master' of https://github.com/minetest/minetest 2022-05-22 12:05:27 +02:00
paradust7 9f338f5a56
Replace all uses of core::list with std::list (#12313) 2022-05-22 00:11:59 +02:00
paradust7 2742fef458
Fixes needed to use irrArray backed by std::vector (#12263) 2022-05-22 00:11:49 +02:00
sfan5 bc59fcf5c5 Bump IrrlichtMt version in CI 2022-05-22 00:11:22 +02:00
sfan5 2f32044273 Don't ignore server disconnects in client code
If the server stops talking to us without saying bye we
should actually end the in-game session with an error message.
2022-05-21 17:49:55 +02:00
paradust7 371f21fb35 Fixes to Android build + option to turn LuaJIT on/off for testing purposes (#12334) 2022-05-21 17:49:41 +02:00
Zughy 8edc0fae5f
Make no_screenshot image more clear (#12346) 2022-05-21 17:48:28 +02:00
paradust7 e1f707d7e1
Patch built-in Lua to fix miscompile on Android (#12347) 2022-05-21 17:46:50 +02:00
sfan5 9ee3dc71f1 Optimize JSON string (de)serialization routines
stringstreams were shown to be slow when reading/writing single characters
and there is lots of potential by having functions perform on existing
buffers whenever possible.
2022-05-21 17:46:10 +02:00
sfan5 70dc23f996 Improve testSerializeJsonString unit tests
this also removes the requirement that / is escaped, there is
no reason for doing so.
2022-05-21 17:46:10 +02:00
rubenwardy 4e9e230e34
Deprecate game.conf name, use title instead (#12030) 2022-05-21 16:23:30 +01:00
x2048 dc45b85a54
Improve shadow filters (#12195)
* Rewrite shadow filtering for the new distortion
* Calculate penumbra radius using a single sample
* Avoid peter-panning effect due to filtering of short shadows
* Add adaptive filter quality for soft shadows
* Avoid sharp shadows on surfaces without normals (e.g. plants)
* Increase default and maximum soft shadow radius
* Make line numbers in shader errors match the code
2022-05-21 16:49:30 +02:00
x2048 a4ef62f5b2
Fix lighting of upright_sprite entities (#12336)
Use MeshNode materials to set the light since ReadOnlyMaterials is now false
2022-05-20 22:35:03 +02:00
x2048 604fb2b738
Fix lighting of the wield mesh (#12341)
* Assign node light to player before final color blend.
  Fixes day/night lightbank ratio for wield meshes
* Update wield mesh light when changing mesh
2022-05-20 22:33:52 +02:00
142 changed files with 2004 additions and 1478 deletions

View File

@ -8,8 +8,8 @@ assignees: ''
##### Minetest version
<!--
Paste Minetest version between quotes below
If you are on a devel version, please add git commit hash
Paste Minetest version between quotes below.
If you are on a devel version, please add git commit hash.
You can use `minetest --version` to find it.
-->
```
@ -29,4 +29,4 @@ OpenGL version:
<!-- Describe your problem here -->
##### Steps to reproduce
<!-- For bug reports or build issues, explain how the problem happened -->
<!-- Explain how the problem has happened, providing a minimal test (i.e. a code snippet reduced to the bone) where possible -->

View File

@ -132,7 +132,7 @@ jobs:
- name: Install deps
run: |
source ./util/ci/common.sh
install_linux_deps --old-irr clang-9
install_linux_deps clang-9
- name: Build prometheus-cpp
run: |
@ -230,7 +230,7 @@ jobs:
with:
repository: minetest/irrlicht
path: lib/irrlichtmt/
ref: "1.9.0mt5"
ref: "1.9.0mt6"
- name: Restore from cache and run vcpkg
uses: lukka/run-vcpkg@v7

View File

@ -42,9 +42,10 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Install luarocks
run: |
sudo apt-get update && sudo apt-get install -y luarocks
- uses: leafo/gh-actions-lua@v9
with:
luaVersion: "5.1.5"
- uses: leafo/gh-actions-luarocks@v4
- name: Install luarocks tools
run: |

View File

@ -22,7 +22,7 @@ on:
- '.github/workflows/macos.yml'
env:
IRRLICHT_TAG: 1.9.0mt5
IRRLICHT_TAG: 1.9.0mt6
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
MINETEST_GAME_BRANCH: master
MINETEST_GAME_NAME: minetest_game

View File

@ -9,7 +9,7 @@ stages:
- deploy
variables:
IRRLICHT_TAG: "1.9.0mt5"
IRRLICHT_TAG: "1.9.0mt6"
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH

View File

@ -78,7 +78,6 @@ if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
# IrrlichtMtConfig.cmake
message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.")
endif()
# This is done here so that relative search paths are more reasonable
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
if(BUILD_CLIENT)
@ -108,9 +107,9 @@ else()
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}")
message(FATAL_ERROR "IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}")
endif()
message(STATUS "Found Irrlicht headers: ${IRRLICHT_INCLUDE_DIR}")
message(STATUS "Found IrrlichtMt headers: ${IRRLICHT_INCLUDE_DIR}")
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
# Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES

View File

@ -63,6 +63,15 @@ Zughy:
appgurueu:
textures/base/pack/server_incompatible.png
erlehmann, Warr1024, rollerozxa:
textures/base/pack/no_screenshot.png
kilbith:
textures/base/pack/server_favorite.png
SmallJoker
textures/base/pack/server_favorite_delete.png (based on server_favorite.png)
License of Minetest source code
-------------------------------

View File

@ -181,7 +181,7 @@ Download minetest_game (otherwise only the "Development Test" game is available)
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
git clone --branch 1.9.0mt7 --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
Download source, without using Git:
@ -223,8 +223,8 @@ Run it:
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
- Debug build is slower, but gives much more useful output in a debugger.
- If you build a bare server you don't need to have the Irrlicht or IrrlichtMt library installed.
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlicht/include`.
- If you build a bare server you don't need to compile IrrlichtMt, just the headers suffice.
- In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlichtmt/include`.
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable

View File

@ -59,6 +59,17 @@
android:name=".UnzipService"
android:enabled="true"
android:exported="false" />
</application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="net.minetest.minetest.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>

View File

@ -26,6 +26,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
@ -34,10 +35,16 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import androidx.annotation.Keep;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.FileProvider;
import java.io.File;
import java.util.Objects;
// Native code finds these methods by name (see porting_android.cpp).
// This annotation prevents the minifier/Proguard from mangling them.
@Keep
public class GameActivity extends NativeActivity {
static {
System.loadLibrary("c++_shared");
@ -179,4 +186,22 @@ public class GameActivity extends NativeActivity {
public String getCachePath() {
return Utils.getCacheDirectory(this).getAbsolutePath();
}
public void shareFile(String path) {
File file = new File(path);
if (!file.exists()) {
Log.e("GameActivity", "File " + file.getAbsolutePath() + " doesn't exist");
return;
}
Uri fileUri = FileProvider.getUriForFile(this, "net.minetest.minetest.fileprovider", file);
Intent intent = new Intent(Intent.ACTION_SEND, fileUri);
intent.setDataAndType(fileUri, getContentResolver().getType(fileUri));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
Intent shareIntent = Intent.createChooser(intent, null);
startActivity(shareIntent);
}
}

View File

@ -0,0 +1,3 @@
<paths>
<external-files-path path="Minetest/" name="minetest" />
</paths>

View File

@ -1,6 +1,7 @@
LOCAL_PATH := $(call my-dir)/..
#LOCAL_ADDRESS_SANITIZER:=true
#USE_BUILTIN_LUA:=true
include $(CLEAR_VARS)
LOCAL_MODULE := Curl
@ -42,11 +43,15 @@ LOCAL_MODULE := Irrlicht
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libIrrlichtMt.a
include $(PREBUILT_STATIC_LIBRARY)
ifndef USE_BUILTIN_LUA
include $(CLEAR_VARS)
LOCAL_MODULE := LuaJIT
LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/libluajit.a
include $(PREBUILT_STATIC_LIBRARY)
endif
include $(CLEAR_VARS)
LOCAL_MODULE := OpenAL
LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a
@ -92,7 +97,6 @@ LOCAL_CFLAGS += \
-DUSE_CURL=1 \
-DUSE_SOUND=1 \
-DUSE_LEVELDB=0 \
-DUSE_LUAJIT=1 \
-DUSE_GETTEXT=1 \
-DVERSION_MAJOR=${versionMajor} \
-DVERSION_MINOR=${versionMinor} \
@ -100,6 +104,12 @@ LOCAL_CFLAGS += \
-DVERSION_EXTRA=${versionExtra} \
$(GPROF_DEF)
ifdef USE_BUILTIN_LUA
LOCAL_CFLAGS += -DUSE_LUAJIT=0
else
LOCAL_CFLAGS += -DUSE_LUAJIT=1
endif
ifdef NDEBUG
LOCAL_CFLAGS += -DNDEBUG=1
endif
@ -120,12 +130,19 @@ LOCAL_C_INCLUDES := \
deps/$(APP_ABI)/Irrlicht/include \
deps/$(APP_ABI)/Gettext/include \
deps/$(APP_ABI)/Iconv/include \
deps/$(APP_ABI)/LuaJIT/include \
deps/$(APP_ABI)/OpenAL-Soft/include \
deps/$(APP_ABI)/SQLite/include \
deps/$(APP_ABI)/Vorbis/include \
deps/$(APP_ABI)/Zstd/include
ifdef USE_BUILTIN_LUA
LOCAL_C_INCLUDES += \
../../lib/lua/src \
../../lib/bitop
else
LOCAL_C_INCLUDES += deps/$(APP_ABI)/LuaJIT/include
endif
LOCAL_SRC_FILES := \
$(wildcard ../../src/client/*.cpp) \
$(wildcard ../../src/client/*/*.cpp) \
@ -207,6 +224,41 @@ LOCAL_SRC_FILES := \
../../src/voxel.cpp \
../../src/voxelalgorithms.cpp
# Built-in Lua
ifdef USE_BUILTIN_LUA
LOCAL_SRC_FILES += \
../../lib/lua/src/lapi.c \
../../lib/lua/src/lauxlib.c \
../../lib/lua/src/lbaselib.c \
../../lib/lua/src/lcode.c \
../../lib/lua/src/ldblib.c \
../../lib/lua/src/ldebug.c \
../../lib/lua/src/ldo.c \
../../lib/lua/src/ldump.c \
../../lib/lua/src/lfunc.c \
../../lib/lua/src/lgc.c \
../../lib/lua/src/linit.c \
../../lib/lua/src/liolib.c \
../../lib/lua/src/llex.c \
../../lib/lua/src/lmathlib.c \
../../lib/lua/src/lmem.c \
../../lib/lua/src/loadlib.c \
../../lib/lua/src/lobject.c \
../../lib/lua/src/lopcodes.c \
../../lib/lua/src/loslib.c \
../../lib/lua/src/lparser.c \
../../lib/lua/src/lstate.c \
../../lib/lua/src/lstring.c \
../../lib/lua/src/lstrlib.c \
../../lib/lua/src/ltable.c \
../../lib/lua/src/ltablib.c \
../../lib/lua/src/ltm.c \
../../lib/lua/src/lundump.c \
../../lib/lua/src/lvm.c \
../../lib/lua/src/lzio.c \
../../lib/bitop/bit.c
endif
# GMP
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
@ -218,12 +270,14 @@ LOCAL_STATIC_LIBRARIES += \
Freetype \
Iconv libcharset \
Irrlicht \
LuaJIT \
OpenAL \
Gettext \
SQLite3 \
Vorbis libvorbisfile libogg \
Zstd
ifndef USE_BUILTIN_LUA
LOCAL_STATIC_LIBRARIES += LuaJIT
endif
LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES

View File

@ -74,6 +74,7 @@ if INIT == "client" then
local def = {}
def.description = desc
def.params = "del <item> | add <item> | list"
def.list_setting = setting
function def.func(param)
local list = (minetest.settings:get(setting) or ""):split(",")
if param == "list" then

View File

@ -22,7 +22,6 @@ local LIST_FORMSPEC_DESCRIPTION = [[
local F = core.formspec_escape
local S = core.get_translator("__builtin")
local check_player_privs = core.check_player_privs
-- CHAT COMMANDS FORMSPEC
@ -58,10 +57,11 @@ local function build_chatcommands_formspec(name, sel, copy)
.. "any entry in the list.").. "\n" ..
S("Double-click to copy the entry to the chat history.")
local privs = core.get_player_privs(name)
for i, data in ipairs(mod_cmds) do
rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
for j, cmds in ipairs(data[2]) do
local has_priv = check_player_privs(name, cmds[2].privs)
local has_priv = privs[cmds[2].privs]
rows[#rows + 1] = ("%s,1,%s,%s"):format(
has_priv and COLOR_GREEN or COLOR_GRAY,
cmds[1], F(cmds[2].params))

View File

@ -204,7 +204,7 @@ end
--------------------------------------------------------------------------------
function string:trim()
return (self:gsub("^%s*(.-)%s*$", "%1"))
return self:match("^%s*(.-)%s*$")
end
--------------------------------------------------------------------------------
@ -245,16 +245,16 @@ function math.round(x)
return math.ceil(x - 0.5)
end
local formspec_escapes = {
["\\"] = "\\\\",
["["] = "\\[",
["]"] = "\\]",
[";"] = "\\;",
[","] = "\\,"
}
function core.formspec_escape(text)
if text ~= nil then
text = string.gsub(text,"\\","\\\\")
text = string.gsub(text,"%]","\\]")
text = string.gsub(text,"%[","\\[")
text = string.gsub(text,";","\\;")
text = string.gsub(text,",","\\,")
end
return text
-- Use explicit character set instead of dot here because it doubles the performance
return text and string.gsub(text, "[\\%[%];,]", formspec_escapes)
end
@ -265,18 +265,21 @@ function core.wrap_text(text, max_length, as_table)
return as_table and {text} or text
end
for word in text:gmatch('%S+') do
local cur_length = #table.concat(line, ' ')
if cur_length > 0 and cur_length + #word + 1 >= max_length then
local line_length = 0
for word in text:gmatch("%S+") do
if line_length > 0 and line_length + #word + 1 >= max_length then
-- word wouldn't fit on current line, move to next line
table.insert(result, table.concat(line, ' '))
line = {}
table.insert(result, table.concat(line, " "))
line = {word}
line_length = #word
else
table.insert(line, word)
line_length = line_length + 1 + #word
end
table.insert(line, word)
end
table.insert(result, table.concat(line, ' '))
return as_table and result or table.concat(result, '\n')
table.insert(result, table.concat(line, " "))
return as_table and result or table.concat(result, "\n")
end
--------------------------------------------------------------------------------
@ -425,54 +428,50 @@ function core.string_to_pos(value)
return nil
end
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
x, y, z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
value = value:match("^%((.-)%)$") or value -- strip parentheses
local x, y, z = value:trim():match("^([%d.-]+)[,%s]%s*([%d.-]+)[,%s]%s*([%d.-]+)$")
if x and y and z then
x = tonumber(x)
y = tonumber(y)
z = tonumber(z)
return vector.new(x, y, z)
end
return nil
end
--------------------------------------------------------------------------------
function core.string_to_area(value)
local p1, p2 = unpack(value:split(") ("))
if p1 == nil or p2 == nil then
return nil
do
local rel_num_cap = "(~?-?%d*%.?%d*)" -- may be overly permissive as this will be tonumber'ed anyways
local num_delim = "[,%s]%s*"
local pattern = "^" .. table.concat({rel_num_cap, rel_num_cap, rel_num_cap}, num_delim) .. "$"
local function parse_area_string(pos, relative_to)
local pp = {}
pp.x, pp.y, pp.z = pos:trim():match(pattern)
return core.parse_coordinates(pp.x, pp.y, pp.z, relative_to)
end
p1 = core.string_to_pos(p1 .. ")")
p2 = core.string_to_pos("(" .. p2)
if p1 == nil or p2 == nil then
return nil
function core.string_to_area(value, relative_to)
local p1, p2 = value:match("^%((.-)%)%s*%((.-)%)$")
if not p1 then
return
end
p1 = parse_area_string(p1, relative_to)
p2 = parse_area_string(p2, relative_to)
if p1 == nil or p2 == nil then
return
end
return p1, p2
end
return p1, p2
end
local function test_string_to_area()
local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2, 4, -12.53)")
assert(p1.x == 10.0 and p1.y == 5 and p1.z == -2)
assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
assert(p1 == nil and p2 == nil)
end
test_string_to_area()
--------------------------------------------------------------------------------
function table.copy(t, seen)
local n = {}
@ -786,6 +785,74 @@ function core.is_nan(number)
return number ~= number
end
--[[ Helper function for parsing an optionally relative number
of a chat command parameter, using the chat command tilde notation.
Parameters:
* arg: String snippet containing the number; possible values:
* "<number>": return as number
* "~<number>": return relative_to + <number>
* "~": return relative_to
* Anything else will return `nil`
* relative_to: Number to which the `arg` number might be relative to
Returns:
A number or `nil`, depending on `arg.
Examples:
* `core.parse_relative_number("5", 10)` returns 5
* `core.parse_relative_number("~5", 10)` returns 15
* `core.parse_relative_number("~", 10)` returns 10
]]
function core.parse_relative_number(arg, relative_to)
if not arg then
return nil
elseif arg == "~" then
return relative_to
elseif string.sub(arg, 1, 1) == "~" then
local number = tonumber(string.sub(arg, 2))
if not number then
return nil
end
if core.is_nan(number) or number == math.huge or number == -math.huge then
return nil
end
return relative_to + number
else
local number = tonumber(arg)
if core.is_nan(number) or number == math.huge or number == -math.huge then
return nil
end
return number
end
end
--[[ Helper function to parse coordinates that might be relative
to another position; supports chat command tilde notation.
Intended to be used in chat command parameter parsing.
Parameters:
* x, y, z: Parsed x, y, and z coordinates as strings
* relative_to: Position to which to compare the position
Syntax of x, y and z:
* "<number>": return as number
* "~<number>": return <number> + player position on this axis
* "~": return player position on this axis
Returns: a vector or nil for invalid input or if player does not exist
]]
function core.parse_coordinates(x, y, z, relative_to)
if not relative_to then
x, y, z = tonumber(x), tonumber(y), tonumber(z)
return x and y and z and { x = x, y = y, z = z }
end
local rx = core.parse_relative_number(x, relative_to.x)
local ry = core.parse_relative_number(y, relative_to.y)
local rz = core.parse_relative_number(z, relative_to.z)
return rx and ry and rz and { x = rx, y = ry, z = rz }
end
function core.inventorycube(img1, img2, img3)
img2 = img2 or img1
img3 = img3 or img1

View File

@ -67,9 +67,107 @@ describe("pos", function()
end)
end)
describe("area parsing", function()
describe("valid inputs", function()
it("accepts absolute numbers", function()
local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2 4 -12.53)")
assert(p1.x == 10 and p1.y == 5 and p1.z == -2)
assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
end)
it("accepts relative numbers", function()
local p1, p2 = core.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})
assert(type(p1) == "table" and type(p2) == "table")
assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
p1, p2 = core.string_to_area("(1 2 3) (~5 ~-5 ~)", {x=10,y=10,z=10})
assert(type(p1) == "table" and type(p2) == "table")
assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
end)
end)
describe("invalid inputs", function()
it("rejects too few numbers", function()
local p1, p2 = core.string_to_area("(1,1) (1,1,1,1)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
end)
it("rejects too many numbers", function()
local p1, p2 = core.string_to_area("(1,1,1,1) (1,1,1,1)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
end)
it("rejects nan & inf", function()
local p1, p2 = core.string_to_area("(1,1,1) (1,1,nan)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(1,1,1) (1,1,~nan)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(1,1,1) (1,~nan,1)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(1,1,1) (1,1,inf)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(1,1,1) (1,1,~inf)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(1,1,1) (1,~inf,1)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)")
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)")
assert(p1 == nil and p2 == nil)
end)
it("rejects words", function()
local p1, p2 = core.string_to_area("bananas", {x=1,y=1,z=1})
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("bananas", "foobar")
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("bananas")
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(bananas,bananas,bananas)")
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(bananas,bananas,bananas) (bananas,bananas,bananas)")
assert(p1 == nil and p2 == nil)
end)
it("requires parenthesis & valid numbers", function()
local p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
assert(p1 == nil and p2 == nil)
p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
assert(p1 == nil and p2 == nil)
end)
end)
end)
describe("table", function()
it("indexof()", function()
assert.equal(1, table.indexof({"foo", "bar"}, "foo"))
assert.equal(-1, table.indexof({"foo", "bar"}, "baz"))
end)
end)
describe("formspec_escape", function()
it("escapes", function()
assert.equal(nil, core.formspec_escape(nil))
assert.equal("", core.formspec_escape(""))
assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\["))
end)
end)

View File

@ -130,8 +130,13 @@ local function parse_range_str(player_name, str)
return false, S("Unable to get position of player @1.", player_name)
end
else
p1, p2 = core.string_to_area(str)
if p1 == nil then
local player = core.get_player_by_name(player_name)
local relpos
if player then
relpos = player:get_pos()
end
p1, p2 = core.string_to_area(str, relpos)
if p1 == nil or p2 == nil then
return false, S("Incorrect area format. "
.. "Expected: (x1,y1,z1) (x2,y2,z2)")
end
@ -570,10 +575,15 @@ core.register_chatcommand("teleport", {
description = S("Teleport to position or player"),
privs = {teleport=true},
func = function(name, param)
local player = core.get_player_by_name(name)
local relpos
if player then
relpos = player:get_pos()
end
local p = {}
p.x, p.y, p.z = param:match("^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
p = vector.apply(p, tonumber)
if p.x and p.y and p.z then
p.x, p.y, p.z = string.match(param, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
if p and p.x and p.y and p.z then
return teleport_to_pos(name, p)
end
@ -587,9 +597,19 @@ core.register_chatcommand("teleport", {
"other players (missing privilege: @1).", "bring")
local teleportee_name
p = {}
teleportee_name, p.x, p.y, p.z = param:match(
"^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
"^([^ ]+) +([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
if teleportee_name then
local teleportee = core.get_player_by_name(teleportee_name)
if not teleportee then
return
end
relpos = teleportee:get_pos()
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
end
p = vector.apply(p, tonumber)
if teleportee_name and p.x and p.y and p.z then
if not has_bring_priv then
return false, missing_bring_msg
@ -842,7 +862,7 @@ core.register_chatcommand("spawnentity", {
description = S("Spawn entity at given (or your) position"),
privs = {give=true, interact=true},
func = function(name, param)
local entityname, p = string.match(param, "^([^ ]+) *(.*)$")
local entityname, pstr = string.match(param, "^([^ ]+) *(.*)$")
if not entityname then
return false, S("EntityName required.")
end
@ -856,11 +876,15 @@ core.register_chatcommand("spawnentity", {
if not core.registered_entities[entityname] then
return false, S("Cannot spawn an unknown entity.")
end
if p == "" then
local p
if pstr == "" then
p = player:get_pos()
else
p = core.string_to_pos(p)
if p == nil then
p = {}
p.x, p.y, p.z = string.match(pstr, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
local relpos = player:get_pos()
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
if not (p and p.x and p.y and p.z) then
return false, S("Invalid parameters (@1).", param)
end
end
@ -1019,6 +1043,13 @@ core.register_chatcommand("status", {
end,
})
local function get_time(timeofday)
local time = math.floor(timeofday * 1440)
local minute = time % 60
local hour = (time - minute) / 60
return time, hour, minute
end
core.register_chatcommand("time", {
params = S("[<0..23>:<0..59> | <0..24000>]"),
description = S("Show or set time of day"),
@ -1037,9 +1068,14 @@ core.register_chatcommand("time", {
return false, S("You don't have permission to run "
.. "this command (missing privilege: @1).", "settime")
end
local hour, minute = param:match("^(%d+):(%d+)$")
if not hour then
local new_time = tonumber(param) or -1
local relative, negative, hour, minute = param:match("^(~?)(%-?)(%d+):(%d+)$")
if not relative then -- checking the first capture against nil suffices
local new_time = core.parse_relative_number(param, core.get_timeofday() * 24000)
if not new_time then
new_time = tonumber(param) or -1
else
new_time = new_time % 24000
end
if new_time ~= new_time or new_time < 0 or new_time > 24000 then
return false, S("Invalid time (must be between 0 and 24000).")
end
@ -1047,14 +1083,29 @@ core.register_chatcommand("time", {
core.log("action", name .. " sets time to " .. new_time)
return true, S("Time of day changed.")
end
local new_time
hour = tonumber(hour)
minute = tonumber(minute)
if hour < 0 or hour > 23 then
return false, S("Invalid hour (must be between 0 and 23 inclusive).")
elseif minute < 0 or minute > 59 then
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
if relative == "" then
if hour < 0 or hour > 23 then
return false, S("Invalid hour (must be between 0 and 23 inclusive).")
elseif minute < 0 or minute > 59 then
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
end
new_time = (hour * 60 + minute) / 1440
else
if minute < 0 or minute > 59 then
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
end
local current_time = core.get_timeofday()
if negative == "-" then -- negative time
hour, minute = -hour, -minute
end
new_time = (current_time + (hour * 60 + minute) / 1440) % 1
local _
_, hour, minute = get_time(new_time)
end
core.set_timeofday((hour * 60 + minute) / 1440)
core.set_timeofday(new_time)
core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute))
return true, S("Time of day changed.")
end,
@ -1136,6 +1187,9 @@ core.register_chatcommand("ban", {
return true, S("Ban list: @1", ban_list)
end
end
if core.is_singleplayer() then
return false, S("You cannot ban players in singleplayer!")
end
if not core.get_player_by_name(param) then
return false, S("Player is not online.")
end

View File

@ -242,3 +242,11 @@ function menu_worldmt_legacy(selected)
end
end
end
function confirmation_formspec(message, confirm_id, confirm_label, cancel_id, cancel_label)
return "size[10,2.5,true]" ..
"label[0.5,0.5;" .. message .. "]" ..
"style[" .. confirm_id .. ";bgcolor=red]" ..
"button[0.5,1.5;2.5,0.5;" .. confirm_id .. ";" .. confirm_label .. "]" ..
"button[7.0,1.5;2.5,0.5;" .. cancel_id .. ";" .. cancel_label .. "]"
end

View File

@ -151,11 +151,9 @@ local function start_install(package, reason)
if conf_path then
local conf = Settings(conf_path)
if name_is_title then
conf:set("name", package.title)
else
conf:set("title", package.title)
conf:set("name", package.name)
conf:set("title", package.title)
if not name_is_title then
conf:set("name", package.name)
end
if not conf:get("description") then
conf:set("description", package.short_description)
@ -360,7 +358,7 @@ function install_dialog.get_formspec()
selected_game_idx = i
end
games[i] = core.formspec_escape(games[i].name)
games[i] = core.formspec_escape(games[i].title)
end
local selected_game = pkgmgr.games[selected_game_idx]
@ -410,7 +408,7 @@ function install_dialog.get_formspec()
"container[0.375,0.70]",
"label[0,0.25;", fgettext("Base Game:"), "]",
"dropdown[2,0;4.25,0.5;gameid;", table.concat(games, ","), ";", selected_game_idx, "]",
"dropdown[2,0;4.25,0.5;selected_game;", table.concat(games, ","), ";", selected_game_idx, "]",
"label[0,0.8;", fgettext("Dependencies:"), "]",
@ -461,9 +459,9 @@ function install_dialog.handle_submit(this, fields)
return true
end
if fields.gameid then
if fields.selected_game then
for _, game in pairs(pkgmgr.games) do
if game.name == fields.gameid then
if game.title == fields.selected_game then
core.settings:set("menu_last_game", game.id)
break
end
@ -490,12 +488,10 @@ local confirm_overwrite = {}
function confirm_overwrite.get_formspec()
local package = confirm_overwrite.package
return "size[11.5,4.5,true]" ..
"label[2,2;" ..
fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name) .. "]"..
"style[install;bgcolor=red]" ..
"button[3.25,3.5;2.5,0.5;install;" .. fgettext("Overwrite") .. "]" ..
"button[5.75,3.5;2.5,0.5;cancel;" .. fgettext("Cancel") .. "]"
return confirmation_formspec(
fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name),
'install', fgettext("Overwrite"),
'cancel', fgettext("Cancel"))
end
function confirm_overwrite.handle_submit(this, fields)

View File

@ -18,15 +18,10 @@
--------------------------------------------------------------------------------
local function delete_content_formspec(dialogdata)
local retval =
"size[11.5,4.5,true]" ..
"label[2,2;" ..
fgettext("Are you sure you want to delete \"$1\"?", dialogdata.content.name) .. "]"..
"style[dlg_delete_content_confirm;bgcolor=red]" ..
"button[3.25,3.5;2.5,0.5;dlg_delete_content_confirm;" .. fgettext("Delete") .. "]" ..
"button[5.75,3.5;2.5,0.5;dlg_delete_content_cancel;" .. fgettext("Cancel") .. "]"
return retval
return confirmation_formspec(
fgettext("Are you sure you want to delete \"$1\"?", dialogdata.content.name),
'dlg_delete_content_confirm', fgettext("Delete"),
'dlg_delete_content_cancel', fgettext("Cancel"))
end
--------------------------------------------------------------------------------

View File

@ -17,14 +17,10 @@
local function delete_world_formspec(dialogdata)
local retval =
"size[10,2.5,true]" ..
"label[0.5,0.5;" ..
fgettext("Delete World \"$1\"?", dialogdata.delete_name) .. "]" ..
"style[world_delete_confirm;bgcolor=red]" ..
"button[0.5,1.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" ..
"button[7.0,1.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]"
return retval
return confirmation_formspec(
fgettext("Delete World \"$1\"?", dialogdata.delete_name),
'world_delete_confirm', fgettext("Delete"),
'world_delete_cancel', fgettext("Cancel"))
end
local function delete_world_buttonhandler(this, fields)

View File

@ -0,0 +1,123 @@
--Minetest
--Copyright (C) 2022 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--------------------------------------------------------------------------------
local function register_formspec(dialogdata)
local title = fgettext("Joining $1", dialogdata.server and dialogdata.server.name or dialogdata.address)
local buttons_y = 4 + 1.3
if dialogdata.error then
buttons_y = buttons_y + 0.8
end
local retval = {
"formspec_version[4]",
"size[8,", tostring(buttons_y + 1.175), "]",
"set_focus[", (dialogdata.name ~= "" and "password" or "name"), "]",
"label[0.375,0.8;", title, "]",
"field[0.375,1.575;7.25,0.8;name;", core.formspec_escape(fgettext("Name")), ";",
core.formspec_escape(dialogdata.name), "]",
"pwdfield[0.375,2.875;7.25,0.8;password;", core.formspec_escape(fgettext("Password")), "]",
"pwdfield[0.375,4.175;7.25,0.8;password_2;", core.formspec_escape(fgettext("Confirm Password")), "]"
}
if dialogdata.error then
table.insert_all(retval, {
"box[0.375,", tostring(buttons_y - 0.9), ";7.25,0.6;darkred]",
"label[0.625,", tostring(buttons_y - 0.6), ";", core.formspec_escape(dialogdata.error), "]",
})
end
table.insert_all(retval, {
"container[0.375,", tostring(buttons_y), "]",
"button[0,0;2.5,0.8;dlg_register_confirm;", fgettext("Register"), "]",
"button[4.75,0;2.5,0.8;dlg_register_cancel;", fgettext("Cancel"), "]",
"container_end[]",
})
return table.concat(retval, "")
end
--------------------------------------------------------------------------------
local function register_buttonhandler(this, fields)
this.data.name = fields.name
this.data.error = nil
if fields.dlg_register_confirm or fields.key_enter then
if fields.name == "" then
this.data.error = fgettext("Missing name")
return true
end
if fields.password ~= fields.password_2 then
this.data.error = fgettext("Passwords do not match")
return true
end
gamedata.playername = fields.name
gamedata.password = fields.password
gamedata.address = this.data.address
gamedata.port = this.data.port
gamedata.allow_login_or_register = "register"
gamedata.selected_world = 0
assert(gamedata.address and gamedata.port)
local server = this.data.server
if server then
serverlistmgr.add_favorite(server)
gamedata.servername = server.name
gamedata.serverdescription = server.description
else
gamedata.servername = ""
gamedata.serverdescription = ""
serverlistmgr.add_favorite({
address = gamedata.address,
port = gamedata.port,
})
end
core.settings:set("name", fields.name)
core.settings:set("address", gamedata.address)
core.settings:set("remote_port", gamedata.port)
core.start()
end
if fields["dlg_register_cancel"] then
this:delete()
return true
end
return false
end
--------------------------------------------------------------------------------
function create_register_dialog(address, port, server)
assert(address)
assert(type(port) == "number")
local retval = dialog_create("dlg_register",
register_formspec,
register_buttonhandler,
nil)
retval.data.address = address
retval.data.port = port
retval.data.server = server
retval.data.name = core.settings:get("name") or ""
return retval
end

View File

@ -17,6 +17,7 @@
mt_color_grey = "#AAAAAA"
mt_color_blue = "#6389FF"
mt_color_lightblue = "#99CCFF"
mt_color_green = "#72FF63"
mt_color_dark_green = "#25C191"
mt_color_orange = "#FF8800"
@ -43,6 +44,7 @@ dofile(menupath .. DIR_DELIM .. "dlg_contentstore.lua")
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
local tabs = {}

View File

@ -646,6 +646,8 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
else
targetpath = core.get_gamepath() .. DIR_DELIM .. basename
end
else
error("basefolder didn't return a recognised type, this shouldn't happen")
end
-- Copy it
@ -739,7 +741,7 @@ function pkgmgr.preparemodlist(data)
retval[#retval + 1] = {
type = "game",
is_game_content = true,
name = fgettext("$1 mods", gamespec.name),
name = fgettext("$1 mods", gamespec.title),
path = gamespec.path
}
end
@ -924,10 +926,10 @@ end
function pkgmgr.gamelist()
local retval = ""
if #pkgmgr.games > 0 then
retval = retval .. core.formspec_escape(pkgmgr.games[1].name)
retval = retval .. core.formspec_escape(pkgmgr.games[1].title)
for i=2,#pkgmgr.games,1 do
retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].name)
retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].title)
end
end
return retval

View File

@ -140,7 +140,9 @@ return {
fgettext("Active renderer:") .. "\n" ..
core.formspec_escape(core.get_screen_info().render_info) .. "]"
if PLATFORM ~= "Android" then
if PLATFORM == "Android" then
fs = fs .. "button[0,4;3.5,1;share_debug;" .. fgettext("Share debug log") .. "]"
else
fs = fs .. "tooltip[userdata;" ..
fgettext("Opens the directory that contains user-provided worlds, games, mods,\n" ..
"and texture packs in a file manager / explorer.") .. "]"
@ -154,6 +156,11 @@ return {
core.open_url("https://www.minetest.net")
end
if fields.share_debug then
local path = core.get_user_path() .. DIR_DELIM .. "debug.txt"
core.share_file(path)
end
if fields.userdata then
core.open_dir(core.get_user_path())
end

View File

@ -88,7 +88,7 @@ if enable_gamebar then
local image = nil
local text = nil
local tooltip = core.formspec_escape(game.name)
local tooltip = core.formspec_escape(game.title)
if (game.menuicon_path or "") ~= "" then
image = core.formspec_escape(game.menuicon_path)

View File

@ -87,27 +87,34 @@ local function get_formspec(tabview, name, tabdata)
"field[4.25,0.5;1.25,0.75;te_port;;" ..
core.formspec_escape(core.settings:get("remote_port")) .. "]" ..
-- Name / Password
"label[0.25,1.55;" .. fgettext("Name") .. "]" ..
"label[3,1.55;" .. fgettext("Password") .. "]" ..
"field[0.25,1.75;2.75,0.75;te_name;;" ..
core.formspec_escape(core.settings:get("name")) .. "]" ..
"pwdfield[3,1.75;2.5,0.75;te_pwd;]" ..
-- Description Background
"label[0.25,2.75;" .. fgettext("Server Description") .. "]" ..
"box[0.25,3;5.25,2.75;#999999]"..
"label[0.25,1.6;" .. fgettext("Server Description") .. "]" ..
"box[0.25,1.85;5.25,2.7;#999999]"..
-- Name / Password
"container[0,4.8]" ..
"label[0.25,0;" .. fgettext("Name") .. "]" ..
"label[3,0;" .. fgettext("Password") .. "]" ..
"field[0.25,0.2;2.625,0.75;te_name;;" .. core.formspec_escape(core.settings:get("name")) .. "]" ..
"pwdfield[2.875,0.2;2.625,0.75;te_pwd;]" ..
"container_end[]" ..
-- Connect
"button[3,6;2.5,0.75;btn_mp_connect;" .. fgettext("Connect") .. "]"
"button[3,6;2.5,0.75;btn_mp_login;" .. fgettext("Login") .. "]"
if core.settings:get_bool("enable_split_login_register") then
retval = retval .. "button[0.25,6;2.5,0.75;btn_mp_register;" .. fgettext("Register") .. "]"
end
if tabdata.selected then
if gamedata.fav then
retval = retval .. "button[0.25,6;2.5,0.75;btn_delete_favorite;" ..
fgettext("Del. Favorite") .. "]"
retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]"
retval = retval .. "style[btn_delete_favorite;padding=6]"
retval = retval .. "image_button[5,1.3;0.5,0.5;" .. defaulttexturedir ..
"server_favorite_delete.png;btn_delete_favorite;]"
end
if gamedata.serverdescription then
retval = retval .. "textarea[0.25,3;5.25,2.75;;;" ..
retval = retval .. "textarea[0.25,1.85;5.2,2.75;;;" ..
core.formspec_escape(gamedata.serverdescription) .. "]"
end
end
@ -339,12 +346,15 @@ local function main_button_handler(tabview, fields, name, tabdata)
return true
end
if (fields.btn_mp_connect or fields.key_enter)
if (fields.btn_mp_login or fields.key_enter)
and fields.te_address ~= "" and fields.te_port then
gamedata.playername = fields.te_name
gamedata.password = fields.te_pwd
gamedata.address = fields.te_address
gamedata.port = tonumber(fields.te_port)
local enable_split_login_register = core.settings:get_bool("enable_split_login_register")
gamedata.allow_login_or_register = enable_split_login_register and "login" or "any"
gamedata.selected_world = 0
local idx = core.get_table_index("servers")
@ -381,6 +391,25 @@ local function main_button_handler(tabview, fields, name, tabdata)
return true
end
if fields.btn_mp_register and fields.te_address ~= "" and fields.te_port then
local idx = core.get_table_index("servers")
local server = idx and tabdata.lookup[idx]
if server and (server.address ~= fields.te_address or server.port ~= tonumber(fields.te_port)) then
server = nil
end
if server and not is_server_protocol_compat_or_error(
server.proto_min, server.proto_max) then
return true
end
local dlg = create_register_dialog(fields.te_address, tonumber(fields.te_port), server)
dlg:set_parent(tabview)
tabview:hide()
dlg:show()
return true
end
return false
end

View File

@ -50,7 +50,7 @@ local labels = {
fgettext("Low"),
fgettext("Medium"),
fgettext("High"),
fgettext("Ultra High")
fgettext("Very High")
}
}
@ -160,7 +160,7 @@ local function formspec(tabview, name, tabdata)
.. getSettingIndex.NodeHighlighting() .. "]" ..
"dropdown[0.25,3.6;3.5;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
.. getSettingIndex.Leaves() .. "]" ..
"box[4,0;3.75,4.5;#999999]" ..
"box[4,0;3.75,4.9;#999999]" ..
"label[4.25,0.1;" .. fgettext("Texturing:") .. "]" ..
"dropdown[4.25,0.55;3.5;dd_filters;" .. dd_options.filters[1] .. ";"
.. getSettingIndex.Filter() .. "]" ..
@ -169,9 +169,6 @@ local function formspec(tabview, name, tabdata)
"label[4.25,2.15;" .. fgettext("Antialiasing:") .. "]" ..
"dropdown[4.25,2.6;3.5;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
.. getSettingIndex.Antialiasing() .. "]" ..
"label[4.25,3.45;" .. fgettext("Screen:") .. "]" ..
"checkbox[4.25,3.6;cb_autosave_screensize;" .. fgettext("Autosave Screen Size") .. ";"
.. dump(core.settings:get_bool("autosave_screensize")) .. "]" ..
"box[8,0;3.75,4.5;#999999]"
local video_driver = core.settings:get("video_driver")
@ -203,10 +200,15 @@ local function formspec(tabview, name, tabdata)
if core.settings:get("touchscreen_threshold") ~= nil then
tab_string = tab_string ..
"label[4.3,4.2;" .. fgettext("Touchthreshold: (px)") .. "]" ..
"dropdown[4.25,4.65;3.5;dd_touchthreshold;0,10,20,30,40,50;" ..
"label[4.25,3.5;" .. fgettext("Touch threshold (px):") .. "]" ..
"dropdown[4.25,3.95;3.5;dd_touchthreshold;0,10,20,30,40,50;" ..
((tonumber(core.settings:get("touchscreen_threshold")) / 10) + 1) ..
"]box[4.0,4.5;3.75,1.0;#999999]"
"]"
else
tab_string = tab_string ..
"label[4.25,3.65;" .. fgettext("Screen:") .. "]" ..
"checkbox[4.25,3.9;cb_autosave_screensize;" .. fgettext("Autosave Screen Size") .. ";"
.. dump(core.settings:get_bool("autosave_screensize")) .. "]"
end
if shaders_enabled then
@ -219,8 +221,9 @@ local function formspec(tabview, name, tabdata)
.. 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")) .. "]"..
"label[8.25,3.0;" .. fgettext("Dynamic shadows: ") .. "]" ..
"dropdown[8.25,3.5;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";"
"label[8.25,2.8;" .. fgettext("Dynamic shadows:") .. "]" ..
"label[8.25,3.2;" .. fgettext("(game support required)") .. "]" ..
"dropdown[8.25,3.7;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";"
.. getSettingIndex.ShadowMapping() .. "]"
else
tab_string = tab_string ..
@ -364,11 +367,11 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
core.settings:set("enable_dynamic_shadows", "false")
else
local shadow_presets = {
[2] = { 55, 512, "true", 0, "false" },
[3] = { 82, 1024, "true", 1, "false" },
[4] = { 240, 2048, "true", 1, "false" },
[5] = { 240, 2048, "true", 2, "true" },
[6] = { 300, 4096, "true", 2, "true" },
[2] = { 62, 512, "true", 0, "false" },
[3] = { 93, 1024, "true", 0, "false" },
[4] = { 140, 2048, "true", 1, "false" },
[5] = { 210, 4096, "true", 2, "true" },
[6] = { 300, 8192, "true", 2, "true" },
}
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
if s then

View File

@ -643,8 +643,8 @@ 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; maximum value: 10.0
shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0
# Minimum value: 1.0; maximum value: 15.0
shadow_soft_radius (Soft shadow radius) float 5.0 1.0 15.0
# Set the tilt of Sun/Moon orbit in degrees.
# Value of 0 means no tilt / vertical orbit.
@ -1045,9 +1045,9 @@ serverlist_file (Serverlist file) string favoriteservers.json
# 0 to disable queueing and -1 to make the queue size unlimited.
max_out_chat_queue_size (Maximum size of the out chat queue) int 20
# Enable register confirmation when connecting to server.
# If disabled, new account will be registered automatically.
enable_register_confirmation (Enable register confirmation) bool true
# If enabled, account registration is separate from login in the UI.
# If disabled, new accounts will be registered automatically when logging in.
enable_split_login_register (Enable split login/register) bool true
[*Advanced]

View File

@ -25,6 +25,7 @@ uniform float animationTimer;
varying float cosLight;
varying float f_normal_length;
varying vec3 shadow_position;
varying float perspective_factor;
#endif
@ -116,23 +117,16 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
#if SHADOW_FILTER == 2
#define PCFBOUND 3.5
#define PCFSAMPLES 64.0
#define PCFBOUND 2.0 // 5x5
#define PCFSAMPLES 25
#elif SHADOW_FILTER == 1
#define PCFBOUND 1.5
#if defined(POISSON_FILTER)
#define PCFSAMPLES 32.0
#else
#define PCFSAMPLES 16.0
#endif
#define PCFBOUND 1.0 // 3x3
#define PCFSAMPLES 9
#else
#define PCFBOUND 0.0
#if defined(POISSON_FILTER)
#define PCFSAMPLES 4.0
#else
#define PCFSAMPLES 1.0
#endif
#define PCFSAMPLES 1
#endif
#ifdef COLORED_SHADOWS
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
@ -149,59 +143,31 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis
}
#endif
float getBaseLength(vec2 smTexCoord)
{
float l = length(2.0 * smTexCoord.xy - 1.0 - CameraPos.xy); // length in texture coords
return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0); // return to undistorted coords
}
#define BASEFILTERRADIUS 1.0
float getDeltaPerspectiveFactor(float l)
float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
return 0.04 * pow(512.0 / f_textureresolution, 0.4) / (xyPerspectiveBias0 * l + xyPerspectiveBias1); // 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 (PCFBOUND == 0.0)
if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
return 0.0;
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 depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
// A factor from 0 to 1 to reduce blurring of short shadows
float sharpness_factor = 1.0;
// conversion factor from shadow depth to blur radius
float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0;
if (depth > 0.0 && f_normal_length > 0.0)
// 5 is empirical factor that controls how fast shadow loses sharpness
sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0);
depth = 0.0;
float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND);
int n = 0;
float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
* f_textureresolution / 2.0 / f_shadowfar;
float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node
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);
return max(BASEFILTERRADIUS * f_textureresolution / 4096.0, sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
}
#ifdef POISSON_FILTER
@ -276,26 +242,23 @@ const vec2[64] poissonDisk = vec2[64](
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
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
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;
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float scale_factor = radius / f_textureresolution;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
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;
clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
@ -306,26 +269,23 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
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
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
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;
vec2 clampedpos;
float visibility = 0.0;
float scale_factor = radius / f_textureresolution;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
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;
clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
@ -341,65 +301,57 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
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;
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float x, y;
float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
float scale_factor = radius / bound / f_textureresolution;
float n = 0.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
clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
n += 1;
n += 1.0;
}
return visibility / n;
return visibility / max(n, 1.0);
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
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;
vec2 clampedpos;
float visibility = 0.0;
float x, y;
float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
float scale_factor = radius / bound / f_textureresolution;
float n = 0.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
clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
n += 1;
n += 1.0;
}
return visibility / n;
return visibility / max(n, 1.0);
}
#endif

View File

@ -39,6 +39,7 @@ centroid varying vec2 varTexCoord;
varying float adj_shadow_strength;
varying float f_normal_length;
varying vec3 shadow_position;
varying float perspective_factor;
#endif
@ -253,6 +254,7 @@ void main(void)
shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (inVertexPosition + vec4(normalOffsetScale * nNormal, 0.0))).xyz;
shadow_position.z -= z_bias;
perspective_factor = pFactor;
if (f_timeofday < 0.2) {
adj_shadow_strength = f_shadow_strength * 0.5 *

View File

@ -1,6 +1,5 @@
uniform sampler2D baseTexture;
uniform vec4 emissiveColor;
uniform vec3 dayLight;
uniform vec4 skyBgColor;
uniform float fogDistance;
@ -26,6 +25,7 @@ uniform float animationTimer;
varying float cosLight;
varying float f_normal_length;
varying vec3 shadow_position;
varying float perspective_factor;
#endif
@ -119,23 +119,16 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
#if SHADOW_FILTER == 2
#define PCFBOUND 3.5
#define PCFSAMPLES 64.0
#define PCFBOUND 2.0 // 5x5
#define PCFSAMPLES 25
#elif SHADOW_FILTER == 1
#define PCFBOUND 1.5
#if defined(POISSON_FILTER)
#define PCFSAMPLES 32.0
#else
#define PCFSAMPLES 16.0
#endif
#define PCFBOUND 1.0 // 3x3
#define PCFSAMPLES 9
#else
#define PCFBOUND 0.0
#if defined(POISSON_FILTER)
#define PCFSAMPLES 4.0
#else
#define PCFSAMPLES 1.0
#endif
#define PCFSAMPLES 1
#endif
#ifdef COLORED_SHADOWS
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
@ -152,59 +145,31 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis
}
#endif
float getBaseLength(vec2 smTexCoord)
{
float l = length(2.0 * smTexCoord.xy - 1.0 - CameraPos.xy); // length in texture coords
return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0); // return to undistorted coords
}
#define BASEFILTERRADIUS 1.0
float getDeltaPerspectiveFactor(float l)
float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
return 0.04 * pow(512.0 / f_textureresolution, 0.4) / (xyPerspectiveBias0 * l + xyPerspectiveBias1); // 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 (PCFBOUND == 0.0)
if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
return 0.0;
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 depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
// A factor from 0 to 1 to reduce blurring of short shadows
float sharpness_factor = 1.0;
// conversion factor from shadow depth to blur radius
float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0;
if (depth > 0.0 && f_normal_length > 0.0)
// 5 is empirical factor that controls how fast shadow loses sharpness
sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0);
depth = 0.0;
float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND);
int n = 0;
float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
* f_textureresolution / 2.0 / f_shadowfar;
float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node
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);
return max(BASEFILTERRADIUS * f_textureresolution / 4096.0, sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
}
#ifdef POISSON_FILTER
@ -279,26 +244,23 @@ const vec2[64] poissonDisk = vec2[64](
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
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
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;
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float scale_factor = radius / f_textureresolution;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
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;
clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
@ -309,26 +271,23 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
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
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
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;
vec2 clampedpos;
float visibility = 0.0;
float scale_factor = radius / f_textureresolution;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
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;
clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
@ -344,65 +303,57 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
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;
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float x, y;
float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
float scale_factor = radius / bound / f_textureresolution;
float n = 0.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
clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
n += 1;
n += 1.0;
}
return visibility / n;
return visibility / max(n, 1.0);
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
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;
vec2 clampedpos;
float visibility = 0.0;
float x, y;
float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
float scale_factor = radius / bound / f_textureresolution;
float n = 0.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
clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
n += 1;
n += 1.0;
}
return visibility / n;
return visibility / max(n, 1.0);
}
#endif
@ -489,7 +440,6 @@ void main(void)
shadow_color = visibility.gba;
#else
if (cosLight > 0.0 || f_normal_length < 1e-3)
if (cosLight > 0.0)
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
else
shadow_int = 1.0;
@ -540,6 +490,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

@ -30,6 +30,7 @@ centroid varying vec2 varTexCoord;
varying float adj_shadow_strength;
varying float f_normal_length;
varying vec3 shadow_position;
varying float perspective_factor;
#endif
varying vec3 eyeVec;
@ -162,6 +163,7 @@ void main(void)
shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (inVertexPosition + vec4(normalOffsetScale * nNormal, 0.0))).xyz;
shadow_position.z -= z_bias;
perspective_factor = pFactor;
if (f_timeofday < 0.2) {
adj_shadow_strength = f_shadow_strength * 0.5 *

View File

@ -1,21 +1,13 @@
# Locate Irrlicht or IrrlichtMt headers on system.
# Locate IrrlichtMt headers on system.
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}"
)
if(IRRLICHT_INCLUDE_DIR)
break()
endif()
endforeach()
find_path(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h
DOC "Path to the directory with IrrlichtMt includes"
PATHS
/usr/local/include/irrlichtmt
/usr/include/irrlichtmt
/system/develop/headers/irrlichtmt #Haiku
PATH_SUFFIXES "include/irrlichtmt"
)
# Handholding for users
if(IRRLICHT_INCLUDE_DIR AND (NOT IS_DIRECTORY "${IRRLICHT_INCLUDE_DIR}" OR

View File

@ -712,6 +712,8 @@ Call these functions only at load time!
* `add` adds something to the list
* `del` del removes something from the list
* `list` lists all items on the list
* The field `list_setting` will be set in the chatcommand definition to be able to recover it the
value of the 3rd parameter (settings) from minetest.registered_chatcommands later.
* `minetest.register_on_chatcommand(function(command, params))`
* Called always when a chatcommand is triggered, before `minetest.registered_chatcommands`
is checked to see if that the command exists, but after the input is parsed.
@ -1632,6 +1634,7 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
description = "Remove privilege from player", -- Full description
func = function(param), -- Called when command is run.
-- Returns boolean success and text output.
list_setting, -- this field will be automatically set by the minetest.register_list_command reflecting the 3rd "setting" parameter
}
### Server info
```lua
@ -1639,7 +1642,8 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or
address = "minetest.example.org", -- The domain name/IP address of a remote server or "" for a local server.
ip = "203.0.113.156", -- The IP address of the server.
port = 30000, -- The port the client is connected to.
protocol_version = 30 -- Will not be accurate at start up as the client might not be connected to the server yet, in that case it will be 0.
protocol_version = 30, -- Will not be accurate at start up as the client might not be connected to the server yet, in that case it will be 0.
seed = 123 -- Map Seed of the current map
}
```

View File

@ -62,7 +62,8 @@ Where `<gameid>` is unique to each game.
The game directory can contain the following files:
* `game.conf`, with the following keys:
* `name`: Required, a human readable title to address the game, e.g. `name = Minetest`.
* `title`: Required, a human-readable title to address the game, e.g. `title = Minetest Game`.
* `name`: (Deprecated) same as title.
* `description`: Short description to be shown in the content tab
* `allowed_mapgens = <comma-separated mapgens>`
e.g. `allowed_mapgens = v5,v6,flat`
@ -1672,10 +1673,16 @@ these formats.
### Serialized
This is called "stackstring" or "itemstring". It is a simple string with
1-3 components: the full item identifier, an optional amount and an optional
wear value. Syntax:
1-4 components:
<identifier> [<amount>[ <wear>]]
1. Full item identifier ("item name")
2. Optional amount
3. Optional wear value
4. Optional item metadata
Syntax:
<identifier> [<amount>[ <wear>[ <metadata>]]]
Examples:
@ -1683,6 +1690,26 @@ Examples:
* `"default:dirt 5"`: 5 dirt
* `"default:pick_stone"`: a new stone pickaxe
* `"default:pick_wood 1 21323"`: a wooden pickaxe, ca. 1/3 worn out
* `[[default:pick_wood 1 21323 "\u0001description\u0002My worn out pick\u0003"]]`:
* a wooden pickaxe from the `default` mod,
* amount must be 1 (pickaxe is a tool), ca. 1/3 worn out (it's a tool),
* with the `description` field set to `"My worn out pick"` in its metadata
* `[[default:dirt 5 0 "\u0001description\u0002Special dirt\u0003"]]`:
* analogeous to the above example
* note how the wear is set to `0` as dirt is not a tool
You should ideally use the `ItemStack` format to build complex item strings
(especially if they use item metadata)
without relying on the serialization format. Example:
local stack = ItemStack("default:pick_wood")
stack:set_wear(21323)
stack:get_meta():set_string("description", "My worn out pick")
local itemstring = stack:to_string()
Additionally the methods `minetest.itemstring_with_palette(item, palette_index)`
and `minetest.itemstring_with_color(item, colorstring)` may be used to create
item strings encoding color information in their metadata.
### Table format
@ -1778,7 +1805,6 @@ An example: Make meat soup from any meat, any water and any bowl:
{"group:water"},
{"group:bowl"},
},
-- preserve = {"group:bowl"}, -- Not implemented yet (TODO)
}
Another example: Make red wool from white wool and red dye:
@ -2011,7 +2037,7 @@ Example definition of the capabilities of an item
max_drop_level=1,
groupcaps={
crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
}
},
damage_groups = {fleshy=2},
}
@ -3551,8 +3577,16 @@ Helper functions
* `minetest.string_to_pos(string)`: returns a position or `nil`
* Same but in reverse.
* If the string can't be parsed to a position, nothing is returned.
* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)")`: returns two positions
* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)", relative_to)`:
* returns two positions
* Converts a string representing an area box into two positions
* X1, Y1, ... Z2 are coordinates
* `relative_to`: Optional. If set to a position, each coordinate
can use the tilde notation for relative positions
* Tilde notation: "~": Relative coordinate
"~<number>": Relative coordinate plus <number>
* Example: `minetest.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})`
returns `{x=1,y=2,z=3}, {x=15,y=5,z=10}`
* `minetest.formspec_escape(string)`: returns a string
* escapes the characters "[", "]", "\", "," and ";", which can not be used
in formspecs.
@ -3584,6 +3618,12 @@ Helper functions
* `minetest.pointed_thing_to_face_pos(placer, pointed_thing)`: returns a
position.
* returns the exact position on the surface of a pointed node
* `minetest.get_tool_wear_after_use(uses [, initial_wear])`
* Simulates a tool being used once and returns the added wear,
such that, if only this function is used to calculate wear,
the tool will break exactly after `uses` times of uses
* `uses`: Number of times the tool can be used
* `initial_wear`: The initial wear the tool starts with (default: 0)
* `minetest.get_dig_params(groups, tool_capabilities [, wear])`:
Simulates an item that digs a node.
Returns a table with the following fields:
@ -6519,7 +6559,13 @@ an itemstring, a table or `nil`.
or those of the hand if none are defined for this item type
* `add_wear(amount)`
* Increases wear by `amount` if the item is a tool, otherwise does nothing
* Valid `amount` range is [0,65536]
* `amount`: number, integer
* `add_wear_by_uses(max_uses)`
* Increases wear in such a way that, if only this function is called,
the item breaks after `max_uses` times
* Valid `max_uses` range is [0,65536]
* Does nothing if item is not a tool or if `max_uses` is 0
* `add_item(item)`: returns leftover `ItemStack`
* Put some item or stack onto this stack
* `item_fits(item)`: returns `true` if item or stack can be fully added to
@ -7092,6 +7138,8 @@ object you are working with still exists.
* `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness)
* `get_lighting()`: returns the current state of lighting for the player.
* Result is a table with the same fields as `light_definition` in `set_lighting`.
* `respawn()`: Respawns the player using the same mechanism as the death screen,
including calling on_respawnplayer callbacks.
`PcgRandom`
-----------
@ -8553,9 +8601,8 @@ See [Decoration types]. Used by `minetest.register_decoration`.
spawn_by = "default:water",
-- Node (or list of nodes) that the decoration only spawns next to.
-- Checks two horizontal planes of 8 neighbouring nodes (including
-- diagonal neighbours), one plane level with the 'place_on' node and a
-- plane one node above that.
-- Checks the 8 neighbouring nodes on the same Y, and also the ones
-- at Y+1, excluding both center nodes.
num_spawn_by = 1,
-- Number of spawn_by nodes that must be surrounding the decoration

View File

@ -46,6 +46,8 @@ core.open_url(url)
core.open_dir(path)
^ opens the path in the system file browser/explorer, returns false on failure.
^ Must be an existing directory.
core.share_file(path)
^ Android only. Shares file using the share popup
core.get_version() (possible in async calls)
^ returns current core version
@ -246,13 +248,14 @@ Package - content which is downloadable from the content db, may or may not be i
* core.get_texturepath() (possible in async calls)
* returns path to default textures
* core.get_game(index)
* `name` in return value is deprecated, use `title` instead.
* returns:
{
id = <id>,
path = <full path to game>,
gamemods_path = <path>,
name = <name of game>,
title = <title of game>,
menuicon_path = <full path to menuicon>,
author = "author",
DEPRECATED:
@ -264,8 +267,9 @@ Package - content which is downloadable from the content db, may or may not be i
* returns
{
name = "name of content",
name = "technical_id",
type = "mod" or "modpack" or "game" or "txp",
title = "Human readable title",
description = "description",
author = "author",
path = "path/to/content",

View File

@ -92,8 +92,6 @@ Set password from contents of file
.B \-\-random\-input
Enable random user input, for testing (client only)
.TP
.B \-\-videomodes
List available video modes (client only)
.TP
.B \-\-speedtests
Run speed tests

View File

@ -1,2 +1,2 @@
name = Development Test
title = Development Test
description = Testing environment to help with testing the engine features of Minetest. It can also be helpful in mod development.

View File

@ -0,0 +1,55 @@
local S = minetest.get_translator("testitems")
--
-- Texture overlays for items
--
-- For the global overlay color test
local GLOBAL_COLOR_ARG = "orange"
-- Punch handler to set random color with "color" argument in item metadata
local overlay_on_use = function(itemstack, user, pointed_thing)
local meta = itemstack:get_meta()
local color = math.random(0x0, 0xFFFFFF)
local colorstr = string.format("#%06x", color)
meta:set_string("color", colorstr)
minetest.log("action", "[testitems] Color of "..itemstack:get_name().." changed to "..colorstr)
return itemstack
end
-- Place handler to clear item metadata color
local overlay_on_place = function(itemstack, user, pointed_thing)
local meta = itemstack:get_meta()
meta:set_string("color", "")
return itemstack
end
minetest.register_craftitem("testitems:overlay_meta", {
description = S("Texture Overlay Test Item, Meta Color") .. "\n" ..
S("Image must be a square with rainbow cross (inventory and wield)") .. "\n" ..
S("Item meta color must only change square color") .. "\n" ..
S("Punch: Set random color") .. "\n" ..
S("Place: Clear color"),
-- Base texture: A grayscale square (can be colorized)
inventory_image = "testitems_overlay_base.png",
wield_image = "testitems_overlay_base.png",
-- Overlay: A rainbow cross (NOT to be colorized!)
inventory_overlay = "testitems_overlay_overlay.png",
wield_overlay = "testitems_overlay_overlay.png",
on_use = overlay_on_use,
on_place = overlay_on_place,
on_secondary_use = overlay_on_place,
})
minetest.register_craftitem("testitems:overlay_global", {
description = S("Texture Overlay Test Item, Global Color") .. "\n" ..
S("Image must be an orange square with rainbow cross (inventory and wield)"),
-- Base texture: A grayscale square (to be colorized)
inventory_image = "testitems_overlay_base.png",
wield_image = "testitems_overlay_base.png",
-- Overlay: A rainbow cross (NOT to be colorized!)
inventory_overlay = "testitems_overlay_overlay.png",
wield_overlay = "testitems_overlay_overlay.png",
color = GLOBAL_COLOR_ARG,
})

View File

@ -0,0 +1,2 @@
name = testitems
description = Test mod to test misc. items that are neither tools nor nodes

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

View File

@ -8,3 +8,4 @@ dofile(path.."/properties.lua")
dofile(path.."/liquids.lua")
dofile(path.."/light.lua")
dofile(path.."/textures.lua")
dofile(path.."/overlays.lua")

View File

@ -0,0 +1,93 @@
local S = minetest.get_translator("testnodes")
minetest.register_node("testnodes:overlay", {
description = S("Texture Overlay Test Node") .. "\n" ..
S("Uncolorized"),
tiles = {{name = "testnodes_overlayable.png"}},
overlay_tiles = {{name = "testnodes_overlay.png"}},
groups = { dig_immediate = 2 },
})
minetest.register_node("testnodes:overlay_color_all", {
description = S("Texture Overlay Test Node, Colorized") .. "\n" ..
S("param2 changes color"),
tiles = {{name = "testnodes_overlayable.png"}},
overlay_tiles = {{name = "testnodes_overlay.png"}},
paramtype2 = "color",
palette = "testnodes_palette_full.png",
groups = { dig_immediate = 2 },
})
minetest.register_node("testnodes:overlay_color_overlay", {
description = S("Texture Overlay Test Node, Colorized Overlay") .. "\n" ..
S("param2 changes color of overlay"),
tiles = {{name = "testnodes_overlayable.png", color="white"}},
overlay_tiles = {{name = "testnodes_overlay.png"}},
paramtype2 = "color",
palette = "testnodes_palette_full.png",
groups = { dig_immediate = 2 },
})
minetest.register_node("testnodes:overlay_color_overlayed", {
description = S("Texture Overlay Test Node, Colorized Base") .. "\n" ..
S("param2 changes color of base texture"),
tiles = {{name = "testnodes_overlayable.png"}},
overlay_tiles = {{name = "testnodes_overlay.png", color="white"}},
paramtype2 = "color",
palette = "testnodes_palette_full.png",
groups = { dig_immediate = 2 },
})
local global_overlay_color = "#FF2000"
minetest.register_node("testnodes:overlay_global", {
description = S("Texture Overlay Test Node, Global Color") .. "\n" ..
S("Global color = @1", global_overlay_color),
tiles = {{name = "testnodes_overlayable.png"}},
overlay_tiles = {{name = "testnodes_overlay.png"}},
color = global_overlay_color,
groups = { dig_immediate = 2 },
})
minetest.register_node("testnodes:overlay_global_color_all", {
description = S("Texture Overlay Test Node, Global Color + Colorized") .. "\n" ..
S("Global color = @1", global_overlay_color) .. "\n" ..
S("param2 changes color"),
tiles = {{name = "testnodes_overlayable.png"}},
overlay_tiles = {{name = "testnodes_overlay.png"}},
color = global_overlay_color,
paramtype2 = "color",
palette = "testnodes_palette_full.png",
groups = { dig_immediate = 2 },
})
minetest.register_node("testnodes:overlay_global_color_overlay", {
description = S("Texture Overlay Test Node, Global Color + Colorized Overlay") .. "\n" ..
S("Global color = @1", global_overlay_color) .. "\n" ..
S("param2 changes color of overlay"),
tiles = {{name = "testnodes_overlayable.png", color=global_overlay_color}},
overlay_tiles = {{name = "testnodes_overlay.png"}},
color = global_overlay_color,
paramtype2 = "color",
palette = "testnodes_palette_full.png",
groups = { dig_immediate = 2 },
})
minetest.register_node("testnodes:overlay_global_color_overlayed", {
description = S("Texture Overlay Test Node, Global Color + Colorized Base") .. "\n" ..
S("Global color = @1", global_overlay_color) .. "\n" ..
S("param2 changes color of base texture"),
tiles = {{name = "testnodes_overlayable.png"}},
overlay_tiles = {{name = "testnodes_overlay.png", color=global_overlay_color}},
color = global_overlay_color,
paramtype2 = "color",
palette = "testnodes_palette_full.png",
groups = { dig_immediate = 2 },
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

View File

@ -48,3 +48,23 @@ local function test_v3s16_metatable(player, pos)
assert(vector.check(found_pos))
end
unittests.register("test_v3s16_metatable", test_v3s16_metatable, {map=true})
local function test_clear_meta(_, pos)
local ref = core.get_meta(pos)
for way = 1, 3 do
ref:set_string("foo", "bar")
assert(ref:contains("foo"))
if way == 1 then
ref:from_table({})
elseif way == 2 then
ref:from_table(nil)
else
ref:set_string("foo", "")
end
assert(#core.find_nodes_with_meta(pos, pos) == 0, "clearing failed " .. way)
end
end
unittests.register("test_clear_meta", test_clear_meta, {map=true})

View File

@ -164,8 +164,13 @@ static int traversetable (global_State *g, Table *h) {
markobject(g, h->metatable);
mode = gfasttm(g, h->metatable, TM_MODE);
if (mode && ttisstring(mode)) { /* is there a weak mode? */
weakkey = (strchr(svalue(mode), 'k') != NULL);
weakvalue = (strchr(svalue(mode), 'v') != NULL);
// Android's 'FORTIFY libc' calls __builtin_object_size on the argument of strchr.
// This produces an incorrect size for the expression `svalue(mode)`, causing
// an assertion. By placing it in a temporary, __builtin_object_size returns
// -1 (for unknown size) which functions correctly.
const char *tmp = svalue(mode);
weakkey = (strchr(tmp, 'k') != NULL);
weakvalue = (strchr(tmp, 'v') != NULL);
if (weakkey || weakvalue) { /* is really weak? */
h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */
h->marked |= cast_byte((weakkey << KEYWEAKBIT) |

View File

@ -136,6 +136,7 @@ if(ENABLE_POSTGRESQL)
if(PostgreSQL_INCLUDE_DIR AND PostgreSQL_LIBRARY)
set(PostgreSQL_FOUND TRUE)
set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR})
set(PostgreSQL_LIBRARIES ${PostgreSQL_LIBRARY})
endif()
else()
find_package(PostgreSQL)

View File

@ -48,7 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *rendering_engine):
m_draw_control(draw_control),
m_client(client)
m_client(client),
m_player_light_color(0xFFFFFFFF)
{
auto smgr = rendering_engine->get_scene_manager();
// note: making the camera node a child of the player node
@ -154,8 +155,10 @@ void Camera::step(f32 dtime)
bool was_under_zero = m_wield_change_timer < 0;
m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125);
if (m_wield_change_timer >= 0 && was_under_zero)
if (m_wield_change_timer >= 0 && was_under_zero) {
m_wieldnode->setItem(m_wield_item_next, m_client);
m_wieldnode->setNodeLightColor(m_player_light_color);
}
if (m_view_bobbing_state != 0)
{
@ -556,7 +559,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
m_wieldnode->setPosition(wield_position);
m_wieldnode->setRotation(wield_rotation);
m_wieldnode->setNodeLightColor(player->light_color);
m_player_light_color = player->light_color;
m_wieldnode->setNodeLightColor(m_player_light_color);
// Set render distance
updateViewingRange();

View File

@ -291,4 +291,7 @@ private:
std::list<Nametag *> m_nametags;
bool m_show_nametag_backgrounds;
// Last known light color of the player
video::SColor m_player_light_color;
};

View File

@ -101,7 +101,8 @@ Client::Client(
MtEventManager *event,
RenderingEngine *rendering_engine,
bool ipv6,
GameUI *game_ui
GameUI *game_ui,
ELoginRegister allow_login_or_register
):
m_mesh_update_thread(this),
m_tsrc(tsrc),
@ -125,7 +126,8 @@ Client::Client(
m_media_downloader(new ClientMediaDownloader()),
m_state(LC_Created),
m_game_ui(game_ui),
m_modchannel_mgr(new ModChannelMgr())
m_modchannel_mgr(new ModChannelMgr()),
m_allow_login_or_register(allow_login_or_register)
{
// Add local player
m_env.setLocalPlayer(new LocalPlayer(this, playername));
@ -399,10 +401,6 @@ void Client::step(float dtime)
initial_step = false;
}
else if(m_state == LC_Created) {
if (m_is_registration_confirmation_state) {
// Waiting confirmation
return;
}
float &counter = m_connection_reinit_timer;
counter -= dtime;
if(counter <= 0.0) {
@ -533,6 +531,7 @@ void Client::step(float dtime)
{
int num_processed_meshes = 0;
std::vector<v3s16> blocks_to_ack;
bool force_update_shadows = false;
while (!m_mesh_update_thread.m_queue_out.empty())
{
num_processed_meshes++;
@ -559,9 +558,11 @@ void Client::step(float dtime)
if (is_empty)
delete r.mesh;
else
else {
// Replace with the new mesh
block->mesh = r.mesh;
force_update_shadows = true;
}
}
} else {
delete r.mesh;
@ -586,6 +587,10 @@ void Client::step(float dtime)
if (num_processed_meshes > 0)
g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
auto shadow_renderer = RenderingEngine::get_shadow_renderer();
if (shadow_renderer && force_update_shadows)
shadow_renderer->setForceUpdateShadowMap();
}
/*
@ -789,16 +794,18 @@ void Client::peerAdded(con::Peer *peer)
infostream << "Client::peerAdded(): peer->id="
<< peer->id << std::endl;
}
void Client::deletingPeer(con::Peer *peer, bool timeout)
{
infostream << "Client::deletingPeer(): "
"Server Peer is getting deleted "
<< "(timeout=" << timeout << ")" << std::endl;
if (timeout) {
m_access_denied = true;
m_access_denied = true;
if (timeout)
m_access_denied_reason = gettext("Connection timed out.");
}
else if (m_access_denied_reason.empty())
m_access_denied_reason = gettext("Connection aborted (protocol error?).");
}
/*
@ -1072,18 +1079,6 @@ void Client::sendInit(const std::string &playerName)
Send(&pkt);
}
void Client::promptConfirmRegistration(AuthMechanism chosen_auth_mechanism)
{
m_chosen_auth_mech = chosen_auth_mechanism;
m_is_registration_confirmation_state = true;
}
void Client::confirmRegistration()
{
m_is_registration_confirmation_state = false;
startAuth(m_chosen_auth_mech);
}
void Client::startAuth(AuthMechanism chosen_auth_mechanism)
{
m_chosen_auth_mech = chosen_auth_mechanism;

View File

@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mesh_generator_thread.h"
#include "network/address.h"
#include "network/peerhandler.h"
#include "gameparams.h"
#include <fstream>
#define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f
@ -126,7 +127,8 @@ public:
MtEventManager *event,
RenderingEngine *rendering_engine,
bool ipv6,
GameUI *game_ui
GameUI *game_ui,
ELoginRegister allow_login_or_register
);
~Client();
@ -348,8 +350,7 @@ public:
u16 getProtoVersion()
{ return m_proto_ver; }
void confirmRegistration();
bool m_is_registration_confirmation_state = false;
ELoginRegister m_allow_login_or_register = ELoginRegister::Any;
bool m_simple_singleplayer_mode;
float mediaReceiveProgress();
@ -467,7 +468,6 @@ private:
static AuthMechanism choseAuthMech(const u32 mechs);
void sendInit(const std::string &playerName);
void promptConfirmRegistration(AuthMechanism chosen_auth_mechanism);
void startAuth(AuthMechanism chosen_auth_mechanism);
void sendDeletedBlocks(std::vector<v3s16> &blocks);
void sendGotBlocks(const std::vector<v3s16> &blocks);

View File

@ -304,6 +304,7 @@ void ClientEnvironment::step(float dtime)
node_at_lplayer = m_map->getNode(p);
u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
lplayer->light_color = encode_light(light, 0); // this transfers light.alpha
final_color_blend(&lplayer->light_color, light, day_night_ratio);
}

View File

@ -451,6 +451,7 @@ bool ClientLauncher::launch_game(std::string &error_message,
start_data.name = menudata.name;
start_data.password = menudata.password;
start_data.address = std::move(menudata.address);
start_data.allow_login_or_register = menudata.allow_login_or_register;
server_name = menudata.servername;
server_description = menudata.serverdescription;

View File

@ -219,13 +219,11 @@ void ClientMap::updateDrawList()
// Number of blocks occlusion culled
u32 blocks_occlusion_culled = 0;
// No occlusion culling when free_move is on and camera is
// inside ground
// No occlusion culling when free_move is on and camera is inside ground
bool occlusion_culling_enabled = true;
if ((g_settings->getBool("free_move") && g_settings->getBool("noclip")) || g_settings->getBool("freecam")) {
if (m_control.allow_noclip) {
MapNode n = getNode(cam_pos_nodes);
if (n.getContent() == CONTENT_IGNORE ||
m_nodedef->get(n).solidness == 2)
if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).solidness == 2)
occlusion_culling_enabled = false;
}
@ -449,15 +447,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
drawcall_count += draw_order.size();
for (auto &descriptor : draw_order) {
scene::IMeshBuffer *buf;
if (descriptor.m_use_partial_buffer) {
descriptor.m_partial_buffer->beforeDraw();
buf = descriptor.m_partial_buffer->getBuffer();
}
else {
buf = descriptor.m_buffer;
}
scene::IMeshBuffer *buf = descriptor.getBuffer();
// Check and abort if the machine is swapping a lot
if (draw.getTimerTime() > 2000) {
@ -489,6 +479,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Do not enable filter on shadow texture to avoid visual artifacts
// with colored shadows.
// Filtering is done in shader code anyway
layer.BilinearFilter = false;
layer.AnisotropicFilter = false;
layer.TrilinearFilter = false;
}
driver->setMaterial(material);
@ -499,7 +491,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
m.setTranslation(block_wpos - offset);
driver->setTransform(video::ETS_WORLD, m);
driver->drawMeshBuffer(buf);
descriptor.draw(driver);
vertex_count += buf->getIndexCount();
}
@ -684,19 +676,17 @@ void ClientMap::renderPostFx(CameraMode cam_mode)
MapNode n = getNode(floatToInt(m_camera_position, BS));
// - If the player is in a solid node, make everything black.
// - If the player is in liquid, draw a semi-transparent overlay.
// - Do not if player is in third person mode
const ContentFeatures& features = m_nodedef->get(n);
video::SColor post_effect_color = features.post_effect_color;
if(features.solidness == 2 && !((g_settings->getBool("noclip") || g_settings->getBool("freecam")) &&
(m_client->checkLocalPrivilege("noclip") || g_settings->getBool("freecam"))) &&
cam_mode == CAMERA_MODE_FIRST)
{
// If the camera is in a solid node, make everything black.
// (first person mode only)
if (features.solidness == 2 && cam_mode == CAMERA_MODE_FIRST &&
!m_control.allow_noclip) {
post_effect_color = video::SColor(255, 0, 0, 0);
}
if (post_effect_color.getAlpha() != 0)
{
if (post_effect_color.getAlpha() != 0) {
// Draw a full-screen rectangle
video::IVideoDriver* driver = SceneManager->getVideoDriver();
v2u32 ss = driver->getScreenSize();
@ -810,15 +800,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
drawcall_count += draw_order.size();
for (auto &descriptor : draw_order) {
scene::IMeshBuffer *buf;
if (descriptor.m_use_partial_buffer) {
descriptor.m_partial_buffer->beforeDraw();
buf = descriptor.m_partial_buffer->getBuffer();
}
else {
buf = descriptor.m_buffer;
}
scene::IMeshBuffer *buf = descriptor.getBuffer();
// Check and abort if the machine is swapping a lot
if (draw.getTimerTime() > 1000) {
@ -843,7 +825,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
m.setTranslation(block_wpos - offset);
driver->setTransform(video::ETS_WORLD, m);
driver->drawMeshBuffer(buf);
descriptor.draw(driver);
vertex_count += buf->getIndexCount();
}
@ -964,3 +946,18 @@ void ClientMap::updateTransparentMeshBuffers()
m_needs_update_transparent_meshes = false;
}
scene::IMeshBuffer* ClientMap::DrawDescriptor::getBuffer()
{
return m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer;
}
void ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver)
{
if (m_use_partial_buffer) {
m_partial_buffer->beforeDraw();
driver->drawMeshBuffer(m_partial_buffer->getBuffer());
m_partial_buffer->afterDraw();
} else {
driver->drawMeshBuffer(m_buffer);
}
}

View File

@ -27,10 +27,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
struct MapDrawControl
{
// Overrides limits by drawing everything
bool range_all = false;
// Wanted drawing range
float wanted_range = 0.0f;
// Overrides limits by drawing everything
bool range_all = false;
// Allow rendering out of bounds
bool allow_noclip = false;
// show a wire frame for debugging
bool show_wireframe = false;
};
@ -174,6 +176,9 @@ private:
DrawDescriptor(v3s16 pos, const PartialMeshBuffer *buffer) :
m_pos(pos), m_partial_buffer(buffer), m_reuse_material(false), m_use_partial_buffer(true)
{}
scene::IMeshBuffer* getBuffer();
void draw(video::IVideoDriver* driver);
};
Client *m_client;

View File

@ -920,12 +920,8 @@ void GenericCAO::setNodeLight(const video::SColor &light_color)
if (m_prop.visual == "upright_sprite") {
if (!m_meshnode)
return;
scene::IMesh *mesh = m_meshnode->getMesh();
for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
buf->getMaterial().EmissiveColor = light_color;
}
for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i)
m_meshnode->getMaterial(i).EmissiveColor = light_color;
} else {
scene::ISceneNode *node = getSceneNode();
if (!node)

View File

@ -43,7 +43,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gameparams.h"
#include "gettext.h"
#include "gui/guiChatConsole.h"
#include "gui/guiConfirmRegistration.h"
#include "gui/guiFormSpecMenu.h"
#include "gui/guiKeyChangeMenu.h"
#include "gui/guiPasswordChange.h"
@ -675,7 +674,8 @@ bool Game::connectToServer(const GameStartData &start_data,
start_data.password, start_data.address,
*draw_control, texture_src, shader_src,
itemdef_manager, nodedef_manager, sound, eventmgr,
m_rendering_engine, connect_address.isIPv6(), m_game_ui.get());
m_rendering_engine, connect_address.isIPv6(), m_game_ui.get(),
start_data.allow_login_or_register);
client->migrateModStorage();
} catch (const BaseException &e) {
*error_message = fmtgettext("Error creating client: %s", e.what());
@ -738,28 +738,16 @@ bool Game::connectToServer(const GameStartData &start_data,
break;
}
if (client->m_is_registration_confirmation_state) {
if (registration_confirmation_shown) {
// Keep drawing the GUI
m_rendering_engine->draw_menu_scene(guienv, dtime, true);
} else {
registration_confirmation_shown = true;
(new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1,
&g_menumgr, client, start_data.name, start_data.password,
connection_aborted, texture_src))->drop();
}
} else {
wait_time += dtime;
// Only time out if we aren't waiting for the server we started
if (!start_data.address.empty() && wait_time > 10) {
*error_message = gettext("Connection timed out.");
errorstream << *error_message << std::endl;
break;
}
// Update status
showOverlayMessage(N_("Connecting to server..."), dtime, 20);
wait_time += dtime;
// Only time out if we aren't waiting for the server we started
if (!start_data.address.empty() && wait_time > 10) {
*error_message = gettext("Connection timed out.");
errorstream << *error_message << std::endl;
break;
}
// Update status
showOverlayMessage(N_("Connecting to server..."), dtime, 20);
}
} catch (con::PeerNotFoundException &e) {
// TODO: Should something be done here? At least an info/error
@ -938,6 +926,8 @@ void Game::processQueues()
void Game::updateDebugState()
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
// debug UI and wireframe
bool has_debug = client->checkPrivilege("debug");
bool has_basic_debug = has_debug || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
@ -952,6 +942,9 @@ void Game::updateDebugState()
hud->disableBlockBounds();
if (!has_debug)
draw_control->show_wireframe = false;
// noclip
draw_control->allow_noclip = (m_cache_enable_noclip && client->checkPrivilege("noclip")) || g_settings->getBool("freecam");
}
void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
@ -3047,7 +3040,10 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
float direct_brightness;
bool sunlight_seen;
if ((m_cache_enable_noclip && m_cache_enable_free_move) || g_settings->getBool("freecam")) {
// When in noclip mode force same sky brightness as above ground so you
// can see properly
if ((draw_control->allow_noclip && m_cache_enable_free_move &&
client->checkPrivilege("fly")) || g_settings->getBool("freecam")) {
direct_brightness = time_brightness;
sunlight_seen = true;
} else {

View File

@ -43,7 +43,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gettext.h"
#include "gui/cheatMenu.h"
#include "gui/guiChatConsole.h"
#include "gui/guiConfirmRegistration.h"
#include "gui/guiFormSpecMenu.h"
#include "gui/guiKeyChangeMenu.h"
#include "gui/guiPasswordChange.h"

View File

@ -450,16 +450,6 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
camera_impact = getSpeed().Y * -1;
}
{
camera_barely_in_ceiling = false;
v3s16 camera_np = floatToInt(getEyePosition(), BS);
MapNode n = map->getNode(camera_np);
if (n.getContent() != CONTENT_IGNORE) {
if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
camera_barely_in_ceiling = true;
}
}
/*
Check properties of the node on which the player is standing
*/
@ -730,8 +720,7 @@ v3f LocalPlayer::getSendSpeed()
v3f LocalPlayer::getEyeOffset() const
{
float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
return v3f(0.0f, BS * eye_height, 0.0f);
return v3f(0.0f, BS * m_eye_height, 0.0f);
}
ClientActiveObject *LocalPlayer::getParent() const
@ -1071,16 +1060,6 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
camera_impact = getSpeed().Y * -1.0f;
}
{
camera_barely_in_ceiling = false;
v3s16 camera_np = floatToInt(getEyePosition(), BS);
MapNode n = map->getNode(camera_np);
if (n.getContent() != CONTENT_IGNORE) {
if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
camera_barely_in_ceiling = true;
}
}
/*
Update the node last under the player
*/

View File

@ -237,7 +237,6 @@ private:
u16 m_breath = PLAYER_MAX_BREATH_DEFAULT;
f32 m_yaw = 0.0f;
f32 m_pitch = 0.0f;
bool camera_barely_in_ceiling = false;
aabb3f m_collisionbox = aabb3f(-BS * 0.30f, 0.0f, -BS * 0.30f, BS * 0.30f,
BS * 1.75f, BS * 0.30f);
float m_eye_height = 1.625f;

View File

@ -1204,15 +1204,16 @@ void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector<s32> &output
void PartialMeshBuffer::beforeDraw() const
{
// Patch the indexes in the mesh buffer before draw
m_buffer->Indices.clear();
if (!m_vertex_indexes.empty()) {
for (auto index : m_vertex_indexes)
m_buffer->Indices.push_back(index);
}
m_buffer->Indices = std::move(m_vertex_indexes);
m_buffer->setDirty(scene::EBT_INDEX);
}
void PartialMeshBuffer::afterDraw() const
{
// Take the data back
m_vertex_indexes = std::move(m_buffer->Indices.steal());
}
/*
MapBlockMesh
*/
@ -1416,30 +1417,22 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
buf->Material = material;
switch (p.layer.material_type) {
// list of transparent materials taken from tile.h
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
{
buf->append(&p.vertices[0], p.vertices.size(),
&p.indices[0], 0);
if (p.layer.isTransparent()) {
buf->append(&p.vertices[0], p.vertices.size(), nullptr, 0);
MeshTriangle t;
t.buffer = buf;
for (u32 i = 0; i < p.indices.size(); i += 3) {
t.p1 = p.indices[i];
t.p2 = p.indices[i + 1];
t.p3 = p.indices[i + 2];
t.updateAttributes();
m_transparent_triangles.push_back(t);
}
MeshTriangle t;
t.buffer = buf;
m_transparent_triangles.reserve(p.indices.size() / 3);
for (u32 i = 0; i < p.indices.size(); i += 3) {
t.p1 = p.indices[i];
t.p2 = p.indices[i + 1];
t.p3 = p.indices[i + 2];
t.updateAttributes();
m_transparent_triangles.push_back(t);
}
break;
default:
} else {
buf->append(&p.vertices[0], p.vertices.size(),
&p.indices[0], p.indices.size());
break;
}
mesh->addMeshBuffer(buf);
buf->drop();
@ -1582,7 +1575,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos)
const auto &t = m_transparent_triangles[i];
if (current_buffer != t.buffer) {
if (current_buffer) {
m_transparent_buffers.emplace_back(current_buffer, current_strain);
m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
current_strain.clear();
}
current_buffer = t.buffer;
@ -1593,7 +1586,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos)
}
if (!current_strain.empty())
m_transparent_buffers.emplace_back(current_buffer, current_strain);
m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
}
void MapBlockMesh::consolidateTransparentBuffers()
@ -1607,7 +1600,7 @@ void MapBlockMesh::consolidateTransparentBuffers()
for (const auto &t : m_transparent_triangles) {
if (current_buffer != t.buffer) {
if (current_buffer != nullptr) {
this->m_transparent_buffers.emplace_back(current_buffer, current_strain);
this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
current_strain.clear();
}
current_buffer = t.buffer;
@ -1618,7 +1611,7 @@ void MapBlockMesh::consolidateTransparentBuffers()
}
if (!current_strain.empty()) {
this->m_transparent_buffers.emplace_back(current_buffer, current_strain);
this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
}
}

View File

@ -140,20 +140,31 @@ private:
s32 root = -1; // index of the root node
};
/*
* PartialMeshBuffer
*
* Attach alternate `Indices` to an existing mesh buffer, to make it possible to use different
* indices with the same vertex buffer.
*
* Irrlicht does not currently support this: `CMeshBuffer` ties together a single vertex buffer
* and a single index buffer. There's no way to share these between mesh buffers.
*
*/
class PartialMeshBuffer
{
public:
PartialMeshBuffer(scene::SMeshBuffer *buffer, const std::vector<u16> &vertex_indexes) :
m_buffer(buffer), m_vertex_indexes(vertex_indexes)
PartialMeshBuffer(scene::SMeshBuffer *buffer, std::vector<u16> &&vertex_indexes) :
m_buffer(buffer), m_vertex_indexes(std::move(vertex_indexes))
{}
scene::IMeshBuffer *getBuffer() const { return m_buffer; }
const std::vector<u16> &getVertexIndexes() const { return m_vertex_indexes; }
void beforeDraw() const;
void afterDraw() const;
private:
scene::SMeshBuffer *m_buffer;
std::vector<u16> m_vertex_indexes;
mutable std::vector<u16> m_vertex_indexes;
};
/*

View File

@ -637,25 +637,10 @@ float RenderingEngine::getDisplayDensity()
#endif
v2u32 RenderingEngine::getDisplaySize()
{
IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
core::dimension2d<u32> deskres =
nulldevice->getVideoModeList()->getDesktopResolution();
nulldevice->drop();
return deskres;
}
#else // __ANDROID__
float RenderingEngine::getDisplayDensity()
{
return porting::getDisplayDensity();
}
v2u32 RenderingEngine::getDisplaySize()
{
return porting::getDisplaySize();
}
#endif // __ANDROID__

View File

@ -55,7 +55,6 @@ public:
static const VideoDriverInfo &getVideoDriverInfo(irr::video::E_DRIVER_TYPE type);
static float getDisplayDensity();
static v2u32 getDisplaySize();
bool setupTopLevelWindow(const std::string &name);
void setupTopLevelXorgWindow(const std::string &name);

View File

@ -771,6 +771,8 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n";
}
shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics
std::string common_header = shaders_header.str();
std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");

View File

@ -27,10 +27,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
using m4f = core::matrix4;
static v3f quantizeDirection(v3f direction, float step)
{
float yaw = std::atan2(direction.Z, direction.X);
float pitch = std::asin(direction.Y); // assume look is normalized
yaw = std::floor(yaw / step) * step;
pitch = std::floor(pitch / step) * step;
return v3f(std::cos(yaw)*std::cos(pitch), std::sin(pitch), std::sin(yaw)*std::cos(pitch));
}
void DirectionalLight::createSplitMatrices(const Camera *cam)
{
const float DISTANCE_STEP = BS * 2.0; // 2 meters
v3f newCenter;
v3f look = cam->getDirection();
look = quantizeDirection(look, M_PI / 12.0); // 15 degrees
// camera view tangents
float tanFovY = tanf(cam->getFovY() * 0.5f);
@ -42,6 +56,10 @@ void DirectionalLight::createSplitMatrices(const Camera *cam)
// adjusted camera positions
v3f cam_pos_world = cam->getPosition();
cam_pos_world = v3f(
floor(cam_pos_world.X / DISTANCE_STEP) * DISTANCE_STEP,
floor(cam_pos_world.Y / DISTANCE_STEP) * DISTANCE_STEP,
floor(cam_pos_world.Z / DISTANCE_STEP) * DISTANCE_STEP);
v3f cam_pos_scene = v3f(cam_pos_world.X - cam->getOffset().X * BS,
cam_pos_world.Y - cam->getOffset().Y * BS,
cam_pos_world.Z - cam->getOffset().Z * BS);
@ -61,7 +79,7 @@ void DirectionalLight::createSplitMatrices(const Camera *cam)
v3f boundVec = (cam_pos_scene + farCorner * sfFar) - center_scene;
float radius = boundVec.getLength();
float length = radius * 3.0f;
v3f eye_displacement = direction * length;
v3f eye_displacement = quantizeDirection(direction, M_PI / 2880 /*15 seconds*/) * length;
// we must compute the viewmat with the position - the camera offset
// but the future_frustum position must be the actual world position

View File

@ -242,7 +242,7 @@ void ShadowRenderer::updateSMTextures()
// detect if SM should be regenerated
for (DirectionalLight &light : m_light_list) {
if (light.should_update_map_shadow) {
if (light.should_update_map_shadow || m_force_update_shadow_map) {
light.should_update_map_shadow = false;
m_current_frame = 0;
reset_sm_texture = true;
@ -271,14 +271,14 @@ void ShadowRenderer::updateSMTextures()
// should put some gl* fn here
if (m_current_frame < m_map_shadow_update_frames) {
if (m_current_frame < m_map_shadow_update_frames || m_force_update_shadow_map) {
m_driver->setRenderTarget(shadowMapTargetTexture, reset_sm_texture, true,
video::SColor(255, 255, 255, 255));
renderShadowMap(shadowMapTargetTexture, light);
// Render transparent part in one pass.
// This is also handled in ClientMap.
if (m_current_frame == m_map_shadow_update_frames - 1) {
if (m_current_frame == m_map_shadow_update_frames - 1 || m_force_update_shadow_map) {
if (m_shadow_map_colored) {
m_driver->setRenderTarget(0, false, false);
m_driver->setRenderTarget(shadowMapTextureColors,
@ -298,7 +298,7 @@ void ShadowRenderer::updateSMTextures()
++m_current_frame;
// pass finished, swap textures and commit light changes
if (m_current_frame == m_map_shadow_update_frames) {
if (m_current_frame == m_map_shadow_update_frames || m_force_update_shadow_map) {
if (shadowMapClientMapFuture != nullptr)
std::swap(shadowMapClientMapFuture, shadowMapClientMap);
@ -306,6 +306,7 @@ void ShadowRenderer::updateSMTextures()
for (DirectionalLight &light : m_light_list)
light.commitFrustum();
}
m_force_update_shadow_map = false;
}
}
@ -432,7 +433,10 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target,
m_driver->setTransform(video::ETS_WORLD,
map_node->getAbsoluteTransformation());
map_node->renderMapShadows(m_driver, material, pass, m_current_frame, m_map_shadow_update_frames);
int frame = m_force_update_shadow_map ? 0 : m_current_frame;
int total_frames = m_force_update_shadow_map ? 1 : m_map_shadow_update_frames;
map_node->renderMapShadows(m_driver, material, pass, frame, total_frames);
break;
}
}
@ -670,6 +674,7 @@ std::string ShadowRenderer::readShaderFile(const std::string &path)
std::string prefix;
if (m_shadow_map_colored)
prefix.append("#define COLORED_SHADOWS 1\n");
prefix.append("#line 0\n");
std::string content;
fs::ReadFile(path, content);

View File

@ -74,6 +74,7 @@ public:
void removeNodeFromShadowList(scene::ISceneNode *node);
void update(video::ITexture *outputTarget = nullptr);
void setForceUpdateShadowMap() { m_force_update_shadow_map = true; }
void drawDebug();
video::ITexture *get_texture()
@ -131,6 +132,7 @@ private:
bool m_shadows_enabled;
bool m_shadows_supported;
bool m_shadow_map_colored;
bool m_force_update_shadow_map;
u8 m_map_shadow_update_frames; /* Use this number of frames to update map shaodw */
u8 m_current_frame{0}; /* Current frame */
f32 m_perspective_bias_xy;

View File

@ -96,7 +96,12 @@ void parseContentInfo(ContentSpec &spec)
Settings conf;
if (!conf_path.empty() && conf.readConfigFile(conf_path.c_str())) {
if (conf.exists("name"))
if (conf.exists("title"))
spec.title = conf.get("title");
else if (spec.type == "game" && conf.exists("name"))
spec.title = conf.get("name");
if (spec.type != "game" && conf.exists("name"))
spec.name = conf.get("name");
if (conf.exists("description"))

View File

@ -27,7 +27,14 @@ struct ContentSpec
std::string type;
std::string author;
u32 release = 0;
/// Technical name / Id
std::string name;
/// Human-readable title
std::string title;
/// Short description
std::string desc;
std::string path;
};

View File

@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <common/c_internal.h>
#include "content/subgames.h"
#include "porting.h"
#include "filesys.h"
@ -45,6 +46,25 @@ bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
}
void SubgameSpec::checkAndLog() const
{
// Log deprecation messages
auto handling_mode = get_deprecated_handling_mode();
if (!deprecation_msgs.empty() && handling_mode != DeprecatedHandlingMode::Ignore) {
std::ostringstream os;
os << "Game " << title << " at " << path << ":" << std::endl;
for (auto msg : deprecation_msgs)
os << "\t" << msg << std::endl;
if (handling_mode == DeprecatedHandlingMode::Error)
throw ModError(os.str());
else
warningstream << os.str();
}
}
struct GameFindPath
{
std::string path;
@ -121,11 +141,13 @@ SubgameSpec findSubgame(const std::string &id)
Settings conf;
conf.readConfigFile(conf_path.c_str());
std::string game_name;
if (conf.exists("name"))
game_name = conf.get("name");
std::string game_title;
if (conf.exists("title"))
game_title = conf.get("title");
else if (conf.exists("name"))
game_title = conf.get("name");
else
game_name = id;
game_title = id;
std::string game_author;
if (conf.exists("author"))
@ -140,8 +162,14 @@ SubgameSpec findSubgame(const std::string &id)
menuicon_path = getImagePath(
game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
#endif
return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name,
SubgameSpec spec(id, game_path, gamemod_path, mods_paths, game_title,
menuicon_path, game_author, game_release);
if (conf.exists("name") && !conf.exists("title"))
spec.deprecation_msgs.push_back("\"name\" setting in game.conf is deprecated, please use \"title\" instead");
return spec;
}
SubgameSpec findWorldSubgame(const std::string &world_path)
@ -159,10 +187,12 @@ SubgameSpec findWorldSubgame(const std::string &world_path)
std::string conf_path = world_gamepath + DIR_DELIM + "game.conf";
conf.readConfigFile(conf_path.c_str());
if (conf.exists("name"))
gamespec.name = conf.get("name");
if (conf.exists("title"))
gamespec.title = conf.get("title");
else if (conf.exists("name"))
gamespec.title = conf.get("name");
else
gamespec.name = world_gameid;
gamespec.title = world_gameid;
return gamespec;
}

View File

@ -29,7 +29,7 @@ class Settings;
struct SubgameSpec
{
std::string id;
std::string name;
std::string title;
std::string author;
int release;
std::string path;
@ -41,20 +41,24 @@ struct SubgameSpec
std::unordered_map<std::string, std::string> addon_mods_paths;
std::string menuicon_path;
// For logging purposes
std::vector<const char *> deprecation_msgs;
SubgameSpec(const std::string &id = "", const std::string &path = "",
const std::string &gamemods_path = "",
const std::unordered_map<std::string, std::string> &addon_mods_paths = {},
const std::string &name = "",
const std::string &title = "",
const std::string &menuicon_path = "",
const std::string &author = "", int release = 0) :
id(id),
name(name), author(author), release(release), path(path),
title(title), author(author), release(release), path(path),
gamemods_path(gamemods_path), addon_mods_paths(addon_mods_paths),
menuicon_path(menuicon_path)
{
}
bool isValid() const { return (!id.empty() && !path.empty()); }
void checkAndLog() const;
};
SubgameSpec findSubgame(const std::string &id);

View File

@ -64,7 +64,7 @@ void set_default_settings()
settings->setDefault("enable_client_modding", "true");
settings->setDefault("max_out_chat_queue_size", "20");
settings->setDefault("pause_on_lost_focus", "false");
settings->setDefault("enable_register_confirmation", "true");
settings->setDefault("enable_split_login_register", "true");
settings->setDefault("chat_weblink_color", "#8888FF");
// Cheat Menu
@ -344,7 +344,7 @@ void set_default_settings()
settings->setDefault("shadow_filters", "1");
settings->setDefault("shadow_poisson_filter", "true");
settings->setDefault("shadow_update_frames", "8");
settings->setDefault("shadow_soft_radius", "1.0");
settings->setDefault("shadow_soft_radius", "5.0");
settings->setDefault("shadow_sky_body_orbit_tilt", "0.0");
// Input
@ -526,7 +526,6 @@ void set_default_settings()
// Altered settings for macOS
#if defined(__MACH__) && defined(__APPLE__)
settings->setDefault("keymap_sneak", "KEY_SHIFT");
settings->setDefault("fps_max", "0");
#endif
#ifdef HAVE_TOUCHSCREENGUI

View File

@ -20,8 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes.h"
struct SubgameSpec;
#include "content/subgames.h"
// Information provided from "main"
struct GameParams
@ -34,6 +33,12 @@ struct GameParams
bool is_dedicated_server;
};
enum class ELoginRegister {
Any = 0,
Login,
Register
};
// Information processed by main menu
struct GameStartData : GameParams
{
@ -46,6 +51,8 @@ struct GameStartData : GameParams
std::string address;
bool local_server;
ELoginRegister allow_login_or_register = ELoginRegister::Any;
// "world_path" must be kept in sync!
WorldSpec world_spec;
};

View File

@ -12,7 +12,6 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiButtonImage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiButtonItemImage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiConfirmRegistration.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEditBox.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp

View File

@ -1,281 +0,0 @@
/*
Minetest
Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
<muhammadrifqipriyosusanto@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "guiConfirmRegistration.h"
#include "client/client.h"
#include "guiButton.h"
#include <IGUICheckBox.h>
#include <IGUIButton.h>
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include "guiEditBoxWithScrollbar.h"
#include "porting.h"
#ifdef HAVE_TOUCHSCREENGUI
#include "client/renderingengine.h"
#endif
#include "gettext.h"
// Continuing from guiPasswordChange.cpp
const int ID_confirmPassword = 262;
const int ID_confirm = 263;
const int ID_intotext = 264;
const int ID_cancel = 265;
const int ID_message = 266;
GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password,
bool *aborted, ISimpleTextureSource *tsrc) :
GUIModalMenu(env, parent, id, menumgr),
m_client(client), m_playername(playername), m_password(password),
m_aborted(aborted), m_tsrc(tsrc)
{
#ifdef HAVE_TOUCHSCREENGUI
m_touchscreen_visible = false;
#endif
}
GUIConfirmRegistration::~GUIConfirmRegistration()
{
removeChildren();
}
void GUIConfirmRegistration::removeChildren()
{
const core::list<gui::IGUIElement *> &children = getChildren();
core::list<gui::IGUIElement *> children_copy;
for (gui::IGUIElement *i : children)
children_copy.push_back(i);
for (gui::IGUIElement *i : children_copy)
i->remove();
}
void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
{
acceptInput();
removeChildren();
/*
Calculate new sizes and positions
*/
#ifdef HAVE_TOUCHSCREENGUI
const float s = m_gui_scale * RenderingEngine::getDisplayDensity() / 2;
#else
const float s = m_gui_scale;
#endif
DesiredRect = core::rect<s32>(
screensize.X / 2 - 600 * s / 2,
screensize.Y / 2 - 360 * s / 2,
screensize.X / 2 + 600 * s / 2,
screensize.Y / 2 + 360 * s / 2
);
recalculateAbsolutePosition(false);
v2s32 size = DesiredRect.getSize();
v2s32 topleft_client(0, 0);
const wchar_t *text;
/*
Add stuff
*/
s32 ypos = 30 * s;
{
core::rect<s32> rect2(0, 0, 540 * s, 180 * s);
rect2 += topleft_client + v2s32(30 * s, ypos);
static const std::string info_text_template = strgettext(
"You are about to join this server with the name \"%s\" for the "
"first time.\n"
"If you proceed, a new account using your credentials will be "
"created on this server.\n"
"Please retype your password and click 'Register and Join' to "
"confirm account creation, or click 'Cancel' to abort.");
char info_text_buf[1024];
porting::mt_snprintf(info_text_buf, sizeof(info_text_buf),
info_text_template.c_str(), m_playername.c_str());
std::wstring info_text_w = utf8_to_wide(info_text_buf);
gui::IGUIEditBox *e = new GUIEditBoxWithScrollBar(info_text_w.c_str(),
true, Environment, this, ID_intotext, rect2, false, true);
e->drop();
e->setMultiLine(true);
e->setWordWrap(true);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
}
ypos += 200 * s;
{
core::rect<s32> rect2(0, 0, 540 * s, 30 * s);
rect2 += topleft_client + v2s32(30 * s, ypos);
gui::IGUIEditBox *e = Environment->addEditBox(m_pass_confirm.c_str(),
rect2, true, this, ID_confirmPassword);
e->setPasswordBox(true);
Environment->setFocus(e);
}
ypos += 50 * s;
{
core::rect<s32> rect2(0, 0, 230 * s, 35 * s);
rect2 = rect2 + v2s32(size.X / 2 - 220 * s, ypos);
text = wgettext("Register and Join");
GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_confirm, text);
delete[] text;
}
{
core::rect<s32> rect2(0, 0, 120 * s, 35 * s);
rect2 = rect2 + v2s32(size.X / 2 + 70 * s, ypos);
text = wgettext("Cancel");
GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_cancel, text);
delete[] text;
}
{
core::rect<s32> rect2(0, 0, 500 * s, 40 * s);
rect2 += topleft_client + v2s32(30 * s, ypos + 40 * s);
text = wgettext("Passwords do not match!");
IGUIElement *e = Environment->addStaticText(
text, rect2, false, true, this, ID_message);
e->setVisible(false);
delete[] text;
}
}
void GUIConfirmRegistration::drawMenu()
{
gui::IGUISkin *skin = Environment->getSkin();
if (!skin)
return;
video::IVideoDriver *driver = Environment->getVideoDriver();
video::SColor bgcolor(140, 0, 0, 0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
gui::IGUIElement::draw();
#ifdef __ANDROID__
getAndroidUIInput();
#endif
}
void GUIConfirmRegistration::closeMenu(bool goNext)
{
if (goNext) {
m_client->confirmRegistration();
} else {
*m_aborted = true;
infostream << "Connect aborted [Escape]" << std::endl;
}
quitMenu();
}
void GUIConfirmRegistration::acceptInput()
{
gui::IGUIElement *e;
e = getElementFromId(ID_confirmPassword);
if (e)
m_pass_confirm = e->getText();
}
bool GUIConfirmRegistration::processInput()
{
if (utf8_to_wide(m_password) != m_pass_confirm) {
gui::IGUIElement *e = getElementFromId(ID_message);
if (e)
e->setVisible(true);
return false;
}
return true;
}
bool GUIConfirmRegistration::OnEvent(const SEvent &event)
{
if (event.EventType == EET_KEY_INPUT_EVENT) {
// clang-format off
if ((event.KeyInput.Key == KEY_ESCAPE ||
event.KeyInput.Key == KEY_CANCEL) &&
event.KeyInput.PressedDown) {
closeMenu(false);
return true;
}
// clang-format on
if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
acceptInput();
if (processInput())
closeMenu(true);
return true;
}
}
if (event.EventType != EET_GUI_EVENT)
return Parent ? Parent->OnEvent(event) : false;
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) {
if (!canTakeFocus(event.GUIEvent.Element)) {
infostream << "GUIConfirmRegistration: Not allowing focus change."
<< std::endl;
// Returning true disables focus change
return true;
}
} else if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
switch (event.GUIEvent.Caller->getID()) {
case ID_confirm:
acceptInput();
if (processInput())
closeMenu(true);
return true;
case ID_cancel:
closeMenu(false);
return true;
}
} else if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
switch (event.GUIEvent.Caller->getID()) {
case ID_confirmPassword:
acceptInput();
if (processInput())
closeMenu(true);
return true;
}
}
return false;
}
#ifdef __ANDROID__
bool GUIConfirmRegistration::getAndroidUIInput()
{
if (!hasAndroidUIInput() || m_jni_field_name != "password")
return false;
// still waiting
if (porting::getInputDialogState() == -1)
return true;
m_jni_field_name.clear();
gui::IGUIElement *e = getElementFromId(ID_confirmPassword);
if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX)
return false;
std::string text = porting::getInputDialogValue();
e->setText(utf8_to_wide(text).c_str());
return false;
}
#endif

View File

@ -1,68 +0,0 @@
/*
Minetest
Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
<muhammadrifqipriyosusanto@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_extrabloated.h"
#include "modalMenu.h"
#include <string>
class Client;
class ISimpleTextureSource;
class GUIConfirmRegistration : public GUIModalMenu
{
public:
GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password,
bool *aborted, ISimpleTextureSource *tsrc);
~GUIConfirmRegistration();
void removeChildren();
/*
Remove and re-add (or reposition) stuff
*/
void regenerateGui(v2u32 screensize);
void drawMenu();
void closeMenu(bool goNext);
void acceptInput();
bool processInput();
bool OnEvent(const SEvent &event);
#ifdef __ANDROID__
bool getAndroidUIInput();
#endif
private:
std::wstring getLabelByID(s32 id) { return L""; }
std::string getNameByID(s32 id) { return "password"; }
Client *m_client = nullptr;
const std::string &m_playername;
const std::string &m_password;
bool *m_aborted = nullptr;
std::wstring m_pass_confirm = L"";
ISimpleTextureSource *m_tsrc;
};

View File

@ -127,7 +127,8 @@ GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
GUIFormSpecMenu::~GUIFormSpecMenu()
{
removeChildren();
removeAllChildren();
removeTooltip();
for (auto &table_it : m_tables)
table_it.second->drop();
@ -174,14 +175,8 @@ void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
}
}
void GUIFormSpecMenu::removeChildren()
void GUIFormSpecMenu::removeTooltip()
{
const core::list<gui::IGUIElement*> &children = getChildren();
while (!children.empty()) {
(*children.getLast())->remove();
}
if (m_tooltip_element) {
m_tooltip_element->remove();
m_tooltip_element->drop();
@ -199,16 +194,7 @@ void GUIFormSpecMenu::setInitialFocus()
// 5. first focusable (not statictext, not tabheader)
// 6. first child element
core::list<gui::IGUIElement*> children = getChildren();
// in case "children" contains any NULL elements, remove them
for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
it != children.end();) {
if (*it)
++it;
else
it = children.erase(it);
}
const auto& children = getChildren();
// 1. first empty editbox
for (gui::IGUIElement *it : children) {
@ -236,8 +222,7 @@ void GUIFormSpecMenu::setInitialFocus()
}
// 4. last button
for (core::list<gui::IGUIElement*>::Iterator it = children.getLast();
it != children.end(); --it) {
for (auto it = children.rbegin(); it != children.rend(); ++it) {
if ((*it)->getType() == gui::EGUIET_BUTTON) {
Environment->setFocus(*it);
return;
@ -257,7 +242,7 @@ void GUIFormSpecMenu::setInitialFocus()
if (children.empty())
Environment->setFocus(this);
else
Environment->setFocus(*(children.begin()));
Environment->setFocus(children.front());
}
GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
@ -3045,7 +3030,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
}
// Remove children
removeChildren();
removeAllChildren();
removeTooltip();
for (auto &table_it : m_tables)
table_it.second->drop();
@ -3341,7 +3327,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
pos_offset = v2f32();
// used for formspec versions < 3
core::list<IGUIElement *>::Iterator legacy_sort_start = Children.getLast();
std::list<IGUIElement *>::iterator legacy_sort_start = std::prev(Children.end()); // last element
if (enable_prepends) {
// Backup the coordinates so that prepends can use the coordinates of choice.
@ -3356,7 +3342,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
// legacy sorting for formspec versions < 3
if (m_formspec_version >= 3)
// prepends do not need to be reordered
legacy_sort_start = Children.getLast();
legacy_sort_start = std::prev(Children.end()); // last element
else if (version_backup >= 3)
// only prepends elements have to be reordered
legacySortElements(legacy_sort_start);
@ -3437,7 +3423,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
}
}
void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator from)
void GUIFormSpecMenu::legacySortElements(std::list<IGUIElement *>::iterator from)
{
/*
Draw order for formspec_version <= 2:
@ -3454,17 +3440,16 @@ void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator fro
if (from == Children.end())
from = Children.begin();
else
from++;
++from;
core::list<IGUIElement *>::Iterator to = Children.end();
std::list<IGUIElement *>::iterator to = Children.end();
// 1: Copy into a sortable container
std::vector<IGUIElement *> elements;
for (auto it = from; it != to; ++it)
elements.emplace_back(*it);
std::vector<IGUIElement *> elements(from, to);
// 2: Sort the container
std::stable_sort(elements.begin(), elements.end(),
[this] (const IGUIElement *a, const IGUIElement *b) -> bool {
// TODO: getSpecByID is a linear search. It should made O(1), or cached here.
const FieldSpec *spec_a = getSpecByID(a->getID());
const FieldSpec *spec_b = getSpecByID(b->getID());
return spec_a && spec_b &&
@ -3472,10 +3457,7 @@ void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator fro
});
// 3: Re-assign the pointers
for (auto e : elements) {
*from = e;
from++;
}
reorderChildren(from, to, elements);
}
#ifdef __ANDROID__
@ -3600,12 +3582,11 @@ void GUIFormSpecMenu::drawMenu()
/*
This is where all the drawing happens.
*/
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
if ((*it)->isNotClipped() ||
for (auto child : Children)
if (child->isNotClipped() ||
AbsoluteClippingRect.isRectCollided(
(*it)->getAbsolutePosition()))
(*it)->draw();
child->getAbsolutePosition()))
child->draw();
for (gui::IGUIElement *e : m_clickthrough_elements)
e->setVisible(false);

View File

@ -212,7 +212,7 @@ public:
m_lockscreensize = basescreensize;
}
void removeChildren();
void removeTooltip();
void setInitialFocus();
void setFocus(const std::string &elementname)
@ -467,7 +467,7 @@ private:
* types were drawn before others.
* This function sorts the elements in the old order for backwards compatibility.
*/
void legacySortElements(core::list<IGUIElement *>::Iterator from);
void legacySortElements(std::list<IGUIElement *>::iterator from);
int m_btn_height;
gui::IGUIFont *m_font = nullptr;

View File

@ -103,7 +103,8 @@ GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
GUIKeyChangeMenu::~GUIKeyChangeMenu()
{
removeChildren();
removeAllChildren();
key_used_text = nullptr;
for (key_setting *ks : key_settings) {
delete[] ks->button_name;
@ -112,23 +113,10 @@ GUIKeyChangeMenu::~GUIKeyChangeMenu()
key_settings.clear();
}
void GUIKeyChangeMenu::removeChildren()
{
const core::list<gui::IGUIElement*> &children = getChildren();
core::list<gui::IGUIElement*> children_copy;
for (gui::IGUIElement*i : children) {
children_copy.push_back(i);
}
for (gui::IGUIElement *i : children_copy) {
i->remove();
}
key_used_text = nullptr;
}
void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{
removeChildren();
removeAllChildren();
key_used_text = nullptr;
const float s = m_gui_scale;
DesiredRect = core::rect<s32>(

View File

@ -46,7 +46,6 @@ public:
IMenuManager *menumgr, ISimpleTextureSource *tsrc);
~GUIKeyChangeMenu();
void removeChildren();
/*
Remove and re-add (or reposition) stuff
*/

View File

@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes_extrabloated.h"
#include "gameparams.h"
#include <string>
#include <list>
@ -50,5 +51,7 @@ struct MainMenuData {
// Data to be passed to the script
MainMenuDataForScript script_data;
ELoginRegister allow_login_or_register = ELoginRegister::Any;
MainMenuData() = default;
};

View File

@ -51,23 +51,6 @@ GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env,
{
}
GUIPasswordChange::~GUIPasswordChange()
{
removeChildren();
}
void GUIPasswordChange::removeChildren()
{
const core::list<gui::IGUIElement *> &children = getChildren();
core::list<gui::IGUIElement *> children_copy;
for (gui::IGUIElement *i : children) {
children_copy.push_back(i);
}
for (gui::IGUIElement *i : children_copy) {
i->remove();
}
}
void GUIPasswordChange::regenerateGui(v2u32 screensize)
{
/*
@ -78,7 +61,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
/*
Remove stuff
*/
removeChildren();
removeAllChildren();
/*
Calculate new sizes and positions

View File

@ -31,9 +31,7 @@ public:
GUIPasswordChange(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
IMenuManager *menumgr, Client *client,
ISimpleTextureSource *tsrc);
~GUIPasswordChange();
void removeChildren();
/*
Remove and re-add (or reposition) stuff
*/

View File

@ -32,13 +32,12 @@ GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
GUIFileSelectMenu::~GUIFileSelectMenu()
{
removeChildren();
setlocale(LC_NUMERIC, "C");
}
void GUIFileSelectMenu::regenerateGui(v2u32 screensize)
{
removeChildren();
removeAllChildren();
m_fileOpenDialog = 0;
core::dimension2du size(600 * m_gui_scale, 400 * m_gui_scale);

View File

@ -59,12 +59,11 @@ bool GUIScrollContainer::OnEvent(const SEvent &event)
void GUIScrollContainer::draw()
{
if (isVisible()) {
core::list<IGUIElement *>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
if ((*it)->isNotClipped() ||
for (auto child : Children)
if (child->isNotClipped() ||
AbsoluteClippingRect.isRectCollided(
(*it)->getAbsolutePosition()))
(*it)->draw();
child->getAbsolutePosition()))
child->draw();
}
}

View File

@ -45,32 +45,12 @@ GUIVolumeChange::GUIVolumeChange(gui::IGUIEnvironment* env,
{
}
GUIVolumeChange::~GUIVolumeChange()
{
removeChildren();
}
void GUIVolumeChange::removeChildren()
{
if (gui::IGUIElement *e = getElementFromId(ID_soundText))
e->remove();
if (gui::IGUIElement *e = getElementFromId(ID_soundExitButton))
e->remove();
if (gui::IGUIElement *e = getElementFromId(ID_soundSlider))
e->remove();
if (gui::IGUIElement *e = getElementFromId(ID_soundMuteButton))
e->remove();
}
void GUIVolumeChange::regenerateGui(v2u32 screensize)
{
/*
Remove stuff
*/
removeChildren();
removeAllChildren();
/*
Calculate new sizes and positions
*/

View File

@ -31,9 +31,6 @@ public:
GUIVolumeChange(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr, ISimpleTextureSource *tsrc);
~GUIVolumeChange();
void removeChildren();
/*
Remove and re-add (or reposition) stuff
*/

View File

@ -64,10 +64,6 @@ public:
// Remove all entries if there are duplicates
m_stack.remove(menu);
/*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
assert(*i == menu);
m_stack.erase(i);*/
if(!m_stack.empty())
m_stack.back()->setVisible(true);
}

View File

@ -108,19 +108,6 @@ void GUIModalMenu::quitMenu()
#endif
}
void GUIModalMenu::removeChildren()
{
const core::list<gui::IGUIElement *> &children = getChildren();
core::list<gui::IGUIElement *> children_copy;
for (gui::IGUIElement *i : children) {
children_copy.push_back(i);
}
for (gui::IGUIElement *i : children_copy) {
i->remove();
}
}
// clang-format off
bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
{
@ -265,13 +252,11 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
return retval;
m_jni_field_name = field_name;
/*~ Imperative, as in "Enter/type in text".
Don't forget the space. */
std::string message = gettext("Enter ");
std::string label = wide_to_utf8(getLabelByID(hovered->getID()));
if (label.empty())
label = "text";
message += strgettext(label) + ":";
/*~ Imperative, as in "Type in text" */
std::string message = fmtgettext("Enter %s:");
// single line text input
int type = 2;

View File

@ -47,7 +47,6 @@ public:
bool canTakeFocus(gui::IGUIElement *e);
void draw();
void quitMenu();
void removeChildren();
virtual void regenerateGui(v2u32 screensize) = 0;
virtual void drawMenu() = 0;

View File

@ -292,9 +292,6 @@ shadow_offset(0), shadow_alpha(0), fallback(0)
Driver->grab();
setInvisibleCharacters(L" ");
// Glyphs aren't reference counted, so don't try to delete them when we free the array.
Glyphs.set_free_when_destroyed(false);
}
bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency)
@ -411,8 +408,7 @@ CGUITTFont::~CGUITTFont()
{
// Delete the glyphs and glyph pages.
reset_images();
CGUITTAssistDelete::Delete(Glyphs);
//Glyphs.clear();
Glyphs.clear();
// We aren't using this face anymore.
auto n = c_faces.find(filename);
@ -675,6 +671,8 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
update_glyph_pages();
auto it = Render_Map.begin();
auto ie = Render_Map.end();
core::array<core::vector2di> tmp_positions;
core::array<core::recti> tmp_source_rects;
while (it != ie)
{
CGUITTGlyphPage* page = it->second;
@ -696,10 +694,8 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
do
++i;
while (i < page->render_positions.size() && page->render_colors[i] == colprev);
core::array<core::vector2di> tmp_positions;
core::array<core::recti> tmp_source_rects;
tmp_positions.set_pointer(&page->render_positions[ibegin], i - ibegin, false, false); // no copy
tmp_source_rects.set_pointer(&page->render_source_rects[ibegin], i - ibegin, false, false);
tmp_positions.set_data(&page->render_positions[ibegin], i - ibegin);
tmp_source_rects.set_data(&page->render_source_rects[ibegin], i - ibegin);
--i;
if (!use_transparency)

View File

@ -37,6 +37,7 @@
#include <map>
#include <irrUString.h>
#include "util/enriched_string.h"
#include "util/basic_macros.h"
#include FT_FREETYPE_H
namespace irr
@ -46,23 +47,34 @@ namespace gui
struct SGUITTFace;
class CGUITTFont;
//! Class to assist in deleting glyphs.
class CGUITTAssistDelete
{
public:
template <class T, typename TAlloc>
static void Delete(core::array<T, TAlloc>& a)
{
TAlloc allocator;
allocator.deallocate(a.pointer());
}
};
//! Structure representing a single TrueType glyph.
struct SGUITTGlyph
{
//! Constructor.
SGUITTGlyph() : isLoaded(false), glyph_page(0), surface(0), parent(0) {}
SGUITTGlyph() :
isLoaded(false),
glyph_page(0),
source_rect(),
offset(),
advance(),
surface(0),
parent(0)
{}
DISABLE_CLASS_COPY(SGUITTGlyph);
//! This class would be trivially copyable except for the reference count on `surface`.
SGUITTGlyph(SGUITTGlyph &&other) :
isLoaded(other.isLoaded),
glyph_page(other.glyph_page),
source_rect(other.source_rect),
offset(other.offset),
advance(other.advance),
surface(other.surface),
parent(other.parent)
{
other.surface = 0;
}
//! Destructor.
~SGUITTGlyph() { unload(); }

View File

@ -152,12 +152,14 @@ void Logger::addOutput(ILogOutput *out)
void Logger::addOutput(ILogOutput *out, LogLevel lev)
{
MutexAutoLock lock(m_mutex);
m_outputs[lev].push_back(out);
m_has_outputs[lev] = true;
}
void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask)
{
MutexAutoLock lock(m_mutex);
for (size_t i = 0; i < LL_MAX; i++) {
if (mask & LOGLEVEL_TO_MASKLEVEL(i)) {
m_outputs[i].push_back(out);
@ -168,6 +170,7 @@ void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask)
void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev)
{
MutexAutoLock lock(m_mutex);
assert(lev < LL_MAX);
for (size_t i = 0; i <= lev; i++) {
m_outputs[i].push_back(out);
@ -177,6 +180,7 @@ void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev)
LogLevelMask Logger::removeOutput(ILogOutput *out)
{
MutexAutoLock lock(m_mutex);
LogLevelMask ret_mask = 0;
for (size_t i = 0; i < LL_MAX; i++) {
std::vector<ILogOutput *>::iterator it;
@ -386,6 +390,6 @@ void LogOutputBuffer::logRaw(LogLevel lev, const std::string &line)
default: break;
}
}
m_buffer.push(color.append(line));
MutexAutoLock lock(m_buffer_mutex);
m_buffer.emplace(color.append(line));
}

View File

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#if !defined(_WIN32) // POSIX
#include <unistd.h>
#endif
#include "threading/mutex_auto_lock.h"
#include "util/basic_macros.h"
#include "util/stream.h"
#include "irrlichttypes.h"
@ -168,24 +169,31 @@ public:
void clear()
{
MutexAutoLock lock(m_buffer_mutex);
m_buffer = std::queue<std::string>();
}
bool empty() const
{
MutexAutoLock lock(m_buffer_mutex);
return m_buffer.empty();
}
std::string get()
{
if (empty())
MutexAutoLock lock(m_buffer_mutex);
if (m_buffer.empty())
return "";
std::string s = m_buffer.front();
std::string s = std::move(m_buffer.front());
m_buffer.pop();
return s;
}
private:
// g_logger serializes calls to logRaw() with a mutex, but that
// doesn't prevent get() / clear() from being called on top of it.
// This mutex prevents that.
mutable std::mutex m_buffer_mutex;
std::queue<std::string> m_buffer;
Logger &m_logger;
};

View File

@ -102,7 +102,7 @@ MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
return sector;
}
std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
auto n = m_sectors.find(p);
if (n == m_sectors.end())
return NULL;

View File

@ -268,7 +268,7 @@ protected:
std::set<MapEventReceiver*> m_event_receivers;
std::map<v2s16, MapSector*> m_sectors;
std::unordered_map<v2s16, MapSector*> m_sectors;
// Be sure to set this to NULL when the cached sector is deleted
MapSector *m_sector_cache = nullptr;

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