]> git.lizzy.rs Git - dragonfireclient.git/commitdiff
Merge branch 'master' of https://github.com/minetest/minetest
authorElias Fleckenstein <eliasfleckenstein@web.de>
Sun, 22 May 2022 10:05:27 +0000 (12:05 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Sun, 22 May 2022 10:05:27 +0000 (12:05 +0200)
61 files changed:
.github/workflows/build.yml
.github/workflows/macos.yml
.gitlab-ci.yml
LICENSE.txt
android/app/src/main/java/net/minetest/minetest/GameActivity.java
android/native/jni/Android.mk
builtin/mainmenu/dlg_contentstore.lua
builtin/mainmenu/pkgmgr.lua
builtin/mainmenu/tab_local.lua
builtin/settingtypes.txt
client/shaders/nodes_shader/opengl_fragment.glsl
client/shaders/nodes_shader/opengl_vertex.glsl
client/shaders/object_shader/opengl_fragment.glsl
client/shaders/object_shader/opengl_vertex.glsl
doc/lua_api.txt
doc/menu_lua_api.txt
games/devtest/game.conf
lib/lua/src/lgc.c
src/client/camera.cpp
src/client/camera.h
src/client/client.cpp
src/client/clientenvironment.cpp
src/client/clientmap.cpp
src/client/clientmap.h
src/client/content_cao.cpp
src/client/mapblock_mesh.cpp
src/client/mapblock_mesh.h
src/client/shader.cpp
src/client/shadows/dynamicshadowsrender.cpp
src/content/content.cpp
src/content/content.h
src/content/subgames.cpp
src/content/subgames.h
src/defaultsettings.cpp
src/gui/guiConfirmRegistration.cpp
src/gui/guiConfirmRegistration.h
src/gui/guiFormSpecMenu.cpp
src/gui/guiFormSpecMenu.h
src/gui/guiKeyChangeMenu.cpp
src/gui/guiKeyChangeMenu.h
src/gui/guiPasswordChange.cpp
src/gui/guiPasswordChange.h
src/gui/guiPathSelectMenu.cpp
src/gui/guiScrollContainer.cpp
src/gui/guiVolumeChange.cpp
src/gui/guiVolumeChange.h
src/gui/mainmenumanager.h
src/gui/modalMenu.cpp
src/gui/modalMenu.h
src/irrlicht_changes/CGUITTFont.cpp
src/irrlicht_changes/CGUITTFont.h
src/script/lua_api/l_mainmenu.cpp
src/server.cpp
src/unittest/test.cpp
src/unittest/test.h
src/unittest/test_serialization.cpp
src/util/serialize.cpp
textures/base/pack/no_screenshot.png
util/buildbot/buildwin32.sh
util/buildbot/buildwin64.sh
util/ci/common.sh

index 2cc83923b3a00f4fb0aa351282af086dee5d39d3..70340d82d584b3b417d985f48eb5dd4e6a5ea971 100644 (file)
@@ -230,7 +230,7 @@ jobs:
         with:
           repository: minetest/irrlicht
           path: lib/irrlichtmt/
-          ref: "1.9.0mt5"
+          ref: "1.9.0mt6"
 
       - name: Restore from cache and run vcpkg
         uses: lukka/run-vcpkg@v7
index c0278a38c0400241cee68187918dfc7e02c5b6e1..346e4f300cf2472534f7ddc4b8af17ced336996a 100644 (file)
@@ -22,7 +22,7 @@ on:
       - '.github/workflows/macos.yml'
 
 env:
-  IRRLICHT_TAG: 1.9.0mt5
+  IRRLICHT_TAG: 1.9.0mt6
   MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
   MINETEST_GAME_BRANCH: master
   MINETEST_GAME_NAME: minetest_game
index c225bfcd47eaa9c95e2d24e299bd891a52bfb4f7..8e81865709e33fdf2d2c4f205d39a4013243e20b 100644 (file)
@@ -9,7 +9,7 @@ stages:
   - deploy
 
 variables:
-  IRRLICHT_TAG: "1.9.0mt5"
+  IRRLICHT_TAG: "1.9.0mt6"
   MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
   CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
 
index 34ee8c9f0dce4f1af9f48d1ba8a949ef78b9e0b6..41d58901e397a09472e8ef97449542c112bd4133 100644 (file)
@@ -63,6 +63,9 @@ Zughy:
 
 appgurueu:
   textures/base/pack/server_incompatible.png
+  
+erlehmann, Warr1024, rollerozxa:
+  textures/base/pack/no_screenshot.png
 
 License of Minetest source code
 -------------------------------
index 46fc9b1de27a55feaba9ca0676dfb02d575d110b..eeb90ea7f7e59ebbe59e0a181e1ad578668c0799 100644 (file)
@@ -34,10 +34,14 @@ import android.widget.Button;
 import android.widget.EditText;
 import android.widget.LinearLayout;
 
+import androidx.annotation.Keep;
 import androidx.appcompat.app.AlertDialog;
 
 import java.util.Objects;
 
+// Native code finds these methods by name (see porting_android.cpp).
+// This annotation prevents the minifier/Proguard from mangling them.
+@Keep
 public class GameActivity extends NativeActivity {
        static {
                System.loadLibrary("c++_shared");
index f8ca74d3c7a6eb37fa31a7ac1de22540b11d7f67..b522042dea42661ea51990be742f8736be89b038 100644 (file)
@@ -1,6 +1,7 @@
 LOCAL_PATH := $(call my-dir)/..
 
 #LOCAL_ADDRESS_SANITIZER:=true
+#USE_BUILTIN_LUA:=true
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := Curl
@@ -42,11 +43,15 @@ LOCAL_MODULE := Irrlicht
 LOCAL_SRC_FILES := deps/$(APP_ABI)/Irrlicht/libIrrlichtMt.a
 include $(PREBUILT_STATIC_LIBRARY)
 
+ifndef USE_BUILTIN_LUA
+
 include $(CLEAR_VARS)
 LOCAL_MODULE := LuaJIT
 LOCAL_SRC_FILES := deps/$(APP_ABI)/LuaJIT/libluajit.a
 include $(PREBUILT_STATIC_LIBRARY)
 
+endif
+
 include $(CLEAR_VARS)
 LOCAL_MODULE := OpenAL
 LOCAL_SRC_FILES := deps/$(APP_ABI)/OpenAL-Soft/libopenal.a
@@ -92,7 +97,6 @@ LOCAL_CFLAGS += \
        -DUSE_CURL=1                    \
        -DUSE_SOUND=1                   \
        -DUSE_LEVELDB=0                 \
-       -DUSE_LUAJIT=1                  \
        -DUSE_GETTEXT=1                 \
        -DVERSION_MAJOR=${versionMajor} \
        -DVERSION_MINOR=${versionMinor} \
@@ -100,6 +104,12 @@ LOCAL_CFLAGS += \
        -DVERSION_EXTRA=${versionExtra} \
        $(GPROF_DEF)
 
+ifdef USE_BUILTIN_LUA
+       LOCAL_CFLAGS += -DUSE_LUAJIT=0
+else
+       LOCAL_CFLAGS += -DUSE_LUAJIT=1
+endif
+
 ifdef NDEBUG
        LOCAL_CFLAGS += -DNDEBUG=1
 endif
@@ -120,12 +130,19 @@ LOCAL_C_INCLUDES := \
        deps/$(APP_ABI)/Irrlicht/include                   \
        deps/$(APP_ABI)/Gettext/include                    \
        deps/$(APP_ABI)/Iconv/include                      \
-       deps/$(APP_ABI)/LuaJIT/include                     \
        deps/$(APP_ABI)/OpenAL-Soft/include                \
        deps/$(APP_ABI)/SQLite/include                     \
        deps/$(APP_ABI)/Vorbis/include                     \
        deps/$(APP_ABI)/Zstd/include
 
+ifdef USE_BUILTIN_LUA
+       LOCAL_C_INCLUDES += \
+               ../../lib/lua/src                    \
+               ../../lib/bitop
+else
+       LOCAL_C_INCLUDES += deps/$(APP_ABI)/LuaJIT/include
+endif
+
 LOCAL_SRC_FILES := \
        $(wildcard ../../src/client/*.cpp)           \
        $(wildcard ../../src/client/*/*.cpp)         \
@@ -207,6 +224,41 @@ LOCAL_SRC_FILES := \
        ../../src/voxel.cpp                          \
        ../../src/voxelalgorithms.cpp
 
+# Built-in Lua
+ifdef USE_BUILTIN_LUA
+       LOCAL_SRC_FILES += \
+               ../../lib/lua/src/lapi.c \
+               ../../lib/lua/src/lauxlib.c \
+               ../../lib/lua/src/lbaselib.c \
+               ../../lib/lua/src/lcode.c \
+               ../../lib/lua/src/ldblib.c \
+               ../../lib/lua/src/ldebug.c \
+               ../../lib/lua/src/ldo.c \
+               ../../lib/lua/src/ldump.c \
+               ../../lib/lua/src/lfunc.c \
+               ../../lib/lua/src/lgc.c \
+               ../../lib/lua/src/linit.c \
+               ../../lib/lua/src/liolib.c \
+               ../../lib/lua/src/llex.c \
+               ../../lib/lua/src/lmathlib.c \
+               ../../lib/lua/src/lmem.c \
+               ../../lib/lua/src/loadlib.c \
+               ../../lib/lua/src/lobject.c \
+               ../../lib/lua/src/lopcodes.c \
+               ../../lib/lua/src/loslib.c \
+               ../../lib/lua/src/lparser.c \
+               ../../lib/lua/src/lstate.c \
+               ../../lib/lua/src/lstring.c \
+               ../../lib/lua/src/lstrlib.c \
+               ../../lib/lua/src/ltable.c \
+               ../../lib/lua/src/ltablib.c \
+               ../../lib/lua/src/ltm.c \
+               ../../lib/lua/src/lundump.c \
+               ../../lib/lua/src/lvm.c \
+               ../../lib/lua/src/lzio.c \
+               ../../lib/bitop/bit.c
+endif
+
 # GMP
 LOCAL_SRC_FILES += ../../lib/gmp/mini-gmp.c
 
@@ -218,12 +270,14 @@ LOCAL_STATIC_LIBRARIES += \
        Freetype \
        Iconv libcharset \
        Irrlicht \
-       LuaJIT \
        OpenAL \
        Gettext \
        SQLite3 \
        Vorbis libvorbisfile libogg \
        Zstd
+ifndef USE_BUILTIN_LUA
+       LOCAL_STATIC_LIBRARIES += LuaJIT
+endif
 LOCAL_STATIC_LIBRARIES += android_native_app_glue $(PROFILER_LIBS)
 
 LOCAL_LDLIBS := -lEGL -lGLESv1_CM -lGLESv2 -landroid -lOpenSLES
index ff32c8c9f7d0d689071ffa375eb32b9f14f37fe7..924c66400829469b3bbe71d9b234775e4e70ce34 100644 (file)
@@ -151,11 +151,9 @@ local function start_install(package, reason)
 
                                if conf_path then
                                        local conf = Settings(conf_path)
-                                       if name_is_title then
-                                               conf:set("name",   package.title)
-                                       else
-                                               conf:set("title",  package.title)
-                                               conf:set("name",   package.name)
+                                       conf:set("title", package.title)
+                                       if not name_is_title then
+                                               conf:set("name", package.name)
                                        end
                                        if not conf:get("description") then
                                                conf:set("description", package.short_description)
@@ -360,7 +358,7 @@ function install_dialog.get_formspec()
                        selected_game_idx = i
                end
 
-               games[i] = core.formspec_escape(games[i].name)
+               games[i] = core.formspec_escape(games[i].title)
        end
 
        local selected_game = pkgmgr.games[selected_game_idx]
@@ -410,7 +408,7 @@ function install_dialog.get_formspec()
                "container[0.375,0.70]",
 
                "label[0,0.25;", fgettext("Base Game:"), "]",
-               "dropdown[2,0;4.25,0.5;gameid;", table.concat(games, ","), ";", selected_game_idx, "]",
+               "dropdown[2,0;4.25,0.5;selected_game;", table.concat(games, ","), ";", selected_game_idx, "]",
 
                "label[0,0.8;", fgettext("Dependencies:"), "]",
 
@@ -461,9 +459,9 @@ function install_dialog.handle_submit(this, fields)
                return true
        end
 
-       if fields.gameid then
+       if fields.selected_game then
                for _, game in pairs(pkgmgr.games) do
-                       if game.name == fields.gameid then
+                       if game.title == fields.selected_game then
                                core.settings:set("menu_last_game", game.id)
                                break
                        end
index 072c41f0cd17ad27a6744a0d06cd5c42343e51f7..c482cac9993fda5cecb0579b8ce9455ad9731362 100644 (file)
@@ -646,6 +646,8 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
                else
                        targetpath = core.get_gamepath() .. DIR_DELIM .. basename
                end
+       else
+               error("basefolder didn't return a recognised type, this shouldn't happen")
        end
 
        -- Copy it
@@ -739,7 +741,7 @@ function pkgmgr.preparemodlist(data)
                retval[#retval + 1] = {
                        type = "game",
                        is_game_content = true,
-                       name = fgettext("$1 mods", gamespec.name),
+                       name = fgettext("$1 mods", gamespec.title),
                        path = gamespec.path
                }
        end
@@ -924,10 +926,10 @@ end
 function pkgmgr.gamelist()
        local retval = ""
        if #pkgmgr.games > 0 then
-               retval = retval .. core.formspec_escape(pkgmgr.games[1].name)
+               retval = retval .. core.formspec_escape(pkgmgr.games[1].title)
 
                for i=2,#pkgmgr.games,1 do
-                       retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].name)
+                       retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].title)
                end
        end
        return retval
index e77c6f04de368ea1e967099144003f04e4e244e8..049d16507ef4338764303a477793e0561d28e7f1 100644 (file)
@@ -88,7 +88,7 @@ if enable_gamebar then
 
                        local image = nil
                        local text = nil
-                       local tooltip = core.formspec_escape(game.name)
+                       local tooltip = core.formspec_escape(game.title)
 
                        if (game.menuicon_path or "") ~= "" then
                                image = core.formspec_escape(game.menuicon_path)
index 2e0bb560a7739e406de88063cfdc224bc57d54a1..a6a2b9c7763ecd4224326117fde3866df801ad08 100644 (file)
@@ -643,8 +643,8 @@ shadow_update_frames (Map shadows update frames) int 8 1 16
 
 #    Set the soft shadow radius size.
 #    Lower values mean sharper shadows, bigger values mean softer shadows.
-#    Minimum value: 1.0; maximum value: 10.0
-shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0
+#    Minimum value: 1.0; maximum value: 15.0
+shadow_soft_radius (Soft shadow radius) float 5.0 1.0 15.0
 
 #    Set the tilt of Sun/Moon orbit in degrees.
 #    Value of 0 means no tilt / vertical orbit.
index 8110f6fd3dfbc2297ad4908d8ff9d2da9ae9968a..c4b947e72a0e07e76e742bda83b01237346885e7 100644 (file)
@@ -25,6 +25,7 @@ uniform float animationTimer;
        varying float cosLight;
        varying float f_normal_length;
        varying vec3 shadow_position;
+       varying float perspective_factor;
 #endif
 
 
@@ -116,23 +117,16 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
 
 
 #if SHADOW_FILTER == 2
-       #define PCFBOUND 3.5
-       #define PCFSAMPLES 64.0
+       #define PCFBOUND 2.0 // 5x5
+       #define PCFSAMPLES 25
 #elif SHADOW_FILTER == 1
-       #define PCFBOUND 1.5
-       #if defined(POISSON_FILTER)
-               #define PCFSAMPLES 32.0
-       #else
-               #define PCFSAMPLES 16.0
-       #endif
+       #define PCFBOUND 1.0 // 3x3
+       #define PCFSAMPLES 9
 #else
        #define PCFBOUND 0.0
-       #if defined(POISSON_FILTER)
-               #define PCFSAMPLES 4.0
-       #else
-               #define PCFSAMPLES 1.0
-       #endif
+       #define PCFSAMPLES 1
 #endif
+
 #ifdef COLORED_SHADOWS
 float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
@@ -149,59 +143,31 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis
 }
 #endif
 
-float getBaseLength(vec2 smTexCoord)
-{
-       float l = length(2.0 * smTexCoord.xy - 1.0 - CameraPos.xy);     // length in texture coords
-       return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0);                              // return to undistorted coords
-}
-
-float getDeltaPerspectiveFactor(float l)
-{
-       return 0.04 * pow(512.0 / f_textureresolution, 0.4) / (xyPerspectiveBias0 * l + xyPerspectiveBias1);                      // original distortion factor, divided by 10
-}
+#define BASEFILTERRADIUS 1.0
 
-float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier)
+float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
-
        // Return fast if sharp shadows are requested
-       if (PCFBOUND == 0.0)
+       if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
                return 0.0;
 
-       if (SOFTSHADOWRADIUS <= 1.0) {
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
-               return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS);
-       }
-
        vec2 clampedpos;
-       float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5);
        float y, x;
-       float depth = 0.0;
-       float pointDepth;
-       float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
-
-       float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND);
-       int n = 0;
-
-       for (y = -bound; y <= bound; y += 1.0)
-       for (x = -bound; x <= bound; x += 1.0) {
-               clampedpos = vec2(x,y);
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius);
-               clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy;
-
-               pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance);
-               if (pointDepth > -0.01) {
-                       depth += pointDepth;
-                       n += 1;
-               }
-       }
-
-       depth = depth / n;
-       depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001;
-
-       perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
-       return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius);
+       float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
+       // A factor from 0 to 1 to reduce blurring of short shadows
+       float sharpness_factor = 1.0;
+       // conversion factor from shadow depth to blur radius
+       float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0;
+       if (depth > 0.0 && f_normal_length > 0.0)
+               // 5 is empirical factor that controls how fast shadow loses sharpness
+               sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0);
+       depth = 0.0;
+
+       float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
+                       * f_textureresolution / 2.0 / f_shadowfar;
+       float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node
+
+       return max(BASEFILTERRADIUS * f_textureresolution / 4096.0,  sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
 }
 
 #ifdef POISSON_FILTER
@@ -276,26 +242,23 @@ const vec2[64] poissonDisk = vec2[64](
 
 vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       vec2 clampedpos;
-       vec4 visibility = vec4(0.0);
-       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
+       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
        if (radius < 0.1) {
                // we are in the middle of even brightness, no need for filtering
                return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
        }
 
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
+       vec2 clampedpos;
+       vec4 visibility = vec4(0.0);
+       float scale_factor = radius / f_textureresolution;
 
-       float texture_size = 1.0 / (f_textureresolution * 0.5);
-       int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
+       int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
+       samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
        int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
        int end_offset = int(samples) + init_offset;
 
        for (int x = init_offset; x < end_offset; x++) {
-               clampedpos = poissonDisk[x];
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
-               clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
+               clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
                visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
        }
 
@@ -306,26 +269,23 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
 
 float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       vec2 clampedpos;
-       float visibility = 0.0;
-       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
+       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
        if (radius < 0.1) {
                // we are in the middle of even brightness, no need for filtering
                return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
        }
 
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
+       vec2 clampedpos;
+       float visibility = 0.0;
+       float scale_factor = radius / f_textureresolution;
 
-       float texture_size = 1.0 / (f_textureresolution * 0.5);
-       int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
+       int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
+       samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
        int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
        int end_offset = int(samples) + init_offset;
 
        for (int x = init_offset; x < end_offset; x++) {
-               clampedpos = poissonDisk[x];
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
-               clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
+               clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
                visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
        }
 
@@ -341,65 +301,57 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 
 vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       vec2 clampedpos;
-       vec4 visibility = vec4(0.0);
-       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
+       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
        if (radius < 0.1) {
                // we are in the middle of even brightness, no need for filtering
                return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
        }
 
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
-
-       float texture_size = 1.0 / (f_textureresolution * 0.5);
-       float y, x;
-       float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
-       int n = 0;
+       vec2 clampedpos;
+       vec4 visibility = vec4(0.0);
+       float x, y;
+       float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
+       bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
+       float scale_factor = radius / bound / f_textureresolution;
+       float n = 0.0;
 
        // basic PCF filter
        for (y = -bound; y <= bound; y += 1.0)
        for (x = -bound; x <= bound; x += 1.0) {
-               clampedpos = vec2(x,y);     // screen offset
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
-               clampedpos =  clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
+               clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
                visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
-               n += 1;
+               n += 1.0;
        }
 
-       return visibility / n;
+       return visibility / max(n, 1.0);
 }
 
 #else
 float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       vec2 clampedpos;
-       float visibility = 0.0;
-       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
+       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
        if (radius < 0.1) {
                // we are in the middle of even brightness, no need for filtering
                return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
        }
 
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
-
-       float texture_size = 1.0 / (f_textureresolution * 0.5);
-       float y, x;
-       float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
-       int n = 0;
+       vec2 clampedpos;
+       float visibility = 0.0;
+       float x, y;
+       float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
+       bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
+       float scale_factor = radius / bound / f_textureresolution;
+       float n = 0.0;
 
        // basic PCF filter
        for (y = -bound; y <= bound; y += 1.0)
        for (x = -bound; x <= bound; x += 1.0) {
-               clampedpos = vec2(x,y);     // screen offset
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
-               clampedpos =  clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
+               clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
                visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
-               n += 1;
+               n += 1.0;
        }
 
-       return visibility / n;
+       return visibility / max(n, 1.0);
 }
 
 #endif
index 3ea0faa36d0b2865cb0eaf1b1ac51b6a2f5c15b3..d1fba283021057a7667ce046e4f61b0a7b6711f7 100644 (file)
@@ -39,6 +39,7 @@ centroid varying vec2 varTexCoord;
        varying float adj_shadow_strength;
        varying float f_normal_length;
        varying vec3 shadow_position;
+       varying float perspective_factor;
 #endif
 
 
@@ -253,6 +254,7 @@ void main(void)
 
                shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (inVertexPosition + vec4(normalOffsetScale * nNormal, 0.0))).xyz;
                shadow_position.z -= z_bias;
+               perspective_factor = pFactor;
 
                if (f_timeofday < 0.2) {
                        adj_shadow_strength = f_shadow_strength * 0.5 *
index 7baf5826ffaa3ab4126b931de6a263fe89c2528d..1fefc764b37bff4c741de7f49270b81f6ce59919 100644 (file)
@@ -1,6 +1,5 @@
 uniform sampler2D baseTexture;
 
-uniform vec4 emissiveColor;
 uniform vec3 dayLight;
 uniform vec4 skyBgColor;
 uniform float fogDistance;
@@ -26,6 +25,7 @@ uniform float animationTimer;
        varying float cosLight;
        varying float f_normal_length;
        varying vec3 shadow_position;
+       varying float perspective_factor;
 #endif
 
 
@@ -119,23 +119,16 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
 
 
 #if SHADOW_FILTER == 2
-       #define PCFBOUND 3.5
-       #define PCFSAMPLES 64.0
+       #define PCFBOUND 2.0 // 5x5
+       #define PCFSAMPLES 25
 #elif SHADOW_FILTER == 1
-       #define PCFBOUND 1.5
-       #if defined(POISSON_FILTER)
-               #define PCFSAMPLES 32.0
-       #else
-               #define PCFSAMPLES 16.0
-       #endif
+       #define PCFBOUND 1.0 // 3x3
+       #define PCFSAMPLES 9
 #else
        #define PCFBOUND 0.0
-       #if defined(POISSON_FILTER)
-               #define PCFSAMPLES 4.0
-       #else
-               #define PCFSAMPLES 1.0
-       #endif
+       #define PCFSAMPLES 1
 #endif
+
 #ifdef COLORED_SHADOWS
 float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
@@ -152,59 +145,31 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis
 }
 #endif
 
-float getBaseLength(vec2 smTexCoord)
-{
-       float l = length(2.0 * smTexCoord.xy - 1.0 - CameraPos.xy);     // length in texture coords
-       return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0);                              // return to undistorted coords
-}
-
-float getDeltaPerspectiveFactor(float l)
-{
-       return 0.04 * pow(512.0 / f_textureresolution, 0.4) / (xyPerspectiveBias0 * l + xyPerspectiveBias1);                      // original distortion factor, divided by 10
-}
+#define BASEFILTERRADIUS 1.0
 
-float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier)
+float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
-
        // Return fast if sharp shadows are requested
-       if (PCFBOUND == 0.0)
+       if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
                return 0.0;
 
-       if (SOFTSHADOWRADIUS <= 1.0) {
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
-               return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS);
-       }
-
        vec2 clampedpos;
-       float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5);
        float y, x;
-       float depth = 0.0;
-       float pointDepth;
-       float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
-
-       float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND);
-       int n = 0;
-
-       for (y = -bound; y <= bound; y += 1.0)
-       for (x = -bound; x <= bound; x += 1.0) {
-               clampedpos = vec2(x,y);
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius);
-               clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy;
-
-               pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance);
-               if (pointDepth > -0.01) {
-                       depth += pointDepth;
-                       n += 1;
-               }
-       }
-
-       depth = depth / n;
-       depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001;
-
-       perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
-       return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius);
+       float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
+       // A factor from 0 to 1 to reduce blurring of short shadows
+       float sharpness_factor = 1.0;
+       // conversion factor from shadow depth to blur radius
+       float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0;
+       if (depth > 0.0 && f_normal_length > 0.0)
+               // 5 is empirical factor that controls how fast shadow loses sharpness
+               sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0);
+       depth = 0.0;
+
+       float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
+                       * f_textureresolution / 2.0 / f_shadowfar;
+       float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node
+
+       return max(BASEFILTERRADIUS * f_textureresolution / 4096.0,  sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
 }
 
 #ifdef POISSON_FILTER
@@ -279,26 +244,23 @@ const vec2[64] poissonDisk = vec2[64](
 
 vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       vec2 clampedpos;
-       vec4 visibility = vec4(0.0);
-       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
+       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
        if (radius < 0.1) {
                // we are in the middle of even brightness, no need for filtering
                return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
        }
 
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
+       vec2 clampedpos;
+       vec4 visibility = vec4(0.0);
+       float scale_factor = radius / f_textureresolution;
 
-       float texture_size = 1.0 / (f_textureresolution * 0.5);
-       int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
+       int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
+       samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
        int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
        int end_offset = int(samples) + init_offset;
 
        for (int x = init_offset; x < end_offset; x++) {
-               clampedpos = poissonDisk[x];
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
-               clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
+               clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
                visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
        }
 
@@ -309,26 +271,23 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
 
 float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       vec2 clampedpos;
-       float visibility = 0.0;
-       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
+       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
        if (radius < 0.1) {
                // we are in the middle of even brightness, no need for filtering
                return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
        }
 
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
+       vec2 clampedpos;
+       float visibility = 0.0;
+       float scale_factor = radius / f_textureresolution;
 
-       float texture_size = 1.0 / (f_textureresolution * 0.5);
-       int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
+       int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
+       samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
        int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
        int end_offset = int(samples) + init_offset;
 
        for (int x = init_offset; x < end_offset; x++) {
-               clampedpos = poissonDisk[x];
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
-               clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
+               clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
                visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
        }
 
@@ -344,65 +303,57 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 
 vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       vec2 clampedpos;
-       vec4 visibility = vec4(0.0);
-       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
+       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
        if (radius < 0.1) {
                // we are in the middle of even brightness, no need for filtering
                return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
        }
 
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
-
-       float texture_size = 1.0 / (f_textureresolution * 0.5);
-       float y, x;
-       float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
-       int n = 0;
+       vec2 clampedpos;
+       vec4 visibility = vec4(0.0);
+       float x, y;
+       float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
+       bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
+       float scale_factor = radius / bound / f_textureresolution;
+       float n = 0.0;
 
        // basic PCF filter
        for (y = -bound; y <= bound; y += 1.0)
        for (x = -bound; x <= bound; x += 1.0) {
-               clampedpos = vec2(x,y);     // screen offset
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
-               clampedpos =  clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
+               clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
                visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
-               n += 1;
+               n += 1.0;
        }
 
-       return visibility / n;
+       return visibility / max(n, 1.0);
 }
 
 #else
 float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
-       vec2 clampedpos;
-       float visibility = 0.0;
-       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
+       float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
        if (radius < 0.1) {
                // we are in the middle of even brightness, no need for filtering
                return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
        }
 
-       float baseLength = getBaseLength(smTexCoord);
-       float perspectiveFactor;
-
-       float texture_size = 1.0 / (f_textureresolution * 0.5);
-       float y, x;
-       float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
-       int n = 0;
+       vec2 clampedpos;
+       float visibility = 0.0;
+       float x, y;
+       float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
+       bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
+       float scale_factor = radius / bound / f_textureresolution;
+       float n = 0.0;
 
        // basic PCF filter
        for (y = -bound; y <= bound; y += 1.0)
        for (x = -bound; x <= bound; x += 1.0) {
-               clampedpos = vec2(x,y);     // screen offset
-               perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
-               clampedpos =  clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
+               clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
                visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
-               n += 1;
+               n += 1.0;
        }
 
-       return visibility / n;
+       return visibility / max(n, 1.0);
 }
 
 #endif
@@ -489,7 +440,6 @@ void main(void)
                        shadow_color = visibility.gba;
 #else
                        if (cosLight > 0.0 || f_normal_length < 1e-3)
-                       if (cosLight > 0.0)
                                shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
                        else
                                shadow_int = 1.0;
@@ -540,6 +490,6 @@ void main(void)
                - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
        col = mix(skyBgColor, col, clarity);
        col = vec4(col.rgb, base.a);
-       
+
        gl_FragColor = col;
 }
index 6dc25f85481ef86e93e237741ffb88f3858ef2dd..dc9c70cdfd9ef2a8a047f9c0f8c9737af9624009 100644 (file)
@@ -30,6 +30,7 @@ centroid varying vec2 varTexCoord;
        varying float adj_shadow_strength;
        varying float f_normal_length;
        varying vec3 shadow_position;
+       varying float perspective_factor;
 #endif
 
 varying vec3 eyeVec;
@@ -162,6 +163,7 @@ void main(void)
 
                shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (inVertexPosition + vec4(normalOffsetScale * nNormal, 0.0))).xyz;
                shadow_position.z -= z_bias;
+               perspective_factor = pFactor;
 
                if (f_timeofday < 0.2) {
                        adj_shadow_strength = f_shadow_strength * 0.5 *
index 8aea490cfcadf36e927c9ae4e3b8500db3d1351b..df49bd766ac0432afa6389e737b476171c8423ff 100644 (file)
@@ -62,7 +62,8 @@ Where `<gameid>` is unique to each game.
 The game directory can contain the following files:
 
 * `game.conf`, with the following keys:
-    * `name`: Required, a human readable title to address the game, e.g. `name = Minetest`.
+    * `title`: Required, a human-readable title to address the game, e.g. `title = Minetest Game`.
+    * `name`: (Deprecated) same as title.
     * `description`: Short description to be shown in the content tab
     * `allowed_mapgens = <comma-separated mapgens>`
       e.g. `allowed_mapgens = v5,v6,flat`
index c2931af310f6a45aec6d43de75577aaf7a214432..68e7b4b9de018edb3bdff85149b3cd74211320ed 100644 (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)
     * returns path to default textures
 * core.get_game(index)
+    * `name` in return value is deprecated, use `title` instead.
     * returns:
 
         {
             id               = <id>,
             path             = <full path to game>,
             gamemods_path    = <path>,
-            name             = <name of game>,
+            title            = <title of game>,
             menuicon_path    = <full path to menuicon>,
             author           = "author",
             DEPRECATED:
@@ -264,8 +265,9 @@ Package - content which is downloadable from the content db, may or may not be i
     * returns
 
         {
-            name             = "name of content",
+            name             = "technical_id",
             type             = "mod" or "modpack" or "game" or "txp",
+            title            = "Human readable title",
             description      = "description",
             author           = "author",
             path             = "path/to/content",
index d6e382ad7e2fba49eebec3763c20fea8dd35c7d6..0f5656c99db8a334ebaebe6e1707e2e80b650084 100644 (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.
index e909c79a9696dd316aba56af686725b724189497..9141a1c60bc0235df545ac5aed3435b75ecc8013 100644 (file)
@@ -164,8 +164,13 @@ static int traversetable (global_State *g, Table *h) {
     markobject(g, h->metatable);
   mode = gfasttm(g, h->metatable, TM_MODE);
   if (mode && ttisstring(mode)) {  /* is there a weak mode? */
-    weakkey = (strchr(svalue(mode), 'k') != NULL);
-    weakvalue = (strchr(svalue(mode), 'v') != NULL);
+    // Android's 'FORTIFY libc' calls __builtin_object_size on the argument of strchr.
+    // This produces an incorrect size for the expression `svalue(mode)`, causing
+    // an assertion. By placing it in a temporary, __builtin_object_size returns
+    // -1 (for unknown size) which functions correctly.
+    const char *tmp = svalue(mode);
+    weakkey = (strchr(tmp, 'k') != NULL);
+    weakvalue = (strchr(tmp, 'v') != NULL);
     if (weakkey || weakvalue) {  /* is really weak? */
       h->marked &= ~(KEYWEAK | VALUEWEAK);  /* clear bits */
       h->marked |= cast_byte((weakkey << KEYWEAKBIT) |
index 0c387262ebe039df92473875de58b4136a196e54..164db876192d945ccb129bc3a2097d2b0db3e0bf 100644 (file)
@@ -48,7 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *rendering_engine):
        m_draw_control(draw_control),
-       m_client(client)
+       m_client(client),
+       m_player_light_color(0xFFFFFFFF)
 {
        auto smgr = rendering_engine->get_scene_manager();
        // note: making the camera node a child of the player node
@@ -154,8 +155,10 @@ void Camera::step(f32 dtime)
        bool was_under_zero = m_wield_change_timer < 0;
        m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125);
 
-       if (m_wield_change_timer >= 0 && was_under_zero)
+       if (m_wield_change_timer >= 0 && was_under_zero) {
                m_wieldnode->setItem(m_wield_item_next, m_client);
+               m_wieldnode->setNodeLightColor(m_player_light_color);
+       }
 
        if (m_view_bobbing_state != 0)
        {
@@ -556,7 +559,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
        m_wieldnode->setPosition(wield_position);
        m_wieldnode->setRotation(wield_rotation);
 
-       m_wieldnode->setNodeLightColor(player->light_color);
+       m_player_light_color = player->light_color;
+       m_wieldnode->setNodeLightColor(m_player_light_color);
 
        // Set render distance
        updateViewingRange();
index ecd71f1e72d2cee62f6956d84b0cc4d2690cd675..6a8cf650df8c20b8b85a9172f7b663842d278bac 100644 (file)
@@ -291,4 +291,7 @@ class Camera
 
        std::list<Nametag *> m_nametags;
        bool m_show_nametag_backgrounds;
+
+       // Last known light color of the player
+       video::SColor m_player_light_color;
 };
index 4e4bb8a97ea8afbe6ecf4ac7a18bba51887b4cc3..2d9d226e47e6797e10215428d5331b85205a77f3 100644 (file)
@@ -789,16 +789,18 @@ void Client::peerAdded(con::Peer *peer)
        infostream << "Client::peerAdded(): peer->id="
                        << peer->id << std::endl;
 }
+
 void Client::deletingPeer(con::Peer *peer, bool timeout)
 {
        infostream << "Client::deletingPeer(): "
                        "Server Peer is getting deleted "
                        << "(timeout=" << timeout << ")" << std::endl;
 
-       if (timeout) {
-               m_access_denied = true;
+       m_access_denied = true;
+       if (timeout)
                m_access_denied_reason = gettext("Connection timed out.");
-       }
+       else
+               m_access_denied_reason = gettext("Connection aborted (protocol error?).");
 }
 
 /*
index 01aaa0408c4767307d426e82bbedb2c8d06d3f21..e847161f935f0b200a072cd7f0bdb27ee1659bae 100644 (file)
@@ -304,6 +304,7 @@ void ClientEnvironment::step(float dtime)
                node_at_lplayer = m_map->getNode(p);
 
                u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
+               lplayer->light_color = encode_light(light, 0); // this transfers light.alpha
                final_color_blend(&lplayer->light_color, light, day_night_ratio);
        }
 
index 51f0f6896774dc10a64cb622c0b5499f492a5586..85b765709ebff6581ce8385b0df0758e984d4fed 100644 (file)
@@ -449,15 +449,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
        drawcall_count += draw_order.size();
 
        for (auto &descriptor : draw_order) {
-               scene::IMeshBuffer *buf;
-               
-               if (descriptor.m_use_partial_buffer) {
-                       descriptor.m_partial_buffer->beforeDraw();
-                       buf = descriptor.m_partial_buffer->getBuffer();
-               }
-               else {
-                       buf = descriptor.m_buffer;
-               }
+               scene::IMeshBuffer *buf = descriptor.getBuffer();
 
                // Check and abort if the machine is swapping a lot
                if (draw.getTimerTime() > 2000) {
@@ -489,6 +481,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                // Do not enable filter on shadow texture to avoid visual artifacts
                                // with colored shadows.
                                // Filtering is done in shader code anyway
+                               layer.BilinearFilter = false;
+                               layer.AnisotropicFilter = false;
                                layer.TrilinearFilter = false;
                        }
                        driver->setMaterial(material);
@@ -499,7 +493,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                m.setTranslation(block_wpos - offset);
 
                driver->setTransform(video::ETS_WORLD, m);
-               driver->drawMeshBuffer(buf);
+               descriptor.draw(driver);
                vertex_count += buf->getIndexCount();
        }
 
@@ -810,15 +804,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
        drawcall_count += draw_order.size();
 
        for (auto &descriptor : draw_order) {
-               scene::IMeshBuffer *buf;
-               
-               if (descriptor.m_use_partial_buffer) {
-                       descriptor.m_partial_buffer->beforeDraw();
-                       buf = descriptor.m_partial_buffer->getBuffer();
-               }
-               else {
-                       buf = descriptor.m_buffer;
-               }
+               scene::IMeshBuffer *buf = descriptor.getBuffer();
 
                // Check and abort if the machine is swapping a lot
                if (draw.getTimerTime() > 1000) {
@@ -843,7 +829,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
                m.setTranslation(block_wpos - offset);
 
                driver->setTransform(video::ETS_WORLD, m);
-               driver->drawMeshBuffer(buf);
+               descriptor.draw(driver);
                vertex_count += buf->getIndexCount();
        }
 
@@ -964,3 +950,18 @@ void ClientMap::updateTransparentMeshBuffers()
        m_needs_update_transparent_meshes = false;
 }
 
+scene::IMeshBuffer* ClientMap::DrawDescriptor::getBuffer()
+{
+       return m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer;
+}
+
+void ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver)
+{
+       if (m_use_partial_buffer) {
+               m_partial_buffer->beforeDraw();
+               driver->drawMeshBuffer(m_partial_buffer->getBuffer());
+               m_partial_buffer->afterDraw();
+       } else {
+               driver->drawMeshBuffer(m_buffer);
+       }
+}
index 6d57f1911571c1471db23e775ce641df4357e83c..823870c6860d699a3623a0b45c1f7d4de0851a67 100644 (file)
@@ -174,6 +174,9 @@ class ClientMap : public Map, public scene::ISceneNode
                DrawDescriptor(v3s16 pos, const PartialMeshBuffer *buffer) :
                        m_pos(pos), m_partial_buffer(buffer), m_reuse_material(false), m_use_partial_buffer(true)
                {}
+
+               scene::IMeshBuffer* getBuffer();
+               void draw(video::IVideoDriver* driver);
        };
 
        Client *m_client;
index ec1fd1c2affadd2c718ab8207a1728d9b3889bd4..eeeb19ac59d4cac3c486aba3d37c6dc36db9086c 100644 (file)
@@ -920,12 +920,8 @@ void GenericCAO::setNodeLight(const video::SColor &light_color)
                if (m_prop.visual == "upright_sprite") {
                        if (!m_meshnode)
                                return;
-
-                       scene::IMesh *mesh = m_meshnode->getMesh();
-                       for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) {
-                               scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
-                               buf->getMaterial().EmissiveColor = light_color;
-                       }
+                       for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i)
+                               m_meshnode->getMaterial(i).EmissiveColor = light_color;
                } else {
                        scene::ISceneNode *node = getSceneNode();
                        if (!node)
index f0d43ec7e2f12dac890382faa75ba1d2a61ac035..9e82fc3e453ad69d5bb8a451749de79a5bf324d3 100644 (file)
@@ -1204,15 +1204,16 @@ void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector<s32> &output
 void PartialMeshBuffer::beforeDraw() const
 {
        // Patch the indexes in the mesh buffer before draw
-
-       m_buffer->Indices.clear();
-       if (!m_vertex_indexes.empty()) {
-               for (auto index : m_vertex_indexes)
-                       m_buffer->Indices.push_back(index);
-       }
+       m_buffer->Indices = std::move(m_vertex_indexes);
        m_buffer->setDirty(scene::EBT_INDEX);
 }
 
+void PartialMeshBuffer::afterDraw() const
+{
+       // Take the data back
+       m_vertex_indexes = std::move(m_buffer->Indices.steal());
+}
+
 /*
        MapBlockMesh
 */
@@ -1582,7 +1583,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos)
                const auto &t = m_transparent_triangles[i];
                if (current_buffer != t.buffer) {
                        if (current_buffer) {
-                               m_transparent_buffers.emplace_back(current_buffer, current_strain);
+                               m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
                                current_strain.clear();
                        }
                        current_buffer = t.buffer;
@@ -1593,7 +1594,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos)
        }
 
        if (!current_strain.empty())
-               m_transparent_buffers.emplace_back(current_buffer, current_strain);
+               m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
 }
 
 void MapBlockMesh::consolidateTransparentBuffers()
@@ -1607,7 +1608,7 @@ void MapBlockMesh::consolidateTransparentBuffers()
        for (const auto &t : m_transparent_triangles) {
                if (current_buffer != t.buffer) {
                        if (current_buffer != nullptr) {
-                               this->m_transparent_buffers.emplace_back(current_buffer, current_strain);
+                               this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
                                current_strain.clear();
                        }
                        current_buffer = t.buffer;
@@ -1618,7 +1619,7 @@ void MapBlockMesh::consolidateTransparentBuffers()
        }
 
        if (!current_strain.empty()) {
-               this->m_transparent_buffers.emplace_back(current_buffer, current_strain);
+               this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
        }
 }
 
index 5e2d70b755a204622131c7f389249a42ecb80a0d..8133627b1ed13cc6423a80cd8b9a3eb405c72284 100644 (file)
@@ -140,20 +140,31 @@ class MapBlockBspTree
        s32 root = -1; // index of the root node
 };
 
+/*
+ * PartialMeshBuffer
+ *
+ * Attach alternate `Indices` to an existing mesh buffer, to make it possible to use different
+ * indices with the same vertex buffer.
+ *
+ * Irrlicht does not currently support this: `CMeshBuffer` ties together a single vertex buffer
+ * and a single index buffer. There's no way to share these between mesh buffers.
+ *
+ */
 class PartialMeshBuffer
 {
 public:
-       PartialMeshBuffer(scene::SMeshBuffer *buffer, const std::vector<u16> &vertex_indexes) :
-                       m_buffer(buffer), m_vertex_indexes(vertex_indexes)
+       PartialMeshBuffer(scene::SMeshBuffer *buffer, std::vector<u16> &&vertex_indexes) :
+                       m_buffer(buffer), m_vertex_indexes(std::move(vertex_indexes))
        {}
 
        scene::IMeshBuffer *getBuffer() const { return m_buffer; }
        const std::vector<u16> &getVertexIndexes() const { return m_vertex_indexes; }
 
        void beforeDraw() const;
+       void afterDraw() const;
 private:
        scene::SMeshBuffer *m_buffer;
-       std::vector<u16> m_vertex_indexes;
+       mutable std::vector<u16> m_vertex_indexes;
 };
 
 /*
index bbb8727612862810c0fb91fde47236773a4cf7be..009a4b3d706c48d27d72119e36dc9dcd59e3aab1 100644 (file)
@@ -771,6 +771,8 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
                shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n";
        }
 
+       shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics
+
        std::string common_header = shaders_header.str();
 
        std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");
index 07dc6daf265595b6bcba690b9f976bf2c5d77687..c13cfe252894af9adc2e63ed137d5ad0df399b9e 100644 (file)
@@ -670,6 +670,7 @@ std::string ShadowRenderer::readShaderFile(const std::string &path)
        std::string prefix;
        if (m_shadow_map_colored)
                prefix.append("#define COLORED_SHADOWS 1\n");
+       prefix.append("#line 0\n");
 
        std::string content;
        fs::ReadFile(path, content);
index 66ef83d42bbfec76362f5512d7426bde565b39b3..e576943ffcc0873aacfaa942f80608b497849bcf 100644 (file)
@@ -96,7 +96,12 @@ void parseContentInfo(ContentSpec &spec)
 
        Settings conf;
        if (!conf_path.empty() && conf.readConfigFile(conf_path.c_str())) {
-               if (conf.exists("name"))
+               if (conf.exists("title"))
+                       spec.title = conf.get("title");
+               else if (spec.type == "game" && conf.exists("name"))
+                       spec.title = conf.get("name");
+
+               if (spec.type != "game" && conf.exists("name"))
                        spec.name = conf.get("name");
 
                if (conf.exists("description"))
index e246ed4113cf1b35b72b0e0d18be5e93515ffe20..ce09a2eb9c081cda3fb8f079bd86a24ffef39830 100644 (file)
@@ -27,7 +27,14 @@ struct ContentSpec
        std::string type;
        std::string author;
        u32 release = 0;
+
+       /// Technical name / Id
        std::string name;
+
+       /// Human-readable title
+       std::string title;
+
+       /// Short description
        std::string desc;
        std::string path;
 };
index 23355990ec442671acd4e7fe8bf73853cd63645a..d0de926ef367e1fdc059bf4a3af82d4901dbbd6a 100644 (file)
@@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include <common/c_internal.h>
 #include "content/subgames.h"
 #include "porting.h"
 #include "filesys.h"
@@ -45,6 +46,25 @@ bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
 
 }
 
+
+void SubgameSpec::checkAndLog() const
+{
+       // Log deprecation messages
+       auto handling_mode = get_deprecated_handling_mode();
+       if (!deprecation_msgs.empty() && handling_mode != DeprecatedHandlingMode::Ignore) {
+               std::ostringstream os;
+               os << "Game " << title << " at " << path << ":" << std::endl;
+               for (auto msg : deprecation_msgs)
+                       os << "\t" << msg << std::endl;
+
+               if (handling_mode == DeprecatedHandlingMode::Error)
+                       throw ModError(os.str());
+               else
+                       warningstream << os.str();
+       }
+}
+
+
 struct GameFindPath
 {
        std::string path;
@@ -121,11 +141,13 @@ SubgameSpec findSubgame(const std::string &id)
        Settings conf;
        conf.readConfigFile(conf_path.c_str());
 
-       std::string game_name;
-       if (conf.exists("name"))
-               game_name = conf.get("name");
+       std::string game_title;
+       if (conf.exists("title"))
+               game_title = conf.get("title");
+       else if (conf.exists("name"))
+               game_title = conf.get("name");
        else
-               game_name = id;
+               game_title = id;
 
        std::string game_author;
        if (conf.exists("author"))
@@ -140,8 +162,14 @@ SubgameSpec findSubgame(const std::string &id)
        menuicon_path = getImagePath(
                        game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
 #endif
-       return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name,
+
+       SubgameSpec spec(id, game_path, gamemod_path, mods_paths, game_title,
                        menuicon_path, game_author, game_release);
+
+       if (conf.exists("name") && !conf.exists("title"))
+               spec.deprecation_msgs.push_back("\"name\" setting in game.conf is deprecated, please use \"title\" instead");
+
+       return spec;
 }
 
 SubgameSpec findWorldSubgame(const std::string &world_path)
@@ -159,10 +187,12 @@ SubgameSpec findWorldSubgame(const std::string &world_path)
                std::string conf_path = world_gamepath + DIR_DELIM + "game.conf";
                conf.readConfigFile(conf_path.c_str());
 
-               if (conf.exists("name"))
-                       gamespec.name = conf.get("name");
+               if (conf.exists("title"))
+                       gamespec.title = conf.get("title");
+               else if (conf.exists("name"))
+                       gamespec.title = conf.get("name");
                else
-                       gamespec.name = world_gameid;
+                       gamespec.title = world_gameid;
 
                return gamespec;
        }
index d36b4952fde076fd490ed6dbb17ce4f066efea30..d5d168243b6a92b2d70da0947e53bc711013fc4a 100644 (file)
@@ -29,7 +29,7 @@ class Settings;
 struct SubgameSpec
 {
        std::string id;
-       std::string name;
+       std::string title;
        std::string author;
        int release;
        std::string path;
@@ -41,20 +41,24 @@ struct SubgameSpec
        std::unordered_map<std::string, std::string> addon_mods_paths;
        std::string menuicon_path;
 
+       // For logging purposes
+       std::vector<const char *> deprecation_msgs;
+
        SubgameSpec(const std::string &id = "", const std::string &path = "",
                        const std::string &gamemods_path = "",
                        const std::unordered_map<std::string, std::string> &addon_mods_paths = {},
-                       const std::string &name = "",
+                       const std::string &title = "",
                        const std::string &menuicon_path = "",
                        const std::string &author = "", int release = 0) :
                        id(id),
-                       name(name), author(author), release(release), path(path),
+                       title(title), author(author), release(release), path(path),
                        gamemods_path(gamemods_path), addon_mods_paths(addon_mods_paths),
                        menuicon_path(menuicon_path)
        {
        }
 
        bool isValid() const { return (!id.empty() && !path.empty()); }
+       void checkAndLog() const;
 };
 
 SubgameSpec findSubgame(const std::string &id);
index ef2f8724dbc9d134bd2bf4f3a9f4e1ef0acc6823..9224d99ed0d5cb24b531b7c1e1f53096787699fe 100644 (file)
@@ -344,7 +344,7 @@ void set_default_settings()
        settings->setDefault("shadow_filters", "1");
        settings->setDefault("shadow_poisson_filter", "true");
        settings->setDefault("shadow_update_frames", "8");
-       settings->setDefault("shadow_soft_radius", "1.0");
+       settings->setDefault("shadow_soft_radius", "5.0");
        settings->setDefault("shadow_sky_body_orbit_tilt", "0.0");
 
        // Input
index b8887a4af1c5cc745df89480cb0d39c53f410220..c5aa9c85e209b6dc50028dd5ee4505fbedd8ae22 100644 (file)
@@ -54,25 +54,10 @@ GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env,
 #endif
 }
 
-GUIConfirmRegistration::~GUIConfirmRegistration()
-{
-       removeChildren();
-}
-
-void GUIConfirmRegistration::removeChildren()
-{
-       const core::list<gui::IGUIElement *> &children = getChildren();
-       core::list<gui::IGUIElement *> children_copy;
-       for (gui::IGUIElement *i : children)
-               children_copy.push_back(i);
-       for (gui::IGUIElement *i : children_copy)
-               i->remove();
-}
-
 void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
 {
        acceptInput();
-       removeChildren();
+       removeAllChildren();
 
        /*
                Calculate new sizes and positions
index d8387201d46d88d2a0aa7e97997873c4603611e6..fb215775639da28bd6493e82f6fe8a73b29fb805 100644 (file)
@@ -34,9 +34,6 @@ class GUIConfirmRegistration : public GUIModalMenu
                        s32 id, IMenuManager *menumgr, Client *client,
                        const std::string &playername, const std::string &password,
                        bool *aborted, ISimpleTextureSource *tsrc);
-       ~GUIConfirmRegistration();
-
-       void removeChildren();
        /*
                Remove and re-add (or reposition) stuff
        */
index f3570ccaf099f52cb8f653a37e3c102dc9bbb78b..422d6da16289530de4ee6858dbf7347dfb92bef0 100644 (file)
@@ -127,7 +127,8 @@ GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
 
 GUIFormSpecMenu::~GUIFormSpecMenu()
 {
-       removeChildren();
+       removeAllChildren();
+       removeTooltip();
 
        for (auto &table_it : m_tables)
                table_it.second->drop();
@@ -174,14 +175,8 @@ void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
        }
 }
 
-void GUIFormSpecMenu::removeChildren()
+void GUIFormSpecMenu::removeTooltip()
 {
-       const core::list<gui::IGUIElement*> &children = getChildren();
-
-       while (!children.empty()) {
-               (*children.getLast())->remove();
-       }
-
        if (m_tooltip_element) {
                m_tooltip_element->remove();
                m_tooltip_element->drop();
@@ -199,16 +194,7 @@ void GUIFormSpecMenu::setInitialFocus()
        // 5. first focusable (not statictext, not tabheader)
        // 6. first child element
 
-       core::list<gui::IGUIElement*> children = getChildren();
-
-       // in case "children" contains any NULL elements, remove them
-       for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
-                       it != children.end();) {
-               if (*it)
-                       ++it;
-               else
-                       it = children.erase(it);
-       }
+       const auto& children = getChildren();
 
        // 1. first empty editbox
        for (gui::IGUIElement *it : children) {
@@ -236,8 +222,7 @@ void GUIFormSpecMenu::setInitialFocus()
        }
 
        // 4. last button
-       for (core::list<gui::IGUIElement*>::Iterator it = children.getLast();
-                       it != children.end(); --it) {
+       for (auto it = children.rbegin(); it != children.rend(); ++it) {
                if ((*it)->getType() == gui::EGUIET_BUTTON) {
                        Environment->setFocus(*it);
                        return;
@@ -257,7 +242,7 @@ void GUIFormSpecMenu::setInitialFocus()
        if (children.empty())
                Environment->setFocus(this);
        else
-               Environment->setFocus(*(children.begin()));
+               Environment->setFocus(children.front());
 }
 
 GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
@@ -3045,7 +3030,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
        }
 
        // Remove children
-       removeChildren();
+       removeAllChildren();
+       removeTooltip();
 
        for (auto &table_it : m_tables)
                table_it.second->drop();
@@ -3341,7 +3327,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
        pos_offset = v2f32();
 
        // used for formspec versions < 3
-       core::list<IGUIElement *>::Iterator legacy_sort_start = Children.getLast();
+       std::list<IGUIElement *>::iterator legacy_sort_start = std::prev(Children.end()); // last element
 
        if (enable_prepends) {
                // Backup the coordinates so that prepends can use the coordinates of choice.
@@ -3356,7 +3342,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                // legacy sorting for formspec versions < 3
                if (m_formspec_version >= 3)
                        // prepends do not need to be reordered
-                       legacy_sort_start = Children.getLast();
+                       legacy_sort_start = std::prev(Children.end()); // last element
                else if (version_backup >= 3)
                        // only prepends elements have to be reordered
                        legacySortElements(legacy_sort_start);
@@ -3437,7 +3423,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
        }
 }
 
-void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator from)
+void GUIFormSpecMenu::legacySortElements(std::list<IGUIElement *>::iterator from)
 {
        /*
                Draw order for formspec_version <= 2:
@@ -3454,17 +3440,16 @@ void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator fro
        if (from == Children.end())
                from = Children.begin();
        else
-               from++;
+               ++from;
 
-       core::list<IGUIElement *>::Iterator to = Children.end();
+       std::list<IGUIElement *>::iterator to = Children.end();
        // 1: Copy into a sortable container
-       std::vector<IGUIElement *> elements;
-       for (auto it = from; it != to; ++it)
-               elements.emplace_back(*it);
+       std::vector<IGUIElement *> elements(from, to);
 
        // 2: Sort the container
        std::stable_sort(elements.begin(), elements.end(),
                        [this] (const IGUIElement *a, const IGUIElement *b) -> bool {
+               // TODO: getSpecByID is a linear search. It should made O(1), or cached here.
                const FieldSpec *spec_a = getSpecByID(a->getID());
                const FieldSpec *spec_b = getSpecByID(b->getID());
                return spec_a && spec_b &&
@@ -3472,10 +3457,7 @@ void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator fro
        });
 
        // 3: Re-assign the pointers
-       for (auto e : elements) {
-               *from = e;
-               from++;
-       }
+       reorderChildren(from, to, elements);
 }
 
 #ifdef __ANDROID__
@@ -3600,12 +3582,11 @@ void GUIFormSpecMenu::drawMenu()
        /*
                This is where all the drawing happens.
        */
-       core::list<IGUIElement*>::Iterator it = Children.begin();
-       for (; it != Children.end(); ++it)
-               if ((*it)->isNotClipped() ||
+       for (auto child : Children)
+               if (child->isNotClipped() ||
                                AbsoluteClippingRect.isRectCollided(
-                                               (*it)->getAbsolutePosition()))
-                       (*it)->draw();
+                                               child->getAbsolutePosition()))
+                       child->draw();
 
        for (gui::IGUIElement *e : m_clickthrough_elements)
                e->setVisible(false);
index 3fedb3b78cd550438a6a8bf9cc5b355c90d6ee95..a584456db23c680b7fe6264b66592fb5ef508c5b 100644 (file)
@@ -212,7 +212,7 @@ class GUIFormSpecMenu : public GUIModalMenu
                m_lockscreensize = basescreensize;
        }
 
-       void removeChildren();
+       void removeTooltip();
        void setInitialFocus();
 
        void setFocus(const std::string &elementname)
@@ -467,7 +467,7 @@ class GUIFormSpecMenu : public GUIModalMenu
         * types were drawn before others.
         * This function sorts the elements in the old order for backwards compatibility.
         */
-       void legacySortElements(core::list<IGUIElement *>::Iterator from);
+       void legacySortElements(std::list<IGUIElement *>::iterator from);
 
        int m_btn_height;
        gui::IGUIFont *m_font = nullptr;
index 249892950a2d265f1c1759c1ba98ffb576cb0c4b..155865472cc1655efcb7736ce35dfc39c642655c 100644 (file)
@@ -103,7 +103,8 @@ GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
 
 GUIKeyChangeMenu::~GUIKeyChangeMenu()
 {
-       removeChildren();
+       removeAllChildren();
+       key_used_text = nullptr;
 
        for (key_setting *ks : key_settings) {
                delete[] ks->button_name;
@@ -112,23 +113,10 @@ GUIKeyChangeMenu::~GUIKeyChangeMenu()
        key_settings.clear();
 }
 
-void GUIKeyChangeMenu::removeChildren()
-{
-       const core::list<gui::IGUIElement*> &children = getChildren();
-       core::list<gui::IGUIElement*> children_copy;
-       for (gui::IGUIElement*i : children) {
-               children_copy.push_back(i);
-       }
-
-       for (gui::IGUIElement *i : children_copy) {
-               i->remove();
-       }
-       key_used_text = nullptr;
-}
-
 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
 {
-       removeChildren();
+       removeAllChildren();
+       key_used_text = nullptr;
 
        const float s = m_gui_scale;
        DesiredRect = core::rect<s32>(
index 1c0f40247fee1f008cf7b85428259469d476a125..84a898774ab114a332222ffdc1226802101f4681 100644 (file)
@@ -46,7 +46,6 @@ class GUIKeyChangeMenu : public GUIModalMenu
                        IMenuManager *menumgr, ISimpleTextureSource *tsrc);
        ~GUIKeyChangeMenu();
 
-       void removeChildren();
        /*
         Remove and re-add (or reposition) stuff
         */
index c983260f6a90807f6ddecbeeb599c7ffcb3fd5b4..c39df176ba14c2aab3a42f1d0a5674d12a7b0d3c 100644 (file)
@@ -51,23 +51,6 @@ GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env,
 {
 }
 
-GUIPasswordChange::~GUIPasswordChange()
-{
-       removeChildren();
-}
-
-void GUIPasswordChange::removeChildren()
-{
-       const core::list<gui::IGUIElement *> &children = getChildren();
-       core::list<gui::IGUIElement *> children_copy;
-       for (gui::IGUIElement *i : children) {
-               children_copy.push_back(i);
-       }
-
-       for (gui::IGUIElement *i : children_copy) {
-               i->remove();
-       }
-}
 void GUIPasswordChange::regenerateGui(v2u32 screensize)
 {
        /*
@@ -78,7 +61,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
        /*
                Remove stuff
        */
-       removeChildren();
+       removeAllChildren();
 
        /*
                Calculate new sizes and positions
index 7141100c08593a6eaf305c1fcc8f26bec4c72a55..452702add3b7afa0e987971a0d48f0b41c9bde70 100644 (file)
@@ -31,9 +31,7 @@ class GUIPasswordChange : public GUIModalMenu
        GUIPasswordChange(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
                        IMenuManager *menumgr, Client *client,
                        ISimpleTextureSource *tsrc);
-       ~GUIPasswordChange();
 
-       void removeChildren();
        /*
                Remove and re-add (or reposition) stuff
        */
index 489927a11d822989a6332caba3225169d82f1463..9c63e06b5116504a9ad9810a4dba73ae043b7674 100644 (file)
@@ -32,13 +32,12 @@ GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
 
 GUIFileSelectMenu::~GUIFileSelectMenu()
 {
-       removeChildren();
        setlocale(LC_NUMERIC, "C");
 }
 
 void GUIFileSelectMenu::regenerateGui(v2u32 screensize)
 {
-       removeChildren();
+       removeAllChildren();
        m_fileOpenDialog = 0;
 
        core::dimension2du size(600 * m_gui_scale, 400 * m_gui_scale);
index 0fe4c41bdea1b42cb94d208cf66100385a73f191..2d71f3453d4e70b996e6c93f0d256c3c8dcf475b 100644 (file)
@@ -59,12 +59,11 @@ bool GUIScrollContainer::OnEvent(const SEvent &event)
 void GUIScrollContainer::draw()
 {
        if (isVisible()) {
-               core::list<IGUIElement *>::Iterator it = Children.begin();
-               for (; it != Children.end(); ++it)
-                       if ((*it)->isNotClipped() ||
+               for (auto child : Children)
+                       if (child->isNotClipped() ||
                                        AbsoluteClippingRect.isRectCollided(
-                                                       (*it)->getAbsolutePosition()))
-                               (*it)->draw();
+                                                       child->getAbsolutePosition()))
+                               child->draw();
        }
 }
 
index 61ab758a11425c5d0e35b6afcf0bc87e7056d32b..0f6f43fe9b6c4f57c7a36e03d8917b4019262af9 100644 (file)
@@ -45,32 +45,12 @@ GUIVolumeChange::GUIVolumeChange(gui::IGUIEnvironment* env,
 {
 }
 
-GUIVolumeChange::~GUIVolumeChange()
-{
-       removeChildren();
-}
-
-void GUIVolumeChange::removeChildren()
-{
-       if (gui::IGUIElement *e = getElementFromId(ID_soundText))
-               e->remove();
-
-       if (gui::IGUIElement *e = getElementFromId(ID_soundExitButton))
-               e->remove();
-
-       if (gui::IGUIElement *e = getElementFromId(ID_soundSlider))
-               e->remove();
-
-       if (gui::IGUIElement *e = getElementFromId(ID_soundMuteButton))
-               e->remove();
-}
-
 void GUIVolumeChange::regenerateGui(v2u32 screensize)
 {
        /*
                Remove stuff
        */
-       removeChildren();
+       removeAllChildren();
        /*
                Calculate new sizes and positions
        */
index 466e17f9d90a29c27f3633471c763dbfc732f5e0..ccdaca00b908a56958339feb553753f6fa375bb2 100644 (file)
@@ -31,9 +31,6 @@ class GUIVolumeChange : public GUIModalMenu
        GUIVolumeChange(gui::IGUIEnvironment* env,
                        gui::IGUIElement* parent, s32 id,
                        IMenuManager *menumgr, ISimpleTextureSource *tsrc);
-       ~GUIVolumeChange();
-
-       void removeChildren();
        /*
                Remove and re-add (or reposition) stuff
        */
index 102492255b78860f2baf11db7ea6ad5ed783b68d..76d3573405b0c1790f85f4a5e1f27025b5eac9be 100644 (file)
@@ -64,10 +64,6 @@ class MainMenuManager : public IMenuManager
                // Remove all entries if there are duplicates
                m_stack.remove(menu);
 
-               /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
-               assert(*i == menu);
-               m_stack.erase(i);*/
-
                if(!m_stack.empty())
                        m_stack.back()->setVisible(true);
        }
index 56a5d2cb9ed39796b76ca2a58adb4b7bc9fceca1..d27f63d9404fc9a7ea9cbe6a12d84c7abd1df3ee 100644 (file)
@@ -108,19 +108,6 @@ void GUIModalMenu::quitMenu()
 #endif
 }
 
-void GUIModalMenu::removeChildren()
-{
-       const core::list<gui::IGUIElement *> &children = getChildren();
-       core::list<gui::IGUIElement *> children_copy;
-       for (gui::IGUIElement *i : children) {
-               children_copy.push_back(i);
-       }
-
-       for (gui::IGUIElement *i : children_copy) {
-               i->remove();
-       }
-}
-
 // clang-format off
 bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
 {
index 06e78f06b465ca2fdd100857b769e269c622d24f..e37c41533636f68a89a930b6e52bf5736a41cc49 100644 (file)
@@ -47,7 +47,6 @@ class GUIModalMenu : public gui::IGUIElement
        bool canTakeFocus(gui::IGUIElement *e);
        void draw();
        void quitMenu();
-       void removeChildren();
 
        virtual void regenerateGui(v2u32 screensize) = 0;
        virtual void drawMenu() = 0;
index fe86adae67fa4929beb838ad813ae3dfdd6c2b63..0f2572ee016923bb2424a3ab5048eb4bb7980dc8 100644 (file)
@@ -292,9 +292,6 @@ shadow_offset(0), shadow_alpha(0), fallback(0)
                Driver->grab();
 
        setInvisibleCharacters(L" ");
-
-       // Glyphs aren't reference counted, so don't try to delete them when we free the array.
-       Glyphs.set_free_when_destroyed(false);
 }
 
 bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency)
@@ -411,8 +408,7 @@ CGUITTFont::~CGUITTFont()
 {
        // Delete the glyphs and glyph pages.
        reset_images();
-       CGUITTAssistDelete::Delete(Glyphs);
-       //Glyphs.clear();
+       Glyphs.clear();
 
        // We aren't using this face anymore.
        auto n = c_faces.find(filename);
@@ -675,6 +671,8 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
        update_glyph_pages();
        auto it = Render_Map.begin();
        auto ie = Render_Map.end();
+       core::array<core::vector2di> tmp_positions;
+       core::array<core::recti> tmp_source_rects;
        while (it != ie)
        {
                CGUITTGlyphPage* page = it->second;
@@ -696,10 +694,8 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
                        do
                                ++i;
                        while (i < page->render_positions.size() && page->render_colors[i] == colprev);
-                       core::array<core::vector2di> tmp_positions;
-                       core::array<core::recti> tmp_source_rects;
-                       tmp_positions.set_pointer(&page->render_positions[ibegin], i - ibegin, false, false); // no copy
-                       tmp_source_rects.set_pointer(&page->render_source_rects[ibegin], i - ibegin, false, false);
+                       tmp_positions.set_data(&page->render_positions[ibegin], i - ibegin);
+                       tmp_source_rects.set_data(&page->render_source_rects[ibegin], i - ibegin);
                        --i;
 
                        if (!use_transparency)
index 9457e5b182fa5ed27789970c533700a05578403e..210222ed4d21a036885ba4017e8e623d5d3d57ef 100644 (file)
@@ -37,6 +37,7 @@
 #include <map>
 #include <irrUString.h>
 #include "util/enriched_string.h"
+#include "util/basic_macros.h"
 #include FT_FREETYPE_H
 
 namespace irr
@@ -46,23 +47,34 @@ namespace gui
        struct SGUITTFace;
        class CGUITTFont;
 
-       //! Class to assist in deleting glyphs.
-       class CGUITTAssistDelete
-       {
-               public:
-                       template <class T, typename TAlloc>
-                       static void Delete(core::array<T, TAlloc>& a)
-                       {
-                               TAlloc allocator;
-                               allocator.deallocate(a.pointer());
-                       }
-       };
-
        //! Structure representing a single TrueType glyph.
        struct SGUITTGlyph
        {
                //! Constructor.
-               SGUITTGlyph() : isLoaded(false), glyph_page(0), surface(0), parent(0) {}
+               SGUITTGlyph() :
+                       isLoaded(false),
+                       glyph_page(0),
+                       source_rect(),
+                       offset(),
+                       advance(),
+                       surface(0),
+                       parent(0)
+               {}
+
+               DISABLE_CLASS_COPY(SGUITTGlyph);
+
+               //! This class would be trivially copyable except for the reference count on `surface`.
+               SGUITTGlyph(SGUITTGlyph &&other) :
+                       isLoaded(other.isLoaded),
+                       glyph_page(other.glyph_page),
+                       source_rect(other.source_rect),
+                       offset(other.offset),
+                       advance(other.advance),
+                       surface(other.surface),
+                       parent(other.parent)
+               {
+                       other.surface = 0;
+               }
 
                //! Destructor.
                ~SGUITTGlyph() { unload(); }
index 2b46a4d51b85041f7eed302d6fe58a7a92f01b40..411250a0b17754fd6c2c9bfc4ead4a5fc5493bb4 100644 (file)
@@ -304,7 +304,11 @@ int ModApiMainMenu::l_get_games(lua_State *L)
                lua_settable(L,    top_lvl2);
 
                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_pushstring(L,  "author");
@@ -356,6 +360,11 @@ int ModApiMainMenu::l_get_content_info(lua_State *L)
        lua_pushstring(L, spec.author.c_str());
        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_setfield(L, -2, "release");
 
index d93f300d287f2152952e3e2252872b849eddd5d9..b6330c96a839705d0ce7b02dc8ecd413b8411aaa 100644 (file)
@@ -440,6 +440,7 @@ void Server::init()
 
        m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
 
+       m_gamespec.checkAndLog();
        m_modmgr->loadMods(m_script);
 
        // Read Textures and calculate sha1 sums
@@ -3109,7 +3110,7 @@ std::string Server::getStatusString()
        // Version
        os << "version: " << g_version_string;
        // 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
        os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
        // Max lag estimate
index e8957afd006514124805649f40e5a31793716282..e5a4fac5b143bf340e5f53241b24cbe14948e57f 100644 (file)
@@ -362,7 +362,7 @@ struct TestMapBlock: public TestBase
 
                MapNode node;
                bool position_valid;
-               core::list<v3s16> validity_exceptions;
+               std::list<v3s16> validity_exceptions;
 
                TC()
                {
@@ -373,7 +373,7 @@ struct TestMapBlock: public TestBase
                {
                        //return position_valid ^ (p==position_valid_exception);
                        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++)
                        {
                                if(p == *i)
index 1102f6d33b2b77c41d5984f6f27dc66a954cd69e..79ea0947101a3ca130c2eb7e8803de14e4cffc09 100644 (file)
@@ -80,7 +80,7 @@ class TestFailedException : public std::exception {
                        << #expected << std::endl                                     \
                        << "    at " << fs::GetFilenameFromPath(__FILE__) << ":"      \
                        << __LINE__ << std::endl                                      \
-                       << "    actual:   " << a << std::endl << "    expected: "     \
+                       << "    actual  : " << a << std::endl << "    expected: "     \
                        << e << std::endl;                                            \
                throw TestFailedException();                                      \
        }                                                                     \
index 660d77d02fe9eecf2d8e8597550bd9cec519cb08..ff6b57507be6c2ffd3b2e10913cc7a7161d8a141 100644 (file)
@@ -169,23 +169,50 @@ void TestSerialization::testDeSerializeLongString()
 
 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
-       UASSERT(serializeJsonString("") == "\"\"");
+       UASSERTEQ(std::string, serializeJsonString(""), "\"\"");
+       reset_is("\"\"");
+       UASSERTEQ(std::string, deSerializeJsonString(is), "");
+       assert_at_eof(is);
 
        // 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);
+
+       // Test optional serialization
+       const std::pair<const char*, const char*> test_pairs[] = {
+               { "abc", "abc" },
+               { "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);
+       }
 
-       // MSVC fails when directly using "\\\\"
-       std::string backslash = "\\";
-       UASSERT(serializeJsonString(teststring2) ==
-               mkstr("\"") +
+       // 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" +
                "\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
                "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
                "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
-               " !\\\"" + teststring2.substr(0x23, 0x2f-0x23) +
-               "\\/" + teststring2.substr(0x30, 0x5c-0x30) +
-               backslash + backslash + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
+               " !\\\"" + teststring2.substr(0x23, 0x5c-0x23) +
+               bs + bs + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
                "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
                "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
                "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
@@ -202,14 +229,31 @@ void TestSerialization::testSerializeJsonString()
                "\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
                "\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
                "\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
-               "\"");
-
-       // Test deserialize
-       std::istringstream is(serializeJsonString(teststring2), std::ios::binary);
-       UASSERT(deSerializeJsonString(is) == teststring2);
-       UASSERT(!is.eof());
-       is.get();
-       UASSERT(is.eof());
+               "\"";
+       std::string serialized = serializeJsonString(teststring2);
+       UASSERTEQ(std::string, serialized, expected);
+
+       reset_is(serialized);
+       UASSERTEQ(std::string, deSerializeJsonString(is), teststring2);
+       UASSERT(!is.eof()); // should have stopped at " so eof must not be set yet
+       assert_at_eof(is);
+
+       // 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");
 }
 
 
index 281061229a29670148531e1f4f8780a9f56b9efe..ee46fd941785e1f04a02ccb0dbb8bbd7a155f288 100644 (file)
@@ -18,15 +18,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "serialize.h"
-#include "pointer.h"
 #include "porting.h"
 #include "util/string.h"
+#include "util/hex.h"
 #include "exceptions.h"
 #include "irrlichttypes.h"
 
-#include <sstream>
-#include <iomanip>
-#include <vector>
+#include <iostream>
+#include <cassert>
 
 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::ostringstream os(std::ios::binary);
-       os << "\"";
+       std::string tmp;
+
+       tmp.reserve(plain.size() + 2);
+       tmp.push_back('"');
 
        for (char c : plain) {
                switch (c) {
                        case '"':
-                               os << "\\\"";
+                               tmp.append("\\\"");
                                break;
                        case '\\':
-                               os << "\\\\";
-                               break;
-                       case '/':
-                               os << "\\/";
+                               tmp.append("\\\\");
                                break;
                        case '\b':
-                               os << "\\b";
+                               tmp.append("\\b");
                                break;
                        case '\f':
-                               os << "\\f";
+                               tmp.append("\\f");
                                break;
                        case '\n':
-                               os << "\\n";
+                               tmp.append("\\n");
                                break;
                        case '\r':
-                               os << "\\r";
+                               tmp.append("\\r");
                                break;
                        case '\t':
-                               os << "\\t";
+                               tmp.append("\\t");
                                break;
                        default: {
                                if (c >= 32 && c <= 126) {
-                                       os << c;
+                                       tmp.push_back(c);
                                } else {
-                                       u32 cnum = (u8)c;
-                                       os << "\\u" << std::hex << std::setw(4)
-                                               << std::setfill('0') << cnum;
+                                       // We pretend that Unicode codepoints map to bytes (they don't)
+                                       u8 cnum = static_cast<u8>(c);
+                                       tmp.append("\\u00");
+                                       tmp.push_back(hex_chars[cnum >> 4]);
+                                       tmp.push_back(hex_chars[cnum & 0xf]);
                                }
                                break;
                        }
                }
        }
 
-       os << "\"";
-       return os.str();
+       tmp.push_back('"');
+       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::ostringstream os(std::ios::binary);
-       char c, c2;
+       std::string tmp;
+       char c;
+       bool was_backslash = false;
 
        // Parse initial doublequote
-       is >> c;
+       c = is.get();
        if (c != '"')
                throw SerializationError("JSON string must start with doublequote");
+       tmp.push_back(c);
 
-       // Parse characters
+       // Grab the entire json string
        for (;;) {
                c = is.get();
                if (is.eof())
                        throw SerializationError("JSON string ended prematurely");
 
-               if (c == '"') {
-                       return os.str();
-               }
-
-               if (c == '\\') {
-                       c2 = is.get();
-                       if (is.eof())
-                               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;
-               }
+               tmp.push_back(c);
+               if (was_backslash)
+                       was_backslash = false;
+               else if (c == '\\')
+                       was_backslash = true;
+               else if (c == '"')
+                       break; // found end of string
        }
 
-       return os.str();
+       deSerializeJsonString(tmp);
+       return tmp;
 }
 
 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::stringstream tmp_os(std::ios_base::binary | std::ios_base::in | std::ios_base::out);
-       bool expect_initial_quote = true;
-       bool is_json = false;
-       bool was_backslash = false;
-       for (;;) {
-               char c = is.get();
-               if (is.eof())
-                       break;
-
-               if (expect_initial_quote && c == '"') {
-                       tmp_os << c;
-                       is_json = true;
-               } 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);
+       // Check for initial quote
+       char c = is.peek();
+       if (is.eof())
+               return "";
+
+       if (c == '"') {
+               // json string: defer to the right implementation
+               return deSerializeJsonString(is);
        }
 
-       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;
 }
 
index 8c7089897218a4aa453493ad6d25e360501dfc5a..14df091a24ef9c3ed15e28688c9b08296a324d7f 100644 (file)
Binary files a/textures/base/pack/no_screenshot.png and b/textures/base/pack/no_screenshot.png differ
index 30e015adbe41b25ee80129366f446d111af1696f..1e0428102b05a567ad4dbfd3dac8fbc3757d9dea 100755 (executable)
@@ -47,7 +47,7 @@ done
        echo "The compiler runtime DLLs could not be found, they might be missing in the final package."
 
 # Get stuff
-irrlicht_version=1.9.0mt5
+irrlicht_version=1.9.0mt6
 ogg_version=1.3.5
 openal_version=1.21.1
 vorbis_version=1.3.7
index 230f48e04ea3d1ec96008b10f579fa290af5356c..923ca66133815064d5b9cbe29174a0491ff85afa 100755 (executable)
@@ -47,7 +47,7 @@ done
        echo "The compiler runtime DLLs could not be found, they might be missing in the final package."
 
 # Get stuff
-irrlicht_version=1.9.0mt5
+irrlicht_version=1.9.0mt6
 ogg_version=1.3.5
 openal_version=1.21.1
 vorbis_version=1.3.7
index 82529c712a7584f5c2edd1d387b1edc787db45f8..16327ec308fd75b1b4dfcf1a230611240bbc6ba3 100644 (file)
@@ -13,7 +13,7 @@ install_linux_deps() {
                shift
                pkgs+=(libirrlicht-dev)
        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
        fi