This commit is contained in:
Elias Fleckenstein 2022-05-22 12:05:27 +02:00
commit 3ff3103e98
No known key found for this signature in database
GPG Key ID: 06927A5199D6C9B2
61 changed files with 602 additions and 600 deletions

View File

@ -230,7 +230,7 @@ jobs:
with: with:
repository: minetest/irrlicht repository: minetest/irrlicht
path: lib/irrlichtmt/ path: lib/irrlichtmt/
ref: "1.9.0mt5" ref: "1.9.0mt6"
- name: Restore from cache and run vcpkg - name: Restore from cache and run vcpkg
uses: lukka/run-vcpkg@v7 uses: lukka/run-vcpkg@v7

View File

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

View File

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

View File

@ -63,6 +63,9 @@ Zughy:
appgurueu: appgurueu:
textures/base/pack/server_incompatible.png textures/base/pack/server_incompatible.png
erlehmann, Warr1024, rollerozxa:
textures/base/pack/no_screenshot.png
License of Minetest source code License of Minetest source code
------------------------------- -------------------------------

View File

@ -34,10 +34,14 @@ import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import androidx.annotation.Keep;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import java.util.Objects; 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 { public class GameActivity extends NativeActivity {
static { static {
System.loadLibrary("c++_shared"); System.loadLibrary("c++_shared");

View File

@ -1,6 +1,7 @@
LOCAL_PATH := $(call my-dir)/.. LOCAL_PATH := $(call my-dir)/..
#LOCAL_ADDRESS_SANITIZER:=true #LOCAL_ADDRESS_SANITIZER:=true
#USE_BUILTIN_LUA:=true
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := Curl LOCAL_MODULE := Curl
@ -42,11 +43,15 @@ LOCAL_MODULE := Irrlicht
LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libIrrlichtMt.a LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libIrrlichtMt.a
include $(PREBUILT_STATIC_LIBRARY) include $(PREBUILT_STATIC_LIBRARY)
ifndef USE_BUILTIN_LUA
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := LuaJIT LOCAL_MODULE := LuaJIT
LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/libluajit.a LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/libluajit.a
include $(PREBUILT_STATIC_LIBRARY) include $(PREBUILT_STATIC_LIBRARY)
endif
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := OpenAL LOCAL_MODULE := OpenAL
LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a
@ -92,7 +97,6 @@ LOCAL_CFLAGS += \
-DUSE_CURL=1 \ -DUSE_CURL=1 \
-DUSE_SOUND=1 \ -DUSE_SOUND=1 \
-DUSE_LEVELDB=0 \ -DUSE_LEVELDB=0 \
-DUSE_LUAJIT=1 \
-DUSE_GETTEXT=1 \ -DUSE_GETTEXT=1 \
-DVERSION_MAJOR=${versionMajor} \ -DVERSION_MAJOR=${versionMajor} \
-DVERSION_MINOR=${versionMinor} \ -DVERSION_MINOR=${versionMinor} \
@ -100,6 +104,12 @@ LOCAL_CFLAGS += \
-DVERSION_EXTRA=${versionExtra} \ -DVERSION_EXTRA=${versionExtra} \
$(GPROF_DEF) $(GPROF_DEF)
ifdef USE_BUILTIN_LUA
LOCAL_CFLAGS += -DUSE_LUAJIT=0
else
LOCAL_CFLAGS += -DUSE_LUAJIT=1
endif
ifdef NDEBUG ifdef NDEBUG
LOCAL_CFLAGS += -DNDEBUG=1 LOCAL_CFLAGS += -DNDEBUG=1
endif endif
@ -120,12 +130,19 @@ LOCAL_C_INCLUDES := \
deps/$(APP_ABI)/Irrlicht/include \ deps/$(APP_ABI)/Irrlicht/include \
deps/$(APP_ABI)/Gettext/include \ deps/$(APP_ABI)/Gettext/include \
deps/$(APP_ABI)/Iconv/include \ deps/$(APP_ABI)/Iconv/include \
deps/$(APP_ABI)/LuaJIT/include \
deps/$(APP_ABI)/OpenAL-Soft/include \ deps/$(APP_ABI)/OpenAL-Soft/include \
deps/$(APP_ABI)/SQLite/include \ deps/$(APP_ABI)/SQLite/include \
deps/$(APP_ABI)/Vorbis/include \ deps/$(APP_ABI)/Vorbis/include \
deps/$(APP_ABI)/Zstd/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 := \ LOCAL_SRC_FILES := \
$(wildcard ../../src/client/*.cpp) \ $(wildcard ../../src/client/*.cpp) \
$(wildcard ../../src/client/*/*.cpp) \ $(wildcard ../../src/client/*/*.cpp) \
@ -207,6 +224,41 @@ LOCAL_SRC_FILES := \
../../src/voxel.cpp \ ../../src/voxel.cpp \
../../src/voxelalgorithms.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 # GMP
LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
@ -218,12 +270,14 @@ LOCAL_STATIC_LIBRARIES += \
Freetype \ Freetype \
Iconv libcharset \ Iconv libcharset \
Irrlicht \ Irrlicht \
LuaJIT \
OpenAL \ OpenAL \
Gettext \ Gettext \
SQLite3 \ SQLite3 \
Vorbis libvorbisfile libogg \ Vorbis libvorbisfile libogg \
Zstd Zstd
ifndef USE_BUILTIN_LUA
LOCAL_STATIC_LIBRARIES += LuaJIT
endif
LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS) LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES

View File

@ -151,11 +151,9 @@ local function start_install(package, reason)
if conf_path then if conf_path then
local conf = Settings(conf_path) local conf = Settings(conf_path)
if name_is_title then conf:set("title", package.title)
conf:set("name", package.title) if not name_is_title then
else conf:set("name", package.name)
conf:set("title", package.title)
conf:set("name", package.name)
end end
if not conf:get("description") then if not conf:get("description") then
conf:set("description", package.short_description) conf:set("description", package.short_description)
@ -360,7 +358,7 @@ function install_dialog.get_formspec()
selected_game_idx = i selected_game_idx = i
end end
games[i] = core.formspec_escape(games[i].name) games[i] = core.formspec_escape(games[i].title)
end end
local selected_game = pkgmgr.games[selected_game_idx] local selected_game = pkgmgr.games[selected_game_idx]
@ -410,7 +408,7 @@ function install_dialog.get_formspec()
"container[0.375,0.70]", "container[0.375,0.70]",
"label[0,0.25;", fgettext("Base Game:"), "]", "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:"), "]", "label[0,0.8;", fgettext("Dependencies:"), "]",
@ -461,9 +459,9 @@ function install_dialog.handle_submit(this, fields)
return true return true
end end
if fields.gameid then if fields.selected_game then
for _, game in pairs(pkgmgr.games) do 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) core.settings:set("menu_last_game", game.id)
break break
end end

View File

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

View File

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

View File

@ -643,8 +643,8 @@ shadow_update_frames (Map shadows update frames) int 8 1 16
# Set the soft shadow radius size. # Set the soft shadow radius size.
# Lower values mean sharper shadows, bigger values mean softer shadows. # Lower values mean sharper shadows, bigger values mean softer shadows.
# Minimum value: 1.0; maximum value: 10.0 # Minimum value: 1.0; maximum value: 15.0
shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0 shadow_soft_radius (Soft shadow radius) float 5.0 1.0 15.0
# Set the tilt of Sun/Moon orbit in degrees. # Set the tilt of Sun/Moon orbit in degrees.
# Value of 0 means no tilt / vertical orbit. # Value of 0 means no tilt / vertical orbit.

View File

@ -25,6 +25,7 @@ uniform float animationTimer;
varying float cosLight; varying float cosLight;
varying float f_normal_length; varying float f_normal_length;
varying vec3 shadow_position; varying vec3 shadow_position;
varying float perspective_factor;
#endif #endif
@ -116,23 +117,16 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
#if SHADOW_FILTER == 2 #if SHADOW_FILTER == 2
#define PCFBOUND 3.5 #define PCFBOUND 2.0 // 5x5
#define PCFSAMPLES 64.0 #define PCFSAMPLES 25
#elif SHADOW_FILTER == 1 #elif SHADOW_FILTER == 1
#define PCFBOUND 1.5 #define PCFBOUND 1.0 // 3x3
#if defined(POISSON_FILTER) #define PCFSAMPLES 9
#define PCFSAMPLES 32.0
#else
#define PCFSAMPLES 16.0
#endif
#else #else
#define PCFBOUND 0.0 #define PCFBOUND 0.0
#if defined(POISSON_FILTER) #define PCFSAMPLES 1
#define PCFSAMPLES 4.0
#else
#define PCFSAMPLES 1.0
#endif
#endif #endif
#ifdef COLORED_SHADOWS #ifdef COLORED_SHADOWS
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
@ -149,59 +143,31 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis
} }
#endif #endif
float getBaseLength(vec2 smTexCoord) #define BASEFILTERRADIUS 1.0
{
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
}
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 // Return fast if sharp shadows are requested
if (PCFBOUND == 0.0) if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
return 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; vec2 clampedpos;
float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5);
float y, x; float y, x;
float depth = 0.0; float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
float pointDepth; // A factor from 0 to 1 to reduce blurring of short shadows
float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier; 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); float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
int n = 0; * 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) return max(BASEFILTERRADIUS * f_textureresolution / 4096.0, sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y);
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius);
clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy;
pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance);
if (pointDepth > -0.01) {
depth += pointDepth;
n += 1;
}
}
depth = depth / n;
depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius);
} }
#ifdef POISSON_FILTER #ifdef POISSON_FILTER
@ -276,26 +242,23 @@ const vec2[64] poissonDisk = vec2[64](
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
vec2 clampedpos; float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
if (radius < 0.1) { if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering // we are in the middle of even brightness, no need for filtering
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
} }
float baseLength = getBaseLength(smTexCoord); vec2 clampedpos;
float perspectiveFactor; vec4 visibility = vec4(0.0);
float scale_factor = radius / f_textureresolution;
float texture_size = 1.0 / (f_textureresolution * 0.5); int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); 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 init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
int end_offset = int(samples) + init_offset; int end_offset = int(samples) + init_offset;
for (int x = init_offset; x < end_offset; x++) { for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x]; clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); 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) float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
vec2 clampedpos; float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
if (radius < 0.1) { if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering // we are in the middle of even brightness, no need for filtering
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
} }
float baseLength = getBaseLength(smTexCoord); vec2 clampedpos;
float perspectiveFactor; float visibility = 0.0;
float scale_factor = radius / f_textureresolution;
float texture_size = 1.0 / (f_textureresolution * 0.5); int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); 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 init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
int end_offset = int(samples) + init_offset; int end_offset = int(samples) + init_offset;
for (int x = init_offset; x < end_offset; x++) { for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x]; clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); 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) vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
vec2 clampedpos; float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
if (radius < 0.1) { if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering // we are in the middle of even brightness, no need for filtering
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
} }
float baseLength = getBaseLength(smTexCoord); vec2 clampedpos;
float perspectiveFactor; vec4 visibility = vec4(0.0);
float x, y;
float texture_size = 1.0 / (f_textureresolution * 0.5); float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
float y, x; bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); float scale_factor = radius / bound / f_textureresolution;
int n = 0; float n = 0.0;
// basic PCF filter // basic PCF filter
for (y = -bound; y <= bound; y += 1.0) for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) { for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y); // screen offset clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
n += 1; n += 1.0;
} }
return visibility / n; return visibility / max(n, 1.0);
} }
#else #else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
vec2 clampedpos; float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
if (radius < 0.1) { if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering // we are in the middle of even brightness, no need for filtering
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
} }
float baseLength = getBaseLength(smTexCoord); vec2 clampedpos;
float perspectiveFactor; float visibility = 0.0;
float x, y;
float texture_size = 1.0 / (f_textureresolution * 0.5); float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
float y, x; bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); float scale_factor = radius / bound / f_textureresolution;
int n = 0; float n = 0.0;
// basic PCF filter // basic PCF filter
for (y = -bound; y <= bound; y += 1.0) for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) { for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y); // screen offset clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
n += 1; n += 1.0;
} }
return visibility / n; return visibility / max(n, 1.0);
} }
#endif #endif

View File

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

View File

@ -1,6 +1,5 @@
uniform sampler2D baseTexture; uniform sampler2D baseTexture;
uniform vec4 emissiveColor;
uniform vec3 dayLight; uniform vec3 dayLight;
uniform vec4 skyBgColor; uniform vec4 skyBgColor;
uniform float fogDistance; uniform float fogDistance;
@ -26,6 +25,7 @@ uniform float animationTimer;
varying float cosLight; varying float cosLight;
varying float f_normal_length; varying float f_normal_length;
varying vec3 shadow_position; varying vec3 shadow_position;
varying float perspective_factor;
#endif #endif
@ -119,23 +119,16 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
#if SHADOW_FILTER == 2 #if SHADOW_FILTER == 2
#define PCFBOUND 3.5 #define PCFBOUND 2.0 // 5x5
#define PCFSAMPLES 64.0 #define PCFSAMPLES 25
#elif SHADOW_FILTER == 1 #elif SHADOW_FILTER == 1
#define PCFBOUND 1.5 #define PCFBOUND 1.0 // 3x3
#if defined(POISSON_FILTER) #define PCFSAMPLES 9
#define PCFSAMPLES 32.0
#else
#define PCFSAMPLES 16.0
#endif
#else #else
#define PCFBOUND 0.0 #define PCFBOUND 0.0
#if defined(POISSON_FILTER) #define PCFSAMPLES 1
#define PCFSAMPLES 4.0
#else
#define PCFSAMPLES 1.0
#endif
#endif #endif
#ifdef COLORED_SHADOWS #ifdef COLORED_SHADOWS
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
@ -152,59 +145,31 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis
} }
#endif #endif
float getBaseLength(vec2 smTexCoord) #define BASEFILTERRADIUS 1.0
{
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
}
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 // Return fast if sharp shadows are requested
if (PCFBOUND == 0.0) if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
return 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; vec2 clampedpos;
float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5);
float y, x; float y, x;
float depth = 0.0; float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
float pointDepth; // A factor from 0 to 1 to reduce blurring of short shadows
float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier; 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); float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
int n = 0; * 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) return max(BASEFILTERRADIUS * f_textureresolution / 4096.0, sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y);
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius);
clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy;
pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance);
if (pointDepth > -0.01) {
depth += pointDepth;
n += 1;
}
}
depth = depth / n;
depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius);
} }
#ifdef POISSON_FILTER #ifdef POISSON_FILTER
@ -279,26 +244,23 @@ const vec2[64] poissonDisk = vec2[64](
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
vec2 clampedpos; float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
if (radius < 0.1) { if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering // we are in the middle of even brightness, no need for filtering
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
} }
float baseLength = getBaseLength(smTexCoord); vec2 clampedpos;
float perspectiveFactor; vec4 visibility = vec4(0.0);
float scale_factor = radius / f_textureresolution;
float texture_size = 1.0 / (f_textureresolution * 0.5); int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); 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 init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
int end_offset = int(samples) + init_offset; int end_offset = int(samples) + init_offset;
for (int x = init_offset; x < end_offset; x++) { for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x]; clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); 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) float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
vec2 clampedpos; float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
if (radius < 0.1) { if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering // we are in the middle of even brightness, no need for filtering
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
} }
float baseLength = getBaseLength(smTexCoord); vec2 clampedpos;
float perspectiveFactor; float visibility = 0.0;
float scale_factor = radius / f_textureresolution;
float texture_size = 1.0 / (f_textureresolution * 0.5); int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); 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 init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
int end_offset = int(samples) + init_offset; int end_offset = int(samples) + init_offset;
for (int x = init_offset; x < end_offset; x++) { for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x]; clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); 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) vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
vec2 clampedpos; float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
if (radius < 0.1) { if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering // we are in the middle of even brightness, no need for filtering
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
} }
float baseLength = getBaseLength(smTexCoord); vec2 clampedpos;
float perspectiveFactor; vec4 visibility = vec4(0.0);
float x, y;
float texture_size = 1.0 / (f_textureresolution * 0.5); float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
float y, x; bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); float scale_factor = radius / bound / f_textureresolution;
int n = 0; float n = 0.0;
// basic PCF filter // basic PCF filter
for (y = -bound; y <= bound; y += 1.0) for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) { for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y); // screen offset clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
n += 1; n += 1.0;
} }
return visibility / n; return visibility / max(n, 1.0);
} }
#else #else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{ {
vec2 clampedpos; float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
if (radius < 0.1) { if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering // we are in the middle of even brightness, no need for filtering
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
} }
float baseLength = getBaseLength(smTexCoord); vec2 clampedpos;
float perspectiveFactor; float visibility = 0.0;
float x, y;
float texture_size = 1.0 / (f_textureresolution * 0.5); float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
float y, x; bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); float scale_factor = radius / bound / f_textureresolution;
int n = 0; float n = 0.0;
// basic PCF filter // basic PCF filter
for (y = -bound; y <= bound; y += 1.0) for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) { for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y); // screen offset clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
n += 1; n += 1.0;
} }
return visibility / n; return visibility / max(n, 1.0);
} }
#endif #endif
@ -489,7 +440,6 @@ void main(void)
shadow_color = visibility.gba; shadow_color = visibility.gba;
#else #else
if (cosLight > 0.0 || f_normal_length < 1e-3) if (cosLight > 0.0 || f_normal_length < 1e-3)
if (cosLight > 0.0)
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z); shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
else else
shadow_int = 1.0; shadow_int = 1.0;
@ -540,6 +490,6 @@ void main(void)
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
col = mix(skyBgColor, col, clarity); col = mix(skyBgColor, col, clarity);
col = vec4(col.rgb, base.a); col = vec4(col.rgb, base.a);
gl_FragColor = col; gl_FragColor = col;
} }

View File

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

View File

@ -62,7 +62,8 @@ Where `<gameid>` is unique to each game.
The game directory can contain the following files: The game directory can contain the following files:
* `game.conf`, with the following keys: * `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 * `description`: Short description to be shown in the content tab
* `allowed_mapgens = <comma-separated mapgens>` * `allowed_mapgens = <comma-separated mapgens>`
e.g. `allowed_mapgens = v5,v6,flat` e.g. `allowed_mapgens = v5,v6,flat`

View File

@ -246,13 +246,14 @@ Package - content which is downloadable from the content db, may or may not be i
* core.get_texturepath() (possible in async calls) * core.get_texturepath() (possible in async calls)
* returns path to default textures * returns path to default textures
* core.get_game(index) * core.get_game(index)
* `name` in return value is deprecated, use `title` instead.
* returns: * returns:
{ {
id = <id>, id = <id>,
path = <full path to game>, path = <full path to game>,
gamemods_path = <path>, gamemods_path = <path>,
name = <name of game>, title = <title of game>,
menuicon_path = <full path to menuicon>, menuicon_path = <full path to menuicon>,
author = "author", author = "author",
DEPRECATED: DEPRECATED:
@ -264,8 +265,9 @@ Package - content which is downloadable from the content db, may or may not be i
* returns * returns
{ {
name = "name of content", name = "technical_id",
type = "mod" or "modpack" or "game" or "txp", type = "mod" or "modpack" or "game" or "txp",
title = "Human readable title",
description = "description", description = "description",
author = "author", author = "author",
path = "path/to/content", path = "path/to/content",

View File

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

View File

@ -164,8 +164,13 @@ static int traversetable (global_State *g, Table *h) {
markobject(g, h->metatable); markobject(g, h->metatable);
mode = gfasttm(g, h->metatable, TM_MODE); mode = gfasttm(g, h->metatable, TM_MODE);
if (mode && ttisstring(mode)) { /* is there a weak mode? */ if (mode && ttisstring(mode)) { /* is there a weak mode? */
weakkey = (strchr(svalue(mode), 'k') != NULL); // Android's 'FORTIFY libc' calls __builtin_object_size on the argument of strchr.
weakvalue = (strchr(svalue(mode), 'v') != NULL); // 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? */ if (weakkey || weakvalue) { /* is really weak? */
h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */
h->marked |= cast_byte((weakkey << KEYWEAKBIT) | h->marked |= cast_byte((weakkey << KEYWEAKBIT) |

View File

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

View File

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

View File

@ -789,16 +789,18 @@ void Client::peerAdded(con::Peer *peer)
infostream << "Client::peerAdded(): peer->id=" infostream << "Client::peerAdded(): peer->id="
<< peer->id << std::endl; << peer->id << std::endl;
} }
void Client::deletingPeer(con::Peer *peer, bool timeout) void Client::deletingPeer(con::Peer *peer, bool timeout)
{ {
infostream << "Client::deletingPeer(): " infostream << "Client::deletingPeer(): "
"Server Peer is getting deleted " "Server Peer is getting deleted "
<< "(timeout=" << timeout << ")" << std::endl; << "(timeout=" << timeout << ")" << std::endl;
if (timeout) { m_access_denied = true;
m_access_denied = true; if (timeout)
m_access_denied_reason = gettext("Connection timed out."); m_access_denied_reason = gettext("Connection timed out.");
} else
m_access_denied_reason = gettext("Connection aborted (protocol error?).");
} }
/* /*

View File

@ -304,6 +304,7 @@ void ClientEnvironment::step(float dtime)
node_at_lplayer = m_map->getNode(p); node_at_lplayer = m_map->getNode(p);
u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); 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); final_color_blend(&lplayer->light_color, light, day_night_ratio);
} }

View File

@ -449,15 +449,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
drawcall_count += draw_order.size(); drawcall_count += draw_order.size();
for (auto &descriptor : draw_order) { for (auto &descriptor : draw_order) {
scene::IMeshBuffer *buf; scene::IMeshBuffer *buf = descriptor.getBuffer();
if (descriptor.m_use_partial_buffer) {
descriptor.m_partial_buffer->beforeDraw();
buf = descriptor.m_partial_buffer->getBuffer();
}
else {
buf = descriptor.m_buffer;
}
// Check and abort if the machine is swapping a lot // Check and abort if the machine is swapping a lot
if (draw.getTimerTime() > 2000) { if (draw.getTimerTime() > 2000) {
@ -489,6 +481,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Do not enable filter on shadow texture to avoid visual artifacts // Do not enable filter on shadow texture to avoid visual artifacts
// with colored shadows. // with colored shadows.
// Filtering is done in shader code anyway // Filtering is done in shader code anyway
layer.BilinearFilter = false;
layer.AnisotropicFilter = false;
layer.TrilinearFilter = false; layer.TrilinearFilter = false;
} }
driver->setMaterial(material); driver->setMaterial(material);
@ -499,7 +493,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
m.setTranslation(block_wpos - offset); m.setTranslation(block_wpos - offset);
driver->setTransform(video::ETS_WORLD, m); driver->setTransform(video::ETS_WORLD, m);
driver->drawMeshBuffer(buf); descriptor.draw(driver);
vertex_count += buf->getIndexCount(); vertex_count += buf->getIndexCount();
} }
@ -810,15 +804,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
drawcall_count += draw_order.size(); drawcall_count += draw_order.size();
for (auto &descriptor : draw_order) { for (auto &descriptor : draw_order) {
scene::IMeshBuffer *buf; scene::IMeshBuffer *buf = descriptor.getBuffer();
if (descriptor.m_use_partial_buffer) {
descriptor.m_partial_buffer->beforeDraw();
buf = descriptor.m_partial_buffer->getBuffer();
}
else {
buf = descriptor.m_buffer;
}
// Check and abort if the machine is swapping a lot // Check and abort if the machine is swapping a lot
if (draw.getTimerTime() > 1000) { if (draw.getTimerTime() > 1000) {
@ -843,7 +829,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
m.setTranslation(block_wpos - offset); m.setTranslation(block_wpos - offset);
driver->setTransform(video::ETS_WORLD, m); driver->setTransform(video::ETS_WORLD, m);
driver->drawMeshBuffer(buf); descriptor.draw(driver);
vertex_count += buf->getIndexCount(); vertex_count += buf->getIndexCount();
} }
@ -964,3 +950,18 @@ void ClientMap::updateTransparentMeshBuffers()
m_needs_update_transparent_meshes = false; m_needs_update_transparent_meshes = false;
} }
scene::IMeshBuffer* ClientMap::DrawDescriptor::getBuffer()
{
return m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer;
}
void ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver)
{
if (m_use_partial_buffer) {
m_partial_buffer->beforeDraw();
driver->drawMeshBuffer(m_partial_buffer->getBuffer());
m_partial_buffer->afterDraw();
} else {
driver->drawMeshBuffer(m_buffer);
}
}

View File

@ -174,6 +174,9 @@ private:
DrawDescriptor(v3s16 pos, const PartialMeshBuffer *buffer) : DrawDescriptor(v3s16 pos, const PartialMeshBuffer *buffer) :
m_pos(pos), m_partial_buffer(buffer), m_reuse_material(false), m_use_partial_buffer(true) 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; Client *m_client;

View File

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

View File

@ -1204,15 +1204,16 @@ void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector<s32> &output
void PartialMeshBuffer::beforeDraw() const void PartialMeshBuffer::beforeDraw() const
{ {
// Patch the indexes in the mesh buffer before draw // Patch the indexes in the mesh buffer before draw
m_buffer->Indices = std::move(m_vertex_indexes);
m_buffer->Indices.clear();
if (!m_vertex_indexes.empty()) {
for (auto index : m_vertex_indexes)
m_buffer->Indices.push_back(index);
}
m_buffer->setDirty(scene::EBT_INDEX); m_buffer->setDirty(scene::EBT_INDEX);
} }
void PartialMeshBuffer::afterDraw() const
{
// Take the data back
m_vertex_indexes = std::move(m_buffer->Indices.steal());
}
/* /*
MapBlockMesh MapBlockMesh
*/ */
@ -1582,7 +1583,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos)
const auto &t = m_transparent_triangles[i]; const auto &t = m_transparent_triangles[i];
if (current_buffer != t.buffer) { if (current_buffer != t.buffer) {
if (current_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_strain.clear();
} }
current_buffer = t.buffer; current_buffer = t.buffer;
@ -1593,7 +1594,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos)
} }
if (!current_strain.empty()) 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() void MapBlockMesh::consolidateTransparentBuffers()
@ -1607,7 +1608,7 @@ void MapBlockMesh::consolidateTransparentBuffers()
for (const auto &t : m_transparent_triangles) { for (const auto &t : m_transparent_triangles) {
if (current_buffer != t.buffer) { if (current_buffer != t.buffer) {
if (current_buffer != nullptr) { 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_strain.clear();
} }
current_buffer = t.buffer; current_buffer = t.buffer;
@ -1618,7 +1619,7 @@ void MapBlockMesh::consolidateTransparentBuffers()
} }
if (!current_strain.empty()) { if (!current_strain.empty()) {
this->m_transparent_buffers.emplace_back(current_buffer, current_strain); this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
} }
} }

View File

@ -140,20 +140,31 @@ private:
s32 root = -1; // index of the root node 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 class PartialMeshBuffer
{ {
public: public:
PartialMeshBuffer(scene::SMeshBuffer *buffer, const std::vector<u16> &vertex_indexes) : PartialMeshBuffer(scene::SMeshBuffer *buffer, std::vector<u16> &&vertex_indexes) :
m_buffer(buffer), m_vertex_indexes(vertex_indexes) m_buffer(buffer), m_vertex_indexes(std::move(vertex_indexes))
{} {}
scene::IMeshBuffer *getBuffer() const { return m_buffer; } scene::IMeshBuffer *getBuffer() const { return m_buffer; }
const std::vector<u16> &getVertexIndexes() const { return m_vertex_indexes; } const std::vector<u16> &getVertexIndexes() const { return m_vertex_indexes; }
void beforeDraw() const; void beforeDraw() const;
void afterDraw() const;
private: private:
scene::SMeshBuffer *m_buffer; scene::SMeshBuffer *m_buffer;
std::vector<u16> m_vertex_indexes; mutable std::vector<u16> m_vertex_indexes;
}; };
/* /*

View File

@ -771,6 +771,8 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n"; 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 common_header = shaders_header.str();
std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl"); std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");

View File

@ -670,6 +670,7 @@ std::string ShadowRenderer::readShaderFile(const std::string &path)
std::string prefix; std::string prefix;
if (m_shadow_map_colored) if (m_shadow_map_colored)
prefix.append("#define COLORED_SHADOWS 1\n"); prefix.append("#define COLORED_SHADOWS 1\n");
prefix.append("#line 0\n");
std::string content; std::string content;
fs::ReadFile(path, content); fs::ReadFile(path, content);

View File

@ -96,7 +96,12 @@ void parseContentInfo(ContentSpec &spec)
Settings conf; Settings conf;
if (!conf_path.empty() && conf.readConfigFile(conf_path.c_str())) { 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"); spec.name = conf.get("name");
if (conf.exists("description")) if (conf.exists("description"))

View File

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

View File

@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include <common/c_internal.h>
#include "content/subgames.h" #include "content/subgames.h"
#include "porting.h" #include "porting.h"
#include "filesys.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 struct GameFindPath
{ {
std::string path; std::string path;
@ -121,11 +141,13 @@ SubgameSpec findSubgame(const std::string &id)
Settings conf; Settings conf;
conf.readConfigFile(conf_path.c_str()); conf.readConfigFile(conf_path.c_str());
std::string game_name; std::string game_title;
if (conf.exists("name")) if (conf.exists("title"))
game_name = conf.get("name"); game_title = conf.get("title");
else if (conf.exists("name"))
game_title = conf.get("name");
else else
game_name = id; game_title = id;
std::string game_author; std::string game_author;
if (conf.exists("author")) if (conf.exists("author"))
@ -140,8 +162,14 @@ SubgameSpec findSubgame(const std::string &id)
menuicon_path = getImagePath( menuicon_path = getImagePath(
game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png"); game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
#endif #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); 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) 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"; std::string conf_path = world_gamepath + DIR_DELIM + "game.conf";
conf.readConfigFile(conf_path.c_str()); conf.readConfigFile(conf_path.c_str());
if (conf.exists("name")) if (conf.exists("title"))
gamespec.name = conf.get("name"); gamespec.title = conf.get("title");
else if (conf.exists("name"))
gamespec.title = conf.get("name");
else else
gamespec.name = world_gameid; gamespec.title = world_gameid;
return gamespec; return gamespec;
} }

View File

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

View File

@ -344,7 +344,7 @@ void set_default_settings()
settings->setDefault("shadow_filters", "1"); settings->setDefault("shadow_filters", "1");
settings->setDefault("shadow_poisson_filter", "true"); settings->setDefault("shadow_poisson_filter", "true");
settings->setDefault("shadow_update_frames", "8"); 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"); settings->setDefault("shadow_sky_body_orbit_tilt", "0.0");
// Input // Input

View File

@ -54,25 +54,10 @@ GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env,
#endif #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) void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
{ {
acceptInput(); acceptInput();
removeChildren(); removeAllChildren();
/* /*
Calculate new sizes and positions Calculate new sizes and positions

View File

@ -34,9 +34,6 @@ public:
s32 id, IMenuManager *menumgr, Client *client, s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password, const std::string &playername, const std::string &password,
bool *aborted, ISimpleTextureSource *tsrc); bool *aborted, ISimpleTextureSource *tsrc);
~GUIConfirmRegistration();
void removeChildren();
/* /*
Remove and re-add (or reposition) stuff Remove and re-add (or reposition) stuff
*/ */

View File

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

View File

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

View File

@ -103,7 +103,8 @@ GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
GUIKeyChangeMenu::~GUIKeyChangeMenu() GUIKeyChangeMenu::~GUIKeyChangeMenu()
{ {
removeChildren(); removeAllChildren();
key_used_text = nullptr;
for (key_setting *ks : key_settings) { for (key_setting *ks : key_settings) {
delete[] ks->button_name; delete[] ks->button_name;
@ -112,23 +113,10 @@ GUIKeyChangeMenu::~GUIKeyChangeMenu()
key_settings.clear(); 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) void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{ {
removeChildren(); removeAllChildren();
key_used_text = nullptr;
const float s = m_gui_scale; const float s = m_gui_scale;
DesiredRect = core::rect<s32>( DesiredRect = core::rect<s32>(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -108,19 +108,6 @@ void GUIModalMenu::quitMenu()
#endif #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 // clang-format off
bool GUIModalMenu::DoubleClickDetection(const SEvent &event) bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
{ {

View File

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

View File

@ -292,9 +292,6 @@ shadow_offset(0), shadow_alpha(0), fallback(0)
Driver->grab(); Driver->grab();
setInvisibleCharacters(L" "); 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) 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. // Delete the glyphs and glyph pages.
reset_images(); reset_images();
CGUITTAssistDelete::Delete(Glyphs); Glyphs.clear();
//Glyphs.clear();
// We aren't using this face anymore. // We aren't using this face anymore.
auto n = c_faces.find(filename); auto n = c_faces.find(filename);
@ -675,6 +671,8 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
update_glyph_pages(); update_glyph_pages();
auto it = Render_Map.begin(); auto it = Render_Map.begin();
auto ie = Render_Map.end(); auto ie = Render_Map.end();
core::array<core::vector2di> tmp_positions;
core::array<core::recti> tmp_source_rects;
while (it != ie) while (it != ie)
{ {
CGUITTGlyphPage* page = it->second; CGUITTGlyphPage* page = it->second;
@ -696,10 +694,8 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
do do
++i; ++i;
while (i < page->render_positions.size() && page->render_colors[i] == colprev); while (i < page->render_positions.size() && page->render_colors[i] == colprev);
core::array<core::vector2di> tmp_positions; tmp_positions.set_data(&page->render_positions[ibegin], i - ibegin);
core::array<core::recti> tmp_source_rects; tmp_source_rects.set_data(&page->render_source_rects[ibegin], i - ibegin);
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);
--i; --i;
if (!use_transparency) if (!use_transparency)

View File

@ -37,6 +37,7 @@
#include <map> #include <map>
#include <irrUString.h> #include <irrUString.h>
#include "util/enriched_string.h" #include "util/enriched_string.h"
#include "util/basic_macros.h"
#include FT_FREETYPE_H #include FT_FREETYPE_H
namespace irr namespace irr
@ -46,23 +47,34 @@ namespace gui
struct SGUITTFace; struct SGUITTFace;
class CGUITTFont; 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. //! Structure representing a single TrueType glyph.
struct SGUITTGlyph struct SGUITTGlyph
{ {
//! Constructor. //! 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. //! Destructor.
~SGUITTGlyph() { unload(); } ~SGUITTGlyph() { unload(); }

View File

@ -304,7 +304,11 @@ int ModApiMainMenu::l_get_games(lua_State *L)
lua_settable(L, top_lvl2); lua_settable(L, top_lvl2);
lua_pushstring(L, "name"); lua_pushstring(L, "name");
lua_pushstring(L, game.name.c_str()); lua_pushstring(L, game.title.c_str());
lua_settable(L, top_lvl2);
lua_pushstring(L, "title");
lua_pushstring(L, game.title.c_str());
lua_settable(L, top_lvl2); lua_settable(L, top_lvl2);
lua_pushstring(L, "author"); lua_pushstring(L, "author");
@ -356,6 +360,11 @@ int ModApiMainMenu::l_get_content_info(lua_State *L)
lua_pushstring(L, spec.author.c_str()); lua_pushstring(L, spec.author.c_str());
lua_setfield(L, -2, "author"); lua_setfield(L, -2, "author");
if (!spec.title.empty()) {
lua_pushstring(L, spec.title.c_str());
lua_setfield(L, -2, "title");
}
lua_pushinteger(L, spec.release); lua_pushinteger(L, spec.release);
lua_setfield(L, -2, "release"); lua_setfield(L, -2, "release");

View File

@ -440,6 +440,7 @@ void Server::init()
m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
m_gamespec.checkAndLog();
m_modmgr->loadMods(m_script); m_modmgr->loadMods(m_script);
// Read Textures and calculate sha1 sums // Read Textures and calculate sha1 sums
@ -3109,7 +3110,7 @@ std::string Server::getStatusString()
// Version // Version
os << "version: " << g_version_string; os << "version: " << g_version_string;
// Game // Game
os << " | game: " << (m_gamespec.name.empty() ? m_gamespec.id : m_gamespec.name); os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
// Uptime // Uptime
os << " | uptime: " << duration_to_string((int) m_uptime_counter->get()); os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
// Max lag estimate // Max lag estimate

View File

@ -362,7 +362,7 @@ struct TestMapBlock: public TestBase
MapNode node; MapNode node;
bool position_valid; bool position_valid;
core::list<v3s16> validity_exceptions; std::list<v3s16> validity_exceptions;
TC() TC()
{ {
@ -373,7 +373,7 @@ struct TestMapBlock: public TestBase
{ {
//return position_valid ^ (p==position_valid_exception); //return position_valid ^ (p==position_valid_exception);
bool exception = false; bool exception = false;
for(core::list<v3s16>::Iterator i=validity_exceptions.begin(); for(std::list<v3s16>::iterator i=validity_exceptions.begin();
i != validity_exceptions.end(); i++) i != validity_exceptions.end(); i++)
{ {
if(p == *i) if(p == *i)

View File

@ -80,7 +80,7 @@ class TestFailedException : public std::exception {
<< #expected << std::endl \ << #expected << std::endl \
<< " at " << fs::GetFilenameFromPath(__FILE__) << ":" \ << " at " << fs::GetFilenameFromPath(__FILE__) << ":" \
<< __LINE__ << std::endl \ << __LINE__ << std::endl \
<< " actual: " << a << std::endl << " expected: " \ << " actual : " << a << std::endl << " expected: " \
<< e << std::endl; \ << e << std::endl; \
throw TestFailedException(); \ throw TestFailedException(); \
} \ } \

View File

@ -169,23 +169,50 @@ void TestSerialization::testDeSerializeLongString()
void TestSerialization::testSerializeJsonString() void TestSerialization::testSerializeJsonString()
{ {
std::istringstream is(std::ios::binary);
const auto reset_is = [&] (const std::string &s) {
is.clear();
is.str(s);
};
const auto assert_at_eof = [] (std::istream &is) {
is.get();
UASSERT(is.eof());
};
// Test blank string // Test blank string
UASSERT(serializeJsonString("") == "\"\""); UASSERTEQ(std::string, serializeJsonString(""), "\"\"");
reset_is("\"\"");
UASSERTEQ(std::string, deSerializeJsonString(is), "");
assert_at_eof(is);
// Test basic string // Test basic string
UASSERT(serializeJsonString("Hello world!") == "\"Hello world!\""); UASSERTEQ(std::string, serializeJsonString("Hello world!"), "\"Hello world!\"");
reset_is("\"Hello world!\"");
UASSERTEQ(std::string, deSerializeJsonString(is), "Hello world!");
assert_at_eof(is);
// MSVC fails when directly using "\\\\" // Test optional serialization
std::string backslash = "\\"; const std::pair<const char*, const char*> test_pairs[] = {
UASSERT(serializeJsonString(teststring2) == { "abc", "abc" },
mkstr("\"") + { "x y z", "\"x y z\"" },
{ "\"", "\"\\\"\"" },
};
for (auto it : test_pairs) {
UASSERTEQ(std::string, serializeJsonStringIfNeeded(it.first), it.second);
reset_is(it.second);
UASSERTEQ(std::string, deSerializeJsonStringIfNeeded(is), it.first);
assert_at_eof(is);
}
// Test all byte values
const std::string bs = "\\"; // MSVC fails when directly using "\\\\"
const std::string expected = mkstr("\"") +
"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" + "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" +
"\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" + "\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" + "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
"\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" + "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
" !\\\"" + teststring2.substr(0x23, 0x2f-0x23) + " !\\\"" + teststring2.substr(0x23, 0x5c-0x23) +
"\\/" + teststring2.substr(0x30, 0x5c-0x30) + bs + bs + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
backslash + backslash + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
"\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" + "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
"\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" + "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
"\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" + "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
@ -202,14 +229,31 @@ void TestSerialization::testSerializeJsonString()
"\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" + "\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
"\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" + "\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
"\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" + "\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
"\""); "\"";
std::string serialized = serializeJsonString(teststring2);
UASSERTEQ(std::string, serialized, expected);
// Test deserialize reset_is(serialized);
std::istringstream is(serializeJsonString(teststring2), std::ios::binary); UASSERTEQ(std::string, deSerializeJsonString(is), teststring2);
UASSERT(deSerializeJsonString(is) == teststring2); UASSERT(!is.eof()); // should have stopped at " so eof must not be set yet
UASSERT(!is.eof()); assert_at_eof(is);
is.get();
UASSERT(is.eof()); // Test that deserialization leaves rest of stream alone
std::string tmp;
reset_is("\"foo\"bar");
UASSERTEQ(std::string, deSerializeJsonString(is), "foo");
std::getline(is, tmp, '\0');
UASSERTEQ(std::string, tmp, "bar");
reset_is("\"x y z\"bar");
UASSERTEQ(std::string, deSerializeJsonStringIfNeeded(is), "x y z");
std::getline(is, tmp, '\0');
UASSERTEQ(std::string, tmp, "bar");
reset_is("foo bar");
UASSERTEQ(std::string, deSerializeJsonStringIfNeeded(is), "foo");
std::getline(is, tmp, '\0');
UASSERTEQ(std::string, tmp, " bar");
} }

View File

@ -18,15 +18,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "serialize.h" #include "serialize.h"
#include "pointer.h"
#include "porting.h" #include "porting.h"
#include "util/string.h" #include "util/string.h"
#include "util/hex.h"
#include "exceptions.h" #include "exceptions.h"
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include <sstream> #include <iostream>
#include <iomanip> #include <cassert>
#include <vector>
FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN; FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN;
@ -120,121 +119,148 @@ std::string deSerializeString32(std::istream &is)
} }
//// ////
//// JSON //// JSON-like strings
//// ////
std::string serializeJsonString(const std::string &plain) std::string serializeJsonString(const std::string &plain)
{ {
std::ostringstream os(std::ios::binary); std::string tmp;
os << "\"";
tmp.reserve(plain.size() + 2);
tmp.push_back('"');
for (char c : plain) { for (char c : plain) {
switch (c) { switch (c) {
case '"': case '"':
os << "\\\""; tmp.append("\\\"");
break; break;
case '\\': case '\\':
os << "\\\\"; tmp.append("\\\\");
break;
case '/':
os << "\\/";
break; break;
case '\b': case '\b':
os << "\\b"; tmp.append("\\b");
break; break;
case '\f': case '\f':
os << "\\f"; tmp.append("\\f");
break; break;
case '\n': case '\n':
os << "\\n"; tmp.append("\\n");
break; break;
case '\r': case '\r':
os << "\\r"; tmp.append("\\r");
break; break;
case '\t': case '\t':
os << "\\t"; tmp.append("\\t");
break; break;
default: { default: {
if (c >= 32 && c <= 126) { if (c >= 32 && c <= 126) {
os << c; tmp.push_back(c);
} else { } else {
u32 cnum = (u8)c; // We pretend that Unicode codepoints map to bytes (they don't)
os << "\\u" << std::hex << std::setw(4) u8 cnum = static_cast<u8>(c);
<< std::setfill('0') << cnum; tmp.append("\\u00");
tmp.push_back(hex_chars[cnum >> 4]);
tmp.push_back(hex_chars[cnum & 0xf]);
} }
break; break;
} }
} }
} }
os << "\""; tmp.push_back('"');
return os.str(); return tmp;
}
static void deSerializeJsonString(std::string &s)
{
assert(s.size() >= 2);
assert(s.front() == '"' && s.back() == '"');
size_t w = 0; // write index
size_t i = 1; // read index
const size_t len = s.size() - 1; // string length with trailing quote removed
while (i < len) {
char c = s[i++];
assert(c != '"');
if (c != '\\') {
s[w++] = c;
continue;
}
if (i >= len)
throw SerializationError("JSON string ended prematurely");
char c2 = s[i++];
switch (c2) {
case 'b':
s[w++] = '\b';
break;
case 'f':
s[w++] = '\f';
break;
case 'n':
s[w++] = '\n';
break;
case 'r':
s[w++] = '\r';
break;
case 't':
s[w++] = '\t';
break;
case 'u': {
if (i + 3 >= len)
throw SerializationError("JSON string ended prematurely");
unsigned char v[4] = {};
for (int j = 0; j < 4; j++)
hex_digit_decode(s[i+j], v[j]);
i += 4;
u32 hexnumber = (v[0] << 12) | (v[1] << 8) | (v[2] << 4) | v[3];
// Note that this does not work for anything other than ASCII
// but these functions do not actually interact with real JSON input.
s[w++] = (int) hexnumber;
break;
}
default:
s[w++] = c2;
break;
}
}
assert(w <= i && i <= len);
// Truncate string to current write index
s.resize(w);
} }
std::string deSerializeJsonString(std::istream &is) std::string deSerializeJsonString(std::istream &is)
{ {
std::ostringstream os(std::ios::binary); std::string tmp;
char c, c2; char c;
bool was_backslash = false;
// Parse initial doublequote // Parse initial doublequote
is >> c; c = is.get();
if (c != '"') if (c != '"')
throw SerializationError("JSON string must start with doublequote"); throw SerializationError("JSON string must start with doublequote");
tmp.push_back(c);
// Parse characters // Grab the entire json string
for (;;) { for (;;) {
c = is.get(); c = is.get();
if (is.eof()) if (is.eof())
throw SerializationError("JSON string ended prematurely"); throw SerializationError("JSON string ended prematurely");
if (c == '"') { tmp.push_back(c);
return os.str(); if (was_backslash)
} was_backslash = false;
else if (c == '\\')
if (c == '\\') { was_backslash = true;
c2 = is.get(); else if (c == '"')
if (is.eof()) break; // found end of string
throw SerializationError("JSON string ended prematurely");
switch (c2) {
case 'b':
os << '\b';
break;
case 'f':
os << '\f';
break;
case 'n':
os << '\n';
break;
case 'r':
os << '\r';
break;
case 't':
os << '\t';
break;
case 'u': {
int hexnumber;
char hexdigits[4 + 1];
is.read(hexdigits, 4);
if (is.eof())
throw SerializationError("JSON string ended prematurely");
hexdigits[4] = 0;
std::istringstream tmp_is(hexdigits, std::ios::binary);
tmp_is >> std::hex >> hexnumber;
os << (char)hexnumber;
break;
}
default:
os << c2;
break;
}
} else {
os << c;
}
} }
return os.str(); deSerializeJsonString(tmp);
return tmp;
} }
std::string serializeJsonStringIfNeeded(const std::string &s) std::string serializeJsonStringIfNeeded(const std::string &s)
@ -248,41 +274,21 @@ std::string serializeJsonStringIfNeeded(const std::string &s)
std::string deSerializeJsonStringIfNeeded(std::istream &is) std::string deSerializeJsonStringIfNeeded(std::istream &is)
{ {
std::stringstream tmp_os(std::ios_base::binary | std::ios_base::in | std::ios_base::out); // Check for initial quote
bool expect_initial_quote = true; char c = is.peek();
bool is_json = false; if (is.eof())
bool was_backslash = false; return "";
for (;;) {
char c = is.get();
if (is.eof())
break;
if (expect_initial_quote && c == '"') { if (c == '"') {
tmp_os << c; // json string: defer to the right implementation
is_json = true; return deSerializeJsonString(is);
} else if(is_json) {
tmp_os << c;
if (was_backslash)
was_backslash = false;
else if (c == '\\')
was_backslash = true;
else if (c == '"')
break; // Found end of string
} else {
if (c == ' ') {
// Found end of word
is.unget();
break;
}
tmp_os << c;
}
expect_initial_quote = false;
}
if (is_json) {
return deSerializeJsonString(tmp_os);
} }
return tmp_os.str(); // not a json string:
std::string tmp;
std::getline(is, tmp, ' ');
if (!is.eof())
is.unget(); // we hit a space, put it back
return tmp;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -47,7 +47,7 @@ done
echo "The compiler runtime DLLs could not be found, they might be missing in the final package." echo "The compiler runtime DLLs could not be found, they might be missing in the final package."
# Get stuff # Get stuff
irrlicht_version=1.9.0mt5 irrlicht_version=1.9.0mt6
ogg_version=1.3.5 ogg_version=1.3.5
openal_version=1.21.1 openal_version=1.21.1
vorbis_version=1.3.7 vorbis_version=1.3.7

View File

@ -47,7 +47,7 @@ done
echo "The compiler runtime DLLs could not be found, they might be missing in the final package." echo "The compiler runtime DLLs could not be found, they might be missing in the final package."
# Get stuff # Get stuff
irrlicht_version=1.9.0mt5 irrlicht_version=1.9.0mt6
ogg_version=1.3.5 ogg_version=1.3.5
openal_version=1.21.1 openal_version=1.21.1
vorbis_version=1.3.7 vorbis_version=1.3.7

View File

@ -13,7 +13,7 @@ install_linux_deps() {
shift shift
pkgs+=(libirrlicht-dev) pkgs+=(libirrlicht-dev)
else else
wget "https://github.com/minetest/irrlicht/releases/download/1.9.0mt5/ubuntu-bionic.tar.gz" wget "https://github.com/minetest/irrlicht/releases/download/1.9.0mt6/ubuntu-bionic.tar.gz"
sudo tar -xaf ubuntu-bionic.tar.gz -C /usr/local sudo tar -xaf ubuntu-bionic.tar.gz -C /usr/local
fi fi