From ac5e8176b9a2b36520bcc78b9d486aea7742d554 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Sun, 22 May 2022 14:28:24 +0000 Subject: [PATCH 01/33] Add relative numbers for commands by prepending ~ (#9588) * Add relative numbers for commands by prepending ~ * Some builtin code cleanup * Disallow nan and inf in minetest.string_to_area * Remove unused local variable teleportee (makes Luacheck happy) * Clean up core.string_to_pos * Make area parsing less permissive * Rewrite tests as busted tests * /time: Fix negative minutes not working Co-authored-by: Lars Mueller --- builtin/common/misc_helpers.lua | 128 +++++++++++++++------ builtin/common/tests/misc_helpers_spec.lua | 90 +++++++++++++++ builtin/game/chat.lua | 87 +++++++++++--- doc/lua_api.txt | 10 +- 4 files changed, 264 insertions(+), 51 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index f5f89acd7..83f17da7b 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -425,54 +425,50 @@ function core.string_to_pos(value) return nil end - local x, y, z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") - if x and y and z then - x = tonumber(x) - y = tonumber(y) - z = tonumber(z) - return vector.new(x, y, z) - end - x, y, z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$") + value = value:match("^%((.-)%)$") or value -- strip parentheses + + local x, y, z = value:trim():match("^([%d.-]+)[,%s]%s*([%d.-]+)[,%s]%s*([%d.-]+)$") if x and y and z then x = tonumber(x) y = tonumber(y) z = tonumber(z) return vector.new(x, y, z) end + return nil end -------------------------------------------------------------------------------- -function core.string_to_area(value) - local p1, p2 = unpack(value:split(") (")) - if p1 == nil or p2 == nil then - return nil + +do + local rel_num_cap = "(~?-?%d*%.?%d*)" -- may be overly permissive as this will be tonumber'ed anyways + local num_delim = "[,%s]%s*" + local pattern = "^" .. table.concat({rel_num_cap, rel_num_cap, rel_num_cap}, num_delim) .. "$" + + local function parse_area_string(pos, relative_to) + local pp = {} + pp.x, pp.y, pp.z = pos:trim():match(pattern) + return core.parse_coordinates(pp.x, pp.y, pp.z, relative_to) end - p1 = core.string_to_pos(p1 .. ")") - p2 = core.string_to_pos("(" .. p2) - if p1 == nil or p2 == nil then - return nil + function core.string_to_area(value, relative_to) + local p1, p2 = value:match("^%((.-)%)%s*%((.-)%)$") + if not p1 then + return + end + + p1 = parse_area_string(p1, relative_to) + p2 = parse_area_string(p2, relative_to) + + if p1 == nil or p2 == nil then + return + end + + return p1, p2 end - - return p1, p2 end -local function test_string_to_area() - local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2, 4, -12.53)") - assert(p1.x == 10.0 and p1.y == 5 and p1.z == -2) - assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53) - - p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53") - assert(p1 == nil and p2 == nil) - - p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53") - assert(p1 == nil and p2 == nil) -end - -test_string_to_area() - -------------------------------------------------------------------------------- function table.copy(t, seen) local n = {} @@ -701,3 +697,71 @@ end function core.is_nan(number) return number ~= number end + +--[[ Helper function for parsing an optionally relative number +of a chat command parameter, using the chat command tilde notation. + +Parameters: +* arg: String snippet containing the number; possible values: + * "": return as number + * "~": return relative_to + + * "~": return relative_to + * Anything else will return `nil` +* relative_to: Number to which the `arg` number might be relative to + +Returns: +A number or `nil`, depending on `arg. + +Examples: +* `core.parse_relative_number("5", 10)` returns 5 +* `core.parse_relative_number("~5", 10)` returns 15 +* `core.parse_relative_number("~", 10)` returns 10 +]] +function core.parse_relative_number(arg, relative_to) + if not arg then + return nil + elseif arg == "~" then + return relative_to + elseif string.sub(arg, 1, 1) == "~" then + local number = tonumber(string.sub(arg, 2)) + if not number then + return nil + end + if core.is_nan(number) or number == math.huge or number == -math.huge then + return nil + end + return relative_to + number + else + local number = tonumber(arg) + if core.is_nan(number) or number == math.huge or number == -math.huge then + return nil + end + return number + end +end + +--[[ Helper function to parse coordinates that might be relative +to another position; supports chat command tilde notation. +Intended to be used in chat command parameter parsing. + +Parameters: +* x, y, z: Parsed x, y, and z coordinates as strings +* relative_to: Position to which to compare the position + +Syntax of x, y and z: +* "": return as number +* "~": return + player position on this axis +* "~": return player position on this axis + +Returns: a vector or nil for invalid input or if player does not exist +]] +function core.parse_coordinates(x, y, z, relative_to) + if not relative_to then + x, y, z = tonumber(x), tonumber(y), tonumber(z) + return x and y and z and { x = x, y = y, z = z } + end + local rx = core.parse_relative_number(x, relative_to.x) + local ry = core.parse_relative_number(y, relative_to.y) + local rz = core.parse_relative_number(z, relative_to.z) + return rx and ry and rz and { x = rx, y = ry, z = rz } +end diff --git a/builtin/common/tests/misc_helpers_spec.lua b/builtin/common/tests/misc_helpers_spec.lua index b11236860..f6ad96619 100644 --- a/builtin/common/tests/misc_helpers_spec.lua +++ b/builtin/common/tests/misc_helpers_spec.lua @@ -67,6 +67,96 @@ describe("pos", function() end) end) +describe("area parsing", function() + describe("valid inputs", function() + it("accepts absolute numbers", function() + local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2 4 -12.53)") + assert(p1.x == 10 and p1.y == 5 and p1.z == -2) + assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53) + end) + + it("accepts relative numbers", function() + local p1, p2 = core.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10}) + assert(type(p1) == "table" and type(p2) == "table") + assert(p1.x == 1 and p1.y == 2 and p1.z == 3) + assert(p2.x == 15 and p2.y == 5 and p2.z == 10) + + p1, p2 = core.string_to_area("(1 2 3) (~5 ~-5 ~)", {x=10,y=10,z=10}) + assert(type(p1) == "table" and type(p2) == "table") + assert(p1.x == 1 and p1.y == 2 and p1.z == 3) + assert(p2.x == 15 and p2.y == 5 and p2.z == 10) + end) + end) + describe("invalid inputs", function() + it("rejects too few numbers", function() + local p1, p2 = core.string_to_area("(1,1) (1,1,1,1)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + end) + + it("rejects too many numbers", function() + local p1, p2 = core.string_to_area("(1,1,1,1) (1,1,1,1)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + end) + + it("rejects nan & inf", function() + local p1, p2 = core.string_to_area("(1,1,1) (1,1,nan)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,1,~nan)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,~nan,1)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,1,inf)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,1,~inf)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,~inf,1)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)") + assert(p1 == nil and p2 == nil) + end) + + it("rejects words", function() + local p1, p2 = core.string_to_area("bananas", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("bananas", "foobar") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("bananas") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(bananas,bananas,bananas)") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(bananas,bananas,bananas) (bananas,bananas,bananas)") + assert(p1 == nil and p2 == nil) + end) + + it("requires parenthesis & valid numbers", function() + local p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53") + assert(p1 == nil and p2 == nil) + end) + end) +end) + describe("table", function() it("indexof()", function() assert.equal(1, table.indexof({"foo", "bar"}, "foo")) diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index c4fb6314e..cfb497cb0 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -130,8 +130,13 @@ local function parse_range_str(player_name, str) return false, S("Unable to get position of player @1.", player_name) end else - p1, p2 = core.string_to_area(str) - if p1 == nil then + local player = core.get_player_by_name(player_name) + local relpos + if player then + relpos = player:get_pos() + end + p1, p2 = core.string_to_area(str, relpos) + if p1 == nil or p2 == nil then return false, S("Incorrect area format. " .. "Expected: (x1,y1,z1) (x2,y2,z2)") end @@ -570,10 +575,15 @@ core.register_chatcommand("teleport", { description = S("Teleport to position or player"), privs = {teleport=true}, func = function(name, param) + local player = core.get_player_by_name(name) + local relpos + if player then + relpos = player:get_pos() + end local p = {} - p.x, p.y, p.z = param:match("^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") - p = vector.apply(p, tonumber) - if p.x and p.y and p.z then + p.x, p.y, p.z = string.match(param, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$") + p = core.parse_coordinates(p.x, p.y, p.z, relpos) + if p and p.x and p.y and p.z then return teleport_to_pos(name, p) end @@ -587,9 +597,19 @@ core.register_chatcommand("teleport", { "other players (missing privilege: @1).", "bring") local teleportee_name + p = {} teleportee_name, p.x, p.y, p.z = param:match( - "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") + "^([^ ]+) +([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$") + if teleportee_name then + local teleportee = core.get_player_by_name(teleportee_name) + if not teleportee then + return + end + relpos = teleportee:get_pos() + p = core.parse_coordinates(p.x, p.y, p.z, relpos) + end p = vector.apply(p, tonumber) + if teleportee_name and p.x and p.y and p.z then if not has_bring_priv then return false, missing_bring_msg @@ -842,7 +862,7 @@ core.register_chatcommand("spawnentity", { description = S("Spawn entity at given (or your) position"), privs = {give=true, interact=true}, func = function(name, param) - local entityname, p = string.match(param, "^([^ ]+) *(.*)$") + local entityname, pstr = string.match(param, "^([^ ]+) *(.*)$") if not entityname then return false, S("EntityName required.") end @@ -856,11 +876,15 @@ core.register_chatcommand("spawnentity", { if not core.registered_entities[entityname] then return false, S("Cannot spawn an unknown entity.") end - if p == "" then + local p + if pstr == "" then p = player:get_pos() else - p = core.string_to_pos(p) - if p == nil then + p = {} + p.x, p.y, p.z = string.match(pstr, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$") + local relpos = player:get_pos() + p = core.parse_coordinates(p.x, p.y, p.z, relpos) + if not (p and p.x and p.y and p.z) then return false, S("Invalid parameters (@1).", param) end end @@ -1019,6 +1043,13 @@ core.register_chatcommand("status", { end, }) +local function get_time(timeofday) + local time = math.floor(timeofday * 1440) + local minute = time % 60 + local hour = (time - minute) / 60 + return time, hour, minute +end + core.register_chatcommand("time", { params = S("[<0..23>:<0..59> | <0..24000>]"), description = S("Show or set time of day"), @@ -1037,9 +1068,14 @@ core.register_chatcommand("time", { return false, S("You don't have permission to run " .. "this command (missing privilege: @1).", "settime") end - local hour, minute = param:match("^(%d+):(%d+)$") - if not hour then - local new_time = tonumber(param) or -1 + local relative, negative, hour, minute = param:match("^(~?)(%-?)(%d+):(%d+)$") + if not relative then -- checking the first capture against nil suffices + local new_time = core.parse_relative_number(param, core.get_timeofday() * 24000) + if not new_time then + new_time = tonumber(param) or -1 + else + new_time = new_time % 24000 + end if new_time ~= new_time or new_time < 0 or new_time > 24000 then return false, S("Invalid time (must be between 0 and 24000).") end @@ -1047,14 +1083,29 @@ core.register_chatcommand("time", { core.log("action", name .. " sets time to " .. new_time) return true, S("Time of day changed.") end + local new_time hour = tonumber(hour) minute = tonumber(minute) - if hour < 0 or hour > 23 then - return false, S("Invalid hour (must be between 0 and 23 inclusive).") - elseif minute < 0 or minute > 59 then - return false, S("Invalid minute (must be between 0 and 59 inclusive).") + if relative == "" then + if hour < 0 or hour > 23 then + return false, S("Invalid hour (must be between 0 and 23 inclusive).") + elseif minute < 0 or minute > 59 then + return false, S("Invalid minute (must be between 0 and 59 inclusive).") + end + new_time = (hour * 60 + minute) / 1440 + else + if minute < 0 or minute > 59 then + return false, S("Invalid minute (must be between 0 and 59 inclusive).") + end + local current_time = core.get_timeofday() + if negative == "-" then -- negative time + hour, minute = -hour, -minute + end + new_time = (current_time + (hour * 60 + minute) / 1440) % 1 + local _ + _, hour, minute = get_time(new_time) end - core.set_timeofday((hour * 60 + minute) / 1440) + core.set_timeofday(new_time) core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute)) return true, S("Time of day changed.") end, diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6046a5902..96b1cc469 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3550,8 +3550,16 @@ Helper functions * `minetest.string_to_pos(string)`: returns a position or `nil` * Same but in reverse. * If the string can't be parsed to a position, nothing is returned. -* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)")`: returns two positions +* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)", relative_to)`: + * returns two positions * Converts a string representing an area box into two positions + * X1, Y1, ... Z2 are coordinates + * `relative_to`: Optional. If set to a position, each coordinate + can use the tilde notation for relative positions + * Tilde notation: "~": Relative coordinate + "~": Relative coordinate plus + * Example: `minetest.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})` + returns `{x=1,y=2,z=3}, {x=15,y=5,z=10}` * `minetest.formspec_escape(string)`: returns a string * escapes the characters "[", "]", "\", "," and ";", which can not be used in formspecs. From fa682270a99383f0f91c37aeed974acc140f34df Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Sun, 22 May 2022 19:23:04 +0000 Subject: [PATCH 02/33] Add missing comma in example in lua_api.txt (#12339) --- doc/lua_api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 96b1cc469..f0a8f7c9b 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2012,7 +2012,7 @@ Example definition of the capabilities of an item max_drop_level=1, groupcaps={ crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}} - } + }, damage_groups = {fleshy=2}, } From c660218e43bfbc9740001d6707618d5eba51b664 Mon Sep 17 00:00:00 2001 From: Zughy <63455151+Zughy@users.noreply.github.com> Date: Mon, 23 May 2022 22:49:48 +0200 Subject: [PATCH 03/33] Docs: clarify spawn_by for decorations --- doc/lua_api.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f0a8f7c9b..882c9c54c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -8560,9 +8560,8 @@ See [Decoration types]. Used by `minetest.register_decoration`. spawn_by = "default:water", -- Node (or list of nodes) that the decoration only spawns next to. - -- Checks two horizontal planes of 8 neighbouring nodes (including - -- diagonal neighbours), one plane level with the 'place_on' node and a - -- plane one node above that. + -- Checks the 8 neighbouring nodes on the same Y, and also the ones + -- at Y+1, excluding both center nodes. num_spawn_by = 1, -- Number of spawn_by nodes that must be surrounding the decoration From 0f9c78c3ebf920fac65030e66367b9940055075f Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 23 May 2022 20:50:10 +0000 Subject: [PATCH 04/33] Fix no_texture.png for unknown nodes with ID < 125 (#12329) --- src/nodedef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nodedef.h b/src/nodedef.h index f90caff8a..8c817179d 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -539,7 +539,7 @@ public: */ inline const ContentFeatures& get(content_t c) const { return - c < m_content_features.size() ? + (c < m_content_features.size() && !m_content_features[c].name.empty()) ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN]; } From 367a2d4b29a4865a8f2d5b9717786a8660f226bc Mon Sep 17 00:00:00 2001 From: paradust7 <102263465+paradust7@users.noreply.github.com> Date: Mon, 23 May 2022 13:50:25 -0700 Subject: [PATCH 05/33] Add missing concurrency protection in logger (#12325) --- src/log.cpp | 8 ++++++-- src/log.h | 12 ++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/log.cpp b/src/log.cpp index 51652fe0a..ef998f161 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -152,12 +152,14 @@ void Logger::addOutput(ILogOutput *out) void Logger::addOutput(ILogOutput *out, LogLevel lev) { + MutexAutoLock lock(m_mutex); m_outputs[lev].push_back(out); m_has_outputs[lev] = true; } void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask) { + MutexAutoLock lock(m_mutex); for (size_t i = 0; i < LL_MAX; i++) { if (mask & LOGLEVEL_TO_MASKLEVEL(i)) { m_outputs[i].push_back(out); @@ -168,6 +170,7 @@ void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask) void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev) { + MutexAutoLock lock(m_mutex); assert(lev < LL_MAX); for (size_t i = 0; i <= lev; i++) { m_outputs[i].push_back(out); @@ -177,6 +180,7 @@ void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev) LogLevelMask Logger::removeOutput(ILogOutput *out) { + MutexAutoLock lock(m_mutex); LogLevelMask ret_mask = 0; for (size_t i = 0; i < LL_MAX; i++) { std::vector::iterator it; @@ -386,6 +390,6 @@ void LogOutputBuffer::logRaw(LogLevel lev, const std::string &line) default: break; } } - - m_buffer.push(color.append(line)); + MutexAutoLock lock(m_buffer_mutex); + m_buffer.emplace(color.append(line)); } diff --git a/src/log.h b/src/log.h index 5c6ba7d64..0a84332e7 100644 --- a/src/log.h +++ b/src/log.h @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if !defined(_WIN32) // POSIX #include #endif +#include "threading/mutex_auto_lock.h" #include "util/basic_macros.h" #include "util/stream.h" #include "irrlichttypes.h" @@ -168,24 +169,31 @@ public: void clear() { + MutexAutoLock lock(m_buffer_mutex); m_buffer = std::queue(); } bool empty() const { + MutexAutoLock lock(m_buffer_mutex); return m_buffer.empty(); } std::string get() { - if (empty()) + MutexAutoLock lock(m_buffer_mutex); + if (m_buffer.empty()) return ""; - std::string s = m_buffer.front(); + std::string s = std::move(m_buffer.front()); m_buffer.pop(); return s; } private: + // g_logger serializes calls to logRaw() with a mutex, but that + // doesn't prevent get() / clear() from being called on top of it. + // This mutex prevents that. + mutable std::mutex m_buffer_mutex; std::queue m_buffer; Logger &m_logger; }; From e16a470d59069692d654f5c1529ab313a01ded67 Mon Sep 17 00:00:00 2001 From: Richard Try Date: Mon, 23 May 2022 23:50:49 +0300 Subject: [PATCH 06/33] Use unordered_map instead of map for MapSectors --- src/map.cpp | 2 +- src/map.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index ce69accb5..213844d57 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -102,7 +102,7 @@ MapSector * Map::getSectorNoGenerateNoLock(v2s16 p) return sector; } - std::map::iterator n = m_sectors.find(p); + auto n = m_sectors.find(p); if (n == m_sectors.end()) return NULL; diff --git a/src/map.h b/src/map.h index 1e5499586..931764215 100644 --- a/src/map.h +++ b/src/map.h @@ -266,7 +266,7 @@ protected: std::set m_event_receivers; - std::map m_sectors; + std::unordered_map m_sectors; // Be sure to set this to NULL when the cached sector is deleted MapSector *m_sector_cache = nullptr; From 5daafc9d336d3f946854874e56a38ae9ac130811 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 22 May 2022 00:37:58 +0200 Subject: [PATCH 07/33] Fix hash implementation for SerializedBlockCache --- src/server.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server.h b/src/server.h index 2c21f5dfc..71f692e87 100644 --- a/src/server.h +++ b/src/server.h @@ -425,11 +425,10 @@ private: std::unordered_set waiting_players; }; - // the standard library does not implement std::hash for pairs so we have this: + // The standard library does not implement std::hash for pairs so we have this: struct SBCHash { size_t operator() (const std::pair &p) const { - return (((size_t) p.first.X) << 48) | (((size_t) p.first.Y) << 32) | - (((size_t) p.first.Z) << 16) | ((size_t) p.second); + return std::hash()(p.first) ^ p.second; } }; From 16a30556dfa8e27c82d026bd63467f82d2e37a1c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 22 May 2022 00:52:11 +0200 Subject: [PATCH 08/33] Formally drop support for building with upstream Irrlicht It stopped working with (at least) the last commit. --- .github/workflows/build.yml | 2 +- CMakeLists.txt | 5 ++-- README.md | 4 +-- .../Modules/MinetestFindIrrlichtHeaders.cmake | 26 +++++++------------ util/ci/common.sh | 3 --- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 70340d82d..1717a282d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -132,7 +132,7 @@ jobs: - name: Install deps run: | source ./util/ci/common.sh - install_linux_deps --old-irr clang-9 + install_linux_deps clang-9 - name: Build prometheus-cpp run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index d8dd85af6..09e3dcccd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,6 @@ if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "") # IrrlichtMtConfig.cmake message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.") endif() -# This is done here so that relative search paths are more reasonable elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt") message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'") if(BUILD_CLIENT) @@ -108,9 +107,9 @@ else() include(MinetestFindIrrlichtHeaders) if(NOT IRRLICHT_INCLUDE_DIR) - message(FATAL_ERROR "Irrlicht or IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}") + message(FATAL_ERROR "IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}") endif() - message(STATUS "Found Irrlicht headers: ${IRRLICHT_INCLUDE_DIR}") + message(STATUS "Found IrrlichtMt headers: ${IRRLICHT_INCLUDE_DIR}") add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED) # Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11 set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES diff --git a/README.md b/README.md index b6b545a22..8d0e68e0c 100644 --- a/README.md +++ b/README.md @@ -223,8 +223,8 @@ Run it: - You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`. - You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=`. - Debug build is slower, but gives much more useful output in a debugger. -- If you build a bare server you don't need to have the Irrlicht or IrrlichtMt library installed. - - In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlicht/include`. +- If you build a bare server you don't need to compile IrrlichtMt, just the headers suffice. + - In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlichtmt/include`. - Minetest will use the IrrlichtMt package that is found first, given by the following order: 1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable diff --git a/cmake/Modules/MinetestFindIrrlichtHeaders.cmake b/cmake/Modules/MinetestFindIrrlichtHeaders.cmake index d33b296d0..e434b582f 100644 --- a/cmake/Modules/MinetestFindIrrlichtHeaders.cmake +++ b/cmake/Modules/MinetestFindIrrlichtHeaders.cmake @@ -1,21 +1,13 @@ -# Locate Irrlicht or IrrlichtMt headers on system. +# Locate IrrlichtMt headers on system. -foreach(libname IN ITEMS IrrlichtMt Irrlicht) - string(TOLOWER "${libname}" libname2) - - find_path(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h - DOC "Path to the directory with IrrlichtMt includes" - PATHS - /usr/local/include/${libname2} - /usr/include/${libname2} - /system/develop/headers/${libname2} #Haiku - PATH_SUFFIXES "include/${libname2}" - ) - - if(IRRLICHT_INCLUDE_DIR) - break() - endif() -endforeach() +find_path(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h + DOC "Path to the directory with IrrlichtMt includes" + PATHS + /usr/local/include/irrlichtmt + /usr/include/irrlichtmt + /system/develop/headers/irrlichtmt #Haiku + PATH_SUFFIXES "include/irrlichtmt" +) # Handholding for users if(IRRLICHT_INCLUDE_DIR AND (NOT IS_DIRECTORY "${IRRLICHT_INCLUDE_DIR}" OR diff --git a/util/ci/common.sh b/util/ci/common.sh index 16327ec30..27034b4fb 100644 --- a/util/ci/common.sh +++ b/util/ci/common.sh @@ -9,9 +9,6 @@ install_linux_deps() { if [[ "$1" == "--no-irr" ]]; then shift - elif [[ "$1" == "--old-irr" ]]; then - shift - pkgs+=(libirrlicht-dev) else 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 From ed26ed5a1f49fc66facd4a745d29441aba5a92c3 Mon Sep 17 00:00:00 2001 From: x2048 Date: Mon, 23 May 2022 23:45:18 +0200 Subject: [PATCH 09/33] Quantize light frustum calculations (#12357) * Quantize light frustum calculations Reduces shadow flicker * Fix function name to match conventions --- src/client/shadows/dynamicshadows.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp index ca2d3ce37..9f26ba94a 100644 --- a/src/client/shadows/dynamicshadows.cpp +++ b/src/client/shadows/dynamicshadows.cpp @@ -27,10 +27,24 @@ with this program; if not, write to the Free Software Foundation, Inc., using m4f = core::matrix4; +static v3f quantizeDirection(v3f direction, float step) +{ + + float yaw = std::atan2(direction.Z, direction.X); + float pitch = std::asin(direction.Y); // assume look is normalized + + yaw = std::floor(yaw / step) * step; + pitch = std::floor(pitch / step) * step; + + return v3f(std::cos(yaw)*std::cos(pitch), std::sin(pitch), std::sin(yaw)*std::cos(pitch)); +} + void DirectionalLight::createSplitMatrices(const Camera *cam) { + const float DISTANCE_STEP = BS * 2.0; // 2 meters v3f newCenter; v3f look = cam->getDirection(); + look = quantizeDirection(look, M_PI / 12.0); // 15 degrees // camera view tangents float tanFovY = tanf(cam->getFovY() * 0.5f); @@ -42,6 +56,10 @@ void DirectionalLight::createSplitMatrices(const Camera *cam) // adjusted camera positions v3f cam_pos_world = cam->getPosition(); + cam_pos_world = v3f( + floor(cam_pos_world.X / DISTANCE_STEP) * DISTANCE_STEP, + floor(cam_pos_world.Y / DISTANCE_STEP) * DISTANCE_STEP, + floor(cam_pos_world.Z / DISTANCE_STEP) * DISTANCE_STEP); v3f cam_pos_scene = v3f(cam_pos_world.X - cam->getOffset().X * BS, cam_pos_world.Y - cam->getOffset().Y * BS, cam_pos_world.Z - cam->getOffset().Z * BS); @@ -61,7 +79,7 @@ void DirectionalLight::createSplitMatrices(const Camera *cam) v3f boundVec = (cam_pos_scene + farCorner * sfFar) - center_scene; float radius = boundVec.getLength(); float length = radius * 3.0f; - v3f eye_displacement = direction * length; + v3f eye_displacement = quantizeDirection(direction, M_PI / 2880 /*15 seconds*/) * length; // we must compute the viewmat with the position - the camera offset // but the future_frustum position must be the actual world position From 5d26ac00883eed2f522e6e0fa3adcd7081814c9b Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 9 May 2022 19:28:27 +0200 Subject: [PATCH 10/33] Improve code in mapblock_mesh.cpp a bit --- src/client/mapblock_mesh.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 965dd5e29..3be9e13b8 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -1349,30 +1349,22 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): scene::SMeshBuffer *buf = new scene::SMeshBuffer(); buf->Material = material; - switch (p.layer.material_type) { - // list of transparent materials taken from tile.h - case TILE_MATERIAL_ALPHA: - case TILE_MATERIAL_LIQUID_TRANSPARENT: - case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: - { - buf->append(&p.vertices[0], p.vertices.size(), - &p.indices[0], 0); + if (p.layer.isTransparent()) { + buf->append(&p.vertices[0], p.vertices.size(), nullptr, 0); - MeshTriangle t; - t.buffer = buf; - for (u32 i = 0; i < p.indices.size(); i += 3) { - t.p1 = p.indices[i]; - t.p2 = p.indices[i + 1]; - t.p3 = p.indices[i + 2]; - t.updateAttributes(); - m_transparent_triangles.push_back(t); - } + MeshTriangle t; + t.buffer = buf; + m_transparent_triangles.reserve(p.indices.size() / 3); + for (u32 i = 0; i < p.indices.size(); i += 3) { + t.p1 = p.indices[i]; + t.p2 = p.indices[i + 1]; + t.p3 = p.indices[i + 2]; + t.updateAttributes(); + m_transparent_triangles.push_back(t); } - break; - default: + } else { buf->append(&p.vertices[0], p.vertices.size(), &p.indices[0], p.indices.size()); - break; } mesh->addMeshBuffer(buf); buf->drop(); From 9a01581cdd3cb54750e9fe4a065aa435262d5225 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 9 May 2022 20:00:22 +0200 Subject: [PATCH 11/33] Get rid of global buffer that would ruin concurrent MapBlock serialization --- src/mapblock.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/mapblock.cpp b/src/mapblock.cpp index e3a6caa19..2bbc0ebbf 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -221,33 +221,36 @@ void MapBlock::expireDayNightDiff() /* Serialization */ + // List relevant id-name pairs for ids in the block using nodedef -// Renumbers the content IDs (starting at 0 and incrementing -// use static memory requires about 65535 * sizeof(int) ram in order to be -// sure we can handle all content ids. But it's absolutely worth it as it's -// a speedup of 4 for one of the major time consuming functions on storing -// mapblocks. -static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1]; +// Renumbers the content IDs (starting at 0 and incrementing) static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes, const NodeDefManager *nodedef) { - memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t)); + // The static memory requires about 65535 * sizeof(int) RAM in order to be + // sure we can handle all content ids. But it's absolutely worth it as it's + // a speedup of 4 for one of the major time consuming functions on storing + // mapblocks. + thread_local std::unique_ptr mapping; + static_assert(sizeof(content_t) == 2, "content_t must be 16-bit"); + if (!mapping) + mapping = std::make_unique(USHRT_MAX + 1); - std::set unknown_contents; + memset(mapping.get(), 0xFF, (USHRT_MAX + 1) * sizeof(content_t)); + + std::unordered_set unknown_contents; content_t id_counter = 0; for (u32 i = 0; i < MapBlock::nodecount; i++) { content_t global_id = nodes[i].getContent(); content_t id = CONTENT_IGNORE; // Try to find an existing mapping - if (getBlockNodeIdMapping_mapping[global_id] != 0xFFFF) { - id = getBlockNodeIdMapping_mapping[global_id]; - } - else - { + if (mapping[global_id] != 0xFFFF) { + id = mapping[global_id]; + } else { // We have to assign a new mapping id = id_counter++; - getBlockNodeIdMapping_mapping[global_id] = id; + mapping[global_id] = id; const ContentFeatures &f = nodedef->get(global_id); const std::string &name = f.name; @@ -265,6 +268,7 @@ static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes, << "Name for node id " << unknown_content << " not known" << std::endl; } } + // Correct ids in the block to match nodedef based on names. // Unknown ones are added to nodedef. // Will not update itself to match id-name pairs in nodedef. From 8b74257bf3cbb54e78614ac6aaaba79adf75cc8e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 9 May 2022 20:59:28 +0200 Subject: [PATCH 12/33] Reduce size of ContentFeatures structure On my system this is a reduction from 4664 to 3704 bytes. This is not for the sake of saving RAM but ensuring commonly used structures fit into caches better. --- src/mapnode.cpp | 62 +++++++++-------- src/nodedef.cpp | 119 +++++++++++++++----------------- src/nodedef.h | 38 +++++++--- src/script/common/c_content.cpp | 49 +++++++------ 4 files changed, 143 insertions(+), 125 deletions(-) diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 73bd620fb..42f020e71 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -266,10 +266,12 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, std::vector &boxes = *p_boxes; if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) { - const std::vector &fixed = nodebox.fixed; + const auto &fixed = nodebox.fixed; int facedir = n.getFaceDir(nodemgr, true); u8 axisdir = facedir>>2; facedir&=0x03; + + boxes.reserve(boxes.size() + fixed.size()); for (aabb3f box : fixed) { if (nodebox.type == NODEBOX_LEVELED) box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS; @@ -437,41 +439,43 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, { size_t boxes_size = boxes.size(); boxes_size += nodebox.fixed.size(); + const auto &c = nodebox.getConnected(); + if (neighbors & 1) - boxes_size += nodebox.connect_top.size(); + boxes_size += c.connect_top.size(); else - boxes_size += nodebox.disconnected_top.size(); + boxes_size += c.disconnected_top.size(); if (neighbors & 2) - boxes_size += nodebox.connect_bottom.size(); + boxes_size += c.connect_bottom.size(); else - boxes_size += nodebox.disconnected_bottom.size(); + boxes_size += c.disconnected_bottom.size(); if (neighbors & 4) - boxes_size += nodebox.connect_front.size(); + boxes_size += c.connect_front.size(); else - boxes_size += nodebox.disconnected_front.size(); + boxes_size += c.disconnected_front.size(); if (neighbors & 8) - boxes_size += nodebox.connect_left.size(); + boxes_size += c.connect_left.size(); else - boxes_size += nodebox.disconnected_left.size(); + boxes_size += c.disconnected_left.size(); if (neighbors & 16) - boxes_size += nodebox.connect_back.size(); + boxes_size += c.connect_back.size(); else - boxes_size += nodebox.disconnected_back.size(); + boxes_size += c.disconnected_back.size(); if (neighbors & 32) - boxes_size += nodebox.connect_right.size(); + boxes_size += c.connect_right.size(); else - boxes_size += nodebox.disconnected_right.size(); + boxes_size += c.disconnected_right.size(); if (neighbors == 0) - boxes_size += nodebox.disconnected.size(); + boxes_size += c.disconnected.size(); if (neighbors < 4) - boxes_size += nodebox.disconnected_sides.size(); + boxes_size += c.disconnected_sides.size(); boxes.reserve(boxes_size); @@ -484,47 +488,47 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, BOXESPUSHBACK(nodebox.fixed); if (neighbors & 1) { - BOXESPUSHBACK(nodebox.connect_top); + BOXESPUSHBACK(c.connect_top); } else { - BOXESPUSHBACK(nodebox.disconnected_top); + BOXESPUSHBACK(c.disconnected_top); } if (neighbors & 2) { - BOXESPUSHBACK(nodebox.connect_bottom); + BOXESPUSHBACK(c.connect_bottom); } else { - BOXESPUSHBACK(nodebox.disconnected_bottom); + BOXESPUSHBACK(c.disconnected_bottom); } if (neighbors & 4) { - BOXESPUSHBACK(nodebox.connect_front); + BOXESPUSHBACK(c.connect_front); } else { - BOXESPUSHBACK(nodebox.disconnected_front); + BOXESPUSHBACK(c.disconnected_front); } if (neighbors & 8) { - BOXESPUSHBACK(nodebox.connect_left); + BOXESPUSHBACK(c.connect_left); } else { - BOXESPUSHBACK(nodebox.disconnected_left); + BOXESPUSHBACK(c.disconnected_left); } if (neighbors & 16) { - BOXESPUSHBACK(nodebox.connect_back); + BOXESPUSHBACK(c.connect_back); } else { - BOXESPUSHBACK(nodebox.disconnected_back); + BOXESPUSHBACK(c.disconnected_back); } if (neighbors & 32) { - BOXESPUSHBACK(nodebox.connect_right); + BOXESPUSHBACK(c.connect_right); } else { - BOXESPUSHBACK(nodebox.disconnected_right); + BOXESPUSHBACK(c.disconnected_right); } if (neighbors == 0) { - BOXESPUSHBACK(nodebox.disconnected); + BOXESPUSHBACK(c.disconnected); } if (neighbors < 4) { - BOXESPUSHBACK(nodebox.disconnected_sides); + BOXESPUSHBACK(c.disconnected_sides); } } diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 8d63870b3..922554a2c 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -56,20 +56,7 @@ void NodeBox::reset() wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2); wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2); // no default for other parts - connect_top.clear(); - connect_bottom.clear(); - connect_front.clear(); - connect_left.clear(); - connect_back.clear(); - connect_right.clear(); - disconnected_top.clear(); - disconnected_bottom.clear(); - disconnected_front.clear(); - disconnected_left.clear(); - disconnected_back.clear(); - disconnected_right.clear(); - disconnected.clear(); - disconnected_sides.clear(); + connected.reset(); } void NodeBox::serialize(std::ostream &os, u16 protocol_version) const @@ -99,7 +86,7 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const writeV3F32(os, wall_side.MinEdge); writeV3F32(os, wall_side.MaxEdge); break; - case NODEBOX_CONNECTED: + case NODEBOX_CONNECTED: { writeU8(os, type); #define WRITEBOX(box) \ @@ -109,22 +96,25 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const writeV3F32(os, i.MaxEdge); \ }; + const auto &c = getConnected(); + WRITEBOX(fixed); - WRITEBOX(connect_top); - WRITEBOX(connect_bottom); - WRITEBOX(connect_front); - WRITEBOX(connect_left); - WRITEBOX(connect_back); - WRITEBOX(connect_right); - WRITEBOX(disconnected_top); - WRITEBOX(disconnected_bottom); - WRITEBOX(disconnected_front); - WRITEBOX(disconnected_left); - WRITEBOX(disconnected_back); - WRITEBOX(disconnected_right); - WRITEBOX(disconnected); - WRITEBOX(disconnected_sides); + WRITEBOX(c.connect_top); + WRITEBOX(c.connect_bottom); + WRITEBOX(c.connect_front); + WRITEBOX(c.connect_left); + WRITEBOX(c.connect_back); + WRITEBOX(c.connect_right); + WRITEBOX(c.disconnected_top); + WRITEBOX(c.disconnected_bottom); + WRITEBOX(c.disconnected_front); + WRITEBOX(c.disconnected_left); + WRITEBOX(c.disconnected_back); + WRITEBOX(c.disconnected_right); + WRITEBOX(c.disconnected); + WRITEBOX(c.disconnected_sides); break; + } default: writeU8(os, type); break; @@ -173,21 +163,23 @@ void NodeBox::deSerialize(std::istream &is) u16 count; + auto &c = getConnected(); + READBOXES(fixed); - READBOXES(connect_top); - READBOXES(connect_bottom); - READBOXES(connect_front); - READBOXES(connect_left); - READBOXES(connect_back); - READBOXES(connect_right); - READBOXES(disconnected_top); - READBOXES(disconnected_bottom); - READBOXES(disconnected_front); - READBOXES(disconnected_left); - READBOXES(disconnected_back); - READBOXES(disconnected_right); - READBOXES(disconnected); - READBOXES(disconnected_sides); + READBOXES(c.connect_top); + READBOXES(c.connect_bottom); + READBOXES(c.connect_front); + READBOXES(c.connect_left); + READBOXES(c.connect_back); + READBOXES(c.connect_right); + READBOXES(c.disconnected_top); + READBOXES(c.disconnected_bottom); + READBOXES(c.disconnected_front); + READBOXES(c.disconnected_left); + READBOXES(c.disconnected_back); + READBOXES(c.disconnected_right); + READBOXES(c.disconnected); + READBOXES(c.disconnected_sides); } } @@ -409,9 +401,9 @@ void ContentFeatures::reset() drowning = 0; light_source = 0; damage_per_second = 0; - node_box = NodeBox(); - selection_box = NodeBox(); - collision_box = NodeBox(); + node_box.reset(); + selection_box.reset(); + collision_box.reset(); waving = 0; legacy_facedir_simple = false; legacy_wallmounted = false; @@ -1091,10 +1083,8 @@ void NodeDefManager::clear() { ContentFeatures f; f.name = "unknown"; - TileDef unknownTile; - unknownTile.name = "unknown_node.png"; for (int t = 0; t < 6; t++) - f.tiledef[t] = unknownTile; + f.tiledef[t].name = "unknown_node.png"; // Insert directly into containers content_t c = CONTENT_UNKNOWN; m_content_features[c] = f; @@ -1296,22 +1286,23 @@ void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features, break; } case NODEBOX_CONNECTED: { + const auto &c = nodebox.getConnected(); // Add all possible connected boxes - boxVectorUnion(nodebox.fixed, box_union); - boxVectorUnion(nodebox.connect_top, box_union); - boxVectorUnion(nodebox.connect_bottom, box_union); - boxVectorUnion(nodebox.connect_front, box_union); - boxVectorUnion(nodebox.connect_left, box_union); - boxVectorUnion(nodebox.connect_back, box_union); - boxVectorUnion(nodebox.connect_right, box_union); - boxVectorUnion(nodebox.disconnected_top, box_union); - boxVectorUnion(nodebox.disconnected_bottom, box_union); - boxVectorUnion(nodebox.disconnected_front, box_union); - boxVectorUnion(nodebox.disconnected_left, box_union); - boxVectorUnion(nodebox.disconnected_back, box_union); - boxVectorUnion(nodebox.disconnected_right, box_union); - boxVectorUnion(nodebox.disconnected, box_union); - boxVectorUnion(nodebox.disconnected_sides, box_union); + boxVectorUnion(nodebox.fixed, box_union); + boxVectorUnion(c.connect_top, box_union); + boxVectorUnion(c.connect_bottom, box_union); + boxVectorUnion(c.connect_front, box_union); + boxVectorUnion(c.connect_left, box_union); + boxVectorUnion(c.connect_back, box_union); + boxVectorUnion(c.connect_right, box_union); + boxVectorUnion(c.disconnected_top, box_union); + boxVectorUnion(c.disconnected_bottom, box_union); + boxVectorUnion(c.disconnected_front, box_union); + boxVectorUnion(c.disconnected_left, box_union); + boxVectorUnion(c.disconnected_back, box_union); + boxVectorUnion(c.disconnected_right, box_union); + boxVectorUnion(c.disconnected, box_union); + boxVectorUnion(c.disconnected_sides, box_union); break; } default: { diff --git a/src/nodedef.h b/src/nodedef.h index 8c817179d..edd8d00a7 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -99,17 +99,8 @@ enum NodeBoxType NODEBOX_CONNECTED, // optionally draws nodeboxes if a neighbor node attaches }; -struct NodeBox +struct NodeBoxConnected { - enum NodeBoxType type; - // NODEBOX_REGULAR (no parameters) - // NODEBOX_FIXED - std::vector fixed; - // NODEBOX_WALLMOUNTED - aabb3f wall_top; - aabb3f wall_bottom; - aabb3f wall_side; // being at the -X side - // NODEBOX_CONNECTED std::vector connect_top; std::vector connect_bottom; std::vector connect_front; @@ -124,9 +115,35 @@ struct NodeBox std::vector disconnected_right; std::vector disconnected; std::vector disconnected_sides; +}; + +struct NodeBox +{ + enum NodeBoxType type; + // NODEBOX_REGULAR (no parameters) + // NODEBOX_FIXED + std::vector fixed; + // NODEBOX_WALLMOUNTED + aabb3f wall_top; + aabb3f wall_bottom; + aabb3f wall_side; // being at the -X side + // NODEBOX_CONNECTED + // (kept externally to not bloat the structure) + std::shared_ptr connected; NodeBox() { reset(); } + ~NodeBox() = default; + + inline NodeBoxConnected &getConnected() { + if (!connected) + connected = std::make_shared(); + return *connected; + } + inline const NodeBoxConnected &getConnected() const { + assert(connected); + return *connected; + } void reset(); void serialize(std::ostream &os, u16 protocol_version) const; @@ -290,7 +307,6 @@ struct ContentFeatures // up down right left back front TileSpec tiles[6]; // Special tiles - // - Currently used for flowing liquids TileSpec special_tiles[CF_SPECIAL_COUNT]; u8 solidness; // Used when choosing which face is drawn u8 visual_solidness; // When solidness=0, this tells how it looks like diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 79830ddb4..f232e9e5d 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1000,22 +1000,25 @@ void push_nodebox(lua_State *L, const NodeBox &box) push_aabb3f(L, box.wall_side); lua_setfield(L, -2, "wall_side"); break; - case NODEBOX_CONNECTED: + case NODEBOX_CONNECTED: { lua_pushstring(L, "connected"); lua_setfield(L, -2, "type"); - push_box(L, box.connect_top); + const auto &c = box.getConnected(); + push_box(L, c.connect_top); lua_setfield(L, -2, "connect_top"); - push_box(L, box.connect_bottom); + push_box(L, c.connect_bottom); lua_setfield(L, -2, "connect_bottom"); - push_box(L, box.connect_front); + push_box(L, c.connect_front); lua_setfield(L, -2, "connect_front"); - push_box(L, box.connect_back); + push_box(L, c.connect_back); lua_setfield(L, -2, "connect_back"); - push_box(L, box.connect_left); + push_box(L, c.connect_left); lua_setfield(L, -2, "connect_left"); - push_box(L, box.connect_right); + push_box(L, c.connect_right); lua_setfield(L, -2, "connect_right"); + // half the boxes are missing here? break; + } default: FATAL_ERROR("Invalid box.type"); break; @@ -1143,20 +1146,24 @@ NodeBox read_nodebox(lua_State *L, int index) NODEBOXREAD(nodebox.wall_top, "wall_top"); NODEBOXREAD(nodebox.wall_bottom, "wall_bottom"); NODEBOXREAD(nodebox.wall_side, "wall_side"); - NODEBOXREADVEC(nodebox.connect_top, "connect_top"); - NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom"); - NODEBOXREADVEC(nodebox.connect_front, "connect_front"); - NODEBOXREADVEC(nodebox.connect_left, "connect_left"); - NODEBOXREADVEC(nodebox.connect_back, "connect_back"); - NODEBOXREADVEC(nodebox.connect_right, "connect_right"); - NODEBOXREADVEC(nodebox.disconnected_top, "disconnected_top"); - NODEBOXREADVEC(nodebox.disconnected_bottom, "disconnected_bottom"); - NODEBOXREADVEC(nodebox.disconnected_front, "disconnected_front"); - NODEBOXREADVEC(nodebox.disconnected_left, "disconnected_left"); - NODEBOXREADVEC(nodebox.disconnected_back, "disconnected_back"); - NODEBOXREADVEC(nodebox.disconnected_right, "disconnected_right"); - NODEBOXREADVEC(nodebox.disconnected, "disconnected"); - NODEBOXREADVEC(nodebox.disconnected_sides, "disconnected_sides"); + + if (nodebox.type == NODEBOX_CONNECTED) { + auto &c = nodebox.getConnected(); + NODEBOXREADVEC(c.connect_top, "connect_top"); + NODEBOXREADVEC(c.connect_bottom, "connect_bottom"); + NODEBOXREADVEC(c.connect_front, "connect_front"); + NODEBOXREADVEC(c.connect_left, "connect_left"); + NODEBOXREADVEC(c.connect_back, "connect_back"); + NODEBOXREADVEC(c.connect_right, "connect_right"); + NODEBOXREADVEC(c.disconnected_top, "disconnected_top"); + NODEBOXREADVEC(c.disconnected_bottom, "disconnected_bottom"); + NODEBOXREADVEC(c.disconnected_front, "disconnected_front"); + NODEBOXREADVEC(c.disconnected_left, "disconnected_left"); + NODEBOXREADVEC(c.disconnected_back, "disconnected_back"); + NODEBOXREADVEC(c.disconnected_right, "disconnected_right"); + NODEBOXREADVEC(c.disconnected, "disconnected"); + NODEBOXREADVEC(c.disconnected_sides, "disconnected_sides"); + } return nodebox; } From ef22c0206f225dbccd67bff9fb888867c63039b3 Mon Sep 17 00:00:00 2001 From: x2048 Date: Thu, 26 May 2022 22:28:34 +0200 Subject: [PATCH 13/33] Force-update shadows when the world is changed (#12364) --- src/client/client.cpp | 9 ++++++++- src/client/shadows/dynamicshadowsrender.cpp | 14 +++++++++----- src/client/shadows/dynamicshadowsrender.h | 2 ++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 8ab96b7d1..e078dc530 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -530,6 +530,7 @@ void Client::step(float dtime) { int num_processed_meshes = 0; std::vector blocks_to_ack; + bool force_update_shadows = false; while (!m_mesh_update_thread.m_queue_out.empty()) { num_processed_meshes++; @@ -556,9 +557,11 @@ void Client::step(float dtime) if (is_empty) delete r.mesh; - else + else { // Replace with the new mesh block->mesh = r.mesh; + force_update_shadows = true; + } } } else { delete r.mesh; @@ -583,6 +586,10 @@ void Client::step(float dtime) if (num_processed_meshes > 0) g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); + + auto shadow_renderer = RenderingEngine::get_shadow_renderer(); + if (shadow_renderer && force_update_shadows) + shadow_renderer->setForceUpdateShadowMap(); } /* diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp index c13cfe252..b8ceeb623 100644 --- a/src/client/shadows/dynamicshadowsrender.cpp +++ b/src/client/shadows/dynamicshadowsrender.cpp @@ -242,7 +242,7 @@ void ShadowRenderer::updateSMTextures() // detect if SM should be regenerated for (DirectionalLight &light : m_light_list) { - if (light.should_update_map_shadow) { + if (light.should_update_map_shadow || m_force_update_shadow_map) { light.should_update_map_shadow = false; m_current_frame = 0; reset_sm_texture = true; @@ -271,14 +271,14 @@ void ShadowRenderer::updateSMTextures() // should put some gl* fn here - if (m_current_frame < m_map_shadow_update_frames) { + if (m_current_frame < m_map_shadow_update_frames || m_force_update_shadow_map) { m_driver->setRenderTarget(shadowMapTargetTexture, reset_sm_texture, true, video::SColor(255, 255, 255, 255)); renderShadowMap(shadowMapTargetTexture, light); // Render transparent part in one pass. // This is also handled in ClientMap. - if (m_current_frame == m_map_shadow_update_frames - 1) { + if (m_current_frame == m_map_shadow_update_frames - 1 || m_force_update_shadow_map) { if (m_shadow_map_colored) { m_driver->setRenderTarget(0, false, false); m_driver->setRenderTarget(shadowMapTextureColors, @@ -298,7 +298,7 @@ void ShadowRenderer::updateSMTextures() ++m_current_frame; // pass finished, swap textures and commit light changes - if (m_current_frame == m_map_shadow_update_frames) { + if (m_current_frame == m_map_shadow_update_frames || m_force_update_shadow_map) { if (shadowMapClientMapFuture != nullptr) std::swap(shadowMapClientMapFuture, shadowMapClientMap); @@ -306,6 +306,7 @@ void ShadowRenderer::updateSMTextures() for (DirectionalLight &light : m_light_list) light.commitFrustum(); } + m_force_update_shadow_map = false; } } @@ -432,7 +433,10 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target, m_driver->setTransform(video::ETS_WORLD, map_node->getAbsoluteTransformation()); - map_node->renderMapShadows(m_driver, material, pass, m_current_frame, m_map_shadow_update_frames); + int frame = m_force_update_shadow_map ? 0 : m_current_frame; + int total_frames = m_force_update_shadow_map ? 1 : m_map_shadow_update_frames; + + map_node->renderMapShadows(m_driver, material, pass, frame, total_frames); break; } } diff --git a/src/client/shadows/dynamicshadowsrender.h b/src/client/shadows/dynamicshadowsrender.h index 0e4ef6b70..2e3b58f6f 100644 --- a/src/client/shadows/dynamicshadowsrender.h +++ b/src/client/shadows/dynamicshadowsrender.h @@ -74,6 +74,7 @@ public: void removeNodeFromShadowList(scene::ISceneNode *node); void update(video::ITexture *outputTarget = nullptr); + void setForceUpdateShadowMap() { m_force_update_shadow_map = true; } void drawDebug(); video::ITexture *get_texture() @@ -131,6 +132,7 @@ private: bool m_shadows_enabled; bool m_shadows_supported; bool m_shadow_map_colored; + bool m_force_update_shadow_map; u8 m_map_shadow_update_frames; /* Use this number of frames to update map shaodw */ u8 m_current_frame{0}; /* Current frame */ f32 m_perspective_bias_xy; From fe299e24d6ad5bb58e077ec372737b117ac6cf94 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 27 May 2022 19:39:36 +0000 Subject: [PATCH 14/33] DevTest: Add nodes and items for testing overlays (#12304) --- games/devtest/mods/testitems/init.lua | 55 +++++++++++ games/devtest/mods/testitems/mod.conf | 2 + .../textures/testitems_overlay_base.png | Bin 0 -> 106 bytes .../textures/testitems_overlay_overlay.png | Bin 0 -> 220 bytes games/devtest/mods/testnodes/init.lua | 1 + games/devtest/mods/testnodes/overlays.lua | 93 ++++++++++++++++++ .../testnodes/textures/testnodes_overlay.png | Bin 0 -> 153 bytes .../textures/testnodes_overlayable.png | Bin 0 -> 87 bytes 8 files changed, 151 insertions(+) create mode 100644 games/devtest/mods/testitems/init.lua create mode 100644 games/devtest/mods/testitems/mod.conf create mode 100644 games/devtest/mods/testitems/textures/testitems_overlay_base.png create mode 100644 games/devtest/mods/testitems/textures/testitems_overlay_overlay.png create mode 100644 games/devtest/mods/testnodes/overlays.lua create mode 100644 games/devtest/mods/testnodes/textures/testnodes_overlay.png create mode 100644 games/devtest/mods/testnodes/textures/testnodes_overlayable.png diff --git a/games/devtest/mods/testitems/init.lua b/games/devtest/mods/testitems/init.lua new file mode 100644 index 000000000..33ebf50fc --- /dev/null +++ b/games/devtest/mods/testitems/init.lua @@ -0,0 +1,55 @@ +local S = minetest.get_translator("testitems") + +-- +-- Texture overlays for items +-- + +-- For the global overlay color test +local GLOBAL_COLOR_ARG = "orange" + +-- Punch handler to set random color with "color" argument in item metadata +local overlay_on_use = function(itemstack, user, pointed_thing) + local meta = itemstack:get_meta() + local color = math.random(0x0, 0xFFFFFF) + local colorstr = string.format("#%06x", color) + meta:set_string("color", colorstr) + minetest.log("action", "[testitems] Color of "..itemstack:get_name().." changed to "..colorstr) + return itemstack +end +-- Place handler to clear item metadata color +local overlay_on_place = function(itemstack, user, pointed_thing) + local meta = itemstack:get_meta() + meta:set_string("color", "") + return itemstack +end + +minetest.register_craftitem("testitems:overlay_meta", { + description = S("Texture Overlay Test Item, Meta Color") .. "\n" .. + S("Image must be a square with rainbow cross (inventory and wield)") .. "\n" .. + S("Item meta color must only change square color") .. "\n" .. + S("Punch: Set random color") .. "\n" .. + S("Place: Clear color"), + -- Base texture: A grayscale square (can be colorized) + inventory_image = "testitems_overlay_base.png", + wield_image = "testitems_overlay_base.png", + -- Overlay: A rainbow cross (NOT to be colorized!) + inventory_overlay = "testitems_overlay_overlay.png", + wield_overlay = "testitems_overlay_overlay.png", + + on_use = overlay_on_use, + on_place = overlay_on_place, + on_secondary_use = overlay_on_place, +}) +minetest.register_craftitem("testitems:overlay_global", { + description = S("Texture Overlay Test Item, Global Color") .. "\n" .. + S("Image must be an orange square with rainbow cross (inventory and wield)"), + -- Base texture: A grayscale square (to be colorized) + inventory_image = "testitems_overlay_base.png", + wield_image = "testitems_overlay_base.png", + -- Overlay: A rainbow cross (NOT to be colorized!) + inventory_overlay = "testitems_overlay_overlay.png", + wield_overlay = "testitems_overlay_overlay.png", + color = GLOBAL_COLOR_ARG, +}) + + diff --git a/games/devtest/mods/testitems/mod.conf b/games/devtest/mods/testitems/mod.conf new file mode 100644 index 000000000..f91febe01 --- /dev/null +++ b/games/devtest/mods/testitems/mod.conf @@ -0,0 +1,2 @@ +name = testitems +description = Test mod to test misc. items that are neither tools nor nodes diff --git a/games/devtest/mods/testitems/textures/testitems_overlay_base.png b/games/devtest/mods/testitems/textures/testitems_overlay_base.png new file mode 100644 index 0000000000000000000000000000000000000000..f473623beceb882cf5e982fe4a99d049443cc625 GIT binary patch literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|hMq2tAr*|t3haN>r4&r|C^0R_ z-q9|eaJ@i?{lM~v0&Egj9XvM(^;vjLV!PNdX9+LE+zQtD$-mA`1ZrmRboFyt=akR{ E01!DH@&Et; literal 0 HcmV?d00001 diff --git a/games/devtest/mods/testitems/textures/testitems_overlay_overlay.png b/games/devtest/mods/testitems/textures/testitems_overlay_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..75a7d9fc3c58528ed454b68afd2f203618e4f7a1 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Wq?nJE0F&G|Nj>~y;sIx#1%h^ zD}47=m z^W>+l<+;V{qGi7P6?1ZA$~mAg#pJARCC95@7n9{IS5_}swb)%RbK^$l?xS4gDLgTe~DWM4faB3Jd literal 0 HcmV?d00001 From e8b2954586ebd9e35e3f9f7230ff6713b65c4967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Fri, 27 May 2022 21:40:38 +0200 Subject: [PATCH 15/33] Builtin: Optimize misc helpers (#12377) Also add formspec_escape unit test --- builtin/common/information_formspecs.lua | 4 +-- builtin/common/misc_helpers.lua | 39 ++++++++++++---------- builtin/common/tests/misc_helpers_spec.lua | 8 +++++ 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/builtin/common/information_formspecs.lua b/builtin/common/information_formspecs.lua index 3405263bf..1445a017c 100644 --- a/builtin/common/information_formspecs.lua +++ b/builtin/common/information_formspecs.lua @@ -22,7 +22,6 @@ local LIST_FORMSPEC_DESCRIPTION = [[ local F = core.formspec_escape local S = core.get_translator("__builtin") -local check_player_privs = core.check_player_privs -- CHAT COMMANDS FORMSPEC @@ -58,10 +57,11 @@ local function build_chatcommands_formspec(name, sel, copy) .. "any entry in the list.").. "\n" .. S("Double-click to copy the entry to the chat history.") + local privs = core.get_player_privs(name) for i, data in ipairs(mod_cmds) do rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. "," for j, cmds in ipairs(data[2]) do - local has_priv = check_player_privs(name, cmds[2].privs) + local has_priv = privs[cmds[2].privs] rows[#rows + 1] = ("%s,1,%s,%s"):format( has_priv and COLOR_GREEN or COLOR_GRAY, cmds[1], F(cmds[2].params)) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 83f17da7b..d2356b505 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -204,7 +204,7 @@ end -------------------------------------------------------------------------------- function string:trim() - return (self:gsub("^%s*(.-)%s*$", "%1")) + return self:match("^%s*(.-)%s*$") end -------------------------------------------------------------------------------- @@ -245,16 +245,16 @@ function math.round(x) return math.ceil(x - 0.5) end - +local formspec_escapes = { + ["\\"] = "\\\\", + ["["] = "\\[", + ["]"] = "\\]", + [";"] = "\\;", + [","] = "\\," +} function core.formspec_escape(text) - if text ~= nil then - text = string.gsub(text,"\\","\\\\") - text = string.gsub(text,"%]","\\]") - text = string.gsub(text,"%[","\\[") - text = string.gsub(text,";","\\;") - text = string.gsub(text,",","\\,") - end - return text + -- Use explicit character set instead of dot here because it doubles the performance + return text and text:gsub("[\\%[%];,]", formspec_escapes) end @@ -265,18 +265,21 @@ function core.wrap_text(text, max_length, as_table) return as_table and {text} or text end - for word in text:gmatch('%S+') do - local cur_length = #table.concat(line, ' ') - if cur_length > 0 and cur_length + #word + 1 >= max_length then + local line_length = 0 + for word in text:gmatch("%S+") do + if line_length > 0 and line_length + #word + 1 >= max_length then -- word wouldn't fit on current line, move to next line - table.insert(result, table.concat(line, ' ')) - line = {} + table.insert(result, table.concat(line, " ")) + line = {word} + line_length = #word + else + table.insert(line, word) + line_length = line_length + 1 + #word end - table.insert(line, word) end - table.insert(result, table.concat(line, ' ')) - return as_table and result or table.concat(result, '\n') + table.insert(result, table.concat(line, " ")) + return as_table and result or table.concat(result, "\n") end -------------------------------------------------------------------------------- diff --git a/builtin/common/tests/misc_helpers_spec.lua b/builtin/common/tests/misc_helpers_spec.lua index f6ad96619..7d046d5b7 100644 --- a/builtin/common/tests/misc_helpers_spec.lua +++ b/builtin/common/tests/misc_helpers_spec.lua @@ -163,3 +163,11 @@ describe("table", function() assert.equal(-1, table.indexof({"foo", "bar"}, "baz")) end) end) + +describe("formspec_escape", function() + it("escapes", function() + assert.equal(nil, core.formspec_escape(nil)) + assert.equal("", core.formspec_escape("")) + assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\[")) + end) +end) From 76000e676bde0191d64dc0afd2f3ae74bb6c40b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sat, 28 May 2022 11:31:56 +0200 Subject: [PATCH 16/33] Lua workflow: Use Leafo's Luarocks action instead of installing outdated packages which lead to failing workflows --- .github/workflows/lua.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lua.yml b/.github/workflows/lua.yml index 3af4a6ee7..2a728f2d3 100644 --- a/.github/workflows/lua.yml +++ b/.github/workflows/lua.yml @@ -42,9 +42,10 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - - name: Install luarocks - run: | - sudo apt-get update && sudo apt-get install -y luarocks + - uses: leafo/gh-actions-lua@v9 + with: + luaVersion: "5.1.5" + - uses: leafo/gh-actions-luarocks@v4 - name: Install luarocks tools run: | From 25ba9d848d8199fd37ea29fe80704116ac9107c1 Mon Sep 17 00:00:00 2001 From: x2048 Date: Sun, 29 May 2022 13:58:57 +0200 Subject: [PATCH 17/33] Default settings, presets and configuration for dynamic shadows (#12359) --- builtin/mainmenu/tab_settings.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index 0e761d324..880978800 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -50,7 +50,7 @@ local labels = { fgettext("Low"), fgettext("Medium"), fgettext("High"), - fgettext("Ultra High") + fgettext("Very High") } } @@ -219,8 +219,9 @@ local function formspec(tabview, name, tabdata) .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. "checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" .. dump(core.settings:get_bool("enable_waving_plants")) .. "]".. - "label[8.25,3.0;" .. fgettext("Dynamic shadows: ") .. "]" .. - "dropdown[8.25,3.5;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";" + "label[8.25,2.8;" .. fgettext("Dynamic shadows:") .. "]" .. + "label[8.25,3.2;" .. fgettext("(game support required)") .. "]" .. + "dropdown[8.25,3.7;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";" .. getSettingIndex.ShadowMapping() .. "]" else tab_string = tab_string .. @@ -364,11 +365,11 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) core.settings:set("enable_dynamic_shadows", "false") else local shadow_presets = { - [2] = { 55, 512, "true", 0, "false" }, - [3] = { 82, 1024, "true", 1, "false" }, - [4] = { 240, 2048, "true", 1, "false" }, - [5] = { 240, 2048, "true", 2, "true" }, - [6] = { 300, 4096, "true", 2, "true" }, + [2] = { 62, 512, "true", 0, "false" }, + [3] = { 93, 1024, "true", 0, "false" }, + [4] = { 140, 2048, "true", 1, "false" }, + [5] = { 210, 4096, "true", 2, "true" }, + [6] = { 300, 8192, "true", 2, "true" }, } local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])] if s then From 0c6a02941320cb089a5965857866ff064ba986b6 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 25 May 2022 18:27:34 +0200 Subject: [PATCH 18/33] Improve a translation string fixes #11442 --- src/gui/modalMenu.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index d27f63d94..b05bed490 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -252,13 +252,11 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) return retval; m_jni_field_name = field_name; - /*~ Imperative, as in "Enter/type in text". - Don't forget the space. */ - std::string message = gettext("Enter "); std::string label = wide_to_utf8(getLabelByID(hovered->getID())); if (label.empty()) label = "text"; - message += strgettext(label) + ":"; + /*~ Imperative, as in "Type in text" */ + std::string message = fmtgettext("Enter %s:"); // single line text input int type = 2; From bccaf5fc2d11c31615d64b9bca91f908d06b7044 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 25 May 2022 19:00:34 +0200 Subject: [PATCH 19/33] Map opaque waving leaves to allfaces drawtype fixes #9842 --- src/nodedef.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 922554a2c..9c85826c4 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -901,8 +901,15 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc solidness = 0; visual_solidness = 1; } else { - drawtype = NDT_NORMAL; - solidness = 2; + if (waving >= 1) { + // waving nodes must make faces so there are no gaps + drawtype = NDT_ALLFACES; + solidness = 0; + visual_solidness = 1; + } else { + drawtype = NDT_NORMAL; + solidness = 2; + } for (TileDef &td : tdef) td.name += std::string("^[noalpha"); } From da71e86633d0b27cd02d7aac9fdac625d141ca13 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 25 May 2022 19:07:49 +0200 Subject: [PATCH 20/33] Protect a few more settings from being set from mods Of those settings main_menu_script has concrete security impact, the rest are added out of abundance of caution. --- src/script/lua_api/l_settings.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp index 14398dda2..3f3fda56e 100644 --- a/src/script/lua_api/l_settings.cpp +++ b/src/script/lua_api/l_settings.cpp @@ -27,9 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" -/* This protects: - * 'secure.*' settings from being set - * some mapgen settings from being set +/* This protects the following from being set: + * 'secure.*' settings + * some security-relevant settings + * (better solution pending) + * some mapgen settings * (not security-criticial, just to avoid messing up user configs) */ #define CHECK_SETTING_SECURITY(L, name) \ @@ -41,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., static inline int checkSettingSecurity(lua_State* L, const std::string &name) { if (ScriptApiSecurity::isSecure(L) && name.compare(0, 7, "secure.") == 0) - throw LuaError("Attempt to set secure setting."); + throw LuaError("Attempted to set secure setting."); bool is_mainmenu = false; #ifndef SERVER @@ -54,6 +56,17 @@ static inline int checkSettingSecurity(lua_State* L, const std::string &name) return -1; } + const char *disallowed[] = { + "main_menu_script", "shader_path", "texture_path", "screenshot_path", + "serverlist_file", "serverlist_url", "map-dir", "contentdb_url", + }; + if (!is_mainmenu) { + for (const char *name2 : disallowed) { + if (name == name2) + throw LuaError("Attempted to set disallowed setting."); + } + } + return 0; } From f195db2d140a7b4f2f2fbc438680c9d5e23a0d6d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 25 May 2022 19:29:11 +0200 Subject: [PATCH 21/33] Add API function to invoke player respawn closes #12272 --- doc/lua_api.txt | 2 ++ src/script/lua_api/l_object.cpp | 17 +++++++++++++++++ src/script/lua_api/l_object.h | 3 +++ src/server.cpp | 5 +++-- src/server.h | 3 ++- 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 882c9c54c..15a067a5e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7099,6 +7099,8 @@ object you are working with still exists. * `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness) * `get_lighting()`: returns the current state of lighting for the player. * Result is a table with the same fields as `light_definition` in `set_lighting`. +* `respawn()`: Respawns the player using the same mechanism as the death screen, + including calling on_respawnplayer callbacks. `PcgRandom` ----------- diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 39b19364e..37ba1521a 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -2323,6 +2323,21 @@ int ObjectRef::l_get_lighting(lua_State *L) return 1; } +// respawn(self) +int ObjectRef::l_respawn(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + getServer(L)->RespawnPlayer(player->getPeerId()); + lua_pushboolean(L, true); + return 1; +} + + ObjectRef::ObjectRef(ServerActiveObject *object): m_object(object) {} @@ -2478,5 +2493,7 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_minimap_modes), luamethod(ObjectRef, set_lighting), luamethod(ObjectRef, get_lighting), + luamethod(ObjectRef, respawn), + {0,0} }; diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 3e4e6681a..b36bab492 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -382,4 +382,7 @@ private: // get_lighting(self) static int l_get_lighting(lua_State *L); + + // respawn(self) + static int l_respawn(lua_State *L); }; diff --git a/src/server.cpp b/src/server.cpp index b6330c96a..c775f5d07 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2784,9 +2784,10 @@ void Server::RespawnPlayer(session_t peer_id) << playersao->getPlayer()->getName() << " respawns" << std::endl; - playersao->setHP(playersao->accessObjectProperties()->hp_max, + const auto *prop = playersao->accessObjectProperties(); + playersao->setHP(prop->hp_max, PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN)); - playersao->setBreath(playersao->accessObjectProperties()->breath_max); + playersao->setBreath(prop->breath_max); bool repositioned = m_script->on_respawnplayer(playersao); if (!repositioned) { diff --git a/src/server.h b/src/server.h index 71f692e87..a9c5bcb5f 100644 --- a/src/server.h +++ b/src/server.h @@ -336,6 +336,8 @@ public: void setLighting(RemotePlayer *player, const Lighting &lighting); + void RespawnPlayer(session_t peer_id); + /* con::PeerHandler implementation. */ void peerAdded(con::Peer *peer); void deletingPeer(con::Peer *peer, bool timeout); @@ -529,7 +531,6 @@ private: */ void HandlePlayerDeath(PlayerSAO* sao, const PlayerHPChangeReason &reason); - void RespawnPlayer(session_t peer_id); void DeleteClient(session_t peer_id, ClientDeletionReason reason); void UpdateCrafting(RemotePlayer *player); bool checkInteractDistance(RemotePlayer *player, const f32 d, const std::string &what); From 261a8db9dd0403f8d0a7d71f46d4cb272e217cd1 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 14:40:00 +0200 Subject: [PATCH 22/33] Optimize Server::sendMetadataChanged a bit The distance check also never worked as intended, now fixed. --- src/server.cpp | 39 ++++++++++++++++++++------------------- src/server.h | 2 +- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index c775f5d07..013c039c3 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -891,7 +891,7 @@ void Server::AsyncRunStep(bool initial_step) // We'll log the amount of each Profiler prof; - std::list node_meta_updates; + std::unordered_set node_meta_updates; while (!m_unsent_map_edit_queue.empty()) { MapEditEvent* event = m_unsent_map_edit_queue.front(); @@ -918,9 +918,7 @@ void Server::AsyncRunStep(bool initial_step) case MEET_BLOCK_NODE_METADATA_CHANGED: { prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1); if (!event->is_private_change) { - // Don't send the change yet. Collect them to eliminate dupes. - node_meta_updates.remove(event->p); - node_meta_updates.push_back(event->p); + node_meta_updates.emplace(event->p); } if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx( @@ -973,7 +971,7 @@ void Server::AsyncRunStep(bool initial_step) } // Send all metadata updates - if (node_meta_updates.size()) + if (!node_meta_updates.empty()) sendMetadataChanged(node_meta_updates); } @@ -2290,12 +2288,12 @@ void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set *far_player } } -void Server::sendMetadataChanged(const std::list &meta_updates, float far_d_nodes) +void Server::sendMetadataChanged(const std::unordered_set &positions, float far_d_nodes) { - float maxd = far_d_nodes * BS; NodeMetadataList meta_updates_list(false); - std::vector clients = m_clients.getClientIDs(); + std::ostringstream os(std::ios::binary); + std::vector clients = m_clients.getClientIDs(); ClientInterface::AutoLock clientlock(m_clients); for (session_t i : clients) { @@ -2303,18 +2301,20 @@ void Server::sendMetadataChanged(const std::list &meta_updates, float far if (!client) continue; - ServerActiveObject *player = m_env->getActiveObject(i); - v3f player_pos = player ? player->getBasePosition() : v3f(); + ServerActiveObject *player = getPlayerSAO(i); + v3s16 player_pos; + if (player) + player_pos = floatToInt(player->getBasePosition(), BS); - for (const v3s16 &pos : meta_updates) { + for (const v3s16 pos : positions) { NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos); if (!meta) continue; v3s16 block_pos = getNodeBlockPos(pos); - if (!client->isBlockSent(block_pos) || (player && - player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) { + if (!client->isBlockSent(block_pos) || + player_pos.getDistanceFrom(pos) > far_d_nodes) { client->SetBlockNotSent(block_pos); continue; } @@ -2326,14 +2326,15 @@ void Server::sendMetadataChanged(const std::list &meta_updates, float far continue; // Send the meta changes - std::ostringstream os(std::ios::binary); + os.str(""); meta_updates_list.serialize(os, client->serialization_version, false, true, true); - std::ostringstream oss(std::ios::binary); - compressZlib(os.str(), oss); + std::string raw = os.str(); + os.str(""); + compressZlib(raw, os); - NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0); - pkt.putLongString(oss.str()); - m_clients.send(i, 0, &pkt, true); + NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i); + pkt.putLongString(os.str()); + Send(&pkt); meta_updates_list.clear(); } diff --git a/src/server.h b/src/server.h index a9c5bcb5f..f1958701f 100644 --- a/src/server.h +++ b/src/server.h @@ -492,7 +492,7 @@ private: std::unordered_set *far_players = nullptr, float far_d_nodes = 100, bool remove_metadata = true); - void sendMetadataChanged(const std::list &meta_updates, + void sendMetadataChanged(const std::unordered_set &positions, float far_d_nodes = 100); // Environment and Connection must be locked when called From 8908a9101608d3343023b470743ef63f1c44b0b7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 15:45:34 +0200 Subject: [PATCH 23/33] Get rid of node metadata when it becomes empty fixes #8943 --- games/devtest/mods/unittests/misc.lua | 20 ++++++++++++++++++++ src/script/lua_api/l_nodemeta.cpp | 13 ++++++++----- src/script/lua_api/l_nodemeta.h | 6 ++++-- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/games/devtest/mods/unittests/misc.lua b/games/devtest/mods/unittests/misc.lua index ba980866a..4b53004b1 100644 --- a/games/devtest/mods/unittests/misc.lua +++ b/games/devtest/mods/unittests/misc.lua @@ -48,3 +48,23 @@ local function test_v3s16_metatable(player, pos) assert(vector.check(found_pos)) end unittests.register("test_v3s16_metatable", test_v3s16_metatable, {map=true}) + +local function test_clear_meta(_, pos) + local ref = core.get_meta(pos) + + for way = 1, 3 do + ref:set_string("foo", "bar") + assert(ref:contains("foo")) + + if way == 1 then + ref:from_table({}) + elseif way == 2 then + ref:from_table(nil) + else + ref:set_string("foo", "") + end + + assert(#core.find_nodes_with_meta(pos, pos) == 0, "clearing failed " .. way) + end +end +unittests.register("test_clear_meta", test_clear_meta, {map=true}) diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 1d052685e..514a1b78b 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -40,7 +40,7 @@ NodeMetaRef* NodeMetaRef::checkobject(lua_State *L, int narg) Metadata* NodeMetaRef::getmeta(bool auto_create) { if (m_is_local) - return m_meta; + return m_local_meta; NodeMetadata *meta = m_env->getMap().getNodeMetadata(m_p); if (meta == NULL && auto_create) { @@ -62,9 +62,12 @@ void NodeMetaRef::clearMeta() void NodeMetaRef::reportMetadataChange(const std::string *name) { SANITY_CHECK(!m_is_local); - // NOTE: This same code is in rollback_interface.cpp // Inform other things that the metadata has changed - NodeMetadata *meta = dynamic_cast(m_meta); + NodeMetadata *meta = dynamic_cast(getmeta(false)); + + // If the metadata is now empty, get rid of it + if (meta && meta->empty()) + clearMeta(); MapEditEvent event; event.type = MEET_BLOCK_NODE_METADATA_CHANGED; @@ -174,8 +177,8 @@ NodeMetaRef::NodeMetaRef(v3s16 p, ServerEnvironment *env): } NodeMetaRef::NodeMetaRef(Metadata *meta): - m_meta(meta), - m_is_local(true) + m_is_local(true), + m_local_meta(meta) { } diff --git a/src/script/lua_api/l_nodemeta.h b/src/script/lua_api/l_nodemeta.h index fdc1766ed..265ece3d0 100644 --- a/src/script/lua_api/l_nodemeta.h +++ b/src/script/lua_api/l_nodemeta.h @@ -33,10 +33,12 @@ class NodeMetadata; class NodeMetaRef : public MetaDataRef { private: + bool m_is_local = false; + // Set for server metadata v3s16 m_p; ServerEnvironment *m_env = nullptr; - Metadata *m_meta = nullptr; - bool m_is_local = false; + // Set for client metadata + Metadata *m_local_meta = nullptr; static const char className[]; static const luaL_Reg methodsServer[]; From 5cd7b0c6e4551fd53171fba8b30ac9f0a4e13a36 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 17:41:23 +0200 Subject: [PATCH 24/33] Remove remains of video mode querying --- doc/minetest.6 | 2 -- src/client/renderingengine.cpp | 15 --------------- src/client/renderingengine.h | 1 - 3 files changed, 18 deletions(-) diff --git a/doc/minetest.6 b/doc/minetest.6 index 6a3601f80..27a3d0024 100644 --- a/doc/minetest.6 +++ b/doc/minetest.6 @@ -92,8 +92,6 @@ Set password from contents of file .B \-\-random\-input Enable random user input, for testing (client only) .TP -.B \-\-videomodes -List available video modes (client only) .TP .B \-\-speedtests Run speed tests diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 455d5e538..7afca4500 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -638,25 +638,10 @@ float RenderingEngine::getDisplayDensity() #endif -v2u32 RenderingEngine::getDisplaySize() -{ - IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL); - - core::dimension2d deskres = - nulldevice->getVideoModeList()->getDesktopResolution(); - nulldevice->drop(); - - return deskres; -} - #else // __ANDROID__ float RenderingEngine::getDisplayDensity() { return porting::getDisplayDensity(); } -v2u32 RenderingEngine::getDisplaySize() -{ - return porting::getDisplaySize(); -} #endif // __ANDROID__ diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 6f104bba9..38420010f 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -55,7 +55,6 @@ public: static const VideoDriverInfo &getVideoDriverInfo(irr::video::E_DRIVER_TYPE type); static float getDisplayDensity(); - static v2u32 getDisplaySize(); bool setupTopLevelWindow(const std::string &name); void setupTopLevelXorgWindow(const std::string &name); From 998e4820c982c0ea9d257c15f93f1f21d85d6327 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 18:01:37 +0200 Subject: [PATCH 25/33] Fix linking with Postgres libs on older cmake versions closes #12149 --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7bba68a64..c068be575 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -136,6 +136,7 @@ if(ENABLE_POSTGRESQL) if(PostgreSQL_INCLUDE_DIR AND PostgreSQL_LIBRARY) set(PostgreSQL_FOUND TRUE) set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR}) + set(PostgreSQL_LIBRARIES ${PostgreSQL_LIBRARY}) endif() else() find_package(PostgreSQL) From 85c824ed136269ee3ee0b650406ce80c8a60c014 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 20:47:16 +0200 Subject: [PATCH 26/33] Make sure real disconnect reason isn't overwritten bug introduced in 2f32044273d107e82fb1c35d4a0f616fa480cdf0 --- src/client/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index e078dc530..93a403e81 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -803,7 +803,7 @@ void Client::deletingPeer(con::Peer *peer, bool timeout) m_access_denied = true; if (timeout) m_access_denied_reason = gettext("Connection timed out."); - else + else if (m_access_denied_reason.empty()) m_access_denied_reason = gettext("Connection aborted (protocol error?)."); } From 303329f2d6bc96f7756c72845de1bd87f62174a1 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 21:32:51 +0200 Subject: [PATCH 27/33] Handle lua entity HP changes correctly (like punches) fixes #11975 --- src/server/luaentity_sao.cpp | 24 ++++++++++++++---------- src/server/luaentity_sao.h | 7 +++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 82f6da231..a4b37ee09 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -337,19 +337,9 @@ u32 LuaEntitySAO::punch(v3f dir, if (result.did_punch) { setHP((s32)getHP() - result.damage, PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); - - // create message and add to list - sendPunchCommand(); } } - if (getHP() == 0 && !isGone()) { - clearParentAttachment(); - clearChildAttachments(); - m_env->getScriptIface()->luaentity_on_death(m_id, puncher); - markForRemoval(); - } - actionstream << puncher->getDescription() << " (id=" << puncher->getId() << ", hp=" << puncher->getHP() << ") punched " << getDescription() << " (id=" << m_id << ", hp=" << m_hp << @@ -402,6 +392,20 @@ std::string LuaEntitySAO::getDescription() void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason) { m_hp = rangelim(hp, 0, U16_MAX); + + sendPunchCommand(); + + if (m_hp == 0 && !isGone()) { + clearParentAttachment(); + clearChildAttachments(); + if (m_registered) { + ServerActiveObject *killer = nullptr; + if (reason.type == PlayerHPChangeReason::PLAYER_PUNCH) + killer = reason.object; + m_env->getScriptIface()->luaentity_on_death(m_id, killer); + } + markForRemoval(); + } } u16 LuaEntitySAO::getHP() const diff --git a/src/server/luaentity_sao.h b/src/server/luaentity_sao.h index 87b664a8b..5a5aea7a9 100644 --- a/src/server/luaentity_sao.h +++ b/src/server/luaentity_sao.h @@ -36,23 +36,30 @@ public: { } ~LuaEntitySAO(); + ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_LUAENTITY; } ActiveObjectType getSendType() const { return ACTIVEOBJECT_TYPE_GENERIC; } virtual void addedToEnvironment(u32 dtime_s); void step(float dtime, bool send_recommended); std::string getClientInitializationData(u16 protocol_version); + bool isStaticAllowed() const { return m_prop.static_save; } bool shouldUnload() const { return true; } void getStaticData(std::string *result) const; + u32 punch(v3f dir, const ToolCapabilities *toolcap = nullptr, ServerActiveObject *puncher = nullptr, float time_from_last_punch = 1000000.0f, u16 initial_wear = 0); + void rightClick(ServerActiveObject *clicker); + void setPos(const v3f &pos); void moveTo(v3f pos, bool continuous); float getMinimumSavedMovement(); + std::string getDescription(); + void setHP(s32 hp, const PlayerHPChangeReason &reason); u16 getHP() const; From 1b68fb768306ad591057b0941bc31460034a4976 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 21:36:58 +0200 Subject: [PATCH 28/33] Don't allow banning in singleplayer fixes #11819 --- builtin/game/chat.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index cfb497cb0..bbcdcf2d0 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -1187,6 +1187,9 @@ core.register_chatcommand("ban", { return true, S("Ban list: @1", ban_list) end end + if core.is_singleplayer() then + return false, S("You cannot ban players in singleplayer!") + end if not core.get_player_by_name(param) then return false, S("Player is not online.") end From bb671c308930dc4cb8d67e8eed3664a23499bd95 Mon Sep 17 00:00:00 2001 From: stefan Date: Wed, 25 May 2022 11:46:12 +0200 Subject: [PATCH 29/33] Remove debug.get/setmetatable from security whitelist fixes #12216 --- src/script/cpp_api/s_security.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index f68cd1777..88e22f16f 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -128,8 +128,6 @@ void ScriptApiSecurity::initializeSecurity() "gethook", "traceback", "getinfo", - "getmetatable", - "setmetatable", "upvalueid", "sethook", "debug", From ea74680df4340172674adfc3335e6ba887c3f79e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 22:14:22 +0200 Subject: [PATCH 30/33] Immediately activate blocks when a player joins issue: #10884 This makes it possible for objects to immediately be activated, but doesn't guarantee it since blocks may still need be emerged. --- src/serverenvironment.cpp | 8 +++++++- src/serverenvironment.h | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 630c111c5..bff563a61 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -626,6 +626,9 @@ PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player, /* Add object to environment */ addActiveObject(playersao); + // Update active blocks asap so objects in those blocks appear on the client + m_force_update_active_blocks = true; + return playersao; } @@ -1332,8 +1335,10 @@ void ServerEnvironment::step(float dtime) /* Manage active block list */ - if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) { + if (m_active_blocks_mgmt_interval.step(dtime, m_cache_active_block_mgmt_interval) || + m_force_update_active_blocks) { ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG); + /* Get player block positions */ @@ -1396,6 +1401,7 @@ void ServerEnvironment::step(float dtime) activateBlock(block); } } + m_force_update_active_blocks = false; /* Mess around in active blocks diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 5dc329a60..223cd3420 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -454,7 +454,8 @@ private: IntervalLimiter m_object_management_interval; // List of active blocks ActiveBlockList m_active_blocks; - IntervalLimiter m_active_blocks_management_interval; + bool m_force_update_active_blocks = false; + IntervalLimiter m_active_blocks_mgmt_interval; IntervalLimiter m_active_block_modifier_interval; IntervalLimiter m_active_blocks_nodemetadata_interval; // Whether the variables below have been read from file yet From c1d03695d4859ff4a531c31b6bddcd8f50384c41 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 22:22:48 +0200 Subject: [PATCH 31/33] Minor code improvements around active block keeping --- src/serverenvironment.cpp | 20 +++++++++++--------- src/serverenvironment.h | 6 ++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index bff563a61..06bfc7b98 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -378,10 +378,7 @@ void ActiveBlockList::update(std::vector &active_players, /* Update m_list */ - m_list.clear(); - for (v3s16 p : newlist) { - m_list.insert(p); - } + m_list = std::move(newlist); } /* @@ -1343,7 +1340,8 @@ void ServerEnvironment::step(float dtime) Get player block positions */ std::vector players; - for (RemotePlayer *player: m_players) { + players.reserve(m_players.size()); + for (RemotePlayer *player : m_players) { // Ignore disconnected players if (player->getPeerId() == PEER_ID_INEXISTENT) continue; @@ -1368,8 +1366,6 @@ void ServerEnvironment::step(float dtime) m_active_blocks.update(players, active_block_range, active_object_range, blocks_removed, blocks_added); - m_active_block_gauge->set(m_active_blocks.size()); - /* Handle removed blocks */ @@ -1393,13 +1389,19 @@ void ServerEnvironment::step(float dtime) for (const v3s16 &p: blocks_added) { MapBlock *block = m_map->getBlockOrEmerge(p); if (!block) { - m_active_blocks.m_list.erase(p); - m_active_blocks.m_abm_list.erase(p); + // TODO: The blocks removed here will only be picked up again + // on the next cycle. To minimize the latency of objects being + // activated we could remember the blocks pending activating + // and activate them instantly as soon as they're loaded. + m_active_blocks.remove(p); continue; } activateBlock(block); } + + // Some blocks may be removed again by the code above so do this here + m_active_block_gauge->set(m_active_blocks.size()); } m_force_update_active_blocks = false; diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 223cd3420..00184421e 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -180,8 +180,14 @@ public: m_list.clear(); } + void remove(v3s16 p) { + m_list.erase(p); + m_abm_list.erase(p); + } + std::set m_list; std::set m_abm_list; + // list of blocks that are always active, not modified by this class std::set m_forceloaded_list; }; From a9a207685a9d55e4fcdd97f5065383e2ec18e114 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 26 May 2022 22:28:33 +0200 Subject: [PATCH 32/33] Reject registering node with empty name fixes #10769 --- src/script/lua_api/l_item.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index b58b994d9..27c1b8875 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -594,6 +594,9 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) // be done if (f.name == "ignore") return 0; + // This would break everything + if (f.name.empty()) + throw LuaError("Cannot register node with empty name"); content_t id = ndef->set(f.name, f); From 9fc018ded10225589d2559d24a5db739e891fb31 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 29 May 2022 16:00:44 +0200 Subject: [PATCH 33/33] Fix use-after-free in node meta cleanup bug introduced in 8908a9101608d3343023b470743ef63f1c44b0b7 --- src/script/lua_api/l_nodemeta.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 514a1b78b..bdc4844c0 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -66,8 +66,10 @@ void NodeMetaRef::reportMetadataChange(const std::string *name) NodeMetadata *meta = dynamic_cast(getmeta(false)); // If the metadata is now empty, get rid of it - if (meta && meta->empty()) + if (meta && meta->empty()) { clearMeta(); + meta = nullptr; + } MapEditEvent event; event.type = MEET_BLOCK_NODE_METADATA_CHANGED;