Merge branch 'upstream/master'

master
Maksim Gamarnik 2016-03-14 11:44:17 +02:00
commit 8f6ddcc57c
66 changed files with 1642 additions and 723 deletions

View File

@ -78,10 +78,9 @@ ifndef NDEBUG
LOCAL_CFLAGS += -g -D_DEBUG -O0 -fno-omit-frame-pointer
else
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a-hard)
LOCAL_CFLAGS += -mfpu=vfpv3-d16 -D_NDK_MATH_NO_SOFTFP=1 -mfloat-abi=hard -march=armv7-a -Ofast -fno-fast-math -fdata-sections -ffunction-sections -fmodulo-sched -fmodulo-sched-allow-regmoves
LOCAL_LDFLAGS = -Wl,--no-warn-mismatch,--gc-sections -lm_hard
# ToDo - disable for x86!
endif
ifdef GPROF
@ -109,7 +108,6 @@ LOCAL_C_INCLUDES := \
deps/luajit/src
LOCAL_SRC_FILES := \
jni/src/areastore.cpp \
jni/src/ban.cpp \
jni/src/camera.cpp \
jni/src/cavegen.cpp \
@ -218,6 +216,7 @@ LOCAL_SRC_FILES := \
jni/src/version.cpp \
jni/src/voxel.cpp \
jni/src/voxelalgorithms.cpp \
jni/src/util/areastore.cpp \
jni/src/util/auth.cpp \
jni/src/util/base64.cpp \
jni/src/util/directiontables.cpp \

View File

@ -189,7 +189,7 @@ function filterlist.process(self)
for k,v in pairs(self.m_raw_list) do
if self.m_filtercriteria == nil or
self.m_filter_fct(v,self.m_filtercriteria) then
table.insert(self.m_processed_list,v)
self.m_processed_list[#self.m_processed_list + 1] = v
end
end

View File

@ -2,7 +2,6 @@
--------------------------------------------------------------------------------
-- Localize functions to avoid table lookups (better performance).
local table_insert = table.insert
local string_sub, string_find = string.sub, string.find
--------------------------------------------------------------------------------
@ -94,13 +93,13 @@ function dump2(o, name, dumped)
-- the form _G["table: 0xFFFFFFF"]
keyStr = string.format("_G[%q]", tostring(k))
-- Dump key table
table_insert(t, dump2(k, keyStr, dumped))
t[#t + 1] = dump2(k, keyStr, dumped)
end
else
keyStr = basic_dump(k)
end
local vname = string.format("%s[%s]", name, keyStr)
table_insert(t, dump2(v, vname, dumped))
t[#t + 1] = dump2(v, vname, dumped)
end
return string.format("%s = {}\n%s", name, table.concat(t))
end
@ -135,7 +134,7 @@ function dump(o, indent, nested, level)
local t = {}
local dumped_indexes = {}
for i, v in ipairs(o) do
table_insert(t, dump(v, indent, nested, level + 1))
t[#t + 1] = dump(v, indent, nested, level + 1)
dumped_indexes[i] = true
end
for k, v in pairs(o) do
@ -144,7 +143,7 @@ function dump(o, indent, nested, level)
k = "["..dump(k, indent, nested, level + 1).."]"
end
v = dump(v, indent, nested, level + 1)
table_insert(t, k.." = "..v)
t[#t + 1] = k.." = "..v
end
end
nested[o] = nil
@ -177,7 +176,7 @@ function string.split(str, delim, include_empty, max_splits, sep_is_pattern)
local s = string_sub(str, pos, np - 1)
if include_empty or (s ~= "") then
max_splits = max_splits - 1
table_insert(items, s)
items[#items + 1] = s
end
pos = npe + 1
until (max_splits == 0) or (pos > (len + 1))
@ -186,8 +185,8 @@ end
--------------------------------------------------------------------------------
function table.indexof(list, val)
for i = 1, #list do
if list[i] == val then
for i, v in ipairs(list) do
if v == val then
return i
end
end
@ -324,7 +323,7 @@ function core.splittext(text,charlimit)
local last_line = ""
while start ~= nil do
if string.len(last_line) + (stop-start) > charlimit then
table_insert(retval, last_line)
retval[#retval + 1] = last_line
last_line = ""
end
@ -335,7 +334,7 @@ function core.splittext(text,charlimit)
last_line = last_line .. string_sub(text, current_idx, stop - 1)
if gotnewline then
table_insert(retval, last_line)
retval[#retval + 1] = last_line
last_line = ""
gotnewline = false
end
@ -353,11 +352,11 @@ function core.splittext(text,charlimit)
--add last part of text
if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
table_insert(retval, last_line)
table_insert(retval, string_sub(text, current_idx))
retval[#retval + 1] = last_line
retval[#retval + 1] = string_sub(text, current_idx)
else
last_line = last_line .. " " .. string_sub(text, current_idx)
table_insert(retval, last_line)
retval[#retval + 1] = last_line
end
return retval
@ -430,14 +429,14 @@ if INIT == "game" then
if iswall then
core.set_node(pos, {name = wield_name,
param2 = dirs1[fdir+1]})
param2 = dirs1[fdir + 1]})
elseif isceiling then
if orient_flags.force_facedir then
core.set_node(pos, {name = wield_name,
param2 = 20})
else
core.set_node(pos, {name = wield_name,
param2 = dirs2[fdir+1]})
param2 = dirs2[fdir + 1]})
end
else -- place right side up
if orient_flags.force_facedir then

View File

@ -104,7 +104,7 @@ function core.serialize(x)
local i = local_index
local_index = local_index + 1
var = "_["..i.."]"
table.insert(local_defs, var.." = "..val)
local_defs[#local_defs + 1] = var.." = "..val
dumped[x] = var
return var
end
@ -135,16 +135,15 @@ function core.serialize(x)
local np = nest_points[x]
for i, v in ipairs(x) do
if not np or not np[i] then
table.insert(vals, dump_or_ref_val(v))
vals[#vals + 1] = dump_or_ref_val(v)
end
idx_dumped[i] = true
end
for k, v in pairs(x) do
if (not np or not np[k]) and
not idx_dumped[k] then
table.insert(vals,
"["..dump_or_ref_val(k).."] = "
..dump_or_ref_val(v))
vals[#vals + 1] = "["..dump_or_ref_val(k).."] = "
..dump_or_ref_val(v)
end
end
return "{"..table.concat(vals, ", ").."}"
@ -156,9 +155,9 @@ function core.serialize(x)
local function dump_nest_points()
for parent, vals in pairs(nest_points) do
for k, v in pairs(vals) do
table.insert(local_defs, dump_or_ref_val(parent)
local_defs[#local_defs + 1] = dump_or_ref_val(parent)
.."["..dump_or_ref_val(k).."] = "
..dump_or_ref_val(v))
..dump_or_ref_val(v)
end
end
end

View File

@ -145,7 +145,12 @@ local buttonbar_metatable = {
if image == nil then image = "" end
if tooltip == nil then tooltip = "" end
table.insert(self.buttons,{ name=name, caption=caption, image=image, tooltip=tooltip})
self.buttons[#self.buttons + 1] = {
name = name,
caption = caption,
image = image,
tooltip = tooltip
}
if self.orientation == "horizontal" then
if ( (self.btn_size * #self.buttons) + (self.btn_size * 0.05 *2)
> self.size.x ) then

View File

@ -46,7 +46,7 @@ local function add_tab(self,tab)
tabdata = {},
}
table.insert(self.tablist,newtab)
self.tablist[#self.tablist + 1] = newtab
if self.last_tab_index == #self.tablist then
self.current_tab = tab.name

View File

@ -20,7 +20,7 @@ function core.privs_to_string(privs, delim)
local list = {}
for priv, bool in pairs(privs) do
if bool then
table.insert(list, priv)
list[#list + 1] = priv
end
end
return table.concat(list, delim)

View File

@ -116,7 +116,7 @@ core.register_chatcommand("help", {
local cmds = {}
for cmd, def in pairs(core.chatcommands) do
if core.check_player_privs(name, def.privs) then
table.insert(cmds, cmd)
cmds[#cmds + 1] = cmd
end
end
table.sort(cmds)
@ -127,7 +127,7 @@ core.register_chatcommand("help", {
local cmds = {}
for cmd, def in pairs(core.chatcommands) do
if core.check_player_privs(name, def.privs) then
table.insert(cmds, format_help_line(cmd, def))
cmds[#cmds + 1] = format_help_line(cmd, def)
end
end
table.sort(cmds)
@ -135,7 +135,7 @@ core.register_chatcommand("help", {
elseif param == "privs" then
local privs = {}
for priv, def in pairs(core.registered_privileges) do
table.insert(privs, priv .. ": " .. def.description)
privs[#privs + 1] = priv .. ": " .. def.description
end
table.sort(privs)
return true, "Available privileges:\n"..table.concat(privs, "\n")
@ -785,6 +785,13 @@ core.register_chatcommand("time", {
end,
})
core.register_chatcommand("days", {
description = "Display day count",
func = function(name, param)
return true, "Current day is " .. core.get_day_count()
end
})
core.register_chatcommand("shutdown", {
description = "shutdown server",
privs = {server=true},

View File

@ -8,6 +8,7 @@ core.features = {
use_texture_alpha = true,
no_legacy_abms = true,
texture_names_parens = true,
area_store_custom_ids = true,
}
function core.has_feature(arg)

View File

@ -233,7 +233,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
place_to = {x = under.x, y = under.y, z = under.z}
end
if core.is_protected(place_to, placer:get_player_name()) then
if core.is_protected(place_to, placer:get_player_name()) and
not minetest.check_player_privs(placer, "protection_bypass") then
core.log("action", placer:get_player_name()
.. " tried to place " .. def.name
.. " at protected position "
@ -444,7 +445,8 @@ function core.node_dig(pos, node, digger)
return
end
if core.is_protected(pos, digger:get_player_name()) then
if core.is_protected(pos, digger:get_player_name()) and
not minetest.check_player_privs(digger, "protection_bypass") then
core.log("action", digger:get_player_name()
.. " tried to dig " .. node.name
.. " at protected position "

View File

@ -40,12 +40,12 @@ end)
function core.after(after, func, ...)
assert(tonumber(time) and type(func) == "function",
"Invalid core.after invocation")
table.insert(jobs, {
jobs[#jobs + 1] = {
func = func,
expire = time + after,
arg = {...},
mod_origin = core.get_last_run_mod()
})
}
end
function core.check_player_privs(player_or_name, ...)
@ -63,14 +63,14 @@ function core.check_player_privs(player_or_name, ...)
-- We were provided with a table like { privA = true, privB = true }.
for priv, value in pairs(requested_privs[1]) do
if value and not player_privs[priv] then
table.insert(missing_privileges, priv)
missing_privileges[#missing_privileges + 1] = priv
end
end
else
-- Only a list, we can process it directly.
for key, priv in pairs(requested_privs) do
if not player_privs[priv] then
table.insert(missing_privileges, priv)
missing_privileges[#missing_privileges + 1] = priv
end
end
end
@ -96,7 +96,7 @@ function core.get_connected_players()
local temp_table = {}
for index, value in pairs(player_list) do
if value:is_player_connected() then
table.insert(temp_table, value)
temp_table[#temp_table + 1] = value
end
end
return temp_table

View File

@ -32,6 +32,7 @@ core.register_privilege("settime", "Can use /time")
core.register_privilege("privs", "Can modify privileges")
core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges")
core.register_privilege("server", "Can do server maintenance stuff")
core.register_privilege("protection_bypass", "Can bypass node protection in the world")
core.register_privilege("shout", "Can speak in chat")
core.register_privilege("ban", "Can ban and unban players")
core.register_privilege("kick", "Can kick players")

View File

@ -11,10 +11,11 @@ local register_alias_raw = core.register_alias_raw
core.register_alias_raw = nil
--
-- Item / entity / ABM registration functions
-- Item / entity / ABM / LBM registration functions
--
core.registered_abms = {}
core.registered_lbms = {}
core.registered_entities = {}
core.registered_items = {}
core.registered_nodes = {}
@ -75,7 +76,14 @@ end
function core.register_abm(spec)
-- Add to core.registered_abms
core.registered_abms[#core.registered_abms+1] = spec
core.registered_abms[#core.registered_abms + 1] = spec
spec.mod_origin = core.get_current_modname() or "??"
end
function core.register_lbm(spec)
-- Add to core.registered_lbms
check_modname_prefix(spec.name)
core.registered_lbms[#core.registered_lbms + 1] = spec
spec.mod_origin = core.get_current_modname() or "??"
end
@ -391,7 +399,7 @@ end
local function make_registration()
local t = {}
local registerfunc = function(func)
table.insert(t, func)
t[#t + 1] = func
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
@ -467,9 +475,9 @@ end
function core.register_on_player_hpchange(func, modifier)
if modifier then
table.insert(core.registered_on_player_hpchanges.modifiers, func)
core.registered_on_player_hpchanges.modifiers[#core.registered_on_player_hpchanges.modifiers + 1] = func
else
table.insert(core.registered_on_player_hpchanges.loggers, func)
core.registered_on_player_hpchanges.loggers[#core.registered_on_player_hpchanges.loggers + 1] = func
end
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",

View File

@ -67,13 +67,13 @@ function order_favorite_list(list)
for i=1,#list,1 do
local fav = list[i]
if is_server_protocol_compat(fav.proto_min, fav.proto_max) then
table.insert(res, fav)
res[#res + 1] = fav
end
end
for i=1,#list,1 do
local fav = list[i]
if not is_server_protocol_compat(fav.proto_min, fav.proto_max) then
table.insert(res, fav)
res[#res + 1] = fav
end
end
return res

View File

@ -151,8 +151,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
if setting_type == "float" then
local default, min, max = remaining_line:match("^"
-- first float is required, the last 2 are optional
.. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "?"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "?"
.. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)"
.."$")

View File

@ -23,7 +23,7 @@ function get_mods(path,retval,modpack)
if name:sub(1, 1) ~= "." then
local prefix = path .. DIR_DELIM .. name .. DIR_DELIM
local toadd = {}
table.insert(retval, toadd)
retval[#retval + 1] = toadd
local mod_conf = Settings(prefix .. "mod.conf"):to_table()
if mod_conf.name then
@ -412,7 +412,7 @@ function modmgr.preparemodlist(data)
for i=1,#global_mods,1 do
global_mods[i].typ = "global_mod"
table.insert(retval,global_mods[i])
retval[#retval + 1] = global_mods[i]
end
--read game mods
@ -421,7 +421,7 @@ function modmgr.preparemodlist(data)
for i=1,#game_mods,1 do
game_mods[i].typ = "game_mod"
table.insert(retval,game_mods[i])
retval[#retval + 1] = game_mods[i]
end
if data.worldpath == nil then

View File

@ -78,7 +78,7 @@ local function get_formspec(tabview, name, tabdata)
descriptionfile:close()
else
descriptionlines = {}
table.insert(descriptionlines,fgettext("No mod description available"))
descriptionlines[#descriptionlines + 1] = fgettext("No mod description available")
end
retval = retval ..

View File

@ -17,110 +17,115 @@
--------------------------------------------------------------------------------
local leaves_style_labels = {
fgettext("Opaque Leaves"),
fgettext("Simple Leaves"),
fgettext("Fancy Leaves")
local labels = {
leaves = {
fgettext("Opaque Leaves"),
fgettext("Simple Leaves"),
fgettext("Fancy Leaves")
},
node_highlighting = {
fgettext("Node Outlining"),
fgettext("Node Highlighting")
},
filters = {
fgettext("No Filter"),
fgettext("Bilinear Filter"),
fgettext("Trilinear Filter")
},
mipmap = {
fgettext("No Mipmap"),
fgettext("Mipmap"),
fgettext("Mipmap + Aniso. Filter")
},
antialiasing = {
fgettext("None"),
fgettext("2x"),
fgettext("4x"),
fgettext("8x")
}
}
local leaves_style = {
{leaves_style_labels[1] .. "," .. leaves_style_labels[2] .. "," .. leaves_style_labels[3]},
{"opaque", "simple", "fancy"},
local dd_options = {
leaves = {
table.concat(labels.leaves, ","),
{"opaque", "simple", "fancy"}
},
node_highlighting = {
table.concat(labels.node_highlighting, ","),
{"box", "halo"}
},
filters = {
table.concat(labels.filters, ","),
{"", "bilinear_filter", "trilinear_filter"}
},
mipmap = {
table.concat(labels.mipmap, ","),
{"", "mip_map", "anisotropic_filter"}
},
antialiasing = {
table.concat(labels.antialiasing, ","),
{"0", "2", "4", "8"}
}
}
local dd_filter_labels = {
fgettext("No Filter"),
fgettext("Bilinear Filter"),
fgettext("Trilinear Filter")
}
local filters = {
{dd_filter_labels[1] .. "," .. dd_filter_labels[2] .. "," .. dd_filter_labels[3]},
{"", "bilinear_filter", "trilinear_filter"},
}
local dd_mipmap_labels = {
fgettext("No Mipmap"),
fgettext("Mipmap"),
fgettext("Mipmap + Aniso. Filter")
}
local mipmap = {
{dd_mipmap_labels[1] .. "," .. dd_mipmap_labels[2] .. "," .. dd_mipmap_labels[3]},
{"", "mip_map", "anisotropic_filter"},
}
local function getLeavesStyleSettingIndex()
local style = core.setting_get("leaves_style")
if (style == leaves_style[2][3]) then
return 3
elseif (style == leaves_style[2][2]) then
return 2
end
return 1
end
local dd_antialiasing_labels = {
fgettext("None"),
fgettext("2x"),
fgettext("4x"),
fgettext("8x"),
}
local antialiasing = {
{dd_antialiasing_labels[1] .. "," .. dd_antialiasing_labels[2] .. "," ..
dd_antialiasing_labels[3] .. "," .. dd_antialiasing_labels[4]},
{"0", "2", "4", "8"}
}
local function getFilterSettingIndex()
if (core.setting_get(filters[2][3]) == "true") then
return 3
end
if (core.setting_get(filters[2][3]) == "false" and core.setting_get(filters[2][2]) == "true") then
return 2
end
return 1
end
local function getMipmapSettingIndex()
if (core.setting_get(mipmap[2][3]) == "true") then
return 3
end
if (core.setting_get(mipmap[2][3]) == "false" and core.setting_get(mipmap[2][2]) == "true") then
return 2
end
return 1
end
local function getAntialiasingSettingIndex()
local antialiasing_setting = core.setting_get("fsaa")
for i = 1, #(antialiasing[2]) do
if antialiasing_setting == antialiasing[2][i] then
return i
local getSettingIndex = {
Leaves = function()
local style = core.setting_get("leaves_style")
for idx, name in pairs(dd_options.leaves[2]) do
if style == name then return idx end
end
return 1
end,
NodeHighlighting = function()
local style = core.setting_get("node_highlighting")
for idx, name in pairs(dd_options.node_highlighting[2]) do
if style == name then return idx end
end
return 1
end,
Filter = function()
if core.setting_get(dd_options.filters[2][3]) == "true" then
return 3
elseif core.setting_get(dd_options.filters[2][3]) == "false" and
core.setting_get(dd_options.filters[2][2]) == "true" then
return 2
end
return 1
end,
Mipmap = function()
if core.setting_get(dd_options.mipmap[2][3]) == "true" then
return 3
elseif core.setting_get(dd_options.mipmap[2][3]) == "false" and
core.setting_get(dd_options.mipmap[2][2]) == "true" then
return 2
end
return 1
end,
Antialiasing = function()
local antialiasing_setting = core.setting_get("fsaa")
for i = 1, #dd_options.antialiasing[2] do
if antialiasing_setting == dd_options.antialiasing[2][i] then
return i
end
end
return 1
end
return 1
end
}
local function antialiasing_fname_to_name(fname)
for i = 1, #(dd_antialiasing_labels) do
if fname == dd_antialiasing_labels[i] then
return antialiasing[2][i]
for i = 1, #labels.antialiasing do
if fname == labels.antialiasing[i] then
return dd_options.antialiasing[2][i]
end
end
return 0
end
local function dlg_confirm_reset_formspec(data)
local retval =
"size[8,3]" ..
return "size[8,3]" ..
"label[1,1;" .. fgettext("Are you sure to reset your singleplayer world?") .. "]" ..
"button[1,2;2.6,0.5;dlg_reset_singleplayer_confirm;" ..
fgettext("Yes") .. "]" ..
"button[4,2;2.8,0.5;dlg_reset_singleplayer_cancel;" ..
fgettext("No!!!") .. "]"
return retval
"button[1,2;2.6,0.5;dlg_reset_singleplayer_confirm;" .. fgettext("Yes") .. "]" ..
"button[4,2;2.8,0.5;dlg_reset_singleplayer_cancel;" .. fgettext("No") .. "]"
end
local function dlg_confirm_reset_btnhandler(this, fields, dialogdata)
@ -129,7 +134,7 @@ local function dlg_confirm_reset_btnhandler(this, fields, dialogdata)
local worldlist = core.get_worlds()
local found_singleplayerworld = false
for i = 1, #worldlist, 1 do
for i = 1, #worldlist do
if worldlist[i].name == "singleplayerworld" then
found_singleplayerworld = true
gamedata.worldindex = i
@ -141,12 +146,10 @@ local function dlg_confirm_reset_btnhandler(this, fields, dialogdata)
end
core.create_world("singleplayerworld", 1)
worldlist = core.get_worlds()
found_singleplayerworld = false
for i = 1, #worldlist, 1 do
for i = 1, #worldlist do
if worldlist[i].name == "singleplayerworld" then
found_singleplayerworld = true
gamedata.worldindex = i
@ -170,41 +173,12 @@ local function showconfirm_reset(tabview)
new_dlg:show()
end
local function gui_scale_to_scrollbar()
local current_value = tonumber(core.setting_get("gui_scaling"))
if (current_value == nil) or current_value < 0.25 then
return 0
end
if current_value <= 1.25 then
return ((current_value - 0.25)/ 1.0) * 700
end
if current_value <= 6 then
return ((current_value -1.25) * 100) + 700
end
return 1000
end
local function scrollbar_to_gui_scale(value)
value = tonumber(value)
if (value <= 700) then
return ((value / 700) * 1.0) + 0.25
end
if (value <= 1000) then
return ((value - 700) / 100) + 1.25
end
return 1
end
local function formspec(tabview, name, tabdata)
local tab_string =
"box[0,0;3.5,4.3;#999999]" ..
"checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting")
.. ";" .. dump(core.setting_getbool("smooth_lighting")) .. "]" ..
"checkbox[0.25,0.5;cb_particles;" .. fgettext("Enable Particles") .. ";"
"box[0,0;3.5,4.5;#999999]" ..
"checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") .. ";"
.. dump(core.setting_getbool("smooth_lighting")) .. "]" ..
"checkbox[0.25,0.5;cb_particles;" .. fgettext("Particles") .. ";"
.. dump(core.setting_getbool("enable_particles")) .. "]" ..
"checkbox[0.25,1;cb_3d_clouds;" .. fgettext("3D Clouds") .. ";"
.. dump(core.setting_getbool("enable_3d_clouds")) .. "]" ..
@ -212,20 +186,20 @@ local function formspec(tabview, name, tabdata)
.. dump(core.setting_getbool("opaque_water")) .. "]" ..
"checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";"
.. dump(core.setting_getbool("connected_glass")) .. "]" ..
"checkbox[0.25,2.5;cb_node_highlighting;" .. fgettext("Node Highlighting") .. ";"
.. dump(core.setting_getbool("enable_node_highlighting")) .. "]" ..
"dropdown[0.25,3.4;3.3;dd_leaves_style;" .. leaves_style[1][1] .. ";"
.. getLeavesStyleSettingIndex() .. "]" ..
"dropdown[0.25,2.8;3.3;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";"
.. getSettingIndex.NodeHighlighting() .. "]" ..
"dropdown[0.25,3.6;3.3;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
.. getSettingIndex.Leaves() .. "]" ..
"box[3.75,0;3.75,3.45;#999999]" ..
"label[3.85,0.1;" .. fgettext("Texturing:") .. "]" ..
"dropdown[3.85,0.55;3.85;dd_filters;" .. filters[1][1] .. ";"
.. getFilterSettingIndex() .. "]" ..
"dropdown[3.85,1.35;3.85;dd_mipmap;" .. mipmap[1][1] .. ";"
.. getMipmapSettingIndex() .. "]" ..
"dropdown[3.85,0.55;3.85;dd_filters;" .. dd_options.filters[1] .. ";"
.. getSettingIndex.Filter() .. "]" ..
"dropdown[3.85,1.35;3.85;dd_mipmap;" .. dd_options.mipmap[1] .. ";"
.. getSettingIndex.Mipmap() .. "]" ..
"label[3.85,2.15;" .. fgettext("Antialiasing:") .. "]" ..
"dropdown[3.85,2.6;3.85;dd_antialiasing;" .. antialiasing[1][1] .. ";"
.. getAntialiasingSettingIndex() .. "]" ..
"box[7.75,0;4,4;#999999]" ..
"dropdown[3.85,2.6;3.85;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
.. getSettingIndex.Antialiasing() .. "]" ..
"box[7.75,0;4,4.4;#999999]" ..
"checkbox[8,0;cb_shaders;" .. fgettext("Shaders") .. ";"
.. dump(core.setting_getbool("enable_shaders")) .. "]"
@ -236,37 +210,40 @@ local function formspec(tabview, name, tabdata)
if core.setting_get("touchscreen_threshold") ~= nil then
tab_string = tab_string ..
"label[4.3,4.1;" .. fgettext("Touchthreshold (px)") .. "]" ..
"dropdown[3.85,4.55;3.85;dd_touchthreshold;0,10,20,30,40,50;" ..
((tonumber(core.setting_get("touchscreen_threshold"))/10)+1) .. "]"
"label[4.3,4.1;" .. fgettext("Touchthreshold (px)") .. "]" ..
"dropdown[3.85,4.55;3.85;dd_touchthreshold;0,10,20,30,40,50;" ..
((tonumber(core.setting_get("touchscreen_threshold")) / 10) + 1) .. "]"
end
if core.setting_getbool("enable_shaders") then
tab_string = tab_string ..
"checkbox[8,0.5;cb_bumpmapping;" .. fgettext("Bumpmapping") .. ";"
.. dump(core.setting_getbool("enable_bumpmapping")) .. "]" ..
"checkbox[8,1.0;cb_generate_normalmaps;" .. fgettext("Generate Normalmaps") .. ";"
.. dump(core.setting_getbool("generate_normalmaps")) .. "]" ..
"checkbox[8,1.5;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
.. dump(core.setting_getbool("enable_parallax_occlusion")) .. "]" ..
"checkbox[8,2.0;cb_waving_water;" .. fgettext("Waving Water") .. ";"
.. dump(core.setting_getbool("enable_waving_water")) .. "]" ..
"checkbox[8,2.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
.. dump(core.setting_getbool("enable_waving_leaves")) .. "]" ..
"checkbox[8,3.0;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
.. dump(core.setting_getbool("enable_waving_plants")) .. "]"
"checkbox[8,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";"
.. dump(core.setting_getbool("enable_bumpmapping")) .. "]" ..
"checkbox[8,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
.. dump(core.setting_getbool("tone_mapping")) .. "]" ..
"checkbox[8,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";"
.. dump(core.setting_getbool("generate_normalmaps")) .. "]" ..
"checkbox[8,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
.. dump(core.setting_getbool("enable_parallax_occlusion")) .. "]" ..
"checkbox[8,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";"
.. dump(core.setting_getbool("enable_waving_water")) .. "]" ..
"checkbox[8,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
.. dump(core.setting_getbool("enable_waving_leaves")) .. "]" ..
"checkbox[8,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
.. dump(core.setting_getbool("enable_waving_plants")) .. "]"
else
tab_string = tab_string ..
"tablecolumns[color;text]" ..
"tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
"table[8.33,0.7;3.5,4;shaders;" ..
"#888888," .. fgettext("Bumpmapping") .. "," ..
"#888888," .. fgettext("Generate Normalmaps") .. "," ..
"#888888," .. fgettext("Parallax Occlusion") .. "," ..
"#888888," .. fgettext("Waving Water") .. "," ..
"#888888," .. fgettext("Waving Leaves") .. "," ..
"#888888," .. fgettext("Waving Plants") .. "," ..
";1]"
"tablecolumns[color;text]" ..
"tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
"table[8.33,0.7;3.5,4;shaders;" ..
"#888888," .. fgettext("Bump Mapping") .. "," ..
"#888888," .. fgettext("Tone Mapping") .. "," ..
"#888888," .. fgettext("Normal Mapping") .. "," ..
"#888888," .. fgettext("Parallax Occlusion") .. "," ..
"#888888," .. fgettext("Waving Water") .. "," ..
"#888888," .. fgettext("Waving Leaves") .. "," ..
"#888888," .. fgettext("Waving Plants") .. "," ..
";1]"
end
return tab_string
@ -283,7 +260,6 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
--mm_texture.update("singleplayer", current_game())
return true
end
if fields["cb_smooth_lighting"] then
core.setting_set("smooth_lighting", fields["cb_smooth_lighting"])
return true
@ -304,13 +280,9 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
core.setting_set("connected_glass", fields["cb_connected_glass"])
return true
end
if fields["cb_node_highlighting"] then
core.setting_set("enable_node_highlighting", fields["cb_node_highlighting"])
return true
end
if fields["cb_shaders"] then
if (core.setting_get("video_driver") == "direct3d8"
or core.setting_get("video_driver") == "direct3d9") then
if (core.setting_get("video_driver") == "direct3d8" or
core.setting_get("video_driver") == "direct3d9") then
core.setting_set("enable_shaders", "false")
gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.")
else
@ -320,9 +292,15 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
end
if fields["cb_bumpmapping"] then
core.setting_set("enable_bumpmapping", fields["cb_bumpmapping"])
return true
end
if fields["cb_tonemapping"] then
core.setting_set("tone_mapping", fields["cb_tonemapping"])
return true
end
if fields["cb_generate_normalmaps"] then
core.setting_set("generate_normalmaps", fields["cb_generate_normalmaps"])
return true
end
if fields["cb_parallax"] then
core.setting_set("enable_parallax_occlusion", fields["cb_parallax"])
@ -339,17 +317,6 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
core.setting_set("enable_waving_plants", fields["cb_waving_plants"])
return true
end
if fields["sb_gui_scaling"] then
local event = core.explode_scrollbar_event(fields["sb_gui_scaling"])
if event.type == "CHG" then
local tosave = string.format("%.2f",scrollbar_to_gui_scale(event.value))
core.setting_set("gui_scaling", tosave)
return true
end
end
if fields["btn_change_keys"] then
core.show_keys_menu()
return true
@ -366,38 +333,40 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
--Note dropdowns have to be handled LAST!
local ddhandled = false
if fields["dd_leaves_style"] == leaves_style_labels[1] then
core.setting_set("leaves_style", leaves_style[2][1])
ddhandled = true
elseif fields["dd_leaves_style"] == leaves_style_labels[2] then
core.setting_set("leaves_style", leaves_style[2][2])
ddhandled = true
elseif fields["dd_leaves_style"] == leaves_style_labels[3] then
core.setting_set("leaves_style", leaves_style[2][3])
ddhandled = true
for i = 1, #labels.leaves do
if fields["dd_leaves_style"] == labels.leaves[i] then
core.setting_set("leaves_style", dd_options.leaves[2][i])
ddhandled = true
end
end
if fields["dd_filters"] == dd_filter_labels[1] then
for i = 1, #labels.node_highlighting do
if fields["dd_node_highlighting"] == labels.node_highlighting[i] then
core.setting_set("node_highlighting", dd_options.node_highlighting[2][i])
ddhandled = true
end
end
if fields["dd_filters"] == labels.filters[1] then
core.setting_set("bilinear_filter", "false")
core.setting_set("trilinear_filter", "false")
ddhandled = true
elseif fields["dd_filters"] == dd_filter_labels[2] then
elseif fields["dd_filters"] == labels.filters[2] then
core.setting_set("bilinear_filter", "true")
core.setting_set("trilinear_filter", "false")
ddhandled = true
elseif fields["dd_filters"] == dd_filter_labels[3] then
elseif fields["dd_filters"] == labels.filters[3] then
core.setting_set("bilinear_filter", "false")
core.setting_set("trilinear_filter", "true")
ddhandled = true
end
if fields["dd_mipmap"] == dd_mipmap_labels[1] then
if fields["dd_mipmap"] == labels.mipmap[1] then
core.setting_set("mip_map", "false")
core.setting_set("anisotropic_filter", "false")
ddhandled = true
elseif fields["dd_mipmap"] == dd_mipmap_labels[2] then
elseif fields["dd_mipmap"] == labels.mipmap[2] then
core.setting_set("mip_map", "true")
core.setting_set("anisotropic_filter", "false")
ddhandled = true
elseif fields["dd_mipmap"] == dd_mipmap_labels[3] then
elseif fields["dd_mipmap"] == labels.mipmap[3] then
core.setting_set("mip_map", "true")
core.setting_set("anisotropic_filter", "true")
ddhandled = true
@ -408,7 +377,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
ddhandled = true
end
if fields["dd_touchthreshold"] then
core.setting_set("touchscreen_threshold",fields["dd_touchthreshold"])
core.setting_set("touchscreen_threshold", fields["dd_touchthreshold"])
ddhandled = true
end

View File

@ -20,7 +20,7 @@ local function filter_texture_pack_list(list)
local retval = {}
for _, item in ipairs(list) do
if item ~= "base" then
table.insert(retval, item)
retval[#retval + 1] = item
end
end

View File

@ -554,6 +554,14 @@ fallback_font_shadow_alpha (Fallback font shadow alpha) int 128 0 255
# Path to save screenshots at.
screenshot_path (Screenshot folder) path
# Format of screenshots.
screenshot_format (Screenshot format) enum png png,jpg,bmp,pcx,ppm,tga
# Screenshot quality. Only used for JPEG format.
# 1 means worst quality; 100 means best quality.
# Use 0 for default quality.
screenshot_quality (Screenshot quality) int 0 0 100
[**Advanced]
# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.
@ -800,7 +808,7 @@ liquid_update (Liquid update tick) float 1.0
# Name of map generator to be used when creating a new world.
# Creating a world in the main menu will override this.
mg_name (Mapgen name) enum v6 v5,v6,v7,flat,fractal,singlenode
mg_name (Mapgen name) enum v6 v5,v6,v7,flat,valleys,fractal,singlenode
# Water surface level of the world.
water_level (Water level) int 1
@ -819,8 +827,10 @@ map_generation_limit (Map generation limit) int 31000 0 31000
# Global map generation attributes.
# In Mapgen v6 the 'decorations' flag controls all decorations except trees
# and junglegrass, in all other mapgens this flag controls all decorations.
# The default flags set in the engine are: caves, light, decorations
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# Flags starting with 'no' are used to explicitly disable them.
mg_flags (Mapgen flags) flags caves,dungeons,light,decorations caves,dungeons,light,decorations,nocaves,nodungeons,nolight,nodecorations
[**Advanced]
@ -875,9 +885,11 @@ mgv5_np_cave2 (Mapgen v5 cave2 noise parameters) noise_params 0, 12, (50, 50, 50
[***Mapgen v6]
# Map generation attributes specific to Mapgen v6.
# When snowbiomes are enabled jungles are enabled and the jungles flag is ignored.
# When snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.
# The default flags set in the engine are: biomeblend, mudflow
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# Flags starting with 'no' are used to explicitly disable them.
mgv6_spflags (Mapgen v6 flags) flags jungles,biomeblend,mudflow,snowbiomes,trees jungles,biomeblend,mudflow,snowbiomes,flat,trees,nojungles,nobiomeblend,nomudflow,nosnowbiomes,noflat,notrees
# Controls size of deserts and beaches in Mapgen v6.
@ -900,9 +912,11 @@ mgv6_np_apple_trees (Mapgen v6 apple trees noise parameters) noise_params 0, 1,
[***Mapgen v7]
# Map generation attributes specific to Mapgen v7.
# 'ridges' are the rivers.
# The 'ridges' flag controls the rivers.
# The default flags set in the engine are: mountains, ridges
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# Flags starting with 'no' are used to explicitly disable them.
mgv7_spflags (Mapgen v7 flags) flags mountains,ridges mountains,ridges,nomountains,noridges
mgv7_np_terrain_base (Mapgen v7 terrain base noise parameters) noise_params 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0
@ -920,9 +934,11 @@ mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (100, 100,
[***Mapgen flat]
# Map generation attributes specific to Mapgen flat.
# Occasional lakes and hills added to the flat world.
# Occasional lakes and hills can be added to the flat world.
# The default flags set in the engine are: none
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# Flags starting with 'no' are used to explicitly disable them.
mgflat_spflags (Mapgen flat flags) flags lakes,hills,,nolakes,nohills
# Y of flat ground.
@ -1027,10 +1043,13 @@ mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, (
[****General]
# Map generation attributes specific to Mapgen Valleys.
# 'altitude_chill' makes higher elevations colder, which may cause biome issues.
# 'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool,
# it may interfere with delicately adjusted biomes.
# The default flags set in the engine are: altitude_chill, humid_rivers
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# "altitude_chill" makes higher elevations colder, which may cause biome issues.
# "humid_rivers" modifies the humidity around rivers and in areas where water would tend to pool. It may interfere with delicately adjusted biomes.
# Flags starting with 'no' are used to explicitly disable them.
mg_valleys_spflags (Valleys C Flags) flags altitude_chill,humid_rivers altitude_chill,noaltitude_chill,humid_rivers,nohumid_rivers
# The altitude at which temperature drops by 20C

View File

@ -617,6 +617,18 @@ A nodebox is defined as any of:
wall_bottom = box,
wall_side = box
}
{
-- A node that has optional boxes depending on neighbouring nodes'
-- presence and type. See also `connects_to`.
type = "connected",
fixed = box OR {box1, box2, ...}
connect_top = box OR {box1, box2, ...}
connect_bottom = box OR {box1, box2, ...}
connect_front = box OR {box1, box2, ...}
connect_left = box OR {box1, box2, ...}
connect_back = box OR {box1, box2, ...}
connect_right = box OR {box1, box2, ...}
}
A `box` is defined as:
@ -1793,6 +1805,7 @@ Call these functions only at load time!
* `minetest.register_entity(name, prototype table)`
* `minetest.register_abm(abm definition)`
* `minetest.register_lbm(lbm definition)`
* `minetest.register_node(name, node definition)`
* `minetest.register_tool(name, item definition)`
* `minetest.register_craftitem(name, item definition)`
@ -1997,6 +2010,8 @@ and `minetest.auth_reload` call the authetification handler.
* `val` is between `0` and `1`; `0` for midnight, `0.5` for midday
* `minetest.get_timeofday()`
* `minetest.get_gametime()`: returns the time, in seconds, since the world was created
* `minetest.get_day_count()`: returns number days elapsed since world was created,
* accounting for time changes.
* `minetest.find_node_near(pos, radius, nodenames)`: returns pos or `nil`
* `radius`: using a maximum metric
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
@ -2726,8 +2741,7 @@ If you chose the parameter-less constructor, a fast implementation will be autom
* `get_area(id, include_borders, include_data)`: returns the area with the id `id`. (optional) Boolean values `include_borders` and `include_data` control what's copied.
* `get_areas_for_pos(pos, include_borders, include_data)`: returns all areas that contain the position `pos`. (optional) Boolean values `include_borders` and `include_data` control what's copied.
* `get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data)`: returns all areas that contain all nodes inside the area specified by `edge1` and `edge2` (inclusive). If `accept_overlap` is true, also areas are returned that have nodes in common with the specified area. (optional) Boolean values `include_borders` and `include_data` control what's copied.
* `insert_area(edge1, edge2, data)`: inserts an area into the store. Returns the id if successful, nil otherwise. The (inclusive) positions `edge1` and `edge2` describe the area, `data`
is a string stored with the area.
* `insert_area(edge1, edge2, data, [id])`: inserts an area into the store. Returns the new area's ID, or nil if the insertion failed. The (inclusive) positions `edge1` and `edge2` describe the area. `data` is a string stored with the area. If passed, `id` will be used as the internal area ID, it must be a unique number between 0 and 2^32-2. If you use the `id` parameter you must always use it, or insertions are likely to fail due to conflicts.
* `reserve(count)`: reserves resources for at most `count` many contained areas. Only needed for efficiency, and only some implementations profit.
* `remove_area(id)`: removes the area with the given id from the store, returns success.
* `set_cache_params(params)`: sets params for the included prefiltering cache. Calling invalidates the cache, so that its elements have to be newly generated.
@ -2737,6 +2751,10 @@ is a string stored with the area.
block_radius = number, -- the radius (in nodes) of the areas the cache generates prefiltered lists for, minimum 16, default 64
limit = number, -- the cache's size, minimum 20, default 1000
}
* `to_string()`: Experimental. Returns area store serialized as a (binary) string.
* `to_file(filename)`: Experimental. Like `to_string()`, but writes the data to a file.
* `from_string(str)`: Experimental. Deserializes string and loads it into the AreaStore. Returns success and, optionally, an error message.
* `from_file(filename)`: Experimental. Like `from_string()`, but reads the data from a file.
### `ItemStack`
An `ItemStack` is a stack of items.
@ -3305,6 +3323,21 @@ Definition tables
action = func(pos, node, active_object_count, active_object_count_wider),
}
### LBM (LoadingBlockModifier) definition (`register_lbm`)
{
name = "modname:replace_legacy_door",
nodenames = {"default:lava_source"},
-- ^ List of node names to trigger the LBM on.
-- Also non-registered nodes will work.
-- Groups (as of group:groupname) will work as well.
run_at_every_load = false,
-- ^ Whether to run the LBM's action every time a block gets loaded,
-- and not just for blocks that were saved last time before LBMs were
-- introduced to the world.
action = func(pos, node),
}
### Item definition (`register_node`, `register_craftitem`, `register_tool`)
{
@ -3440,6 +3473,12 @@ Definition tables
light_source = 0, -- Amount of light emitted by node
damage_per_second = 0, -- If player is inside node, this damage is caused
node_box = {type="regular"}, -- See "Node boxes"
connects_to = nodenames, --[[
* Used for nodebox nodes with the type == "connected"
* Specifies to what neighboring nodes connections will be drawn
* e.g. `{"group:fence", "default:wood"}` or `"default:stone"` ]]
connect_sides = { "top", "bottom", "front", "left", "back", "right" }, --[[
^ Tells connected nodebox nodes to connect only to these sides of this node. ]]
mesh = "model",
selection_box = {type="regular"}, -- See "Node boxes" --[[
^ If drawtype "nodebox" is used and selection_box is nil, then node_box is used. ]]

View File

@ -653,6 +653,16 @@
# type: path
# screenshot_path =
# Format of screenshots.
# type: enum values: png, jpg, bmp, pcx, ppm, tga
# screenshot_format = png
# Screenshot quality. Only used for JPEG format.
# 1 means worst quality; 100 means best quality.
# Use 0 for default quality.
# type: int min: 0 max: 100
# screenshot_quality = 0
### Advanced
# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.
@ -986,7 +996,7 @@
# Name of map generator to be used when creating a new world.
# Creating a world in the main menu will override this.
# type: enum values: v5, v6, v7, flat, fractal, singlenode
# type: enum values: v5, v6, v7, flat, valleys, fractal, singlenode
# mg_name = v6
# Water surface level of the world.
@ -1009,8 +1019,10 @@
# Global map generation attributes.
# In Mapgen v6 the 'decorations' flag controls all decorations except trees
# and junglegrass, in all other mapgens this flag controls all decorations.
# The default flags set in the engine are: caves, light, decorations
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# Flags starting with 'no' are used to explicitly disable them.
# type: flags possible values: caves, dungeons, light, decorations, nocaves, nodungeons, nolight, nodecorations
# mg_flags = caves,dungeons,light,decorations
@ -1077,9 +1089,11 @@
#### Mapgen v6
# Map generation attributes specific to Mapgen v6.
# When snowbiomes are enabled jungles are enabled and the jungles flag is ignored.
# When snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.
# The default flags set in the engine are: biomeblend, mudflow
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# Flags starting with 'no' are used to explicitly disable them.
# type: flags possible values: jungles, biomeblend, mudflow, snowbiomes, flat, trees, nojungles, nobiomeblend, nomudflow, nosnowbiomes, noflat, notrees
# mgv6_spflags = jungles,biomeblend,mudflow,snowbiomes,trees
@ -1127,9 +1141,11 @@
#### Mapgen v7
# Map generation attributes specific to Mapgen v7.
# 'ridges' are the rivers.
# The 'ridges' flag controls the rivers.
# The default flags set in the engine are: mountains, ridges
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# Flags starting with 'no' are used to explicitly disable them.
# type: flags possible values: mountains, ridges, nomountains, noridges
# mgv7_spflags = mountains,ridges
@ -1169,9 +1185,11 @@
#### Mapgen flat
# Map generation attributes specific to Mapgen flat.
# Occasional lakes and hills added to the flat world.
# Occasional lakes and hills can be added to the flat world.
# The default flags set in the engine are: none
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# Flags starting with 'no' are used to explicitly disable them.
# type: flags possible values: lakes, hills, , nolakes, nohills
# mgflat_spflags =
@ -1303,10 +1321,13 @@
##### General
# Map generation attributes specific to Mapgen Valleys.
# 'altitude_chill' makes higher elevations colder, which may cause biome issues.
# 'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool,
# it may interfere with delicately adjusted biomes.
# The default flags set in the engine are: altitude_chill, humid_rivers
# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with "no" are used to explicitly disable them.
# "altitude_chill" makes higher elevations colder, which may cause biome issues.
# "humid_rivers" modifies the humidity around rivers and in areas where water would tend to pool. It may interfere with delicately adjusted biomes.
# Flags starting with 'no' are used to explicitly disable them.
# type: flags possible values: altitude_chill, noaltitude_chill, humid_rivers, nohumid_rivers
# mg_valleys_spflags = altitude_chill,humid_rivers

View File

@ -348,7 +348,6 @@ add_subdirectory(unittest)
add_subdirectory(util)
set(common_SRCS
areastore.cpp
ban.cpp
cavegen.cpp
chat.cpp

View File

@ -1813,9 +1813,12 @@ void Client::makeScreenshot(IrrlichtDevice *device)
+ DIR_DELIM
+ std::string("screenshot_")
+ std::string(timetstamp_c);
std::string filename_ext = ".png";
std::string filename_ext = "." + g_settings->get("screenshot_format");
std::string filename;
u32 quality = (u32)g_settings->getS32("screenshot_quality");
quality = MYMIN(MYMAX(quality, 0), 100) / 100.0 * 255;
// Try to find a unique filename
unsigned serial = 0;
@ -1837,7 +1840,7 @@ void Client::makeScreenshot(IrrlichtDevice *device)
raw_image->copyTo(image);
std::ostringstream sstr;
if (driver->writeImageToFile(image, filename.c_str())) {
if (driver->writeImageToFile(image, filename.c_str(), quality)) {
sstr << "Saved screenshot to '" << filename << "'";
} else {
sstr << "Failed to save screenshot '" << filename << "'";

View File

@ -370,17 +370,21 @@ queue_full_break:
void RemoteClient::GotBlock(v3s16 p)
{
if(m_blocks_sending.find(p) != m_blocks_sending.end())
m_blocks_sending.erase(p);
else
{
m_excess_gotblocks++;
if (m_blocks_modified.find(p) == m_blocks_modified.end()) {
if (m_blocks_sending.find(p) != m_blocks_sending.end())
m_blocks_sending.erase(p);
else
m_excess_gotblocks++;
m_blocks_sent.insert(p);
}
m_blocks_sent.insert(p);
}
void RemoteClient::SentBlock(v3s16 p)
{
if (m_blocks_modified.find(p) != m_blocks_modified.end())
m_blocks_modified.erase(p);
if(m_blocks_sending.find(p) == m_blocks_sending.end())
m_blocks_sending[p] = 0.0;
else
@ -397,6 +401,7 @@ void RemoteClient::SetBlockNotSent(v3s16 p)
m_blocks_sending.erase(p);
if(m_blocks_sent.find(p) != m_blocks_sent.end())
m_blocks_sent.erase(p);
m_blocks_modified.insert(p);
}
void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
@ -409,6 +414,7 @@ void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
i != blocks.end(); ++i)
{
v3s16 p = i->first;
m_blocks_modified.insert(p);
if(m_blocks_sending.find(p) != m_blocks_sending.end())
m_blocks_sending.erase(p);

View File

@ -394,6 +394,16 @@ private:
*/
std::map<v3s16, float> m_blocks_sending;
/*
Blocks that have been modified since last sending them.
These blocks will not be marked as sent, even if the
client reports it has received them to account for blocks
that are being modified while on the line.
List of block positions.
*/
std::set<v3s16> m_blocks_modified;
/*
Count of excess GotBlocks().
There is an excess amount because the client sometimes

View File

@ -185,6 +185,13 @@ bool wouldCollideWithCeiling(
return false;
}
static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
Map *map, MapNode n, int v, int *neighbors)
{
MapNode n2 = map->getNodeNoEx(p);
if (nodedef->nodeboxConnects(n, n2, v))
*neighbors |= v;
}
collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
f32 pos_max_d, const aabb3f &box_0,
@ -261,12 +268,41 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
// Object collides into walkable nodes
any_position_valid = true;
const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
INodeDefManager *nodedef = gamedef->getNodeDefManager();
const ContentFeatures &f = nodedef->get(n);
if(f.walkable == false)
continue;
int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(gamedef->ndef());
int neighbors = 0;
if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) {
v3s16 p2 = p;
p2.Y++;
getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors);
p2 = p;
p2.Y--;
getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors);
p2 = p;
p2.Z--;
getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors);
p2 = p;
p2.X--;
getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors);
p2 = p;
p2.Z++;
getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors);
p2 = p;
p2.X++;
getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors);
}
std::vector<aabb3f> nodeboxes;
n.getCollisionBoxes(gamedef->ndef(), &nodeboxes, neighbors);
for(std::vector<aabb3f>::iterator
i = nodeboxes.begin();
i != nodeboxes.end(); ++i)

View File

@ -163,6 +163,14 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
}
}
static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
MeshMakeData *data, MapNode n, int v, int *neighbors)
{
MapNode n2 = data->m_vmanip.getNodeNoEx(p);
if (nodedef->nodeboxConnects(n, n2, v))
*neighbors |= v;
}
/*
TODO: Fix alpha blending for special nodes
Currently only the last element rendered is blended correct
@ -1501,7 +1509,38 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
v3f pos = intToFloat(p, BS);
std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
int neighbors = 0;
// locate possible neighboring nodes to connect to
if (f.node_box.type == NODEBOX_CONNECTED) {
v3s16 p2 = p;
p2.Y++;
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors);
p2 = p;
p2.Y--;
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors);
p2 = p;
p2.Z--;
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors);
p2 = p;
p2.X--;
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors);
p2 = p;
p2.Z++;
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors);
p2 = p;
p2.X++;
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors);
}
std::vector<aabb3f> boxes;
n.getNodeBoxes(nodedef, &boxes, neighbors);
for(std::vector<aabb3f>::iterator
i = boxes.begin();
i != boxes.end(); ++i)

View File

@ -124,6 +124,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("invert_mouse", "false");
settings->setDefault("enable_clouds", "true");
settings->setDefault("screenshot_path", ".");
settings->setDefault("screenshot_format", "png");
settings->setDefault("screenshot_quality", "0");
settings->setDefault("view_bobbing_amount", "1.0");
settings->setDefault("fall_bobbing_amount", "1.0");
settings->setDefault("enable_3d_clouds", "true");

View File

@ -48,6 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
Environment::Environment():
m_time_of_day_speed(0),
m_time_of_day(9000),
@ -206,6 +208,8 @@ void Environment::setDayNightRatioOverride(bool enable, u32 value)
void Environment::setTimeOfDay(u32 time)
{
MutexAutoLock lock(this->m_time_lock);
if (m_time_of_day > time)
m_day_count++;
m_time_of_day = time;
m_time_of_day_f = (float)time / 24000.0;
}
@ -236,8 +240,10 @@ void Environment::stepTimeOfDay(float dtime)
bool sync_f = false;
if (units > 0) {
// Sync at overflow
if (m_time_of_day + units >= 24000)
if (m_time_of_day + units >= 24000) {
sync_f = true;
m_day_count++;
}
m_time_of_day = (m_time_of_day + units) % 24000;
if (sync_f)
m_time_of_day_f = (float)m_time_of_day / 24000.0;
@ -254,6 +260,13 @@ void Environment::stepTimeOfDay(float dtime)
}
}
u32 Environment::getDayCount()
{
// Atomic<u32> counter
return m_day_count;
}
/*
ABMWithState
*/
@ -270,6 +283,223 @@ ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
timer = myrand_range(minval, maxval);
}
/*
LBMManager
*/
void LBMContentMapping::deleteContents()
{
for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
it != lbm_list.end(); ++it) {
delete *it;
}
}
void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
{
// Add the lbm_def to the LBMContentMapping.
// Unknown names get added to the global NameIdMapping.
INodeDefManager *nodedef = gamedef->ndef();
lbm_list.push_back(lbm_def);
for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
it != lbm_def->trigger_contents.end(); ++it) {
std::set<content_t> c_ids;
bool found = nodedef->getIds(*it, c_ids);
if (!found) {
content_t c_id = gamedef->allocateUnknownNodeId(*it);
if (c_id == CONTENT_IGNORE) {
// Seems it can't be allocated.
warningstream << "Could not internalize node name \"" << *it
<< "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
continue;
}
c_ids.insert(c_id);
}
for (std::set<content_t>::const_iterator iit =
c_ids.begin(); iit != c_ids.end(); ++iit) {
content_t c_id = *iit;
map[c_id].push_back(lbm_def);
}
}
}
const std::vector<LoadingBlockModifierDef *> *
LBMContentMapping::lookup(content_t c) const
{
container_map::const_iterator it = map.find(c);
if (it == map.end())
return NULL;
// This first dereferences the iterator, returning
// a std::vector<LoadingBlockModifierDef *>
// reference, then we convert it to a pointer.
return &(it->second);
}
LBMManager::~LBMManager()
{
for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
delete it->second;
}
for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
it != m_lbm_lookup.end(); ++it) {
(it->second).deleteContents();
}
}
void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
{
// Precondition, in query mode the map isn't used anymore
FATAL_ERROR_IF(m_query_mode == true,
"attempted to modify LBMManager in query mode");
if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
throw ModError("Error adding LBM \"" + lbm_def->name +
"\": Does not follow naming conventions: "
"Only chararacters [a-z0-9_:] are allowed.");
}
m_lbm_defs[lbm_def->name] = lbm_def;
}
void LBMManager::loadIntroductionTimes(const std::string &times,
IGameDef *gamedef, u32 now)
{
m_query_mode = true;
// name -> time map.
// Storing it in a map first instead of
// handling the stuff directly in the loop
// removes all duplicate entries.
// TODO make this std::unordered_map
std::map<std::string, u32> introduction_times;
/*
The introduction times string consists of name~time entries,
with each entry terminated by a semicolon. The time is decimal.
*/
size_t idx = 0;
size_t idx_new;
while ((idx_new = times.find(";", idx)) != std::string::npos) {
std::string entry = times.substr(idx, idx_new - idx);
std::vector<std::string> components = str_split(entry, '~');
if (components.size() != 2)
throw SerializationError("Introduction times entry \""
+ entry + "\" requires exactly one '~'!");
const std::string &name = components[0];
u32 time = from_string<u32>(components[1]);
introduction_times[name] = time;
idx = idx_new + 1;
}
// Put stuff from introduction_times into m_lbm_lookup
for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
it != introduction_times.end(); ++it) {
const std::string &name = it->first;
u32 time = it->second;
std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
m_lbm_defs.find(name);
if (def_it == m_lbm_defs.end()) {
// This seems to be an LBM entry for
// an LBM we haven't loaded. Discard it.
continue;
}
LoadingBlockModifierDef *lbm_def = def_it->second;
if (lbm_def->run_at_every_load) {
// This seems to be an LBM entry for
// an LBM that runs at every load.
// Don't add it just yet.
continue;
}
m_lbm_lookup[time].addLBM(lbm_def, gamedef);
// Erase the entry so that we know later
// what elements didn't get put into m_lbm_lookup
m_lbm_defs.erase(name);
}
// Now also add the elements from m_lbm_defs to m_lbm_lookup
// that weren't added in the previous step.
// They are introduced first time to this world,
// or are run at every load (introducement time hardcoded to U32_MAX).
LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
if (it->second->run_at_every_load) {
lbms_running_always.addLBM(it->second, gamedef);
} else {
lbms_we_introduce_now.addLBM(it->second, gamedef);
}
}
// Clear the list, so that we don't delete remaining elements
// twice in the destructor
m_lbm_defs.clear();
}
std::string LBMManager::createIntroductionTimesString()
{
// Precondition, we must be in query mode
FATAL_ERROR_IF(m_query_mode == false,
"attempted to query on non fully set up LBMManager");
std::ostringstream oss;
for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
it != m_lbm_lookup.end(); ++it) {
u32 time = it->first;
std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
iit != lbm_list.end(); ++iit) {
// Don't add if the LBM runs at every load,
// then introducement time is hardcoded
// and doesn't need to be stored
if ((*iit)->run_at_every_load)
continue;
oss << (*iit)->name << "~" << time << ";";
}
}
return oss.str();
}
void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
{
// Precondition, we need m_lbm_lookup to be initialized
FATAL_ERROR_IF(m_query_mode == false,
"attempted to query on non fully set up LBMManager");
v3s16 pos_of_block = block->getPosRelative();
v3s16 pos;
MapNode n;
content_t c;
lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
{
n = block->getNodeNoEx(pos);
c = n.getContent();
for (LBMManager::lbm_lookup_map::const_iterator iit = it;
iit != m_lbm_lookup.end(); ++iit) {
const std::vector<LoadingBlockModifierDef *> *lbm_list =
iit->second.lookup(c);
if (!lbm_list)
continue;
for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
lbm_list->begin(); iit != lbm_list->end(); ++iit) {
(*iit)->trigger(env, pos + pos_of_block, n);
}
}
}
}
/*
ActiveBlockList
*/
@ -505,6 +735,10 @@ void ServerEnvironment::saveMeta()
args.setU64("game_time", m_game_time);
args.setU64("time_of_day", getTimeOfDay());
args.setU64("last_clear_objects_time", m_last_clear_objects_time);
args.setU64("lbm_introduction_times_version", 1);
args.set("lbm_introduction_times",
m_lbm_mgr.createIntroductionTimesString());
args.setU64("day_count", m_day_count);
args.writeLines(ss);
ss<<"EnvArgsEnd\n";
@ -542,19 +776,35 @@ void ServerEnvironment::loadMeta()
throw SerializationError("Couldn't load env meta game_time");
}
try {
setTimeOfDay(args.getU64("time_of_day"));
} catch (SettingNotFoundException &e) {
// This is not as important
setTimeOfDay(9000);
}
setTimeOfDay(args.exists("time_of_day") ?
// set day to morning by default
args.getU64("time_of_day") : 9000);
try {
m_last_clear_objects_time = args.getU64("last_clear_objects_time");
} catch (SettingNotFoundException &e) {
m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
// If missing, do as if clearObjects was never called
m_last_clear_objects_time = 0;
args.getU64("last_clear_objects_time") : 0;
std::string lbm_introduction_times = "";
try {
u64 ver = args.getU64("lbm_introduction_times_version");
if (ver == 1) {
lbm_introduction_times = args.get("lbm_introduction_times");
} else {
infostream << "ServerEnvironment::loadMeta(): Non-supported"
<< " introduction time version " << ver << std::endl;
}
} catch (SettingNotFoundException &e) {
// No problem, this is expected. Just continue with an empty string
}
m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
m_day_count = args.exists("day_count") ?
args.getU64("day_count") : 0;
}
void ServerEnvironment::loadDefaultMeta()
{
m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
}
struct ActiveABM
@ -770,6 +1020,9 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
// Activate stored objects
activateObjects(block, dtime_s);
/* Handle LoadingBlockModifiers */
m_lbm_mgr.applyLBMs(this, block, stamp);
// Run node timers
std::map<v3s16, NodeTimer> elapsed_timers =
block->m_node_timers.step((float)dtime_s);
@ -795,6 +1048,11 @@ void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
m_abms.push_back(ABMWithState(abm));
}
void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
{
m_lbm_mgr.addLBMDef(lbm);
}
bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
{
INodeDefManager *ndef = m_gamedef->ndef();

View File

@ -95,6 +95,8 @@ public:
void setDayNightRatioOverride(bool enable, u32 value);
u32 getDayCount();
// counter used internally when triggering ABMs
u32 m_added_objects;
@ -117,6 +119,9 @@ protected:
// Overriding the day-night ratio is useful for custom sky visuals
bool m_enable_day_night_ratio_override;
u32 m_day_night_ratio_override;
// Days from the server start, accounts for time shift
// in game (e.g. /time or bed usage)
Atomic<u32> m_day_count;
/*
* Above: values managed by m_time_lock
*/
@ -139,7 +144,7 @@ private:
};
/*
Active block modifier interface.
{Active, Loading} block modifier interface.
These are fed into ServerEnvironment at initialization time;
ServerEnvironment handles deleting them.
@ -177,6 +182,77 @@ struct ABMWithState
ABMWithState(ActiveBlockModifier *abm_);
};
struct LoadingBlockModifierDef
{
// Set of contents to trigger on
std::set<std::string> trigger_contents;
std::string name;
bool run_at_every_load;
virtual ~LoadingBlockModifierDef() {}
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
};
struct LBMContentMapping
{
typedef std::map<content_t, std::vector<LoadingBlockModifierDef *> > container_map;
container_map map;
std::vector<LoadingBlockModifierDef *> lbm_list;
// Needs to be separate method (not inside destructor),
// because the LBMContentMapping may be copied and destructed
// many times during operation in the lbm_lookup_map.
void deleteContents();
void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef);
const std::vector<LoadingBlockModifierDef *> *lookup(content_t c) const;
};
class LBMManager
{
public:
LBMManager():
m_query_mode(false)
{}
~LBMManager();
// Don't call this after loadIntroductionTimes() ran.
void addLBMDef(LoadingBlockModifierDef *lbm_def);
void loadIntroductionTimes(const std::string &times,
IGameDef *gamedef, u32 now);
// Don't call this before loadIntroductionTimes() ran.
std::string createIntroductionTimesString();
// Don't call this before loadIntroductionTimes() ran.
void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp);
// Warning: do not make this std::unordered_map, order is relevant here
typedef std::map<u32, LBMContentMapping> lbm_lookup_map;
private:
// Once we set this to true, we can only query,
// not modify
bool m_query_mode;
// For m_query_mode == false:
// The key of the map is the LBM def's name.
// TODO make this std::unordered_map
std::map<std::string, LoadingBlockModifierDef *> m_lbm_defs;
// For m_query_mode == true:
// The key of the map is the LBM def's first introduction time.
lbm_lookup_map m_lbm_lookup;
// Returns an iterator to the LBMs that were introduced
// after the given time. This is guaranteed to return
// valid values for everything
lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time)
{ return m_lbm_lookup.lower_bound(time); }
};
/*
List of active blocks, used by ServerEnvironment
*/
@ -254,6 +330,9 @@ public:
*/
void saveMeta();
void loadMeta();
// to be called instead of loadMeta if
// env_meta.txt doesn't exist (e.g. new world)
void loadDefaultMeta();
/*
External ActiveObject interface
@ -312,11 +391,12 @@ public:
void activateBlock(MapBlock *block, u32 additional_dtime=0);
/*
ActiveBlockModifiers
{Active,Loading}BlockModifiers
-------------------------------------------
*/
void addActiveBlockModifier(ActiveBlockModifier *abm);
void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm);
/*
Other stuff
@ -427,6 +507,7 @@ private:
u32 m_last_clear_objects_time;
// Active block modifiers
std::vector<ABMWithState> m_abms;
LBMManager m_lbm_mgr;
// An interval for generally sending object positions and stuff
float m_recommended_send_interval;
// Estimate for general maximum lag as determined by server.

View File

@ -358,7 +358,9 @@ PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_positio
if (!isPointableNode(n, client, liquids_pointable)) {
continue;
}
std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
std::vector<aabb3f> boxes;
n.getSelectionBoxes(nodedef, &boxes);
v3s16 np(x, y, z);
v3f npf = intToFloat(np, BS);
@ -389,7 +391,8 @@ PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_positio
f32 d = 0.001 * BS;
MapNode n = map.getNodeNoEx(pointed_pos);
v3f npf = intToFloat(pointed_pos, BS);
std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
std::vector<aabb3f> boxes;
n.getSelectionBoxes(nodedef, &boxes);
f32 face_min_distance = 1000 * BS;
for (std::vector<aabb3f>::const_iterator
i = boxes.begin();

View File

@ -122,6 +122,8 @@ void GUIChatConsole::openConsole(f32 height)
m_desired_height_fraction = height;
m_desired_height = height * m_screensize.Y;
reformatConsole();
m_animate_time_old = getTimeMs();
IGUIElement::setVisible(true);
Environment->setFocus(this);
m_menumgr->createdMenu(this);
}
@ -243,6 +245,11 @@ void GUIChatConsole::animate(u32 msec)
{
// animate the console height
s32 goal = m_open ? m_desired_height : 0;
// Set invisible if close animation finished (reset by openConsole)
if (!m_open && m_height == 0)
IGUIElement::setVisible(false);
if (m_height != goal)
{
s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0);
@ -628,3 +635,13 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
return Parent ? Parent->OnEvent(event) : false;
}
void GUIChatConsole::setVisible(bool visible)
{
m_open = visible;
IGUIElement::setVisible(visible);
if (!visible) {
m_height = 0;
recalculateConsolePosition();
}
}

View File

@ -77,6 +77,8 @@ public:
virtual bool OnEvent(const SEvent& event);
virtual void setVisible(bool visible);
private:
void reformatConsole();
void recalculateConsolePosition();

View File

@ -183,7 +183,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
*/
if (control.sneak && m_sneak_node_exists &&
!(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
physics_override_sneak) {
physics_override_sneak && !got_teleported) {
f32 maxd = 0.5 * BS + sneak_max;
v3f lwn_f = intToFloat(m_sneak_node, BS);
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
@ -204,6 +204,9 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
}
}
if (got_teleported)
got_teleported = false;
// this shouldn't be hardcoded but transmitted from server
float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
@ -310,7 +313,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
if (sneak_node_found) {
f32 cb_max = 0;
MapNode n = map->getNodeNoEx(m_sneak_node);
std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr);
std::vector<aabb3f> nodeboxes;
n.getCollisionBoxes(nodemgr, &nodeboxes);
for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
it != nodeboxes.end(); ++it) {
aabb3f box = *it;

View File

@ -573,6 +573,12 @@ void MapgenFlat::generateCaves(s16 max_stone_y)
for (s16 y = node_max.Y + 1; y >= node_min.Y - 1;
y--, index3d -= ystride, vm->m_area.add_y(em, vi, -1)) {
// Don't excavate the overgenerated stone at node_max.Y + 1,
// this creates a 'roof' over the tunnel, preventing light in
// tunnels at mapchunk borders when generating mapchunks upwards.
if (y > node_max.Y)
continue;
content_t c = vm->m_data[vi].getContent();
if (c == CONTENT_AIR || c == biome->c_water_top ||
c == biome->c_water) {

View File

@ -701,6 +701,12 @@ void MapgenFractal::generateCaves(s16 max_stone_y)
for (s16 y = node_max.Y + 1; y >= node_min.Y - 1;
y--, index3d -= ystride, vm->m_area.add_y(em, vi, -1)) {
// Don't excavate the overgenerated stone at node_max.Y + 1,
// this creates a 'roof' over the tunnel, preventing light in
// tunnels at mapchunk borders when generating mapchunks upwards.
if (y > node_max.Y)
continue;
content_t c = vm->m_data[vi].getContent();
if (c == CONTENT_AIR || c == biome->c_water_top ||
c == biome->c_water) {

View File

@ -519,6 +519,12 @@ void MapgenV5::generateCaves(int max_stone_y)
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
u32 vi = vm->m_area.index(node_min.X, y, z);
for (s16 x = node_min.X; x <= node_max.X; x++, vi++, index++) {
// Don't excavate the overgenerated stone at node_max.Y + 1,
// this creates a 'roof' over the tunnel, preventing light in
// tunnels at mapchunk borders when generating mapchunks upwards.
if (y > node_max.Y)
continue;
float d1 = contour(noise_cave1->result[index]);
float d2 = contour(noise_cave2->result[index]);
if (d1 * d2 > 0.125f) {

View File

@ -883,6 +883,12 @@ void MapgenV7::generateCaves(s16 max_stone_y)
for (s16 y = node_max.Y + 1; y >= node_min.Y - 1;
y--, index3d -= ystride, vm->m_area.add_y(em, vi, -1)) {
// Don't excavate the overgenerated stone at node_max.Y + 1,
// this creates a 'roof' over the tunnel, preventing light in
// tunnels at mapchunk borders when generating mapchunks upwards.
if (y > node_max.Y)
continue;
content_t c = vm->m_data[vi].getContent();
if (c == CONTENT_AIR || c == biome->c_water_top ||
c == biome->c_water) {

View File

@ -931,6 +931,12 @@ void MapgenValleys::generateCaves(s16 max_stone_y)
for (s16 y = node_max.Y + 1;
y >= node_min.Y - 1;
y--, index_3d -= ystride, vm->m_area.add_y(em, index_data, -1)) {
// Don't excavate the overgenerated stone at node_max.Y + 1,
// this creates a 'roof' over the tunnel, preventing light in
// tunnels at mapchunk borders when generating mapchunks upwards.
if (y > node_max.Y)
continue;
float terrain = noise_terrain_height->result[index_2d];
// Saves some time.

View File

@ -214,12 +214,12 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot)
}
}
static std::vector<aabb3f> transformNodeBox(const MapNode &n,
const NodeBox &nodebox, INodeDefManager *nodemgr)
void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
INodeDefManager *nodemgr, std::vector<aabb3f> *p_boxes, u8 neighbors = 0)
{
std::vector<aabb3f> boxes;
if(nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED)
{
std::vector<aabb3f> &boxes = *p_boxes;
if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
const std::vector<aabb3f> &fixed = nodebox.fixed;
int facedir = n.getFaceDir(nodemgr);
u8 axisdir = facedir>>2;
@ -395,32 +395,71 @@ static std::vector<aabb3f> transformNodeBox(const MapNode &n,
boxes.push_back(box);
}
}
else if (nodebox.type == NODEBOX_CONNECTED)
{
size_t boxes_size = boxes.size();
boxes_size += nodebox.fixed.size();
if (neighbors & 1)
boxes_size += nodebox.connect_top.size();
if (neighbors & 2)
boxes_size += nodebox.connect_bottom.size();
if (neighbors & 4)
boxes_size += nodebox.connect_front.size();
if (neighbors & 8)
boxes_size += nodebox.connect_left.size();
if (neighbors & 16)
boxes_size += nodebox.connect_back.size();
if (neighbors & 32)
boxes_size += nodebox.connect_right.size();
boxes.reserve(boxes_size);
#define BOXESPUSHBACK(c) do { \
for (std::vector<aabb3f>::const_iterator \
it = (c).begin(); \
it != (c).end(); ++it) \
(boxes).push_back(*it); \
} while (0)
BOXESPUSHBACK(nodebox.fixed);
if (neighbors & 1)
BOXESPUSHBACK(nodebox.connect_top);
if (neighbors & 2)
BOXESPUSHBACK(nodebox.connect_bottom);
if (neighbors & 4)
BOXESPUSHBACK(nodebox.connect_front);
if (neighbors & 8)
BOXESPUSHBACK(nodebox.connect_left);
if (neighbors & 16)
BOXESPUSHBACK(nodebox.connect_back);
if (neighbors & 32)
BOXESPUSHBACK(nodebox.connect_right);
}
else // NODEBOX_REGULAR
{
boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2));
}
return boxes;
}
std::vector<aabb3f> MapNode::getNodeBoxes(INodeDefManager *nodemgr) const
void MapNode::getNodeBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
{
const ContentFeatures &f = nodemgr->get(*this);
return transformNodeBox(*this, f.node_box, nodemgr);
transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
}
std::vector<aabb3f> MapNode::getCollisionBoxes(INodeDefManager *nodemgr) const
void MapNode::getCollisionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
{
const ContentFeatures &f = nodemgr->get(*this);
if (f.collision_box.fixed.empty())
return transformNodeBox(*this, f.node_box, nodemgr);
transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
else
return transformNodeBox(*this, f.collision_box, nodemgr);
transformNodeBox(*this, f.collision_box, nodemgr, boxes, neighbors);
}
std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
void MapNode::getSelectionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes)
{
const ContentFeatures &f = nodemgr->get(*this);
return transformNodeBox(*this, f.selection_box, nodemgr);
transformNodeBox(*this, f.selection_box, nodemgr, boxes);
}
u8 MapNode::getMaxLevel(INodeDefManager *nodemgr) const

View File

@ -240,17 +240,17 @@ struct MapNode
/*
Gets list of node boxes (used for rendering (NDT_NODEBOX))
*/
std::vector<aabb3f> getNodeBoxes(INodeDefManager *nodemgr) const;
void getNodeBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors = 0);
/*
Gets list of selection boxes
*/
std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
void getSelectionBoxes(INodeDefManager *nodemg, std::vector<aabb3f> *boxes);
/*
Gets list of collision boxes
*/
std::vector<aabb3f> getCollisionBoxes(INodeDefManager *nodemgr) const;
void getCollisionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors = 0);
/*
Liquid helpers

View File

@ -552,6 +552,7 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
*pkt >> pos >> pitch >> yaw;
player->got_teleported = true;
player->setPosition(pos);
infostream << "Client got TOCLIENT_MOVE_PLAYER"

View File

@ -135,6 +135,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL_VERSION 27:
backface_culling: backwards compatibility for playing with
newer client on pre-27 servers.
Add nodedef v3 - connected nodeboxes
*/
#define LATEST_PROTOCOL_VERSION 27

View File

@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h"
#include "debug.h"
#include "gamedef.h"
#include "mapnode.h"
#include <fstream> // Used in applyTextureOverrides()
/*
@ -48,44 +49,91 @@ void NodeBox::reset()
wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, 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);
// no default for other parts
connect_top.clear();
connect_bottom.clear();
connect_front.clear();
connect_left.clear();
connect_back.clear();
connect_right.clear();
}
void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
{
int version = protocol_version >= 21 ? 2 : 1;
int version = 1;
if (protocol_version >= 27)
version = 3;
else if (protocol_version >= 21)
version = 2;
writeU8(os, version);
if (version == 1 && type == NODEBOX_LEVELED)
writeU8(os, NODEBOX_FIXED);
else
writeU8(os, type);
switch (type) {
case NODEBOX_LEVELED:
case NODEBOX_FIXED:
if (version == 1)
writeU8(os, NODEBOX_FIXED);
else
writeU8(os, type);
if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
{
writeU16(os, fixed.size());
for(std::vector<aabb3f>::const_iterator
for (std::vector<aabb3f>::const_iterator
i = fixed.begin();
i != fixed.end(); ++i)
{
writeV3F1000(os, i->MinEdge);
writeV3F1000(os, i->MaxEdge);
}
}
else if(type == NODEBOX_WALLMOUNTED)
{
break;
case NODEBOX_WALLMOUNTED:
writeU8(os, type);
writeV3F1000(os, wall_top.MinEdge);
writeV3F1000(os, wall_top.MaxEdge);
writeV3F1000(os, wall_bottom.MinEdge);
writeV3F1000(os, wall_bottom.MaxEdge);
writeV3F1000(os, wall_side.MinEdge);
writeV3F1000(os, wall_side.MaxEdge);
break;
case NODEBOX_CONNECTED:
if (version <= 2) {
// send old clients nodes that can't be walked through
// to prevent abuse
writeU8(os, NODEBOX_FIXED);
writeU16(os, 1);
writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
} else {
writeU8(os, type);
#define WRITEBOX(box) do { \
writeU16(os, (box).size()); \
for (std::vector<aabb3f>::const_iterator \
i = (box).begin(); \
i != (box).end(); ++i) { \
writeV3F1000(os, i->MinEdge); \
writeV3F1000(os, i->MaxEdge); \
}; } while (0)
WRITEBOX(fixed);
WRITEBOX(connect_top);
WRITEBOX(connect_bottom);
WRITEBOX(connect_front);
WRITEBOX(connect_left);
WRITEBOX(connect_back);
WRITEBOX(connect_right);
}
break;
default:
writeU8(os, type);
break;
}
}
void NodeBox::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version < 1 || version > 2)
if (version < 1 || version > 3)
throw SerializationError("unsupported NodeBox version");
reset();
@ -112,6 +160,26 @@ void NodeBox::deSerialize(std::istream &is)
wall_side.MinEdge = readV3F1000(is);
wall_side.MaxEdge = readV3F1000(is);
}
else if (type == NODEBOX_CONNECTED)
{
#define READBOXES(box) do { \
count = readU16(is); \
(box).reserve(count); \
while (count--) { \
v3f min = readV3F1000(is); \
v3f max = readV3F1000(is); \
(box).push_back(aabb3f(min, max)); }; } while (0)
u16 count;
READBOXES(fixed);
READBOXES(connect_top);
READBOXES(connect_bottom);
READBOXES(connect_front);
READBOXES(connect_left);
READBOXES(connect_back);
READBOXES(connect_right);
}
}
/*
@ -261,6 +329,9 @@ void ContentFeatures::reset()
sound_footstep = SimpleSoundSpec();
sound_dig = SimpleSoundSpec("__group");
sound_dug = SimpleSoundSpec();
connects_to.clear();
connects_to_ids.clear();
connect_sides = 0;
}
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
@ -328,6 +399,11 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
os<<serializeString(mesh);
collision_box.serialize(os, protocol_version);
writeU8(os, floodable);
writeU16(os, connects_to_ids.size());
for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
i != connects_to_ids.end(); ++i)
writeU16(os, *i);
writeU8(os, connect_sides);
}
void ContentFeatures::deSerialize(std::istream &is)
@ -402,6 +478,11 @@ void ContentFeatures::deSerialize(std::istream &is)
mesh = deSerializeString(is);
collision_box.deSerialize(is);
floodable = readU8(is);
u16 connects_to_size = readU16(is);
connects_to_ids.clear();
for (u16 i = 0; i < connects_to_size; i++)
connects_to_ids.insert(readU16(is));
connect_sides = readU8(is);
}catch(SerializationError &e) {};
}
@ -419,7 +500,7 @@ public:
inline virtual const ContentFeatures& get(const MapNode &n) const;
virtual bool getId(const std::string &name, content_t &result) const;
virtual content_t getId(const std::string &name) const;
virtual void getIds(const std::string &name, std::set<content_t> &result) const;
virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
virtual const ContentFeatures& get(const std::string &name) const;
content_t allocateId();
virtual content_t set(const std::string &name, const ContentFeatures &def);
@ -439,6 +520,8 @@ public:
virtual bool cancelNodeResolveCallback(NodeResolver *nr);
virtual void runNodeResolveCallbacks();
virtual void resetNodeResolveState();
virtual void mapNodeboxConnections();
virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
private:
void addNameIdMapping(content_t i, std::string name);
@ -603,22 +686,23 @@ content_t CNodeDefManager::getId(const std::string &name) const
}
void CNodeDefManager::getIds(const std::string &name,
bool CNodeDefManager::getIds(const std::string &name,
std::set<content_t> &result) const
{
//TimeTaker t("getIds", NULL, PRECISION_MICRO);
if (name.substr(0,6) != "group:") {
content_t id = CONTENT_IGNORE;
if(getId(name, id))
bool exists = getId(name, id);
if (exists)
result.insert(id);
return;
return exists;
}
std::string group = name.substr(6);
std::map<std::string, GroupItems>::const_iterator
i = m_group_to_items.find(group);
if (i == m_group_to_items.end())
return;
return true;
const GroupItems &items = i->second;
for (GroupItems::const_iterator j = items.begin();
@ -627,6 +711,7 @@ void CNodeDefManager::getIds(const std::string &name,
result.insert((*j).first);
}
//printf("getIds: %dus\n", t.stop());
return true;
}
@ -1436,6 +1521,57 @@ void CNodeDefManager::resetNodeResolveState()
m_pending_resolve_callbacks.clear();
}
void CNodeDefManager::mapNodeboxConnections()
{
for (u32 i = 0; i < m_content_features.size(); i++) {
ContentFeatures *f = &m_content_features[i];
if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
continue;
for (std::vector<std::string>::iterator it = f->connects_to.begin();
it != f->connects_to.end(); ++it) {
getIds(*it, f->connects_to_ids);
}
}
}
bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
{
const ContentFeatures &f1 = get(from);
if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
return false;
// lookup target in connected set
if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
return false;
const ContentFeatures &f2 = get(to);
if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
// ignores actually looking if back connection exists
return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
// does to node declare usable faces?
if (f2.connect_sides > 0) {
if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
static const u8 rot[33 * 4] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32, 16, 8, 4 // 32 - left
};
return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
}
return (f2.connect_sides & connect_face);
}
// the target is just a regular node, so connect no matter back connection
return true;
}
////
//// NodeResolver

View File

@ -80,6 +80,7 @@ enum NodeBoxType
NODEBOX_FIXED, // Static separately defined box(es)
NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side)
NODEBOX_LEVELED, // Same as fixed, but with dynamic height from param2. for snow, ...
NODEBOX_CONNECTED, // optionally draws nodeboxes if a neighbor node attaches
};
struct NodeBox
@ -92,6 +93,13 @@ struct NodeBox
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_bottom;
std::vector<aabb3f> connect_front;
std::vector<aabb3f> connect_left;
std::vector<aabb3f> connect_back;
std::vector<aabb3f> connect_right;
NodeBox()
{ reset(); }
@ -263,12 +271,17 @@ struct ContentFeatures
bool legacy_facedir_simple;
// Set to true if wall_mounted used to be set to true
bool legacy_wallmounted;
// for NDT_CONNECTED pairing
u8 connect_sides;
// Sound properties
SimpleSoundSpec sound_footstep;
SimpleSoundSpec sound_dig;
SimpleSoundSpec sound_dug;
std::vector<std::string> connects_to;
std::set<content_t> connects_to_ids;
/*
Methods
*/
@ -303,7 +316,8 @@ public:
virtual bool getId(const std::string &name, content_t &result) const=0;
virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names
virtual void getIds(const std::string &name, std::set<content_t> &result)
// returns false if node name not found, true otherwise
virtual bool getIds(const std::string &name, std::set<content_t> &result)
const=0;
virtual const ContentFeatures &get(const std::string &name) const=0;
@ -313,6 +327,7 @@ public:
virtual void pendNodeResolve(NodeResolver *nr)=0;
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
virtual bool nodeboxConnects(const MapNode from, const MapNode to, u8 connect_face)=0;
};
class IWritableNodeDefManager : public INodeDefManager {
@ -327,7 +342,7 @@ public:
// If not found, returns CONTENT_IGNORE
virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names
virtual void getIds(const std::string &name, std::set<content_t> &result)
virtual bool getIds(const std::string &name, std::set<content_t> &result)
const=0;
// If not found, returns the features of CONTENT_UNKNOWN
virtual const ContentFeatures &get(const std::string &name) const=0;
@ -367,6 +382,7 @@ public:
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
virtual void runNodeResolveCallbacks()=0;
virtual void resetNodeResolveState()=0;
virtual void mapNodeboxConnections()=0;
};
IWritableNodeDefManager *createNodeDefManager();

View File

@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Player::Player(IGameDef *gamedef, const char *name):
got_teleported(false),
touching_ground(false),
in_liquid(false),
in_liquid_stable(false),

View File

@ -318,6 +318,7 @@ public:
// Use a function, if isDead can be defined by other conditions
bool isDead() { return hp == 0; }
bool got_teleported;
bool touching_ground;
// This oscillates so that the player jumps a bit above the surface
bool in_liquid;

View File

@ -535,6 +535,46 @@ ContentFeatures read_content_features(lua_State *L, int index)
f.node_box = read_nodebox(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "connects_to");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, table) != 0) {
// Value at -1
f.connects_to.push_back(lua_tostring(L, -1));
lua_pop(L, 1);
}
}
lua_pop(L, 1);
lua_getfield(L, index, "connect_sides");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, table) != 0) {
// Value at -1
std::string side(lua_tostring(L, -1));
// Note faces are flipped to make checking easier
if (side == "top")
f.connect_sides |= 2;
else if (side == "bottom")
f.connect_sides |= 1;
else if (side == "front")
f.connect_sides |= 16;
else if (side == "left")
f.connect_sides |= 32;
else if (side == "back")
f.connect_sides |= 4;
else if (side == "right")
f.connect_sides |= 8;
else
warningstream << "Unknown value for \"connect_sides\": "
<< side << std::endl;
lua_pop(L, 1);
}
}
lua_pop(L, 1);
lua_getfield(L, index, "selection_box");
if(lua_istable(L, -1))
f.selection_box = read_nodebox(L, -1);
@ -627,25 +667,31 @@ NodeBox read_nodebox(lua_State *L, int index)
nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR);
lua_getfield(L, index, "fixed");
if(lua_istable(L, -1))
nodebox.fixed = read_aabb3f_vector(L, -1, BS);
lua_pop(L, 1);
#define NODEBOXREAD(n, s) \
do { \
lua_getfield(L, index, (s)); \
if (lua_istable(L, -1)) \
(n) = read_aabb3f(L, -1, BS); \
lua_pop(L, 1); \
} while (0)
lua_getfield(L, index, "wall_top");
if(lua_istable(L, -1))
nodebox.wall_top = read_aabb3f(L, -1, BS);
lua_pop(L, 1);
lua_getfield(L, index, "wall_bottom");
if(lua_istable(L, -1))
nodebox.wall_bottom = read_aabb3f(L, -1, BS);
lua_pop(L, 1);
lua_getfield(L, index, "wall_side");
if(lua_istable(L, -1))
nodebox.wall_side = read_aabb3f(L, -1, BS);
lua_pop(L, 1);
#define NODEBOXREADVEC(n, s) \
do { \
lua_getfield(L, index, (s)); \
if (lua_istable(L, -1)) \
(n) = read_aabb3f_vector(L, -1, BS); \
lua_pop(L, 1); \
} while (0)
NODEBOXREADVEC(nodebox.fixed, "fixed");
NODEBOXREAD(nodebox.wall_top, "wall_top");
NODEBOXREAD(nodebox.wall_bottom, "wall_bottom");
NODEBOXREAD(nodebox.wall_side, "wall_side");
NODEBOXREADVEC(nodebox.connect_top, "connect_top");
NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom");
NODEBOXREADVEC(nodebox.connect_front, "connect_front");
NODEBOXREADVEC(nodebox.connect_left, "connect_left");
NODEBOXREADVEC(nodebox.connect_back, "connect_back");
NODEBOXREADVEC(nodebox.connect_right, "connect_right");
}
return nodebox;
}

View File

@ -83,6 +83,7 @@ public:
protected:
friend class LuaABM;
friend class LuaLBM;
friend class InvRef;
friend class ObjectRef;
friend class NodeMetaRef;

View File

@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "server.h"
void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
u32 blockseed)
u32 blockseed)
{
SCRIPTAPI_PRECHECKHEADER
@ -44,7 +44,7 @@ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
void ScriptApiEnv::environment_Step(float dtime)
{
SCRIPTAPI_PRECHECKHEADER
//infostream<<"scriptapi_environment_step"<<std::endl;
//infostream << "scriptapi_environment_step" << std::endl;
// Get core.registered_globalsteps
lua_getglobal(L, "core");
@ -58,7 +58,7 @@ void ScriptApiEnv::environment_Step(float dtime)
}
}
void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type)
void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &type)
{
SCRIPTAPI_PRECHECKHEADER
@ -82,78 +82,127 @@ void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type)
void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
{
SCRIPTAPI_PRECHECKHEADER
verbosestream<<"scriptapi_add_environment"<<std::endl;
verbosestream << "scriptapi_add_environment" << std::endl;
setEnv(env);
/*
Add ActiveBlockModifiers to environment
Add {Loading,Active}BlockModifiers to environment
*/
// Get core.registered_abms
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_abms");
luaL_checktype(L, -1, LUA_TTABLE);
int registered_abms = lua_gettop(L);
if(lua_istable(L, registered_abms)){
int table = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, table) != 0){
// key at index -2 and value at index -1
int id = lua_tonumber(L, -2);
int current_abm = lua_gettop(L);
if (!lua_istable(L, registered_abms)) {
lua_pop(L, 1);
throw LuaError("core.registered_abms was not a lua table, as expected.");
}
lua_pushnil(L);
while (lua_next(L, registered_abms)) {
// key at index -2 and value at index -1
int id = lua_tonumber(L, -2);
int current_abm = lua_gettop(L);
std::set<std::string> trigger_contents;
lua_getfield(L, current_abm, "nodenames");
if(lua_istable(L, -1)){
int table = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, table) != 0){
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
trigger_contents.insert(lua_tostring(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if(lua_isstring(L, -1)){
std::set<std::string> trigger_contents;
lua_getfield(L, current_abm, "nodenames");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, table)) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
trigger_contents.insert(lua_tostring(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
lua_pop(L, 1);
std::set<std::string> required_neighbors;
lua_getfield(L, current_abm, "neighbors");
if(lua_istable(L, -1)){
int table = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, table) != 0){
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
required_neighbors.insert(lua_tostring(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if(lua_isstring(L, -1)){
required_neighbors.insert(lua_tostring(L, -1));
}
lua_pop(L, 1);
float trigger_interval = 10.0;
getfloatfield(L, current_abm, "interval", trigger_interval);
int trigger_chance = 50;
getintfield(L, current_abm, "chance", trigger_chance);
bool simple_catch_up = true;
getboolfield(L, current_abm, "catch_up", simple_catch_up);
LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors,
trigger_interval, trigger_chance, simple_catch_up);
env->addActiveBlockModifier(abm);
// removes value, keeps key for next iteration
lua_pop(L, 1);
} else if (lua_isstring(L, -1)) {
trigger_contents.insert(lua_tostring(L, -1));
}
lua_pop(L, 1);
std::set<std::string> required_neighbors;
lua_getfield(L, current_abm, "neighbors");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, table)) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
required_neighbors.insert(lua_tostring(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if (lua_isstring(L, -1)) {
required_neighbors.insert(lua_tostring(L, -1));
}
lua_pop(L, 1);
float trigger_interval = 10.0;
getfloatfield(L, current_abm, "interval", trigger_interval);
int trigger_chance = 50;
getintfield(L, current_abm, "chance", trigger_chance);
bool simple_catch_up = true;
getboolfield(L, current_abm, "catch_up", simple_catch_up);
LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors,
trigger_interval, trigger_chance, simple_catch_up);
env->addActiveBlockModifier(abm);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
lua_pop(L, 1);
// Get core.registered_lbms
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_lbms");
int registered_lbms = lua_gettop(L);
if (!lua_istable(L, registered_lbms)) {
lua_pop(L, 1);
throw LuaError("core.registered_lbms was not a lua table, as expected.");
}
lua_pushnil(L);
while (lua_next(L, registered_lbms)) {
// key at index -2 and value at index -1
int id = lua_tonumber(L, -2);
int current_lbm = lua_gettop(L);
std::set<std::string> trigger_contents;
lua_getfield(L, current_lbm, "nodenames");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, table)) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
trigger_contents.insert(lua_tostring(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if (lua_isstring(L, -1)) {
trigger_contents.insert(lua_tostring(L, -1));
}
lua_pop(L, 1);
std::string name;
getstringfield(L, current_lbm, "name", name);
bool run_at_every_load = getboolfield_default(L, current_lbm,
"run_at_every_load", false);
LuaLBM *lbm = new LuaLBM(L, id, trigger_contents, name,
run_at_every_load);
env->addLoadingBlockModifierDef(lbm);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
lua_pop(L, 1);
}

View File

@ -26,7 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class ServerEnvironment;
struct ScriptCallbackState;
class ScriptApiEnv : virtual public ScriptApiBase {
class ScriptApiEnv : virtual public ScriptApiBase
{
public:
// Called on environment step
void environment_Step(float dtime);
@ -35,7 +36,7 @@ public:
void environment_OnGenerated(v3s16 minp, v3s16 maxp, u32 blockseed);
// Called on player event
void player_event(ServerActiveObject *player, std::string type);
void player_event(ServerActiveObject *player, const std::string &type);
// Called after emerge of a block queued from core.emerge_area()
void on_emerge_area_completion(v3s16 blockpos, int action,

View File

@ -82,6 +82,7 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] =
{NODEBOX_FIXED, "fixed"},
{NODEBOX_WALLMOUNTED, "wallmounted"},
{NODEBOX_LEVELED, "leveled"},
{NODEBOX_CONNECTED, "connected"},
{0, NULL},
};

View File

@ -25,9 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define CHECK_SECURE_PATH(L, path) \
if (!ScriptApiSecurity::checkPath(L, path)) { \
lua_pushstring(L, (std::string("Attempt to access external file ") + \
path + " with mod security on.").c_str()); \
lua_error(L); \
throw LuaError(std::string("Attempt to access external file ") + \
path + " with mod security on."); \
}
#define CHECK_SECURE_PATH_OPTIONAL(L, path) \
if (ScriptApiSecurity::isSecure(L)) { \

View File

@ -23,11 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_converter.h"
#include "cpp_api/s_security.h"
#include "irr_v3d.h"
#include "areastore.h"
#include "util/areastore.h"
#include "filesys.h"
#ifndef ANDROID
#include "cmake_config.h"
#endif
#include <fstream>
static inline void get_data_and_border_flags(lua_State *L, u8 start_i,
@ -73,6 +70,22 @@ static inline void push_areas(lua_State *L, const std::vector<Area *> &areas,
}
}
// Deserializes value and handles errors
static int deserialization_helper(lua_State *L, AreaStore *as,
std::istream &is)
{
try {
as->deserialize(is);
} catch (const SerializationError &e) {
lua_pushboolean(L, false);
lua_pushstring(L, e.what());
return 2;
}
lua_pushboolean(L, true);
return 1;
}
// garbage collector
int LuaAreaStore::gc_object(lua_State *L)
{
@ -151,7 +164,7 @@ int LuaAreaStore::l_get_areas_in_area(lua_State *L)
return 1;
}
// insert_area(edge1, edge2, data)
// insert_area(edge1, edge2, data, id)
int LuaAreaStore::l_insert_area(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@ -159,26 +172,18 @@ int LuaAreaStore::l_insert_area(lua_State *L)
LuaAreaStore *o = checkobject(L, 1);
AreaStore *ast = o->as;
Area a;
a.minedge = check_v3s16(L, 2);
a.maxedge = check_v3s16(L, 3);
a.extremifyEdges();
a.id = ast->getFreeId(a.minedge, a.maxedge);
if (a.id == AREA_ID_INVALID) {
// couldn't get free id
lua_pushnil(L);
return 1;
}
Area a(check_v3s16(L, 2), check_v3s16(L, 3));
size_t d_len;
const char *data = luaL_checklstring(L, 4, &d_len);
a.data = std::string(data, d_len);
ast->insertArea(a);
if (lua_isnumber(L, 5))
a.id = lua_tonumber(L, 5);
if (!ast->insertArea(&a))
return 0;
lua_pushnumber(L, a.id);
return 1;
@ -231,17 +236,15 @@ int LuaAreaStore::l_set_cache_params(lua_State *L)
return 0;
}
#if 0
// to_string()
int LuaAreaStore::l_to_string(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
LuaAreaStore *o = checkobject(L, 1);
AreaStore *ast = o->as;
std::ostringstream os(std::ios_base::binary);
ast->serialize(os);
o->as->serialize(os);
std::string str = os.str();
lua_pushlstring(L, str.c_str(), str.length());
@ -272,16 +275,12 @@ int LuaAreaStore::l_from_string(lua_State *L)
NO_MAP_LOCK_REQUIRED;
LuaAreaStore *o = checkobject(L, 1);
AreaStore *ast = o->as;
size_t len;
const char *str = luaL_checklstring(L, 2, &len);
std::istringstream is(std::string(str, len), std::ios::binary);
bool success = ast->deserialize(is);
lua_pushboolean(L, success);
return 1;
return deserialization_helper(L, o->as, is);
}
// from_file(filename)
@ -290,26 +289,17 @@ int LuaAreaStore::l_from_file(lua_State *L)
NO_MAP_LOCK_REQUIRED;
LuaAreaStore *o = checkobject(L, 1);
AreaStore *ast = o->as;
const char *filename = luaL_checkstring(L, 2);
CHECK_SECURE_PATH_OPTIONAL(L, filename);
std::ifstream is(filename, std::ios::binary);
bool success = ast->deserialize(is);
lua_pushboolean(L, success);
return 1;
return deserialization_helper(L, o->as, is);
}
#endif
LuaAreaStore::LuaAreaStore()
{
#if USE_SPATIAL
this->as = new SpatialAreaStore();
#else
this->as = new VectorAreaStore();
#endif
this->as = AreaStore::getOptimalImplementation();
}
LuaAreaStore::LuaAreaStore(const std::string &type)
@ -395,9 +385,9 @@ const luaL_reg LuaAreaStore::methods[] = {
luamethod(LuaAreaStore, reserve),
luamethod(LuaAreaStore, remove_area),
luamethod(LuaAreaStore, set_cache_params),
/* luamethod(LuaAreaStore, to_string),
luamethod(LuaAreaStore, to_string),
luamethod(LuaAreaStore, to_file),
luamethod(LuaAreaStore, from_string),
luamethod(LuaAreaStore, from_file),*/
luamethod(LuaAreaStore, from_file),
{0,0}
};

View File

@ -17,15 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef L_AREASTORE_H_
#define L_AREASTORE_H_
#ifndef L_AREA_STORE_H_
#define L_AREA_STORE_H_
#include "lua_api/l_base.h"
#include "areastore.h"
/*
AreaStore
*/
class AreaStore;
class LuaAreaStore : public ModApiBase {
private:
@ -44,11 +43,11 @@ private:
static int l_set_cache_params(lua_State *L);
/* static int l_to_string(lua_State *L);
static int l_to_string(lua_State *L);
static int l_to_file(lua_State *L);
static int l_from_string(lua_State *L);
static int l_from_file(lua_State *L); */
static int l_from_file(lua_State *L);
public:
AreaStore *as;
@ -66,4 +65,4 @@ public:
static void Register(lua_State *L);
};
#endif /* L_AREASTORE_H_ */
#endif // L_AREA_STORE_H_

View File

@ -90,6 +90,46 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
lua_pop(L, 1); // Pop error handler
}
void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n)
{
GameScripting *scriptIface = env->getScriptIface();
scriptIface->realityCheck();
lua_State *L = scriptIface->getStack();
sanity_check(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
int error_handler = PUSH_ERROR_HANDLER(L);
// Get registered_lbms
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_lbms");
luaL_checktype(L, -1, LUA_TTABLE);
lua_remove(L, -2); // Remove core
// Get registered_lbms[m_id]
lua_pushnumber(L, m_id);
lua_gettable(L, -2);
FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table");
lua_remove(L, -2); // Remove registered_lbms
scriptIface->setOriginFromTable(-1);
// Call action
luaL_checktype(L, -1, LUA_TTABLE);
lua_getfield(L, -1, "action");
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_remove(L, -2); // Remove registered_lbms[m_id]
push_v3s16(L, p);
pushnode(L, n, env->getGameDef()->ndef());
int result = lua_pcall(L, 2, 0, error_handler);
if (result)
scriptIface->scriptError(result, "LuaLBM::trigger");
lua_pop(L, 1); // Pop error handler
}
void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
{
ScriptCallbackState *state = (ScriptCallbackState *)param;
@ -521,6 +561,15 @@ int ModApiEnvMod::l_get_timeofday(lua_State *L)
return 1;
}
// get_day_count() -> int
int ModApiEnvMod::l_get_day_count(lua_State *L)
{
GET_ENV_PTR;
lua_pushnumber(L, env->getDayCount());
return 1;
}
// get_gametime()
int ModApiEnvMod::l_get_gametime(lua_State *L)
{
@ -1015,6 +1064,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
API_FCT(set_timeofday);
API_FCT(get_timeofday);
API_FCT(get_gametime);
API_FCT(get_day_count);
API_FCT(find_node_near);
API_FCT(find_nodes_in_area);
API_FCT(find_nodes_in_area_under_air);

View File

@ -113,6 +113,9 @@ private:
// get_gametime()
static int l_get_gametime(lua_State *L);
// get_day_count() -> int
static int l_get_day_count(lua_State *L);
// find_node_near(pos, radius, nodenames) -> pos or nil
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
static int l_find_node_near(lua_State *L);
@ -220,6 +223,24 @@ public:
u32 active_object_count, u32 active_object_count_wider);
};
class LuaLBM : public LoadingBlockModifierDef
{
private:
int m_id;
public:
LuaLBM(lua_State *L, int id,
const std::set<std::string> &trigger_contents,
const std::string &name,
bool run_at_every_load):
m_id(id)
{
this->run_at_every_load = run_at_every_load;
this->trigger_contents = trigger_contents;
this->name = name;
}
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n);
};
struct ScriptCallbackState {
GameScripting *script;
int callback_ref;

View File

@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serialization.h"
#include "json/json.h"
#include "cpp_api/s_security.h"
#include "areastore.h"
#include "porting.h"
#include "debug.h"
#include "log.h"
@ -77,8 +76,7 @@ int ModApiUtil::l_get_us_time(lua_State *L)
#define CHECK_SECURE_SETTING(L, name) \
if (ScriptApiSecurity::isSecure(L) && \
name.compare(0, 7, "secure.") == 0) { \
lua_pushliteral(L, "Attempt to set secure setting."); \
lua_error(L); \
throw LuaError("Attempt to set secure setting."); \
}
// setting_set(name, value)

View File

@ -320,6 +320,9 @@ Server::Server(
// Perform pending node name resolutions
m_nodedef->runNodeResolveCallbacks();
// unmap node names for connected nodeboxes
m_nodedef->mapNodeboxConnections();
// init the recipe hashes to speed up crafting
m_craftdef->initHashes(this);
@ -344,10 +347,11 @@ Server::Server(
servermap->addEventReceiver(this);
// If file exists, load environment metadata
if(fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt"))
{
infostream<<"Server: Loading environment metadata"<<std::endl;
if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
infostream << "Server: Loading environment metadata" << std::endl;
m_env->loadMeta();
} else {
m_env->loadDefaultMeta();
}
// Add some test ActiveBlockModifiers to environment
@ -2560,7 +2564,7 @@ void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode
const std::string &str_reason, bool reconnect)
{
if (proto_ver >= 25) {
SendAccessDenied(peer_id, reason, str_reason);
SendAccessDenied(peer_id, reason, str_reason, reconnect);
} else {
std::wstring wreason = utf8_to_wide(
reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :

View File

@ -272,6 +272,10 @@ fake_function() {
gettext("Fallback font shadow alpha");
gettext("Screenshot folder");
gettext("Path to save screenshots at.");
gettext("Screenshot format");
gettext("Format of screenshots.");
gettext("Screenshot quality");
gettext("Screenshot quality. Only used for JPEG format.\n1 means worst quality; 100 means best quality.\nUse 0 for default quality.");
gettext("Advanced");
gettext("DPI");
gettext("Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.");
@ -421,7 +425,7 @@ fake_function() {
gettext("Map generation limit");
gettext("Where the map generator stops.\nPlease note:\n- Limited to 31000 (setting above has no effect)\n- The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks).\n- Those groups have an offset of -32, -32 nodes from the origin.\n- Only groups which are within the map_generation_limit are generated");
gettext("Mapgen flags");
gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand junglegrass, in all other mapgens this flag controls all decorations.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them.");
gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand junglegrass, in all other mapgens this flag controls all decorations.\nThe default flags set in the engine are: caves, light, decorations\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
gettext("Advanced");
gettext("Chunk size");
gettext("Size of chunks to be generated at once by mapgen, stated in mapblocks (16 nodes).");
@ -448,7 +452,7 @@ fake_function() {
gettext("Mapgen v5 cave2 noise parameters");
gettext("Mapgen v6");
gettext("Mapgen v6 flags");
gettext("Map generation attributes specific to Mapgen v6.\nWhen snowbiomes are enabled jungles are enabled and the jungles flag is ignored.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them.");
gettext("Map generation attributes specific to Mapgen v6.\nWhen snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.\nThe default flags set in the engine are: biomeblend, mudflow\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
gettext("Mapgen v6 desert frequency");
gettext("Controls size of deserts and beaches in Mapgen v6.\nWhen snowbiomes are enabled 'mgv6_freq_desert' is ignored.");
gettext("Mapgen v6 beach frequency");
@ -465,7 +469,7 @@ fake_function() {
gettext("Mapgen v6 apple trees noise parameters");
gettext("Mapgen v7");
gettext("Mapgen v7 flags");
gettext("Map generation attributes specific to Mapgen v7.\n'ridges' are the rivers.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them.");
gettext("Map generation attributes specific to Mapgen v7.\nThe 'ridges' flag controls the rivers.\nThe default flags set in the engine are: mountains, ridges\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
gettext("Mapgen v7 terrain base noise parameters");
gettext("Mapgen v7 terrain altitude noise parameters");
gettext("Mapgen v7 terrain persistation noise parameters");
@ -479,7 +483,7 @@ fake_function() {
gettext("Mapgen v7 cave2 noise parameters");
gettext("Mapgen flat");
gettext("Mapgen flat flags");
gettext("Map generation attributes specific to Mapgen flat.\nOccasional lakes and hills added to the flat world.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them.");
gettext("Map generation attributes specific to Mapgen flat.\nOccasional lakes and hills can be added to the flat world.\nThe default flags set in the engine are: none\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
gettext("Mapgen flat ground level");
gettext("Y of flat ground.");
gettext("Mapgen flat large cave depth");
@ -523,7 +527,7 @@ fake_function() {
gettext("Mapgen Valleys");
gettext("General");
gettext("Valleys C Flags");
gettext("Map generation attributes specific to Mapgen Valleys.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with \"no\" are used to explicitly disable them.\n\"altitude_chill\" makes higher elevations colder, which may cause biome issues.\n\"humid_rivers\" modifies the humidity around rivers and in areas where water would tend to pool. It may interfere with delicately adjusted biomes.");
gettext("Map generation attributes specific to Mapgen Valleys.\n'altitude_chill' makes higher elevations colder, which may cause biome issues.\n'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool,\nit may interfere with delicately adjusted biomes.\nThe default flags set in the engine are: altitude_chill, humid_rivers\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
gettext("Altitude Chill");
gettext("The altitude at which temperature drops by 20C");
gettext("Large cave depth");

View File

@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "test.h"
#include "areastore.h"
#include "util/areastore.h"
class TestAreaStore : public TestBase {
public:
@ -31,6 +31,7 @@ public:
void genericStoreTest(AreaStore *store);
void testVectorStore();
void testSpatialStore();
void testSerialization();
};
static TestAreaStore g_test_instance;
@ -41,6 +42,7 @@ void TestAreaStore::runTests(IGameDef *gamedef)
#if USE_SPATIAL
TEST(testSpatialStore);
#endif
TEST(testSerialization);
}
////////////////////////////////////////////////////////////////////////////////
@ -62,18 +64,15 @@ void TestAreaStore::testSpatialStore()
void TestAreaStore::genericStoreTest(AreaStore *store)
{
Area a(v3s16(-10, -3, 5), v3s16(0, 29, 7));
a.id = 1;
Area b(v3s16(-5, -2, 5), v3s16(0, 28, 6));
b.id = 2;
Area c(v3s16(-7, -3, 6), v3s16(-1, 27, 7));
c.id = 3;
std::vector<Area *> res;
UASSERTEQ(size_t, store->size(), 0);
store->reserve(2); // sic
store->insertArea(a);
store->insertArea(b);
store->insertArea(c);
store->insertArea(&a);
store->insertArea(&b);
store->insertArea(&c);
UASSERTEQ(size_t, store->size(), 3);
store->getAreasForPos(&res, v3s16(-1, 0, 6));
@ -81,20 +80,18 @@ void TestAreaStore::genericStoreTest(AreaStore *store)
res.clear();
store->getAreasForPos(&res, v3s16(0, 0, 7));
UASSERTEQ(size_t, res.size(), 1);
UASSERTEQ(u32, res[0]->id, 1);
res.clear();
store->removeArea(1);
store->removeArea(a.id);
store->getAreasForPos(&res, v3s16(0, 0, 7));
UASSERTEQ(size_t, res.size(), 0);
res.clear();
store->insertArea(a);
store->insertArea(&a);
store->getAreasForPos(&res, v3s16(0, 0, 7));
UASSERTEQ(size_t, res.size(), 1);
UASSERTEQ(u32, res[0]->id, 1);
res.clear();
store->getAreasInArea(&res, v3s16(-10, -3, 5), v3s16(0, 29, 7), false);
@ -109,21 +106,57 @@ void TestAreaStore::genericStoreTest(AreaStore *store)
UASSERTEQ(size_t, res.size(), 3);
res.clear();
store->removeArea(1);
store->removeArea(2);
store->removeArea(3);
store->removeArea(a.id);
store->removeArea(b.id);
store->removeArea(c.id);
Area d(v3s16(-100, -300, -200), v3s16(-50, -200, -100));
d.id = 4;
d.data = "Hi!";
store->insertArea(d);
store->insertArea(&d);
store->getAreasForPos(&res, v3s16(-75, -250, -150));
UASSERTEQ(size_t, res.size(), 1);
UASSERTEQ(u32, res[0]->id, 4);
UASSERTEQ(u16, res[0]->data.size(), 3);
UASSERT(strncmp(res[0]->data.c_str(), "Hi!", 3) == 0);
res.clear();
store->removeArea(4);
store->removeArea(d.id);
}
void TestAreaStore::testSerialization()
{
VectorAreaStore store;
Area a(v3s16(-1, 0, 1), v3s16(0, 1, 2));
a.data = "Area A";
store.insertArea(&a);
Area b(v3s16(123, 456, 789), v3s16(32000, 100, 10));
b.data = "Area B";
store.insertArea(&b);
std::ostringstream os;
store.serialize(os);
std::string str = os.str();
std::string str_wanted("\x00" // Version
"\x00\x02" // Count
"\xFF\xFF\x00\x00\x00\x01" // Area A min edge
"\x00\x00\x00\x01\x00\x02" // Area A max edge
"\x00\x06" // Area A data length
"Area A" // Area A data
"\x00\x7B\x00\x64\x00\x0A" // Area B min edge (last two swapped with max edge for sorting)
"\x7D\x00\x01\xC8\x03\x15" // Area B max edge (^)
"\x00\x06" // Area B data length
"Area B", // Area B data
1 + 2 +
6 + 6 + 2 + 6 +
6 + 6 + 2 + 6);
UASSERTEQ(std::string, str, str_wanted);
std::istringstream is(str);
store.deserialize(is);
UASSERTEQ(size_t, store.size(), 4); // deserialize() doesn't clear the store
}

View File

@ -1,4 +1,5 @@
set(UTIL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp

View File

@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "areastore.h"
#include "util/areastore.h"
#include "util/serialize.h"
#include "util/container.h"
@ -44,97 +44,70 @@ with this program; if not, write to the Free Software Foundation, Inc.,
AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Y) && \
AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Z))
u16 AreaStore::size() const
{
return areas_map.size();
}
u32 AreaStore::getFreeId(v3s16 minedge, v3s16 maxedge)
AreaStore *AreaStore::getOptimalImplementation()
{
int keep_on = 100;
while (keep_on--) {
m_highest_id++;
// Handle overflows, we dont want to return 0
if (m_highest_id == AREA_ID_INVALID)
m_highest_id++;
if (areas_map.find(m_highest_id) == areas_map.end())
return m_highest_id;
}
// search failed
return AREA_ID_INVALID;
#if USE_SPATIAL
return new SpatialAreaStore();
#else
return new VectorAreaStore();
#endif
}
const Area *AreaStore::getArea(u32 id) const
{
const Area *res = NULL;
std::map<u32, Area>::const_iterator itr = areas_map.find(id);
if (itr != areas_map.end()) {
res = &itr->second;
}
return res;
AreaMap::const_iterator it = areas_map.find(id);
if (it == areas_map.end())
return NULL;
return &it->second;
}
#if 0
Currently, serialisation is commented out. This is because of multiple reasons:
1. Why do we store the areastore into a file, why not into the database?
2. We don't use libspatial's serialisation, but we should, or perhaps not, because
it would remove the ability to switch. Perhaps write migration routines?
3. Various things need fixing, e.g. the size is serialized as
c++ implementation defined size_t
bool AreaStore::deserialize(std::istream &is)
{
u8 ver = readU8(is);
if (ver != 1)
return false;
u16 count_areas = readU16(is);
for (u16 i = 0; i < count_areas; i++) {
// deserialize an area
Area a;
a.id = readU32(is);
a.minedge = readV3S16(is);
a.maxedge = readV3S16(is);
a.datalen = readU16(is);
a.data = new char[a.datalen];
is.read((char *) a.data, a.datalen);
insertArea(a);
}
return true;
}
static bool serialize_area(void *ostr, Area *a)
{
std::ostream &os = *((std::ostream *) ostr);
writeU32(os, a->id);
writeV3S16(os, a->minedge);
writeV3S16(os, a->maxedge);
writeU16(os, a->datalen);
os.write(a->data, a->datalen);
return false;
}
void AreaStore::serialize(std::ostream &os) const
{
// write initial data
writeU8(os, 1); // serialisation version
writeU16(os, areas_map.size()); //DANGER: not platform independent
forEach(&serialize_area, &os);
writeU8(os, 0); // Serialisation version
// TODO: Compression?
writeU16(os, areas_map.size());
for (AreaMap::const_iterator it = areas_map.begin();
it != areas_map.end(); ++it) {
const Area &a = it->second;
writeV3S16(os, a.minedge);
writeV3S16(os, a.maxedge);
writeU16(os, a.data.size());
os.write(a.data.data(), a.data.size());
}
}
#endif
void AreaStore::deserialize(std::istream &is)
{
u8 ver = readU8(is);
if (ver != 0)
throw SerializationError("Unknown AreaStore "
"serialization version!");
u16 num_areas = readU16(is);
for (u32 i = 0; i < num_areas; ++i) {
Area a;
a.minedge = readV3S16(is);
a.maxedge = readV3S16(is);
u16 data_len = readU16(is);
char *data = new char[data_len];
is.read(data, data_len);
a.data = std::string(data, data_len);
insertArea(&a);
}
}
void AreaStore::invalidateCache()
{
if (cache_enabled) {
if (m_cache_enabled) {
m_res_cache.invalidate();
}
}
void AreaStore::setCacheParams(bool enabled, u8 block_radius, size_t limit)
{
cache_enabled = enabled;
m_cache_enabled = enabled;
m_cacheblock_radius = MYMAX(block_radius, 16);
m_res_cache.setLimit(MYMAX(limit, 20));
invalidateCache();
@ -163,7 +136,7 @@ void AreaStore::cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *de
void AreaStore::getAreasForPos(std::vector<Area *> *result, v3s16 pos)
{
if (cache_enabled) {
if (m_cache_enabled) {
v3s16 mblock = getContainerPos(pos, m_cacheblock_radius);
const std::vector<Area *> *pre_list = m_res_cache.lookupCache(mblock);
@ -185,42 +158,41 @@ void AreaStore::getAreasForPos(std::vector<Area *> *result, v3s16 pos)
////
void VectorAreaStore::insertArea(const Area &a)
bool VectorAreaStore::insertArea(Area *a)
{
areas_map[a.id] = a;
m_areas.push_back(&(areas_map[a.id]));
if (a->id == U32_MAX)
a->id = getNextId();
std::pair<AreaMap::iterator, bool> res =
areas_map.insert(std::make_pair(a->id, *a));
if (!res.second)
// ID is not unique
return false;
m_areas.push_back(&res.first->second);
invalidateCache();
}
void VectorAreaStore::reserve(size_t count)
{
m_areas.reserve(count);
return true;
}
bool VectorAreaStore::removeArea(u32 id)
{
std::map<u32, Area>::iterator itr = areas_map.find(id);
if (itr != areas_map.end()) {
size_t msiz = m_areas.size();
for (size_t i = 0; i < msiz; i++) {
Area * b = m_areas[i];
if (b->id == id) {
areas_map.erase(itr);
m_areas.erase(m_areas.begin() + i);
invalidateCache();
return true;
}
AreaMap::iterator it = areas_map.find(id);
if (it == areas_map.end())
return false;
Area *a = &it->second;
for (std::vector<Area *>::iterator v_it = m_areas.begin();
v_it != m_areas.end(); ++v_it) {
if (*v_it == a) {
m_areas.erase(v_it);
break;
}
// we should never get here, it means we did find it in map,
// but not in the vector
}
return false;
areas_map.erase(it);
invalidateCache();
return true;
}
void VectorAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
{
size_t msiz = m_areas.size();
for (size_t i = 0; i < msiz; i++) {
for (size_t i = 0; i < m_areas.size(); ++i) {
Area *b = m_areas[i];
if (AST_CONTAINS_PT(b, pos)) {
result->push_back(b);
@ -231,9 +203,8 @@ void VectorAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
void VectorAreaStore::getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap)
{
size_t msiz = m_areas.size();
for (size_t i = 0; i < msiz; i++) {
Area * b = m_areas[i];
for (size_t i = 0; i < m_areas.size(); ++i) {
Area *b = m_areas[i];
if (accept_overlap ? AST_AREAS_OVERLAP(minedge, maxedge, b) :
AST_CONTAINS_AREA(minedge, maxedge, b)) {
result->push_back(b);
@ -241,19 +212,6 @@ void VectorAreaStore::getAreasInArea(std::vector<Area *> *result,
}
}
#if 0
bool VectorAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
{
size_t msiz = m_areas.size();
for (size_t i = 0; i < msiz; i++) {
if (callback(args, m_areas[i])) {
return true;
}
}
return false;
}
#endif
#if USE_SPATIAL
static inline SpatialIndex::Region get_spatial_region(const v3s16 minedge,
@ -273,11 +231,16 @@ static inline SpatialIndex::Point get_spatial_point(const v3s16 pos)
}
void SpatialAreaStore::insertArea(const Area &a)
bool SpatialAreaStore::insertArea(Area *a)
{
areas_map[a.id] = a;
m_tree->insertData(0, NULL, get_spatial_region(a.minedge, a.maxedge), a.id);
if (a->id == U32_MAX)
a->id = getNextId();
if (!areas_map.insert(std::make_pair(a->id, *a)).second)
// ID is not unique
return false;
m_tree->insertData(0, NULL, get_spatial_region(a->minedge, a->maxedge), a->id);
invalidateCache();
return true;
}
bool SpatialAreaStore::removeArea(u32 id)
@ -287,6 +250,7 @@ bool SpatialAreaStore::removeArea(u32 id)
Area *a = &itr->second;
bool result = m_tree->deleteData(get_spatial_region(a->minedge,
a->maxedge), id);
areas_map.erase(itr);
invalidateCache();
return result;
} else {
@ -312,14 +276,6 @@ void SpatialAreaStore::getAreasInArea(std::vector<Area *> *result,
}
}
#if 0
bool SpatialAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
{
// TODO ?? (this is only needed for serialisation, but libspatial has its own serialisation)
return false;
}
#endif
SpatialAreaStore::~SpatialAreaStore()
{
delete m_tree;

View File

@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef AREASTORE_H_
#define AREASTORE_H_
#ifndef AREA_STORE_H_
#define AREA_STORE_H_
#include "irr_v3d.h"
#include "noise.h" // for PcgRandom
@ -36,141 +36,147 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h"
#endif
#define AST_EXTREMIFY(min, max, pa, pb) \
(min).X = MYMIN((pa).X, (pb).X); \
(min).Y = MYMIN((pa).Y, (pb).Y); \
(min).Z = MYMIN((pa).Z, (pb).Z); \
(max).X = MYMAX((pa).X, (pb).X); \
(max).Y = MYMAX((pa).Y, (pb).Y); \
(max).Z = MYMAX((pa).Z, (pb).Z);
#define AREA_ID_INVALID 0
struct Area {
Area(const v3s16 &minedge, const v3s16 &maxedge)
Area() : id(U32_MAX) {}
Area(const v3s16 &mine, const v3s16 &maxe) :
id(U32_MAX), minedge(mine), maxedge(maxe)
{
this->minedge = minedge;
this->maxedge = maxedge;
}
Area() {}
void extremifyEdges()
{
v3s16 nminedge;
v3s16 nmaxedge;
AST_EXTREMIFY(nminedge, nmaxedge, minedge, maxedge)
maxedge = nmaxedge;
minedge = nminedge;
sortBoxVerticies(minedge, maxedge);
}
u32 id;
v3s16 minedge;
v3s16 maxedge;
v3s16 minedge, maxedge;
std::string data;
};
std::vector<std::string> get_areastore_typenames();
class AreaStore {
protected:
// TODO change to unordered_map when we can
std::map<u32, Area> areas_map;
void invalidateCache();
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos) = 0;
bool cache_enabled; // don't write to this from subclasses, only read.
public:
virtual void insertArea(const Area &a) = 0;
AreaStore() :
m_cache_enabled(true),
m_cacheblock_radius(64),
m_res_cache(1000, &cacheMiss, this),
m_next_id(0)
{}
virtual ~AreaStore() {}
static AreaStore *getOptimalImplementation();
virtual void reserve(size_t count) {};
size_t size() const { return areas_map.size(); }
/// Add an area to the store.
/// Updates the area's ID if it hasn't already been set.
/// @return Whether the area insertion was successful.
virtual bool insertArea(Area *a) = 0;
/// Removes an area from the store by ID.
/// @return Whether the area was in the store and removed.
virtual bool removeArea(u32 id) = 0;
/// Finds areas that the passed position is contained in.
/// Stores output in passed vector.
void getAreasForPos(std::vector<Area *> *result, v3s16 pos);
/// Finds areas that are completely contained inside the area defined
/// by the passed edges. If @p accept_overlap is true this finds any
/// areas that intersect with the passed area at any point.
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap) = 0;
#if 0
// calls a passed function for every stored area, until the
// callback returns true. If that happens, it returns true,
// if the search is exhausted, it returns false
virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const = 0;
#endif
virtual ~AreaStore()
{}
AreaStore() :
cache_enabled(true),
m_cacheblock_radius(64),
m_res_cache(1000, &cacheMiss, this),
m_highest_id(0)
{
}
/// Sets cache parameters.
void setCacheParams(bool enabled, u8 block_radius, size_t limit);
u32 getFreeId(v3s16 minedge, v3s16 maxedge);
/// Returns a pointer to the area coresponding to the passed ID,
/// or NULL if it doesn't exist.
const Area *getArea(u32 id) const;
u16 size() const;
#if 0
bool deserialize(std::istream &is);
void serialize(std::ostream &is) const;
#endif
private:
static void cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *dest);
u8 m_cacheblock_radius; // if you modify this, call invalidateCache()
LRUCache<v3s16, std::vector<Area *> > m_res_cache;
u32 m_highest_id;
/// Serializes the store's areas to a binary ostream.
void serialize(std::ostream &is) const;
/// Deserializes the Areas from a binary istream.
/// This does not currently clear the AreaStore before adding the
/// areas, making it possible to deserialize multiple serialized
/// AreaStores.
void deserialize(std::istream &is);
protected:
/// Invalidates the getAreasForPos cache.
/// Call after adding or removing an area.
void invalidateCache();
/// Implementation of getAreasForPos.
/// getAreasForPos calls this if the cache is disabled.
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos) = 0;
/// Returns the next area ID and increments it.
u32 getNextId() { return m_next_id++; }
// Note: This can't be an unordered_map, since all
// references would be invalidated on rehash.
typedef std::map<u32, Area> AreaMap;
AreaMap areas_map;
private:
/// Called by the cache when a value isn't found in the cache.
static void cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *dest);
bool m_cache_enabled;
/// Range, in nodes, of the getAreasForPos cache.
/// If you modify this, call invalidateCache()
u8 m_cacheblock_radius;
LRUCache<v3s16, std::vector<Area *> > m_res_cache;
u32 m_next_id;
};
class VectorAreaStore : public AreaStore {
protected:
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
public:
virtual void insertArea(const Area &a);
virtual void reserve(size_t count);
virtual void reserve(size_t count) { m_areas.reserve(count); }
virtual bool insertArea(Area *a);
virtual bool removeArea(u32 id);
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap);
// virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const;
protected:
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
private:
std::vector<Area *> m_areas;
};
#if USE_SPATIAL
class SpatialAreaStore : public AreaStore {
protected:
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
public:
SpatialAreaStore();
virtual void insertArea(const Area &a);
virtual ~SpatialAreaStore();
virtual bool insertArea(Area *a);
virtual bool removeArea(u32 id);
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap);
// virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const;
virtual ~SpatialAreaStore();
protected:
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
private:
SpatialIndex::ISpatialIndex *m_tree;
SpatialIndex::IStorageManager *m_storagemanager;
class VectorResultVisitor : public SpatialIndex::IVisitor {
private:
SpatialAreaStore *m_store;
std::vector<Area *> *m_result;
public:
VectorResultVisitor(std::vector<Area *> *result, SpatialAreaStore *store)
{
m_store = store;
m_result = result;
}
VectorResultVisitor(std::vector<Area *> *result, SpatialAreaStore *store) :
m_store(store),
m_result(result)
{}
~VectorResultVisitor() {}
virtual void visitNode(const SpatialIndex::INode &in)
{
}
virtual void visitNode(const SpatialIndex::INode &in) {}
virtual void visitData(const SpatialIndex::IData &in)
{
@ -187,10 +193,12 @@ private:
visitData(*(v[i]));
}
~VectorResultVisitor() {}
private:
SpatialAreaStore *m_store;
std::vector<Area *> *m_result;
};
};
#endif
#endif // USE_SPATIAL
#endif /* AREASTORE_H_ */
#endif // AREA_STORE_H_

View File

@ -299,15 +299,6 @@ inline s32 mystoi(const std::string &str, s32 min, s32 max)
}
/// Returns a 64-bit value represented by the string \p str (decimal).
inline s64 stoi64(const std::string &str)
{
std::stringstream tmp(str);
s64 t;
tmp >> t;
return t;
}
// MSVC2010 includes it's own versions of these
//#if !defined(_MSC_VER) || _MSC_VER < 1600
@ -346,9 +337,22 @@ inline float mystof(const std::string &str)
#define stoi mystoi
#define stof mystof
/// Returns a value represented by the string \p val.
template <typename T>
inline T from_string(const std::string &str)
{
std::stringstream tmp(str);
T t;
tmp >> t;
return t;
}
/// Returns a 64-bit signed value represented by the string \p str (decimal).
inline s64 stoi64(const std::string &str) { return from_string<s64>(str); }
// TODO: Replace with C++11 std::to_string.
/// Returns A string representing the value \p val.
/// Returns a string representing the value \p val.
template <typename T>
inline std::string to_string(T val)
{