Merge branch 'master' of https://github.com/minetest/minetest
commit
827b9f8d70
|
@ -132,7 +132,7 @@ jobs:
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps --old-irr clang-9
|
install_linux_deps clang-9
|
||||||
|
|
||||||
- name: Build prometheus-cpp
|
- name: Build prometheus-cpp
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -42,9 +42,10 @@ jobs:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install luarocks
|
- uses: leafo/gh-actions-lua@v9
|
||||||
run: |
|
with:
|
||||||
sudo apt-get update && sudo apt-get install -y luarocks
|
luaVersion: "5.1.5"
|
||||||
|
- uses: leafo/gh-actions-luarocks@v4
|
||||||
|
|
||||||
- name: Install luarocks tools
|
- name: Install luarocks tools
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -78,7 +78,6 @@ if(NOT "${IRRLICHTMT_BUILD_DIR}" STREQUAL "")
|
||||||
# IrrlichtMtConfig.cmake
|
# IrrlichtMtConfig.cmake
|
||||||
message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.")
|
message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.")
|
||||||
endif()
|
endif()
|
||||||
# This is done here so that relative search paths are more reasonable
|
|
||||||
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
|
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
|
||||||
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
|
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
|
||||||
if(BUILD_CLIENT)
|
if(BUILD_CLIENT)
|
||||||
|
@ -108,9 +107,9 @@ else()
|
||||||
|
|
||||||
include(MinetestFindIrrlichtHeaders)
|
include(MinetestFindIrrlichtHeaders)
|
||||||
if(NOT IRRLICHT_INCLUDE_DIR)
|
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()
|
endif()
|
||||||
message(STATUS "Found Irrlicht headers: ${IRRLICHT_INCLUDE_DIR}")
|
message(STATUS "Found IrrlichtMt headers: ${IRRLICHT_INCLUDE_DIR}")
|
||||||
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
|
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
|
# 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
|
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
|
||||||
|
|
|
@ -223,8 +223,8 @@ Run it:
|
||||||
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
|
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
|
||||||
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
|
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
|
||||||
- Debug build is slower, but gives much more useful output in a debugger.
|
- 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.
|
- 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/irrlicht/include`.
|
- 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:
|
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
|
||||||
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
|
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
|
||||||
|
|
|
@ -22,7 +22,6 @@ local LIST_FORMSPEC_DESCRIPTION = [[
|
||||||
|
|
||||||
local F = core.formspec_escape
|
local F = core.formspec_escape
|
||||||
local S = core.get_translator("__builtin")
|
local S = core.get_translator("__builtin")
|
||||||
local check_player_privs = core.check_player_privs
|
|
||||||
|
|
||||||
|
|
||||||
-- CHAT COMMANDS FORMSPEC
|
-- CHAT COMMANDS FORMSPEC
|
||||||
|
@ -58,10 +57,11 @@ local function build_chatcommands_formspec(name, sel, copy)
|
||||||
.. "any entry in the list.").. "\n" ..
|
.. "any entry in the list.").. "\n" ..
|
||||||
S("Double-click to copy the entry to the chat history.")
|
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
|
for i, data in ipairs(mod_cmds) do
|
||||||
rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
|
rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
|
||||||
for j, cmds in ipairs(data[2]) do
|
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(
|
rows[#rows + 1] = ("%s,1,%s,%s"):format(
|
||||||
has_priv and COLOR_GREEN or COLOR_GRAY,
|
has_priv and COLOR_GREEN or COLOR_GRAY,
|
||||||
cmds[1], F(cmds[2].params))
|
cmds[1], F(cmds[2].params))
|
||||||
|
|
|
@ -204,7 +204,7 @@ end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function string:trim()
|
function string:trim()
|
||||||
return (self:gsub("^%s*(.-)%s*$", "%1"))
|
return self:match("^%s*(.-)%s*$")
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
@ -245,16 +245,16 @@ function math.round(x)
|
||||||
return math.ceil(x - 0.5)
|
return math.ceil(x - 0.5)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local formspec_escapes = {
|
||||||
|
["\\"] = "\\\\",
|
||||||
|
["["] = "\\[",
|
||||||
|
["]"] = "\\]",
|
||||||
|
[";"] = "\\;",
|
||||||
|
[","] = "\\,"
|
||||||
|
}
|
||||||
function core.formspec_escape(text)
|
function core.formspec_escape(text)
|
||||||
if text ~= nil then
|
-- Use explicit character set instead of dot here because it doubles the performance
|
||||||
text = string.gsub(text,"\\","\\\\")
|
return text and text:gsub("[\\%[%];,]", formspec_escapes)
|
||||||
text = string.gsub(text,"%]","\\]")
|
|
||||||
text = string.gsub(text,"%[","\\[")
|
|
||||||
text = string.gsub(text,";","\\;")
|
|
||||||
text = string.gsub(text,",","\\,")
|
|
||||||
end
|
|
||||||
return text
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -265,18 +265,21 @@ function core.wrap_text(text, max_length, as_table)
|
||||||
return as_table and {text} or text
|
return as_table and {text} or text
|
||||||
end
|
end
|
||||||
|
|
||||||
for word in text:gmatch('%S+') do
|
local line_length = 0
|
||||||
local cur_length = #table.concat(line, ' ')
|
for word in text:gmatch("%S+") do
|
||||||
if cur_length > 0 and cur_length + #word + 1 >= max_length then
|
if line_length > 0 and line_length + #word + 1 >= max_length then
|
||||||
-- word wouldn't fit on current line, move to next line
|
-- word wouldn't fit on current line, move to next line
|
||||||
table.insert(result, table.concat(line, ' '))
|
table.insert(result, table.concat(line, " "))
|
||||||
line = {}
|
line = {word}
|
||||||
|
line_length = #word
|
||||||
|
else
|
||||||
|
table.insert(line, word)
|
||||||
|
line_length = line_length + 1 + #word
|
||||||
end
|
end
|
||||||
table.insert(line, word)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(result, table.concat(line, ' '))
|
table.insert(result, table.concat(line, " "))
|
||||||
return as_table and result or table.concat(result, '\n')
|
return as_table and result or table.concat(result, "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
@ -425,54 +428,50 @@ function core.string_to_pos(value)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local x, y, z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
value = value:match("^%((.-)%)$") or value -- strip parentheses
|
||||||
if x and y and z then
|
|
||||||
x = tonumber(x)
|
local x, y, z = value:trim():match("^([%d.-]+)[,%s]%s*([%d.-]+)[,%s]%s*([%d.-]+)$")
|
||||||
y = tonumber(y)
|
|
||||||
z = tonumber(z)
|
|
||||||
return vector.new(x, y, z)
|
|
||||||
end
|
|
||||||
x, y, z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
|
|
||||||
if x and y and z then
|
if x and y and z then
|
||||||
x = tonumber(x)
|
x = tonumber(x)
|
||||||
y = tonumber(y)
|
y = tonumber(y)
|
||||||
z = tonumber(z)
|
z = tonumber(z)
|
||||||
return vector.new(x, y, z)
|
return vector.new(x, y, z)
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function core.string_to_area(value)
|
|
||||||
local p1, p2 = unpack(value:split(") ("))
|
do
|
||||||
if p1 == nil or p2 == nil then
|
local rel_num_cap = "(~?-?%d*%.?%d*)" -- may be overly permissive as this will be tonumber'ed anyways
|
||||||
return nil
|
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
|
end
|
||||||
|
|
||||||
p1 = core.string_to_pos(p1 .. ")")
|
function core.string_to_area(value, relative_to)
|
||||||
p2 = core.string_to_pos("(" .. p2)
|
local p1, p2 = value:match("^%((.-)%)%s*%((.-)%)$")
|
||||||
if p1 == nil or p2 == nil then
|
if not p1 then
|
||||||
return nil
|
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
|
end
|
||||||
|
|
||||||
return p1, p2
|
|
||||||
end
|
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)
|
function table.copy(t, seen)
|
||||||
local n = {}
|
local n = {}
|
||||||
|
@ -786,6 +785,74 @@ function core.is_nan(number)
|
||||||
return number ~= number
|
return number ~= number
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[ Helper function for parsing an optionally relative number
|
||||||
|
of a chat command parameter, using the chat command tilde notation.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
* arg: String snippet containing the number; possible values:
|
||||||
|
* "<number>": return as number
|
||||||
|
* "~<number>": return relative_to + <number>
|
||||||
|
* "~": return relative_to
|
||||||
|
* Anything else will return `nil`
|
||||||
|
* relative_to: Number to which the `arg` number might be relative to
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A number or `nil`, depending on `arg.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* `core.parse_relative_number("5", 10)` returns 5
|
||||||
|
* `core.parse_relative_number("~5", 10)` returns 15
|
||||||
|
* `core.parse_relative_number("~", 10)` returns 10
|
||||||
|
]]
|
||||||
|
function core.parse_relative_number(arg, relative_to)
|
||||||
|
if not arg then
|
||||||
|
return nil
|
||||||
|
elseif arg == "~" then
|
||||||
|
return relative_to
|
||||||
|
elseif string.sub(arg, 1, 1) == "~" then
|
||||||
|
local number = tonumber(string.sub(arg, 2))
|
||||||
|
if not number then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if core.is_nan(number) or number == math.huge or number == -math.huge then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return relative_to + number
|
||||||
|
else
|
||||||
|
local number = tonumber(arg)
|
||||||
|
if core.is_nan(number) or number == math.huge or number == -math.huge then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return number
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[ Helper function to parse coordinates that might be relative
|
||||||
|
to another position; supports chat command tilde notation.
|
||||||
|
Intended to be used in chat command parameter parsing.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
* x, y, z: Parsed x, y, and z coordinates as strings
|
||||||
|
* relative_to: Position to which to compare the position
|
||||||
|
|
||||||
|
Syntax of x, y and z:
|
||||||
|
* "<number>": return as number
|
||||||
|
* "~<number>": return <number> + player position on this axis
|
||||||
|
* "~": return player position on this axis
|
||||||
|
|
||||||
|
Returns: a vector or nil for invalid input or if player does not exist
|
||||||
|
]]
|
||||||
|
function core.parse_coordinates(x, y, z, relative_to)
|
||||||
|
if not relative_to then
|
||||||
|
x, y, z = tonumber(x), tonumber(y), tonumber(z)
|
||||||
|
return x and y and z and { x = x, y = y, z = z }
|
||||||
|
end
|
||||||
|
local rx = core.parse_relative_number(x, relative_to.x)
|
||||||
|
local ry = core.parse_relative_number(y, relative_to.y)
|
||||||
|
local rz = core.parse_relative_number(z, relative_to.z)
|
||||||
|
return rx and ry and rz and { x = rx, y = ry, z = rz }
|
||||||
|
end
|
||||||
|
|
||||||
function core.inventorycube(img1, img2, img3)
|
function core.inventorycube(img1, img2, img3)
|
||||||
img2 = img2 or img1
|
img2 = img2 or img1
|
||||||
img3 = img3 or img1
|
img3 = img3 or img1
|
||||||
|
|
|
@ -67,9 +67,107 @@ describe("pos", function()
|
||||||
end)
|
end)
|
||||||
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()
|
describe("table", function()
|
||||||
it("indexof()", function()
|
it("indexof()", function()
|
||||||
assert.equal(1, table.indexof({"foo", "bar"}, "foo"))
|
assert.equal(1, table.indexof({"foo", "bar"}, "foo"))
|
||||||
assert.equal(-1, table.indexof({"foo", "bar"}, "baz"))
|
assert.equal(-1, table.indexof({"foo", "bar"}, "baz"))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("formspec_escape", function()
|
||||||
|
it("escapes", function()
|
||||||
|
assert.equal(nil, core.formspec_escape(nil))
|
||||||
|
assert.equal("", core.formspec_escape(""))
|
||||||
|
assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\["))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
|
@ -130,8 +130,13 @@ local function parse_range_str(player_name, str)
|
||||||
return false, S("Unable to get position of player @1.", player_name)
|
return false, S("Unable to get position of player @1.", player_name)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
p1, p2 = core.string_to_area(str)
|
local player = core.get_player_by_name(player_name)
|
||||||
if p1 == nil then
|
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. "
|
return false, S("Incorrect area format. "
|
||||||
.. "Expected: (x1,y1,z1) (x2,y2,z2)")
|
.. "Expected: (x1,y1,z1) (x2,y2,z2)")
|
||||||
end
|
end
|
||||||
|
@ -570,10 +575,15 @@ core.register_chatcommand("teleport", {
|
||||||
description = S("Teleport to position or player"),
|
description = S("Teleport to position or player"),
|
||||||
privs = {teleport=true},
|
privs = {teleport=true},
|
||||||
func = function(name, param)
|
func = function(name, param)
|
||||||
|
local player = core.get_player_by_name(name)
|
||||||
|
local relpos
|
||||||
|
if player then
|
||||||
|
relpos = player:get_pos()
|
||||||
|
end
|
||||||
local p = {}
|
local p = {}
|
||||||
p.x, p.y, p.z = param:match("^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
p.x, p.y, p.z = string.match(param, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
|
||||||
p = vector.apply(p, tonumber)
|
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
|
||||||
if p.x and p.y and p.z then
|
if p and p.x and p.y and p.z then
|
||||||
return teleport_to_pos(name, p)
|
return teleport_to_pos(name, p)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -587,9 +597,19 @@ core.register_chatcommand("teleport", {
|
||||||
"other players (missing privilege: @1).", "bring")
|
"other players (missing privilege: @1).", "bring")
|
||||||
|
|
||||||
local teleportee_name
|
local teleportee_name
|
||||||
|
p = {}
|
||||||
teleportee_name, p.x, p.y, p.z = param:match(
|
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)
|
p = vector.apply(p, tonumber)
|
||||||
|
|
||||||
if teleportee_name and p.x and p.y and p.z then
|
if teleportee_name and p.x and p.y and p.z then
|
||||||
if not has_bring_priv then
|
if not has_bring_priv then
|
||||||
return false, missing_bring_msg
|
return false, missing_bring_msg
|
||||||
|
@ -842,7 +862,7 @@ core.register_chatcommand("spawnentity", {
|
||||||
description = S("Spawn entity at given (or your) position"),
|
description = S("Spawn entity at given (or your) position"),
|
||||||
privs = {give=true, interact=true},
|
privs = {give=true, interact=true},
|
||||||
func = function(name, param)
|
func = function(name, param)
|
||||||
local entityname, p = string.match(param, "^([^ ]+) *(.*)$")
|
local entityname, pstr = string.match(param, "^([^ ]+) *(.*)$")
|
||||||
if not entityname then
|
if not entityname then
|
||||||
return false, S("EntityName required.")
|
return false, S("EntityName required.")
|
||||||
end
|
end
|
||||||
|
@ -856,11 +876,15 @@ core.register_chatcommand("spawnentity", {
|
||||||
if not core.registered_entities[entityname] then
|
if not core.registered_entities[entityname] then
|
||||||
return false, S("Cannot spawn an unknown entity.")
|
return false, S("Cannot spawn an unknown entity.")
|
||||||
end
|
end
|
||||||
if p == "" then
|
local p
|
||||||
|
if pstr == "" then
|
||||||
p = player:get_pos()
|
p = player:get_pos()
|
||||||
else
|
else
|
||||||
p = core.string_to_pos(p)
|
p = {}
|
||||||
if p == nil then
|
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)
|
return false, S("Invalid parameters (@1).", param)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1019,6 +1043,13 @@ core.register_chatcommand("status", {
|
||||||
end,
|
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", {
|
core.register_chatcommand("time", {
|
||||||
params = S("[<0..23>:<0..59> | <0..24000>]"),
|
params = S("[<0..23>:<0..59> | <0..24000>]"),
|
||||||
description = S("Show or set time of day"),
|
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 "
|
return false, S("You don't have permission to run "
|
||||||
.. "this command (missing privilege: @1).", "settime")
|
.. "this command (missing privilege: @1).", "settime")
|
||||||
end
|
end
|
||||||
local hour, minute = param:match("^(%d+):(%d+)$")
|
local relative, negative, hour, minute = param:match("^(~?)(%-?)(%d+):(%d+)$")
|
||||||
if not hour then
|
if not relative then -- checking the first capture against nil suffices
|
||||||
local new_time = tonumber(param) or -1
|
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
|
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).")
|
return false, S("Invalid time (must be between 0 and 24000).")
|
||||||
end
|
end
|
||||||
|
@ -1047,14 +1083,29 @@ core.register_chatcommand("time", {
|
||||||
core.log("action", name .. " sets time to " .. new_time)
|
core.log("action", name .. " sets time to " .. new_time)
|
||||||
return true, S("Time of day changed.")
|
return true, S("Time of day changed.")
|
||||||
end
|
end
|
||||||
|
local new_time
|
||||||
hour = tonumber(hour)
|
hour = tonumber(hour)
|
||||||
minute = tonumber(minute)
|
minute = tonumber(minute)
|
||||||
if hour < 0 or hour > 23 then
|
if relative == "" then
|
||||||
return false, S("Invalid hour (must be between 0 and 23 inclusive).")
|
if hour < 0 or hour > 23 then
|
||||||
elseif minute < 0 or minute > 59 then
|
return false, S("Invalid hour (must be between 0 and 23 inclusive).")
|
||||||
return false, S("Invalid minute (must be between 0 and 59 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
|
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))
|
core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute))
|
||||||
return true, S("Time of day changed.")
|
return true, S("Time of day changed.")
|
||||||
end,
|
end,
|
||||||
|
@ -1136,6 +1187,9 @@ core.register_chatcommand("ban", {
|
||||||
return true, S("Ban list: @1", ban_list)
|
return true, S("Ban list: @1", ban_list)
|
||||||
end
|
end
|
||||||
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
|
if not core.get_player_by_name(param) then
|
||||||
return false, S("Player is not online.")
|
return false, S("Player is not online.")
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,7 +50,7 @@ local labels = {
|
||||||
fgettext("Low"),
|
fgettext("Low"),
|
||||||
fgettext("Medium"),
|
fgettext("Medium"),
|
||||||
fgettext("High"),
|
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")) .. "]" ..
|
.. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
|
||||||
"checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
|
"checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
|
||||||
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"..
|
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"..
|
||||||
"label[8.25,3.0;" .. fgettext("Dynamic shadows: ") .. "]" ..
|
"label[8.25,2.8;" .. fgettext("Dynamic shadows:") .. "]" ..
|
||||||
"dropdown[8.25,3.5;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";"
|
"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() .. "]"
|
.. getSettingIndex.ShadowMapping() .. "]"
|
||||||
else
|
else
|
||||||
tab_string = tab_string ..
|
tab_string = tab_string ..
|
||||||
|
@ -364,11 +365,11 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
|
||||||
core.settings:set("enable_dynamic_shadows", "false")
|
core.settings:set("enable_dynamic_shadows", "false")
|
||||||
else
|
else
|
||||||
local shadow_presets = {
|
local shadow_presets = {
|
||||||
[2] = { 55, 512, "true", 0, "false" },
|
[2] = { 62, 512, "true", 0, "false" },
|
||||||
[3] = { 82, 1024, "true", 1, "false" },
|
[3] = { 93, 1024, "true", 0, "false" },
|
||||||
[4] = { 240, 2048, "true", 1, "false" },
|
[4] = { 140, 2048, "true", 1, "false" },
|
||||||
[5] = { 240, 2048, "true", 2, "true" },
|
[5] = { 210, 4096, "true", 2, "true" },
|
||||||
[6] = { 300, 4096, "true", 2, "true" },
|
[6] = { 300, 8192, "true", 2, "true" },
|
||||||
}
|
}
|
||||||
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
|
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
|
||||||
if s then
|
if s then
|
||||||
|
|
|
@ -1,21 +1,13 @@
|
||||||
# Locate Irrlicht or IrrlichtMt headers on system.
|
# Locate IrrlichtMt headers on system.
|
||||||
|
|
||||||
foreach(libname IN ITEMS IrrlichtMt Irrlicht)
|
find_path(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h
|
||||||
string(TOLOWER "${libname}" libname2)
|
DOC "Path to the directory with IrrlichtMt includes"
|
||||||
|
PATHS
|
||||||
find_path(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h
|
/usr/local/include/irrlichtmt
|
||||||
DOC "Path to the directory with IrrlichtMt includes"
|
/usr/include/irrlichtmt
|
||||||
PATHS
|
/system/develop/headers/irrlichtmt #Haiku
|
||||||
/usr/local/include/${libname2}
|
PATH_SUFFIXES "include/irrlichtmt"
|
||||||
/usr/include/${libname2}
|
)
|
||||||
/system/develop/headers/${libname2} #Haiku
|
|
||||||
PATH_SUFFIXES "include/${libname2}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(IRRLICHT_INCLUDE_DIR)
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# Handholding for users
|
# Handholding for users
|
||||||
if(IRRLICHT_INCLUDE_DIR AND (NOT IS_DIRECTORY "${IRRLICHT_INCLUDE_DIR}" OR
|
if(IRRLICHT_INCLUDE_DIR AND (NOT IS_DIRECTORY "${IRRLICHT_INCLUDE_DIR}" OR
|
||||||
|
|
|
@ -2012,7 +2012,7 @@ Example definition of the capabilities of an item
|
||||||
max_drop_level=1,
|
max_drop_level=1,
|
||||||
groupcaps={
|
groupcaps={
|
||||||
crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
|
crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
|
||||||
}
|
},
|
||||||
damage_groups = {fleshy=2},
|
damage_groups = {fleshy=2},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3552,8 +3552,16 @@ Helper functions
|
||||||
* `minetest.string_to_pos(string)`: returns a position or `nil`
|
* `minetest.string_to_pos(string)`: returns a position or `nil`
|
||||||
* Same but in reverse.
|
* Same but in reverse.
|
||||||
* If the string can't be parsed to a position, nothing is returned.
|
* 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
|
* Converts a string representing an area box into two positions
|
||||||
|
* X1, Y1, ... Z2 are coordinates
|
||||||
|
* `relative_to`: Optional. If set to a position, each coordinate
|
||||||
|
can use the tilde notation for relative positions
|
||||||
|
* Tilde notation: "~": Relative coordinate
|
||||||
|
"~<number>": Relative coordinate plus <number>
|
||||||
|
* Example: `minetest.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})`
|
||||||
|
returns `{x=1,y=2,z=3}, {x=15,y=5,z=10}`
|
||||||
* `minetest.formspec_escape(string)`: returns a string
|
* `minetest.formspec_escape(string)`: returns a string
|
||||||
* escapes the characters "[", "]", "\", "," and ";", which can not be used
|
* escapes the characters "[", "]", "\", "," and ";", which can not be used
|
||||||
in formspecs.
|
in formspecs.
|
||||||
|
@ -7093,6 +7101,8 @@ object you are working with still exists.
|
||||||
* `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness)
|
* `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.
|
* `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`.
|
* 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`
|
`PcgRandom`
|
||||||
-----------
|
-----------
|
||||||
|
@ -8554,9 +8564,8 @@ See [Decoration types]. Used by `minetest.register_decoration`.
|
||||||
|
|
||||||
spawn_by = "default:water",
|
spawn_by = "default:water",
|
||||||
-- Node (or list of nodes) that the decoration only spawns next to.
|
-- Node (or list of nodes) that the decoration only spawns next to.
|
||||||
-- Checks two horizontal planes of 8 neighbouring nodes (including
|
-- Checks the 8 neighbouring nodes on the same Y, and also the ones
|
||||||
-- diagonal neighbours), one plane level with the 'place_on' node and a
|
-- at Y+1, excluding both center nodes.
|
||||||
-- plane one node above that.
|
|
||||||
|
|
||||||
num_spawn_by = 1,
|
num_spawn_by = 1,
|
||||||
-- Number of spawn_by nodes that must be surrounding the decoration
|
-- Number of spawn_by nodes that must be surrounding the decoration
|
||||||
|
|
|
@ -92,8 +92,6 @@ Set password from contents of file
|
||||||
.B \-\-random\-input
|
.B \-\-random\-input
|
||||||
Enable random user input, for testing (client only)
|
Enable random user input, for testing (client only)
|
||||||
.TP
|
.TP
|
||||||
.B \-\-videomodes
|
|
||||||
List available video modes (client only)
|
|
||||||
.TP
|
.TP
|
||||||
.B \-\-speedtests
|
.B \-\-speedtests
|
||||||
Run speed tests
|
Run speed tests
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
local S = minetest.get_translator("testitems")
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Texture overlays for items
|
||||||
|
--
|
||||||
|
|
||||||
|
-- For the global overlay color test
|
||||||
|
local GLOBAL_COLOR_ARG = "orange"
|
||||||
|
|
||||||
|
-- Punch handler to set random color with "color" argument in item metadata
|
||||||
|
local overlay_on_use = function(itemstack, user, pointed_thing)
|
||||||
|
local meta = itemstack:get_meta()
|
||||||
|
local color = math.random(0x0, 0xFFFFFF)
|
||||||
|
local colorstr = string.format("#%06x", color)
|
||||||
|
meta:set_string("color", colorstr)
|
||||||
|
minetest.log("action", "[testitems] Color of "..itemstack:get_name().." changed to "..colorstr)
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
-- Place handler to clear item metadata color
|
||||||
|
local overlay_on_place = function(itemstack, user, pointed_thing)
|
||||||
|
local meta = itemstack:get_meta()
|
||||||
|
meta:set_string("color", "")
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_craftitem("testitems:overlay_meta", {
|
||||||
|
description = S("Texture Overlay Test Item, Meta Color") .. "\n" ..
|
||||||
|
S("Image must be a square with rainbow cross (inventory and wield)") .. "\n" ..
|
||||||
|
S("Item meta color must only change square color") .. "\n" ..
|
||||||
|
S("Punch: Set random color") .. "\n" ..
|
||||||
|
S("Place: Clear color"),
|
||||||
|
-- Base texture: A grayscale square (can be colorized)
|
||||||
|
inventory_image = "testitems_overlay_base.png",
|
||||||
|
wield_image = "testitems_overlay_base.png",
|
||||||
|
-- Overlay: A rainbow cross (NOT to be colorized!)
|
||||||
|
inventory_overlay = "testitems_overlay_overlay.png",
|
||||||
|
wield_overlay = "testitems_overlay_overlay.png",
|
||||||
|
|
||||||
|
on_use = overlay_on_use,
|
||||||
|
on_place = overlay_on_place,
|
||||||
|
on_secondary_use = overlay_on_place,
|
||||||
|
})
|
||||||
|
minetest.register_craftitem("testitems:overlay_global", {
|
||||||
|
description = S("Texture Overlay Test Item, Global Color") .. "\n" ..
|
||||||
|
S("Image must be an orange square with rainbow cross (inventory and wield)"),
|
||||||
|
-- Base texture: A grayscale square (to be colorized)
|
||||||
|
inventory_image = "testitems_overlay_base.png",
|
||||||
|
wield_image = "testitems_overlay_base.png",
|
||||||
|
-- Overlay: A rainbow cross (NOT to be colorized!)
|
||||||
|
inventory_overlay = "testitems_overlay_overlay.png",
|
||||||
|
wield_overlay = "testitems_overlay_overlay.png",
|
||||||
|
color = GLOBAL_COLOR_ARG,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
name = testitems
|
||||||
|
description = Test mod to test misc. items that are neither tools nor nodes
|
Binary file not shown.
After Width: | Height: | Size: 106 B |
Binary file not shown.
After Width: | Height: | Size: 220 B |
|
@ -8,3 +8,4 @@ dofile(path.."/properties.lua")
|
||||||
dofile(path.."/liquids.lua")
|
dofile(path.."/liquids.lua")
|
||||||
dofile(path.."/light.lua")
|
dofile(path.."/light.lua")
|
||||||
dofile(path.."/textures.lua")
|
dofile(path.."/textures.lua")
|
||||||
|
dofile(path.."/overlays.lua")
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
local S = minetest.get_translator("testnodes")
|
||||||
|
|
||||||
|
minetest.register_node("testnodes:overlay", {
|
||||||
|
description = S("Texture Overlay Test Node") .. "\n" ..
|
||||||
|
S("Uncolorized"),
|
||||||
|
tiles = {{name = "testnodes_overlayable.png"}},
|
||||||
|
overlay_tiles = {{name = "testnodes_overlay.png"}},
|
||||||
|
groups = { dig_immediate = 2 },
|
||||||
|
})
|
||||||
|
minetest.register_node("testnodes:overlay_color_all", {
|
||||||
|
description = S("Texture Overlay Test Node, Colorized") .. "\n" ..
|
||||||
|
S("param2 changes color"),
|
||||||
|
tiles = {{name = "testnodes_overlayable.png"}},
|
||||||
|
overlay_tiles = {{name = "testnodes_overlay.png"}},
|
||||||
|
paramtype2 = "color",
|
||||||
|
palette = "testnodes_palette_full.png",
|
||||||
|
|
||||||
|
|
||||||
|
groups = { dig_immediate = 2 },
|
||||||
|
})
|
||||||
|
minetest.register_node("testnodes:overlay_color_overlay", {
|
||||||
|
description = S("Texture Overlay Test Node, Colorized Overlay") .. "\n" ..
|
||||||
|
S("param2 changes color of overlay"),
|
||||||
|
tiles = {{name = "testnodes_overlayable.png", color="white"}},
|
||||||
|
overlay_tiles = {{name = "testnodes_overlay.png"}},
|
||||||
|
paramtype2 = "color",
|
||||||
|
palette = "testnodes_palette_full.png",
|
||||||
|
|
||||||
|
|
||||||
|
groups = { dig_immediate = 2 },
|
||||||
|
})
|
||||||
|
minetest.register_node("testnodes:overlay_color_overlayed", {
|
||||||
|
description = S("Texture Overlay Test Node, Colorized Base") .. "\n" ..
|
||||||
|
S("param2 changes color of base texture"),
|
||||||
|
tiles = {{name = "testnodes_overlayable.png"}},
|
||||||
|
overlay_tiles = {{name = "testnodes_overlay.png", color="white"}},
|
||||||
|
paramtype2 = "color",
|
||||||
|
palette = "testnodes_palette_full.png",
|
||||||
|
|
||||||
|
|
||||||
|
groups = { dig_immediate = 2 },
|
||||||
|
})
|
||||||
|
|
||||||
|
local global_overlay_color = "#FF2000"
|
||||||
|
minetest.register_node("testnodes:overlay_global", {
|
||||||
|
description = S("Texture Overlay Test Node, Global Color") .. "\n" ..
|
||||||
|
S("Global color = @1", global_overlay_color),
|
||||||
|
tiles = {{name = "testnodes_overlayable.png"}},
|
||||||
|
overlay_tiles = {{name = "testnodes_overlay.png"}},
|
||||||
|
color = global_overlay_color,
|
||||||
|
|
||||||
|
|
||||||
|
groups = { dig_immediate = 2 },
|
||||||
|
})
|
||||||
|
minetest.register_node("testnodes:overlay_global_color_all", {
|
||||||
|
description = S("Texture Overlay Test Node, Global Color + Colorized") .. "\n" ..
|
||||||
|
S("Global color = @1", global_overlay_color) .. "\n" ..
|
||||||
|
S("param2 changes color"),
|
||||||
|
tiles = {{name = "testnodes_overlayable.png"}},
|
||||||
|
overlay_tiles = {{name = "testnodes_overlay.png"}},
|
||||||
|
color = global_overlay_color,
|
||||||
|
paramtype2 = "color",
|
||||||
|
palette = "testnodes_palette_full.png",
|
||||||
|
|
||||||
|
|
||||||
|
groups = { dig_immediate = 2 },
|
||||||
|
})
|
||||||
|
minetest.register_node("testnodes:overlay_global_color_overlay", {
|
||||||
|
description = S("Texture Overlay Test Node, Global Color + Colorized Overlay") .. "\n" ..
|
||||||
|
S("Global color = @1", global_overlay_color) .. "\n" ..
|
||||||
|
S("param2 changes color of overlay"),
|
||||||
|
tiles = {{name = "testnodes_overlayable.png", color=global_overlay_color}},
|
||||||
|
overlay_tiles = {{name = "testnodes_overlay.png"}},
|
||||||
|
color = global_overlay_color,
|
||||||
|
paramtype2 = "color",
|
||||||
|
palette = "testnodes_palette_full.png",
|
||||||
|
|
||||||
|
|
||||||
|
groups = { dig_immediate = 2 },
|
||||||
|
})
|
||||||
|
minetest.register_node("testnodes:overlay_global_color_overlayed", {
|
||||||
|
description = S("Texture Overlay Test Node, Global Color + Colorized Base") .. "\n" ..
|
||||||
|
S("Global color = @1", global_overlay_color) .. "\n" ..
|
||||||
|
S("param2 changes color of base texture"),
|
||||||
|
tiles = {{name = "testnodes_overlayable.png"}},
|
||||||
|
overlay_tiles = {{name = "testnodes_overlay.png", color=global_overlay_color}},
|
||||||
|
color = global_overlay_color,
|
||||||
|
paramtype2 = "color",
|
||||||
|
palette = "testnodes_palette_full.png",
|
||||||
|
|
||||||
|
|
||||||
|
groups = { dig_immediate = 2 },
|
||||||
|
})
|
Binary file not shown.
After Width: | Height: | Size: 153 B |
Binary file not shown.
After Width: | Height: | Size: 87 B |
|
@ -48,3 +48,23 @@ local function test_v3s16_metatable(player, pos)
|
||||||
assert(vector.check(found_pos))
|
assert(vector.check(found_pos))
|
||||||
end
|
end
|
||||||
unittests.register("test_v3s16_metatable", test_v3s16_metatable, {map=true})
|
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})
|
||||||
|
|
|
@ -136,6 +136,7 @@ if(ENABLE_POSTGRESQL)
|
||||||
if(PostgreSQL_INCLUDE_DIR AND PostgreSQL_LIBRARY)
|
if(PostgreSQL_INCLUDE_DIR AND PostgreSQL_LIBRARY)
|
||||||
set(PostgreSQL_FOUND TRUE)
|
set(PostgreSQL_FOUND TRUE)
|
||||||
set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR})
|
set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR})
|
||||||
|
set(PostgreSQL_LIBRARIES ${PostgreSQL_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
find_package(PostgreSQL)
|
find_package(PostgreSQL)
|
||||||
|
|
|
@ -533,6 +533,7 @@ void Client::step(float dtime)
|
||||||
{
|
{
|
||||||
int num_processed_meshes = 0;
|
int num_processed_meshes = 0;
|
||||||
std::vector<v3s16> blocks_to_ack;
|
std::vector<v3s16> blocks_to_ack;
|
||||||
|
bool force_update_shadows = false;
|
||||||
while (!m_mesh_update_thread.m_queue_out.empty())
|
while (!m_mesh_update_thread.m_queue_out.empty())
|
||||||
{
|
{
|
||||||
num_processed_meshes++;
|
num_processed_meshes++;
|
||||||
|
@ -559,9 +560,11 @@ void Client::step(float dtime)
|
||||||
|
|
||||||
if (is_empty)
|
if (is_empty)
|
||||||
delete r.mesh;
|
delete r.mesh;
|
||||||
else
|
else {
|
||||||
// Replace with the new mesh
|
// Replace with the new mesh
|
||||||
block->mesh = r.mesh;
|
block->mesh = r.mesh;
|
||||||
|
force_update_shadows = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
delete r.mesh;
|
delete r.mesh;
|
||||||
|
@ -586,6 +589,10 @@ void Client::step(float dtime)
|
||||||
|
|
||||||
if (num_processed_meshes > 0)
|
if (num_processed_meshes > 0)
|
||||||
g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -799,7 +806,7 @@ void Client::deletingPeer(con::Peer *peer, bool timeout)
|
||||||
m_access_denied = true;
|
m_access_denied = true;
|
||||||
if (timeout)
|
if (timeout)
|
||||||
m_access_denied_reason = gettext("Connection timed out.");
|
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?).");
|
m_access_denied_reason = gettext("Connection aborted (protocol error?).");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1417,30 +1417,22 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||||
|
|
||||||
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
|
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
|
||||||
buf->Material = material;
|
buf->Material = material;
|
||||||
switch (p.layer.material_type) {
|
if (p.layer.isTransparent()) {
|
||||||
// list of transparent materials taken from tile.h
|
buf->append(&p.vertices[0], p.vertices.size(), nullptr, 0);
|
||||||
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);
|
|
||||||
|
|
||||||
MeshTriangle t;
|
MeshTriangle t;
|
||||||
t.buffer = buf;
|
t.buffer = buf;
|
||||||
for (u32 i = 0; i < p.indices.size(); i += 3) {
|
m_transparent_triangles.reserve(p.indices.size() / 3);
|
||||||
t.p1 = p.indices[i];
|
for (u32 i = 0; i < p.indices.size(); i += 3) {
|
||||||
t.p2 = p.indices[i + 1];
|
t.p1 = p.indices[i];
|
||||||
t.p3 = p.indices[i + 2];
|
t.p2 = p.indices[i + 1];
|
||||||
t.updateAttributes();
|
t.p3 = p.indices[i + 2];
|
||||||
m_transparent_triangles.push_back(t);
|
t.updateAttributes();
|
||||||
}
|
m_transparent_triangles.push_back(t);
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
default:
|
|
||||||
buf->append(&p.vertices[0], p.vertices.size(),
|
buf->append(&p.vertices[0], p.vertices.size(),
|
||||||
&p.indices[0], p.indices.size());
|
&p.indices[0], p.indices.size());
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
mesh->addMeshBuffer(buf);
|
mesh->addMeshBuffer(buf);
|
||||||
buf->drop();
|
buf->drop();
|
||||||
|
|
|
@ -637,25 +637,10 @@ float RenderingEngine::getDisplayDensity()
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
v2u32 RenderingEngine::getDisplaySize()
|
|
||||||
{
|
|
||||||
IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
|
|
||||||
|
|
||||||
core::dimension2d<u32> deskres =
|
|
||||||
nulldevice->getVideoModeList()->getDesktopResolution();
|
|
||||||
nulldevice->drop();
|
|
||||||
|
|
||||||
return deskres;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // __ANDROID__
|
#else // __ANDROID__
|
||||||
float RenderingEngine::getDisplayDensity()
|
float RenderingEngine::getDisplayDensity()
|
||||||
{
|
{
|
||||||
return porting::getDisplayDensity();
|
return porting::getDisplayDensity();
|
||||||
}
|
}
|
||||||
|
|
||||||
v2u32 RenderingEngine::getDisplaySize()
|
|
||||||
{
|
|
||||||
return porting::getDisplaySize();
|
|
||||||
}
|
|
||||||
#endif // __ANDROID__
|
#endif // __ANDROID__
|
||||||
|
|
|
@ -55,7 +55,6 @@ public:
|
||||||
|
|
||||||
static const VideoDriverInfo &getVideoDriverInfo(irr::video::E_DRIVER_TYPE type);
|
static const VideoDriverInfo &getVideoDriverInfo(irr::video::E_DRIVER_TYPE type);
|
||||||
static float getDisplayDensity();
|
static float getDisplayDensity();
|
||||||
static v2u32 getDisplaySize();
|
|
||||||
|
|
||||||
bool setupTopLevelWindow(const std::string &name);
|
bool setupTopLevelWindow(const std::string &name);
|
||||||
void setupTopLevelXorgWindow(const std::string &name);
|
void setupTopLevelXorgWindow(const std::string &name);
|
||||||
|
|
|
@ -27,10 +27,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
using m4f = core::matrix4;
|
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)
|
void DirectionalLight::createSplitMatrices(const Camera *cam)
|
||||||
{
|
{
|
||||||
|
const float DISTANCE_STEP = BS * 2.0; // 2 meters
|
||||||
v3f newCenter;
|
v3f newCenter;
|
||||||
v3f look = cam->getDirection();
|
v3f look = cam->getDirection();
|
||||||
|
look = quantizeDirection(look, M_PI / 12.0); // 15 degrees
|
||||||
|
|
||||||
// camera view tangents
|
// camera view tangents
|
||||||
float tanFovY = tanf(cam->getFovY() * 0.5f);
|
float tanFovY = tanf(cam->getFovY() * 0.5f);
|
||||||
|
@ -42,6 +56,10 @@ void DirectionalLight::createSplitMatrices(const Camera *cam)
|
||||||
|
|
||||||
// adjusted camera positions
|
// adjusted camera positions
|
||||||
v3f cam_pos_world = cam->getPosition();
|
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,
|
v3f cam_pos_scene = v3f(cam_pos_world.X - cam->getOffset().X * BS,
|
||||||
cam_pos_world.Y - cam->getOffset().Y * BS,
|
cam_pos_world.Y - cam->getOffset().Y * BS,
|
||||||
cam_pos_world.Z - cam->getOffset().Z * 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;
|
v3f boundVec = (cam_pos_scene + farCorner * sfFar) - center_scene;
|
||||||
float radius = boundVec.getLength();
|
float radius = boundVec.getLength();
|
||||||
float length = radius * 3.0f;
|
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
|
// we must compute the viewmat with the position - the camera offset
|
||||||
// but the future_frustum position must be the actual world position
|
// but the future_frustum position must be the actual world position
|
||||||
|
|
|
@ -242,7 +242,7 @@ void ShadowRenderer::updateSMTextures()
|
||||||
|
|
||||||
// detect if SM should be regenerated
|
// detect if SM should be regenerated
|
||||||
for (DirectionalLight &light : m_light_list) {
|
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;
|
light.should_update_map_shadow = false;
|
||||||
m_current_frame = 0;
|
m_current_frame = 0;
|
||||||
reset_sm_texture = true;
|
reset_sm_texture = true;
|
||||||
|
@ -271,14 +271,14 @@ void ShadowRenderer::updateSMTextures()
|
||||||
// should put some gl* fn here
|
// 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,
|
m_driver->setRenderTarget(shadowMapTargetTexture, reset_sm_texture, true,
|
||||||
video::SColor(255, 255, 255, 255));
|
video::SColor(255, 255, 255, 255));
|
||||||
renderShadowMap(shadowMapTargetTexture, light);
|
renderShadowMap(shadowMapTargetTexture, light);
|
||||||
|
|
||||||
// Render transparent part in one pass.
|
// Render transparent part in one pass.
|
||||||
// This is also handled in ClientMap.
|
// 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) {
|
if (m_shadow_map_colored) {
|
||||||
m_driver->setRenderTarget(0, false, false);
|
m_driver->setRenderTarget(0, false, false);
|
||||||
m_driver->setRenderTarget(shadowMapTextureColors,
|
m_driver->setRenderTarget(shadowMapTextureColors,
|
||||||
|
@ -298,7 +298,7 @@ void ShadowRenderer::updateSMTextures()
|
||||||
++m_current_frame;
|
++m_current_frame;
|
||||||
|
|
||||||
// pass finished, swap textures and commit light changes
|
// 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)
|
if (shadowMapClientMapFuture != nullptr)
|
||||||
std::swap(shadowMapClientMapFuture, shadowMapClientMap);
|
std::swap(shadowMapClientMapFuture, shadowMapClientMap);
|
||||||
|
|
||||||
|
@ -306,6 +306,7 @@ void ShadowRenderer::updateSMTextures()
|
||||||
for (DirectionalLight &light : m_light_list)
|
for (DirectionalLight &light : m_light_list)
|
||||||
light.commitFrustum();
|
light.commitFrustum();
|
||||||
}
|
}
|
||||||
|
m_force_update_shadow_map = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +433,10 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target,
|
||||||
m_driver->setTransform(video::ETS_WORLD,
|
m_driver->setTransform(video::ETS_WORLD,
|
||||||
map_node->getAbsoluteTransformation());
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,7 @@ public:
|
||||||
void removeNodeFromShadowList(scene::ISceneNode *node);
|
void removeNodeFromShadowList(scene::ISceneNode *node);
|
||||||
|
|
||||||
void update(video::ITexture *outputTarget = nullptr);
|
void update(video::ITexture *outputTarget = nullptr);
|
||||||
|
void setForceUpdateShadowMap() { m_force_update_shadow_map = true; }
|
||||||
void drawDebug();
|
void drawDebug();
|
||||||
|
|
||||||
video::ITexture *get_texture()
|
video::ITexture *get_texture()
|
||||||
|
@ -131,6 +132,7 @@ private:
|
||||||
bool m_shadows_enabled;
|
bool m_shadows_enabled;
|
||||||
bool m_shadows_supported;
|
bool m_shadows_supported;
|
||||||
bool m_shadow_map_colored;
|
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_map_shadow_update_frames; /* Use this number of frames to update map shaodw */
|
||||||
u8 m_current_frame{0}; /* Current frame */
|
u8 m_current_frame{0}; /* Current frame */
|
||||||
f32 m_perspective_bias_xy;
|
f32 m_perspective_bias_xy;
|
||||||
|
|
|
@ -252,13 +252,11 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
m_jni_field_name = field_name;
|
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()));
|
std::string label = wide_to_utf8(getLabelByID(hovered->getID()));
|
||||||
if (label.empty())
|
if (label.empty())
|
||||||
label = "text";
|
label = "text";
|
||||||
message += strgettext(label) + ":";
|
/*~ Imperative, as in "Type in text" */
|
||||||
|
std::string message = fmtgettext("Enter %s:");
|
||||||
|
|
||||||
// single line text input
|
// single line text input
|
||||||
int type = 2;
|
int type = 2;
|
||||||
|
|
|
@ -152,12 +152,14 @@ void Logger::addOutput(ILogOutput *out)
|
||||||
|
|
||||||
void Logger::addOutput(ILogOutput *out, LogLevel lev)
|
void Logger::addOutput(ILogOutput *out, LogLevel lev)
|
||||||
{
|
{
|
||||||
|
MutexAutoLock lock(m_mutex);
|
||||||
m_outputs[lev].push_back(out);
|
m_outputs[lev].push_back(out);
|
||||||
m_has_outputs[lev] = true;
|
m_has_outputs[lev] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask)
|
void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask)
|
||||||
{
|
{
|
||||||
|
MutexAutoLock lock(m_mutex);
|
||||||
for (size_t i = 0; i < LL_MAX; i++) {
|
for (size_t i = 0; i < LL_MAX; i++) {
|
||||||
if (mask & LOGLEVEL_TO_MASKLEVEL(i)) {
|
if (mask & LOGLEVEL_TO_MASKLEVEL(i)) {
|
||||||
m_outputs[i].push_back(out);
|
m_outputs[i].push_back(out);
|
||||||
|
@ -168,6 +170,7 @@ void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask)
|
||||||
|
|
||||||
void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev)
|
void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev)
|
||||||
{
|
{
|
||||||
|
MutexAutoLock lock(m_mutex);
|
||||||
assert(lev < LL_MAX);
|
assert(lev < LL_MAX);
|
||||||
for (size_t i = 0; i <= lev; i++) {
|
for (size_t i = 0; i <= lev; i++) {
|
||||||
m_outputs[i].push_back(out);
|
m_outputs[i].push_back(out);
|
||||||
|
@ -177,6 +180,7 @@ void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev)
|
||||||
|
|
||||||
LogLevelMask Logger::removeOutput(ILogOutput *out)
|
LogLevelMask Logger::removeOutput(ILogOutput *out)
|
||||||
{
|
{
|
||||||
|
MutexAutoLock lock(m_mutex);
|
||||||
LogLevelMask ret_mask = 0;
|
LogLevelMask ret_mask = 0;
|
||||||
for (size_t i = 0; i < LL_MAX; i++) {
|
for (size_t i = 0; i < LL_MAX; i++) {
|
||||||
std::vector<ILogOutput *>::iterator it;
|
std::vector<ILogOutput *>::iterator it;
|
||||||
|
@ -386,6 +390,6 @@ void LogOutputBuffer::logRaw(LogLevel lev, const std::string &line)
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MutexAutoLock lock(m_buffer_mutex);
|
||||||
m_buffer.push(color.append(line));
|
m_buffer.emplace(color.append(line));
|
||||||
}
|
}
|
||||||
|
|
12
src/log.h
12
src/log.h
|
@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#if !defined(_WIN32) // POSIX
|
#if !defined(_WIN32) // POSIX
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include "threading/mutex_auto_lock.h"
|
||||||
#include "util/basic_macros.h"
|
#include "util/basic_macros.h"
|
||||||
#include "util/stream.h"
|
#include "util/stream.h"
|
||||||
#include "irrlichttypes.h"
|
#include "irrlichttypes.h"
|
||||||
|
@ -168,24 +169,31 @@ public:
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
|
MutexAutoLock lock(m_buffer_mutex);
|
||||||
m_buffer = std::queue<std::string>();
|
m_buffer = std::queue<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const
|
||||||
{
|
{
|
||||||
|
MutexAutoLock lock(m_buffer_mutex);
|
||||||
return m_buffer.empty();
|
return m_buffer.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get()
|
std::string get()
|
||||||
{
|
{
|
||||||
if (empty())
|
MutexAutoLock lock(m_buffer_mutex);
|
||||||
|
if (m_buffer.empty())
|
||||||
return "";
|
return "";
|
||||||
std::string s = m_buffer.front();
|
std::string s = std::move(m_buffer.front());
|
||||||
m_buffer.pop();
|
m_buffer.pop();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// g_logger serializes calls to logRaw() with a mutex, but that
|
||||||
|
// doesn't prevent get() / clear() from being called on top of it.
|
||||||
|
// This mutex prevents that.
|
||||||
|
mutable std::mutex m_buffer_mutex;
|
||||||
std::queue<std::string> m_buffer;
|
std::queue<std::string> m_buffer;
|
||||||
Logger &m_logger;
|
Logger &m_logger;
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,7 +102,7 @@ MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
|
||||||
return sector;
|
return sector;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
|
auto n = m_sectors.find(p);
|
||||||
|
|
||||||
if (n == m_sectors.end())
|
if (n == m_sectors.end())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -268,7 +268,7 @@ protected:
|
||||||
|
|
||||||
std::set<MapEventReceiver*> m_event_receivers;
|
std::set<MapEventReceiver*> m_event_receivers;
|
||||||
|
|
||||||
std::map<v2s16, MapSector*> m_sectors;
|
std::unordered_map<v2s16, MapSector*> m_sectors;
|
||||||
|
|
||||||
// Be sure to set this to NULL when the cached sector is deleted
|
// Be sure to set this to NULL when the cached sector is deleted
|
||||||
MapSector *m_sector_cache = nullptr;
|
MapSector *m_sector_cache = nullptr;
|
||||||
|
|
|
@ -221,33 +221,36 @@ void MapBlock::expireDayNightDiff()
|
||||||
/*
|
/*
|
||||||
Serialization
|
Serialization
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// List relevant id-name pairs for ids in the block using nodedef
|
// List relevant id-name pairs for ids in the block using nodedef
|
||||||
// Renumbers the content IDs (starting at 0 and incrementing
|
// 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];
|
|
||||||
static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
|
static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
|
||||||
const NodeDefManager *nodedef)
|
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<content_t[]> mapping;
|
||||||
|
static_assert(sizeof(content_t) == 2, "content_t must be 16-bit");
|
||||||
|
if (!mapping)
|
||||||
|
mapping = std::make_unique<content_t[]>(USHRT_MAX + 1);
|
||||||
|
|
||||||
std::set<content_t> unknown_contents;
|
memset(mapping.get(), 0xFF, (USHRT_MAX + 1) * sizeof(content_t));
|
||||||
|
|
||||||
|
std::unordered_set<content_t> unknown_contents;
|
||||||
content_t id_counter = 0;
|
content_t id_counter = 0;
|
||||||
for (u32 i = 0; i < MapBlock::nodecount; i++) {
|
for (u32 i = 0; i < MapBlock::nodecount; i++) {
|
||||||
content_t global_id = nodes[i].getContent();
|
content_t global_id = nodes[i].getContent();
|
||||||
content_t id = CONTENT_IGNORE;
|
content_t id = CONTENT_IGNORE;
|
||||||
|
|
||||||
// Try to find an existing mapping
|
// Try to find an existing mapping
|
||||||
if (getBlockNodeIdMapping_mapping[global_id] != 0xFFFF) {
|
if (mapping[global_id] != 0xFFFF) {
|
||||||
id = getBlockNodeIdMapping_mapping[global_id];
|
id = mapping[global_id];
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// We have to assign a new mapping
|
// We have to assign a new mapping
|
||||||
id = id_counter++;
|
id = id_counter++;
|
||||||
getBlockNodeIdMapping_mapping[global_id] = id;
|
mapping[global_id] = id;
|
||||||
|
|
||||||
const ContentFeatures &f = nodedef->get(global_id);
|
const ContentFeatures &f = nodedef->get(global_id);
|
||||||
const std::string &name = f.name;
|
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;
|
<< "Name for node id " << unknown_content << " not known" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct ids in the block to match nodedef based on names.
|
// Correct ids in the block to match nodedef based on names.
|
||||||
// Unknown ones are added to nodedef.
|
// Unknown ones are added to nodedef.
|
||||||
// Will not update itself to match id-name pairs in nodedef.
|
// Will not update itself to match id-name pairs in nodedef.
|
||||||
|
|
|
@ -266,10 +266,12 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
|
||||||
std::vector<aabb3f> &boxes = *p_boxes;
|
std::vector<aabb3f> &boxes = *p_boxes;
|
||||||
|
|
||||||
if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
|
if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
|
||||||
const std::vector<aabb3f> &fixed = nodebox.fixed;
|
const auto &fixed = nodebox.fixed;
|
||||||
int facedir = n.getFaceDir(nodemgr, true);
|
int facedir = n.getFaceDir(nodemgr, true);
|
||||||
u8 axisdir = facedir>>2;
|
u8 axisdir = facedir>>2;
|
||||||
facedir&=0x03;
|
facedir&=0x03;
|
||||||
|
|
||||||
|
boxes.reserve(boxes.size() + fixed.size());
|
||||||
for (aabb3f box : fixed) {
|
for (aabb3f box : fixed) {
|
||||||
if (nodebox.type == NODEBOX_LEVELED)
|
if (nodebox.type == NODEBOX_LEVELED)
|
||||||
box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS;
|
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();
|
size_t boxes_size = boxes.size();
|
||||||
boxes_size += nodebox.fixed.size();
|
boxes_size += nodebox.fixed.size();
|
||||||
|
const auto &c = nodebox.getConnected();
|
||||||
|
|
||||||
if (neighbors & 1)
|
if (neighbors & 1)
|
||||||
boxes_size += nodebox.connect_top.size();
|
boxes_size += c.connect_top.size();
|
||||||
else
|
else
|
||||||
boxes_size += nodebox.disconnected_top.size();
|
boxes_size += c.disconnected_top.size();
|
||||||
|
|
||||||
if (neighbors & 2)
|
if (neighbors & 2)
|
||||||
boxes_size += nodebox.connect_bottom.size();
|
boxes_size += c.connect_bottom.size();
|
||||||
else
|
else
|
||||||
boxes_size += nodebox.disconnected_bottom.size();
|
boxes_size += c.disconnected_bottom.size();
|
||||||
|
|
||||||
if (neighbors & 4)
|
if (neighbors & 4)
|
||||||
boxes_size += nodebox.connect_front.size();
|
boxes_size += c.connect_front.size();
|
||||||
else
|
else
|
||||||
boxes_size += nodebox.disconnected_front.size();
|
boxes_size += c.disconnected_front.size();
|
||||||
|
|
||||||
if (neighbors & 8)
|
if (neighbors & 8)
|
||||||
boxes_size += nodebox.connect_left.size();
|
boxes_size += c.connect_left.size();
|
||||||
else
|
else
|
||||||
boxes_size += nodebox.disconnected_left.size();
|
boxes_size += c.disconnected_left.size();
|
||||||
|
|
||||||
if (neighbors & 16)
|
if (neighbors & 16)
|
||||||
boxes_size += nodebox.connect_back.size();
|
boxes_size += c.connect_back.size();
|
||||||
else
|
else
|
||||||
boxes_size += nodebox.disconnected_back.size();
|
boxes_size += c.disconnected_back.size();
|
||||||
|
|
||||||
if (neighbors & 32)
|
if (neighbors & 32)
|
||||||
boxes_size += nodebox.connect_right.size();
|
boxes_size += c.connect_right.size();
|
||||||
else
|
else
|
||||||
boxes_size += nodebox.disconnected_right.size();
|
boxes_size += c.disconnected_right.size();
|
||||||
|
|
||||||
if (neighbors == 0)
|
if (neighbors == 0)
|
||||||
boxes_size += nodebox.disconnected.size();
|
boxes_size += c.disconnected.size();
|
||||||
|
|
||||||
if (neighbors < 4)
|
if (neighbors < 4)
|
||||||
boxes_size += nodebox.disconnected_sides.size();
|
boxes_size += c.disconnected_sides.size();
|
||||||
|
|
||||||
boxes.reserve(boxes_size);
|
boxes.reserve(boxes_size);
|
||||||
|
|
||||||
|
@ -484,47 +488,47 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
|
||||||
BOXESPUSHBACK(nodebox.fixed);
|
BOXESPUSHBACK(nodebox.fixed);
|
||||||
|
|
||||||
if (neighbors & 1) {
|
if (neighbors & 1) {
|
||||||
BOXESPUSHBACK(nodebox.connect_top);
|
BOXESPUSHBACK(c.connect_top);
|
||||||
} else {
|
} else {
|
||||||
BOXESPUSHBACK(nodebox.disconnected_top);
|
BOXESPUSHBACK(c.disconnected_top);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neighbors & 2) {
|
if (neighbors & 2) {
|
||||||
BOXESPUSHBACK(nodebox.connect_bottom);
|
BOXESPUSHBACK(c.connect_bottom);
|
||||||
} else {
|
} else {
|
||||||
BOXESPUSHBACK(nodebox.disconnected_bottom);
|
BOXESPUSHBACK(c.disconnected_bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neighbors & 4) {
|
if (neighbors & 4) {
|
||||||
BOXESPUSHBACK(nodebox.connect_front);
|
BOXESPUSHBACK(c.connect_front);
|
||||||
} else {
|
} else {
|
||||||
BOXESPUSHBACK(nodebox.disconnected_front);
|
BOXESPUSHBACK(c.disconnected_front);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neighbors & 8) {
|
if (neighbors & 8) {
|
||||||
BOXESPUSHBACK(nodebox.connect_left);
|
BOXESPUSHBACK(c.connect_left);
|
||||||
} else {
|
} else {
|
||||||
BOXESPUSHBACK(nodebox.disconnected_left);
|
BOXESPUSHBACK(c.disconnected_left);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neighbors & 16) {
|
if (neighbors & 16) {
|
||||||
BOXESPUSHBACK(nodebox.connect_back);
|
BOXESPUSHBACK(c.connect_back);
|
||||||
} else {
|
} else {
|
||||||
BOXESPUSHBACK(nodebox.disconnected_back);
|
BOXESPUSHBACK(c.disconnected_back);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neighbors & 32) {
|
if (neighbors & 32) {
|
||||||
BOXESPUSHBACK(nodebox.connect_right);
|
BOXESPUSHBACK(c.connect_right);
|
||||||
} else {
|
} else {
|
||||||
BOXESPUSHBACK(nodebox.disconnected_right);
|
BOXESPUSHBACK(c.disconnected_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neighbors == 0) {
|
if (neighbors == 0) {
|
||||||
BOXESPUSHBACK(nodebox.disconnected);
|
BOXESPUSHBACK(c.disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neighbors < 4) {
|
if (neighbors < 4) {
|
||||||
BOXESPUSHBACK(nodebox.disconnected_sides);
|
BOXESPUSHBACK(c.disconnected_sides);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
130
src/nodedef.cpp
130
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_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);
|
wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
|
||||||
// no default for other parts
|
// no default for other parts
|
||||||
connect_top.clear();
|
connected.reset();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
|
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.MinEdge);
|
||||||
writeV3F32(os, wall_side.MaxEdge);
|
writeV3F32(os, wall_side.MaxEdge);
|
||||||
break;
|
break;
|
||||||
case NODEBOX_CONNECTED:
|
case NODEBOX_CONNECTED: {
|
||||||
writeU8(os, type);
|
writeU8(os, type);
|
||||||
|
|
||||||
#define WRITEBOX(box) \
|
#define WRITEBOX(box) \
|
||||||
|
@ -109,22 +96,25 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
|
||||||
writeV3F32(os, i.MaxEdge); \
|
writeV3F32(os, i.MaxEdge); \
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const auto &c = getConnected();
|
||||||
|
|
||||||
WRITEBOX(fixed);
|
WRITEBOX(fixed);
|
||||||
WRITEBOX(connect_top);
|
WRITEBOX(c.connect_top);
|
||||||
WRITEBOX(connect_bottom);
|
WRITEBOX(c.connect_bottom);
|
||||||
WRITEBOX(connect_front);
|
WRITEBOX(c.connect_front);
|
||||||
WRITEBOX(connect_left);
|
WRITEBOX(c.connect_left);
|
||||||
WRITEBOX(connect_back);
|
WRITEBOX(c.connect_back);
|
||||||
WRITEBOX(connect_right);
|
WRITEBOX(c.connect_right);
|
||||||
WRITEBOX(disconnected_top);
|
WRITEBOX(c.disconnected_top);
|
||||||
WRITEBOX(disconnected_bottom);
|
WRITEBOX(c.disconnected_bottom);
|
||||||
WRITEBOX(disconnected_front);
|
WRITEBOX(c.disconnected_front);
|
||||||
WRITEBOX(disconnected_left);
|
WRITEBOX(c.disconnected_left);
|
||||||
WRITEBOX(disconnected_back);
|
WRITEBOX(c.disconnected_back);
|
||||||
WRITEBOX(disconnected_right);
|
WRITEBOX(c.disconnected_right);
|
||||||
WRITEBOX(disconnected);
|
WRITEBOX(c.disconnected);
|
||||||
WRITEBOX(disconnected_sides);
|
WRITEBOX(c.disconnected_sides);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
writeU8(os, type);
|
writeU8(os, type);
|
||||||
break;
|
break;
|
||||||
|
@ -173,21 +163,23 @@ void NodeBox::deSerialize(std::istream &is)
|
||||||
|
|
||||||
u16 count;
|
u16 count;
|
||||||
|
|
||||||
|
auto &c = getConnected();
|
||||||
|
|
||||||
READBOXES(fixed);
|
READBOXES(fixed);
|
||||||
READBOXES(connect_top);
|
READBOXES(c.connect_top);
|
||||||
READBOXES(connect_bottom);
|
READBOXES(c.connect_bottom);
|
||||||
READBOXES(connect_front);
|
READBOXES(c.connect_front);
|
||||||
READBOXES(connect_left);
|
READBOXES(c.connect_left);
|
||||||
READBOXES(connect_back);
|
READBOXES(c.connect_back);
|
||||||
READBOXES(connect_right);
|
READBOXES(c.connect_right);
|
||||||
READBOXES(disconnected_top);
|
READBOXES(c.disconnected_top);
|
||||||
READBOXES(disconnected_bottom);
|
READBOXES(c.disconnected_bottom);
|
||||||
READBOXES(disconnected_front);
|
READBOXES(c.disconnected_front);
|
||||||
READBOXES(disconnected_left);
|
READBOXES(c.disconnected_left);
|
||||||
READBOXES(disconnected_back);
|
READBOXES(c.disconnected_back);
|
||||||
READBOXES(disconnected_right);
|
READBOXES(c.disconnected_right);
|
||||||
READBOXES(disconnected);
|
READBOXES(c.disconnected);
|
||||||
READBOXES(disconnected_sides);
|
READBOXES(c.disconnected_sides);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,9 +401,9 @@ void ContentFeatures::reset()
|
||||||
drowning = 0;
|
drowning = 0;
|
||||||
light_source = 0;
|
light_source = 0;
|
||||||
damage_per_second = 0;
|
damage_per_second = 0;
|
||||||
node_box = NodeBox();
|
node_box.reset();
|
||||||
selection_box = NodeBox();
|
selection_box.reset();
|
||||||
collision_box = NodeBox();
|
collision_box.reset();
|
||||||
waving = 0;
|
waving = 0;
|
||||||
legacy_facedir_simple = false;
|
legacy_facedir_simple = false;
|
||||||
legacy_wallmounted = false;
|
legacy_wallmounted = false;
|
||||||
|
@ -909,8 +901,15 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
||||||
solidness = 0;
|
solidness = 0;
|
||||||
visual_solidness = 1;
|
visual_solidness = 1;
|
||||||
} else {
|
} else {
|
||||||
drawtype = NDT_NORMAL;
|
if (waving >= 1) {
|
||||||
solidness = 2;
|
// 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)
|
for (TileDef &td : tdef)
|
||||||
td.name += std::string("^[noalpha");
|
td.name += std::string("^[noalpha");
|
||||||
}
|
}
|
||||||
|
@ -1091,10 +1090,8 @@ void NodeDefManager::clear()
|
||||||
{
|
{
|
||||||
ContentFeatures f;
|
ContentFeatures f;
|
||||||
f.name = "unknown";
|
f.name = "unknown";
|
||||||
TileDef unknownTile;
|
|
||||||
unknownTile.name = "unknown_node.png";
|
|
||||||
for (int t = 0; t < 6; t++)
|
for (int t = 0; t < 6; t++)
|
||||||
f.tiledef[t] = unknownTile;
|
f.tiledef[t].name = "unknown_node.png";
|
||||||
// Insert directly into containers
|
// Insert directly into containers
|
||||||
content_t c = CONTENT_UNKNOWN;
|
content_t c = CONTENT_UNKNOWN;
|
||||||
m_content_features[c] = f;
|
m_content_features[c] = f;
|
||||||
|
@ -1296,22 +1293,23 @@ void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NODEBOX_CONNECTED: {
|
case NODEBOX_CONNECTED: {
|
||||||
|
const auto &c = nodebox.getConnected();
|
||||||
// Add all possible connected boxes
|
// Add all possible connected boxes
|
||||||
boxVectorUnion(nodebox.fixed, box_union);
|
boxVectorUnion(nodebox.fixed, box_union);
|
||||||
boxVectorUnion(nodebox.connect_top, box_union);
|
boxVectorUnion(c.connect_top, box_union);
|
||||||
boxVectorUnion(nodebox.connect_bottom, box_union);
|
boxVectorUnion(c.connect_bottom, box_union);
|
||||||
boxVectorUnion(nodebox.connect_front, box_union);
|
boxVectorUnion(c.connect_front, box_union);
|
||||||
boxVectorUnion(nodebox.connect_left, box_union);
|
boxVectorUnion(c.connect_left, box_union);
|
||||||
boxVectorUnion(nodebox.connect_back, box_union);
|
boxVectorUnion(c.connect_back, box_union);
|
||||||
boxVectorUnion(nodebox.connect_right, box_union);
|
boxVectorUnion(c.connect_right, box_union);
|
||||||
boxVectorUnion(nodebox.disconnected_top, box_union);
|
boxVectorUnion(c.disconnected_top, box_union);
|
||||||
boxVectorUnion(nodebox.disconnected_bottom, box_union);
|
boxVectorUnion(c.disconnected_bottom, box_union);
|
||||||
boxVectorUnion(nodebox.disconnected_front, box_union);
|
boxVectorUnion(c.disconnected_front, box_union);
|
||||||
boxVectorUnion(nodebox.disconnected_left, box_union);
|
boxVectorUnion(c.disconnected_left, box_union);
|
||||||
boxVectorUnion(nodebox.disconnected_back, box_union);
|
boxVectorUnion(c.disconnected_back, box_union);
|
||||||
boxVectorUnion(nodebox.disconnected_right, box_union);
|
boxVectorUnion(c.disconnected_right, box_union);
|
||||||
boxVectorUnion(nodebox.disconnected, box_union);
|
boxVectorUnion(c.disconnected, box_union);
|
||||||
boxVectorUnion(nodebox.disconnected_sides, box_union);
|
boxVectorUnion(c.disconnected_sides, box_union);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|
|
@ -99,17 +99,8 @@ enum NodeBoxType
|
||||||
NODEBOX_CONNECTED, // optionally draws nodeboxes if a neighbor node attaches
|
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<aabb3f> fixed;
|
|
||||||
// NODEBOX_WALLMOUNTED
|
|
||||||
aabb3f wall_top;
|
|
||||||
aabb3f wall_bottom;
|
|
||||||
aabb3f wall_side; // being at the -X side
|
|
||||||
// NODEBOX_CONNECTED
|
|
||||||
std::vector<aabb3f> connect_top;
|
std::vector<aabb3f> connect_top;
|
||||||
std::vector<aabb3f> connect_bottom;
|
std::vector<aabb3f> connect_bottom;
|
||||||
std::vector<aabb3f> connect_front;
|
std::vector<aabb3f> connect_front;
|
||||||
|
@ -124,9 +115,35 @@ struct NodeBox
|
||||||
std::vector<aabb3f> disconnected_right;
|
std::vector<aabb3f> disconnected_right;
|
||||||
std::vector<aabb3f> disconnected;
|
std::vector<aabb3f> disconnected;
|
||||||
std::vector<aabb3f> disconnected_sides;
|
std::vector<aabb3f> disconnected_sides;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NodeBox
|
||||||
|
{
|
||||||
|
enum NodeBoxType type;
|
||||||
|
// NODEBOX_REGULAR (no parameters)
|
||||||
|
// NODEBOX_FIXED
|
||||||
|
std::vector<aabb3f> 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<NodeBoxConnected> connected;
|
||||||
|
|
||||||
NodeBox()
|
NodeBox()
|
||||||
{ reset(); }
|
{ reset(); }
|
||||||
|
~NodeBox() = default;
|
||||||
|
|
||||||
|
inline NodeBoxConnected &getConnected() {
|
||||||
|
if (!connected)
|
||||||
|
connected = std::make_shared<NodeBoxConnected>();
|
||||||
|
return *connected;
|
||||||
|
}
|
||||||
|
inline const NodeBoxConnected &getConnected() const {
|
||||||
|
assert(connected);
|
||||||
|
return *connected;
|
||||||
|
}
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void serialize(std::ostream &os, u16 protocol_version) const;
|
void serialize(std::ostream &os, u16 protocol_version) const;
|
||||||
|
@ -290,7 +307,6 @@ struct ContentFeatures
|
||||||
// up down right left back front
|
// up down right left back front
|
||||||
TileSpec tiles[6];
|
TileSpec tiles[6];
|
||||||
// Special tiles
|
// Special tiles
|
||||||
// - Currently used for flowing liquids
|
|
||||||
TileSpec special_tiles[CF_SPECIAL_COUNT];
|
TileSpec special_tiles[CF_SPECIAL_COUNT];
|
||||||
u8 solidness; // Used when choosing which face is drawn
|
u8 solidness; // Used when choosing which face is drawn
|
||||||
u8 visual_solidness; // When solidness=0, this tells how it looks like
|
u8 visual_solidness; // When solidness=0, this tells how it looks like
|
||||||
|
@ -539,7 +555,7 @@ public:
|
||||||
*/
|
*/
|
||||||
inline const ContentFeatures& get(content_t c) const {
|
inline const ContentFeatures& get(content_t c) const {
|
||||||
return
|
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];
|
m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1055,22 +1055,25 @@ void push_nodebox(lua_State *L, const NodeBox &box)
|
||||||
push_aabb3f(L, box.wall_side);
|
push_aabb3f(L, box.wall_side);
|
||||||
lua_setfield(L, -2, "wall_side");
|
lua_setfield(L, -2, "wall_side");
|
||||||
break;
|
break;
|
||||||
case NODEBOX_CONNECTED:
|
case NODEBOX_CONNECTED: {
|
||||||
lua_pushstring(L, "connected");
|
lua_pushstring(L, "connected");
|
||||||
lua_setfield(L, -2, "type");
|
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");
|
lua_setfield(L, -2, "connect_top");
|
||||||
push_box(L, box.connect_bottom);
|
push_box(L, c.connect_bottom);
|
||||||
lua_setfield(L, -2, "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");
|
lua_setfield(L, -2, "connect_front");
|
||||||
push_box(L, box.connect_back);
|
push_box(L, c.connect_back);
|
||||||
lua_setfield(L, -2, "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");
|
lua_setfield(L, -2, "connect_left");
|
||||||
push_box(L, box.connect_right);
|
push_box(L, c.connect_right);
|
||||||
lua_setfield(L, -2, "connect_right");
|
lua_setfield(L, -2, "connect_right");
|
||||||
|
// half the boxes are missing here?
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
FATAL_ERROR("Invalid box.type");
|
FATAL_ERROR("Invalid box.type");
|
||||||
break;
|
break;
|
||||||
|
@ -1198,20 +1201,24 @@ NodeBox read_nodebox(lua_State *L, int index)
|
||||||
NODEBOXREAD(nodebox.wall_top, "wall_top");
|
NODEBOXREAD(nodebox.wall_top, "wall_top");
|
||||||
NODEBOXREAD(nodebox.wall_bottom, "wall_bottom");
|
NODEBOXREAD(nodebox.wall_bottom, "wall_bottom");
|
||||||
NODEBOXREAD(nodebox.wall_side, "wall_side");
|
NODEBOXREAD(nodebox.wall_side, "wall_side");
|
||||||
NODEBOXREADVEC(nodebox.connect_top, "connect_top");
|
|
||||||
NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom");
|
if (nodebox.type == NODEBOX_CONNECTED) {
|
||||||
NODEBOXREADVEC(nodebox.connect_front, "connect_front");
|
auto &c = nodebox.getConnected();
|
||||||
NODEBOXREADVEC(nodebox.connect_left, "connect_left");
|
NODEBOXREADVEC(c.connect_top, "connect_top");
|
||||||
NODEBOXREADVEC(nodebox.connect_back, "connect_back");
|
NODEBOXREADVEC(c.connect_bottom, "connect_bottom");
|
||||||
NODEBOXREADVEC(nodebox.connect_right, "connect_right");
|
NODEBOXREADVEC(c.connect_front, "connect_front");
|
||||||
NODEBOXREADVEC(nodebox.disconnected_top, "disconnected_top");
|
NODEBOXREADVEC(c.connect_left, "connect_left");
|
||||||
NODEBOXREADVEC(nodebox.disconnected_bottom, "disconnected_bottom");
|
NODEBOXREADVEC(c.connect_back, "connect_back");
|
||||||
NODEBOXREADVEC(nodebox.disconnected_front, "disconnected_front");
|
NODEBOXREADVEC(c.connect_right, "connect_right");
|
||||||
NODEBOXREADVEC(nodebox.disconnected_left, "disconnected_left");
|
NODEBOXREADVEC(c.disconnected_top, "disconnected_top");
|
||||||
NODEBOXREADVEC(nodebox.disconnected_back, "disconnected_back");
|
NODEBOXREADVEC(c.disconnected_bottom, "disconnected_bottom");
|
||||||
NODEBOXREADVEC(nodebox.disconnected_right, "disconnected_right");
|
NODEBOXREADVEC(c.disconnected_front, "disconnected_front");
|
||||||
NODEBOXREADVEC(nodebox.disconnected, "disconnected");
|
NODEBOXREADVEC(c.disconnected_left, "disconnected_left");
|
||||||
NODEBOXREADVEC(nodebox.disconnected_sides, "disconnected_sides");
|
NODEBOXREADVEC(c.disconnected_back, "disconnected_back");
|
||||||
|
NODEBOXREADVEC(c.disconnected_right, "disconnected_right");
|
||||||
|
NODEBOXREADVEC(c.disconnected, "disconnected");
|
||||||
|
NODEBOXREADVEC(c.disconnected_sides, "disconnected_sides");
|
||||||
|
}
|
||||||
|
|
||||||
return nodebox;
|
return nodebox;
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,8 +129,6 @@ void ScriptApiSecurity::initializeSecurity()
|
||||||
"gethook",
|
"gethook",
|
||||||
"traceback",
|
"traceback",
|
||||||
"getinfo",
|
"getinfo",
|
||||||
"getmetatable",
|
|
||||||
"setmetatable",
|
|
||||||
"upvalueid",
|
"upvalueid",
|
||||||
"sethook",
|
"sethook",
|
||||||
"debug",
|
"debug",
|
||||||
|
|
|
@ -599,6 +599,9 @@ int ModApiItemMod::l_register_item_raw(lua_State *L)
|
||||||
// be done
|
// be done
|
||||||
if (f.name == "ignore")
|
if (f.name == "ignore")
|
||||||
return 0;
|
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);
|
content_t id = ndef->set(f.name, f);
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ NodeMetaRef* NodeMetaRef::checkobject(lua_State *L, int narg)
|
||||||
Metadata* NodeMetaRef::getmeta(bool auto_create)
|
Metadata* NodeMetaRef::getmeta(bool auto_create)
|
||||||
{
|
{
|
||||||
if (m_is_local)
|
if (m_is_local)
|
||||||
return m_meta;
|
return m_local_meta;
|
||||||
|
|
||||||
NodeMetadata *meta = m_env->getMap().getNodeMetadata(m_p);
|
NodeMetadata *meta = m_env->getMap().getNodeMetadata(m_p);
|
||||||
if (meta == NULL && auto_create) {
|
if (meta == NULL && auto_create) {
|
||||||
|
@ -62,9 +62,14 @@ void NodeMetaRef::clearMeta()
|
||||||
void NodeMetaRef::reportMetadataChange(const std::string *name)
|
void NodeMetaRef::reportMetadataChange(const std::string *name)
|
||||||
{
|
{
|
||||||
SANITY_CHECK(!m_is_local);
|
SANITY_CHECK(!m_is_local);
|
||||||
// NOTE: This same code is in rollback_interface.cpp
|
|
||||||
// Inform other things that the metadata has changed
|
// Inform other things that the metadata has changed
|
||||||
NodeMetadata *meta = dynamic_cast<NodeMetadata*>(m_meta);
|
NodeMetadata *meta = dynamic_cast<NodeMetadata*>(getmeta(false));
|
||||||
|
|
||||||
|
// If the metadata is now empty, get rid of it
|
||||||
|
if (meta && meta->empty()) {
|
||||||
|
clearMeta();
|
||||||
|
meta = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
MapEditEvent event;
|
MapEditEvent event;
|
||||||
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
|
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
|
||||||
|
@ -174,8 +179,8 @@ NodeMetaRef::NodeMetaRef(v3s16 p, ServerEnvironment *env):
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeMetaRef::NodeMetaRef(Metadata *meta):
|
NodeMetaRef::NodeMetaRef(Metadata *meta):
|
||||||
m_meta(meta),
|
m_is_local(true),
|
||||||
m_is_local(true)
|
m_local_meta(meta)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,12 @@ class NodeMetadata;
|
||||||
|
|
||||||
class NodeMetaRef : public MetaDataRef {
|
class NodeMetaRef : public MetaDataRef {
|
||||||
private:
|
private:
|
||||||
|
bool m_is_local = false;
|
||||||
|
// Set for server metadata
|
||||||
v3s16 m_p;
|
v3s16 m_p;
|
||||||
ServerEnvironment *m_env = nullptr;
|
ServerEnvironment *m_env = nullptr;
|
||||||
Metadata *m_meta = nullptr;
|
// Set for client metadata
|
||||||
bool m_is_local = false;
|
Metadata *m_local_meta = nullptr;
|
||||||
|
|
||||||
static const char className[];
|
static const char className[];
|
||||||
static const luaL_Reg methodsServer[];
|
static const luaL_Reg methodsServer[];
|
||||||
|
|
|
@ -2323,6 +2323,21 @@ int ObjectRef::l_get_lighting(lua_State *L)
|
||||||
return 1;
|
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):
|
ObjectRef::ObjectRef(ServerActiveObject *object):
|
||||||
m_object(object)
|
m_object(object)
|
||||||
{}
|
{}
|
||||||
|
@ -2478,5 +2493,7 @@ luaL_Reg ObjectRef::methods[] = {
|
||||||
luamethod(ObjectRef, set_minimap_modes),
|
luamethod(ObjectRef, set_minimap_modes),
|
||||||
luamethod(ObjectRef, set_lighting),
|
luamethod(ObjectRef, set_lighting),
|
||||||
luamethod(ObjectRef, get_lighting),
|
luamethod(ObjectRef, get_lighting),
|
||||||
|
luamethod(ObjectRef, respawn),
|
||||||
|
|
||||||
{0,0}
|
{0,0}
|
||||||
};
|
};
|
||||||
|
|
|
@ -382,4 +382,7 @@ private:
|
||||||
|
|
||||||
// get_lighting(self)
|
// get_lighting(self)
|
||||||
static int l_get_lighting(lua_State *L);
|
static int l_get_lighting(lua_State *L);
|
||||||
|
|
||||||
|
// respawn(self)
|
||||||
|
static int l_respawn(lua_State *L);
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,9 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
|
||||||
/* This protects:
|
/* This protects the following from being set:
|
||||||
* 'secure.*' settings from being set
|
* 'secure.*' settings
|
||||||
* some mapgen settings from being set
|
* some security-relevant settings
|
||||||
|
* (better solution pending)
|
||||||
|
* some mapgen settings
|
||||||
* (not security-criticial, just to avoid messing up user configs)
|
* (not security-criticial, just to avoid messing up user configs)
|
||||||
*/
|
*/
|
||||||
#define CHECK_SETTING_SECURITY(L, name) \
|
#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)
|
static inline int checkSettingSecurity(lua_State* L, const std::string &name)
|
||||||
{
|
{
|
||||||
if (ScriptApiSecurity::isSecure(L) && name.compare(0, 7, "secure.") == 0)
|
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;
|
bool is_mainmenu = false;
|
||||||
#ifndef SERVER
|
#ifndef SERVER
|
||||||
|
@ -54,6 +56,17 @@ static inline int checkSettingSecurity(lua_State* L, const std::string &name)
|
||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -891,7 +891,7 @@ void Server::AsyncRunStep(bool initial_step)
|
||||||
// We'll log the amount of each
|
// We'll log the amount of each
|
||||||
Profiler prof;
|
Profiler prof;
|
||||||
|
|
||||||
std::list<v3s16> node_meta_updates;
|
std::unordered_set<v3s16> node_meta_updates;
|
||||||
|
|
||||||
while (!m_unsent_map_edit_queue.empty()) {
|
while (!m_unsent_map_edit_queue.empty()) {
|
||||||
MapEditEvent* event = m_unsent_map_edit_queue.front();
|
MapEditEvent* event = m_unsent_map_edit_queue.front();
|
||||||
|
@ -918,9 +918,7 @@ void Server::AsyncRunStep(bool initial_step)
|
||||||
case MEET_BLOCK_NODE_METADATA_CHANGED: {
|
case MEET_BLOCK_NODE_METADATA_CHANGED: {
|
||||||
prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
|
prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
|
||||||
if (!event->is_private_change) {
|
if (!event->is_private_change) {
|
||||||
// Don't send the change yet. Collect them to eliminate dupes.
|
node_meta_updates.emplace(event->p);
|
||||||
node_meta_updates.remove(event->p);
|
|
||||||
node_meta_updates.push_back(event->p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
|
if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
|
||||||
|
@ -973,7 +971,7 @@ void Server::AsyncRunStep(bool initial_step)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send all metadata updates
|
// Send all metadata updates
|
||||||
if (node_meta_updates.size())
|
if (!node_meta_updates.empty())
|
||||||
sendMetadataChanged(node_meta_updates);
|
sendMetadataChanged(node_meta_updates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2290,12 +2288,12 @@ void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_player
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
|
void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
|
||||||
{
|
{
|
||||||
float maxd = far_d_nodes * BS;
|
|
||||||
NodeMetadataList meta_updates_list(false);
|
NodeMetadataList meta_updates_list(false);
|
||||||
std::vector<session_t> clients = m_clients.getClientIDs();
|
std::ostringstream os(std::ios::binary);
|
||||||
|
|
||||||
|
std::vector<session_t> clients = m_clients.getClientIDs();
|
||||||
ClientInterface::AutoLock clientlock(m_clients);
|
ClientInterface::AutoLock clientlock(m_clients);
|
||||||
|
|
||||||
for (session_t i : clients) {
|
for (session_t i : clients) {
|
||||||
|
@ -2303,18 +2301,20 @@ void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far
|
||||||
if (!client)
|
if (!client)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ServerActiveObject *player = m_env->getActiveObject(i);
|
ServerActiveObject *player = getPlayerSAO(i);
|
||||||
v3f player_pos = player ? player->getBasePosition() : v3f();
|
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);
|
NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
|
||||||
|
|
||||||
if (!meta)
|
if (!meta)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
v3s16 block_pos = getNodeBlockPos(pos);
|
v3s16 block_pos = getNodeBlockPos(pos);
|
||||||
if (!client->isBlockSent(block_pos) || (player &&
|
if (!client->isBlockSent(block_pos) ||
|
||||||
player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
|
player_pos.getDistanceFrom(pos) > far_d_nodes) {
|
||||||
client->SetBlockNotSent(block_pos);
|
client->SetBlockNotSent(block_pos);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2326,14 +2326,15 @@ void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Send the meta changes
|
// Send the meta changes
|
||||||
std::ostringstream os(std::ios::binary);
|
os.str("");
|
||||||
meta_updates_list.serialize(os, client->serialization_version, false, true, true);
|
meta_updates_list.serialize(os, client->serialization_version, false, true, true);
|
||||||
std::ostringstream oss(std::ios::binary);
|
std::string raw = os.str();
|
||||||
compressZlib(os.str(), oss);
|
os.str("");
|
||||||
|
compressZlib(raw, os);
|
||||||
|
|
||||||
NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
|
NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
|
||||||
pkt.putLongString(oss.str());
|
pkt.putLongString(os.str());
|
||||||
m_clients.send(i, 0, &pkt, true);
|
Send(&pkt);
|
||||||
|
|
||||||
meta_updates_list.clear();
|
meta_updates_list.clear();
|
||||||
}
|
}
|
||||||
|
@ -2784,9 +2785,10 @@ void Server::RespawnPlayer(session_t peer_id)
|
||||||
<< playersao->getPlayer()->getName()
|
<< playersao->getPlayer()->getName()
|
||||||
<< " respawns" << std::endl;
|
<< " respawns" << std::endl;
|
||||||
|
|
||||||
playersao->setHP(playersao->accessObjectProperties()->hp_max,
|
const auto *prop = playersao->accessObjectProperties();
|
||||||
|
playersao->setHP(prop->hp_max,
|
||||||
PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
|
PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
|
||||||
playersao->setBreath(playersao->accessObjectProperties()->breath_max);
|
playersao->setBreath(prop->breath_max);
|
||||||
|
|
||||||
bool repositioned = m_script->on_respawnplayer(playersao);
|
bool repositioned = m_script->on_respawnplayer(playersao);
|
||||||
if (!repositioned) {
|
if (!repositioned) {
|
||||||
|
|
10
src/server.h
10
src/server.h
|
@ -336,6 +336,8 @@ public:
|
||||||
|
|
||||||
void setLighting(RemotePlayer *player, const Lighting &lighting);
|
void setLighting(RemotePlayer *player, const Lighting &lighting);
|
||||||
|
|
||||||
|
void RespawnPlayer(session_t peer_id);
|
||||||
|
|
||||||
/* con::PeerHandler implementation. */
|
/* con::PeerHandler implementation. */
|
||||||
void peerAdded(con::Peer *peer);
|
void peerAdded(con::Peer *peer);
|
||||||
void deletingPeer(con::Peer *peer, bool timeout);
|
void deletingPeer(con::Peer *peer, bool timeout);
|
||||||
|
@ -425,11 +427,10 @@ private:
|
||||||
std::unordered_set<session_t> waiting_players;
|
std::unordered_set<session_t> 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 {
|
struct SBCHash {
|
||||||
size_t operator() (const std::pair<v3s16, u16> &p) const {
|
size_t operator() (const std::pair<v3s16, u16> &p) const {
|
||||||
return (((size_t) p.first.X) << 48) | (((size_t) p.first.Y) << 32) |
|
return std::hash<v3s16>()(p.first) ^ p.second;
|
||||||
(((size_t) p.first.Z) << 16) | ((size_t) p.second);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -491,7 +492,7 @@ private:
|
||||||
std::unordered_set<u16> *far_players = nullptr,
|
std::unordered_set<u16> *far_players = nullptr,
|
||||||
float far_d_nodes = 100, bool remove_metadata = true);
|
float far_d_nodes = 100, bool remove_metadata = true);
|
||||||
|
|
||||||
void sendMetadataChanged(const std::list<v3s16> &meta_updates,
|
void sendMetadataChanged(const std::unordered_set<v3s16> &positions,
|
||||||
float far_d_nodes = 100);
|
float far_d_nodes = 100);
|
||||||
|
|
||||||
// Environment and Connection must be locked when called
|
// Environment and Connection must be locked when called
|
||||||
|
@ -530,7 +531,6 @@ private:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void HandlePlayerDeath(PlayerSAO* sao, const PlayerHPChangeReason &reason);
|
void HandlePlayerDeath(PlayerSAO* sao, const PlayerHPChangeReason &reason);
|
||||||
void RespawnPlayer(session_t peer_id);
|
|
||||||
void DeleteClient(session_t peer_id, ClientDeletionReason reason);
|
void DeleteClient(session_t peer_id, ClientDeletionReason reason);
|
||||||
void UpdateCrafting(RemotePlayer *player);
|
void UpdateCrafting(RemotePlayer *player);
|
||||||
bool checkInteractDistance(RemotePlayer *player, const f32 d, const std::string &what);
|
bool checkInteractDistance(RemotePlayer *player, const f32 d, const std::string &what);
|
||||||
|
|
|
@ -337,19 +337,9 @@ u32 LuaEntitySAO::punch(v3f dir,
|
||||||
if (result.did_punch) {
|
if (result.did_punch) {
|
||||||
setHP((s32)getHP() - result.damage,
|
setHP((s32)getHP() - result.damage,
|
||||||
PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
|
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() <<
|
actionstream << puncher->getDescription() << " (id=" << puncher->getId() <<
|
||||||
", hp=" << puncher->getHP() << ") punched " <<
|
", hp=" << puncher->getHP() << ") punched " <<
|
||||||
getDescription() << " (id=" << m_id << ", hp=" << m_hp <<
|
getDescription() << " (id=" << m_id << ", hp=" << m_hp <<
|
||||||
|
@ -402,6 +392,20 @@ std::string LuaEntitySAO::getDescription()
|
||||||
void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
|
void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
|
||||||
{
|
{
|
||||||
m_hp = rangelim(hp, 0, U16_MAX);
|
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
|
u16 LuaEntitySAO::getHP() const
|
||||||
|
|
|
@ -36,23 +36,30 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
~LuaEntitySAO();
|
~LuaEntitySAO();
|
||||||
|
|
||||||
ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_LUAENTITY; }
|
ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_LUAENTITY; }
|
||||||
ActiveObjectType getSendType() const { return ACTIVEOBJECT_TYPE_GENERIC; }
|
ActiveObjectType getSendType() const { return ACTIVEOBJECT_TYPE_GENERIC; }
|
||||||
virtual void addedToEnvironment(u32 dtime_s);
|
virtual void addedToEnvironment(u32 dtime_s);
|
||||||
void step(float dtime, bool send_recommended);
|
void step(float dtime, bool send_recommended);
|
||||||
std::string getClientInitializationData(u16 protocol_version);
|
std::string getClientInitializationData(u16 protocol_version);
|
||||||
|
|
||||||
bool isStaticAllowed() const { return m_prop.static_save; }
|
bool isStaticAllowed() const { return m_prop.static_save; }
|
||||||
bool shouldUnload() const { return true; }
|
bool shouldUnload() const { return true; }
|
||||||
void getStaticData(std::string *result) const;
|
void getStaticData(std::string *result) const;
|
||||||
|
|
||||||
u32 punch(v3f dir, const ToolCapabilities *toolcap = nullptr,
|
u32 punch(v3f dir, const ToolCapabilities *toolcap = nullptr,
|
||||||
ServerActiveObject *puncher = nullptr,
|
ServerActiveObject *puncher = nullptr,
|
||||||
float time_from_last_punch = 1000000.0f,
|
float time_from_last_punch = 1000000.0f,
|
||||||
u16 initial_wear = 0);
|
u16 initial_wear = 0);
|
||||||
|
|
||||||
void rightClick(ServerActiveObject *clicker);
|
void rightClick(ServerActiveObject *clicker);
|
||||||
|
|
||||||
void setPos(const v3f &pos);
|
void setPos(const v3f &pos);
|
||||||
void moveTo(v3f pos, bool continuous);
|
void moveTo(v3f pos, bool continuous);
|
||||||
float getMinimumSavedMovement();
|
float getMinimumSavedMovement();
|
||||||
|
|
||||||
std::string getDescription();
|
std::string getDescription();
|
||||||
|
|
||||||
void setHP(s32 hp, const PlayerHPChangeReason &reason);
|
void setHP(s32 hp, const PlayerHPChangeReason &reason);
|
||||||
u16 getHP() const;
|
u16 getHP() const;
|
||||||
|
|
||||||
|
|
|
@ -378,10 +378,7 @@ void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
|
||||||
/*
|
/*
|
||||||
Update m_list
|
Update m_list
|
||||||
*/
|
*/
|
||||||
m_list.clear();
|
m_list = std::move(newlist);
|
||||||
for (v3s16 p : newlist) {
|
|
||||||
m_list.insert(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -626,6 +623,9 @@ PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
|
||||||
/* Add object to environment */
|
/* Add object to environment */
|
||||||
addActiveObject(playersao);
|
addActiveObject(playersao);
|
||||||
|
|
||||||
|
// Update active blocks asap so objects in those blocks appear on the client
|
||||||
|
m_force_update_active_blocks = true;
|
||||||
|
|
||||||
return playersao;
|
return playersao;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1332,13 +1332,16 @@ void ServerEnvironment::step(float dtime)
|
||||||
/*
|
/*
|
||||||
Manage active block list
|
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);
|
ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get player block positions
|
Get player block positions
|
||||||
*/
|
*/
|
||||||
std::vector<PlayerSAO*> players;
|
std::vector<PlayerSAO*> players;
|
||||||
for (RemotePlayer *player: m_players) {
|
players.reserve(m_players.size());
|
||||||
|
for (RemotePlayer *player : m_players) {
|
||||||
// Ignore disconnected players
|
// Ignore disconnected players
|
||||||
if (player->getPeerId() == PEER_ID_INEXISTENT)
|
if (player->getPeerId() == PEER_ID_INEXISTENT)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1363,8 +1366,6 @@ void ServerEnvironment::step(float dtime)
|
||||||
m_active_blocks.update(players, active_block_range, active_object_range,
|
m_active_blocks.update(players, active_block_range, active_object_range,
|
||||||
blocks_removed, blocks_added);
|
blocks_removed, blocks_added);
|
||||||
|
|
||||||
m_active_block_gauge->set(m_active_blocks.size());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Handle removed blocks
|
Handle removed blocks
|
||||||
*/
|
*/
|
||||||
|
@ -1388,14 +1389,21 @@ void ServerEnvironment::step(float dtime)
|
||||||
for (const v3s16 &p: blocks_added) {
|
for (const v3s16 &p: blocks_added) {
|
||||||
MapBlock *block = m_map->getBlockOrEmerge(p);
|
MapBlock *block = m_map->getBlockOrEmerge(p);
|
||||||
if (!block) {
|
if (!block) {
|
||||||
m_active_blocks.m_list.erase(p);
|
// TODO: The blocks removed here will only be picked up again
|
||||||
m_active_blocks.m_abm_list.erase(p);
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
activateBlock(block);
|
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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mess around in active blocks
|
Mess around in active blocks
|
||||||
|
|
|
@ -180,8 +180,14 @@ public:
|
||||||
m_list.clear();
|
m_list.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove(v3s16 p) {
|
||||||
|
m_list.erase(p);
|
||||||
|
m_abm_list.erase(p);
|
||||||
|
}
|
||||||
|
|
||||||
std::set<v3s16> m_list;
|
std::set<v3s16> m_list;
|
||||||
std::set<v3s16> m_abm_list;
|
std::set<v3s16> m_abm_list;
|
||||||
|
// list of blocks that are always active, not modified by this class
|
||||||
std::set<v3s16> m_forceloaded_list;
|
std::set<v3s16> m_forceloaded_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -454,7 +460,8 @@ private:
|
||||||
IntervalLimiter m_object_management_interval;
|
IntervalLimiter m_object_management_interval;
|
||||||
// List of active blocks
|
// List of active blocks
|
||||||
ActiveBlockList m_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_block_modifier_interval;
|
||||||
IntervalLimiter m_active_blocks_nodemetadata_interval;
|
IntervalLimiter m_active_blocks_nodemetadata_interval;
|
||||||
// Whether the variables below have been read from file yet
|
// Whether the variables below have been read from file yet
|
||||||
|
|
|
@ -9,9 +9,6 @@ install_linux_deps() {
|
||||||
|
|
||||||
if [[ "$1" == "--no-irr" ]]; then
|
if [[ "$1" == "--no-irr" ]]; then
|
||||||
shift
|
shift
|
||||||
elif [[ "$1" == "--old-irr" ]]; then
|
|
||||||
shift
|
|
||||||
pkgs+=(libirrlicht-dev)
|
|
||||||
else
|
else
|
||||||
wget "https://github.com/minetest/irrlicht/releases/download/1.9.0mt6/ubuntu-bionic.tar.gz"
|
wget "https://github.com/minetest/irrlicht/releases/download/1.9.0mt6/ubuntu-bionic.tar.gz"
|
||||||
sudo tar -xaf ubuntu-bionic.tar.gz -C /usr/local
|
sudo tar -xaf ubuntu-bionic.tar.gz -C /usr/local
|
||||||
|
|
Loading…
Reference in New Issue