Compare commits
72 Commits
Author | SHA1 | Date |
---|---|---|
Cora de la Mouche | 6df1590803 | |
Cora de la Mouche | 2c050a42d7 | |
cora | 64e7dda46e | |
cora | 7a59bcda75 | |
Cora de la Mouche | f8fd5c11b6 | |
PrairieWind | c66ae6717a | |
Elias Fleckenstein | 296cce39d3 | |
Elias Fleckenstein | b11c0a6721 | |
Lars Müller | edc7df5480 | |
paradust7 | 951604e29f | |
Zughy | 381f84ee27 | |
sfan5 | 14c283a623 | |
rubenwardy | 4baf56520d | |
sfan5 | a69b7abe00 | |
rubenwardy | 03d86ea0b4 | |
ROllerozxa | 21323ef1ff | |
savilli | 1f39948bc3 | |
Lars Mueller | e82985c0a1 | |
JosiahWI | 8e5bd82c4d | |
sfan5 | 575caa8015 | |
sfan5 | 5f3af7d18b | |
Wuzzy | 6a6b579c54 | |
ROllerozxa | 6d163b72dc | |
Zughy | b72932b445 | |
Elias Fleckenstein | 827b9f8d70 | |
sfan5 | 9fc018ded1 | |
sfan5 | a9a207685a | |
sfan5 | c1d03695d4 | |
sfan5 | ea74680df4 | |
stefan | bb671c3089 | |
sfan5 | 1b68fb7683 | |
sfan5 | 303329f2d6 | |
sfan5 | 85c824ed13 | |
sfan5 | 998e4820c9 | |
sfan5 | 5cd7b0c6e4 | |
sfan5 | 8908a91016 | |
sfan5 | 261a8db9dd | |
sfan5 | f195db2d14 | |
sfan5 | da71e86633 | |
sfan5 | bccaf5fc2d | |
sfan5 | 0c6a029413 | |
x2048 | 25ba9d848d | |
Lars Müller | 76000e676b | |
Lars Müller | e8b2954586 | |
Wuzzy | fe299e24d6 | |
x2048 | ef22c0206f | |
sfan5 | 8b74257bf3 | |
sfan5 | 9a01581cdd | |
sfan5 | 5d26ac0088 | |
x2048 | ed26ed5a1f | |
sfan5 | 16a30556df | |
sfan5 | 5daafc9d33 | |
Richard Try | e16a470d59 | |
paradust7 | 367a2d4b29 | |
Wuzzy | 0f9c78c3eb | |
Zughy | c660218e43 | |
Wuzzy | fa682270a9 | |
Wuzzy | ac5e8176b9 | |
Elias Fleckenstein | 3ff3103e98 | |
paradust7 | 9f338f5a56 | |
paradust7 | 2742fef458 | |
sfan5 | bc59fcf5c5 | |
sfan5 | 2f32044273 | |
paradust7 | 371f21fb35 | |
Zughy | 8edc0fae5f | |
paradust7 | e1f707d7e1 | |
sfan5 | 9ee3dc71f1 | |
sfan5 | 70dc23f996 | |
rubenwardy | 4e9e230e34 | |
x2048 | dc45b85a54 | |
x2048 | a4ef62f5b2 | |
x2048 | 604fb2b738 |
|
@ -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 -->
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
-------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<paths>
|
||||
<external-files-path path="Minetest/" name="minetest" />
|
||||
</paths>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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 = {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
||||
|
|
@ -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 |
|
@ -8,3 +8,4 @@ dofile(path.."/properties.lua")
|
|||
dofile(path.."/liquids.lua")
|
||||
dofile(path.."/light.lua")
|
||||
dofile(path.."/textures.lua")
|
||||
dofile(path.."/overlays.lua")
|
||||
|
|
|
@ -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 |
|
@ -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})
|
||||
|
|
|
@ -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) |
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -46,7 +46,6 @@ public:
|
|||
IMenuManager *menumgr, ISimpleTextureSource *tsrc);
|
||||
~GUIKeyChangeMenu();
|
||||
|
||||
void removeChildren();
|
||||
/*
|
||||
Remove and re-add (or reposition) stuff
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
12
src/log.h
12
src/log.h
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue