2021-03-09 08:35:49 +01:00
|
|
|
--
|
|
|
|
-- fs51 - Compatibility layer for Minetest formspecs
|
|
|
|
--
|
|
|
|
-- Copyright © 2021 by luk3yx.
|
|
|
|
--
|
|
|
|
|
2021-03-10 06:32:18 +01:00
|
|
|
local fixers = ...
|
|
|
|
local get_player_information, type = minetest.get_player_information, type
|
2021-03-13 09:08:30 +01:00
|
|
|
local function remove_hypertext(text)
|
|
|
|
-- If the text doesn't contain backslashes use gsub for performance
|
|
|
|
if not text:find('\\', 1, true) then
|
|
|
|
return text:gsub('<[^>]+>', '')
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Otherwise iterate over it
|
|
|
|
local res = ''
|
|
|
|
local escaping, ignoring
|
|
|
|
for i = 1, #text do
|
|
|
|
local char = text:sub(i, i)
|
|
|
|
if ignoring then
|
|
|
|
ignoring = char ~= '>'
|
|
|
|
elseif escaping then
|
|
|
|
res = res .. char
|
|
|
|
escaping = false
|
|
|
|
elseif char == '<' then
|
|
|
|
ignoring = true
|
|
|
|
elseif char == '\\' then
|
|
|
|
escaping = true
|
|
|
|
else
|
|
|
|
res = res .. char
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return res
|
|
|
|
end
|
|
|
|
|
2022-01-24 09:14:35 +01:00
|
|
|
-- Backport index_event by modifying the dropdown items so that they all start
|
|
|
|
-- with \x1b(fs51@idx_<N>). This is then parsed out here if the player has been
|
|
|
|
-- shown any formspec with a dropdown that has index_event. The extra check is
|
|
|
|
-- done to prevent trying to parse every single field for every single player.
|
|
|
|
local dropdown_hack_enabled = {}
|
2022-01-24 09:29:12 +01:00
|
|
|
minetest.after(0, minetest.register_on_player_receive_fields,
|
|
|
|
function(player, _, fields)
|
2022-01-24 09:14:35 +01:00
|
|
|
if dropdown_hack_enabled[player:get_player_name()] then
|
2022-01-24 20:12:23 +01:00
|
|
|
local to_update = {}
|
|
|
|
for field, raw_value in pairs(fields) do
|
|
|
|
if field:sub(1, 6) == "\1fs51\1" then
|
|
|
|
to_update[field] = raw_value:match("^\27%(fs51@idx_([0-9]+)%)")
|
2022-01-24 09:14:35 +01:00
|
|
|
end
|
|
|
|
end
|
2022-01-24 20:12:23 +01:00
|
|
|
|
|
|
|
for field, value in pairs(to_update) do
|
|
|
|
fields[field] = nil
|
|
|
|
fields[field:sub(7)] = value
|
|
|
|
end
|
2022-01-24 09:14:35 +01:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
|
|
dropdown_hack_enabled[player:get_player_name()] = nil
|
|
|
|
end)
|
|
|
|
|
2021-03-09 08:35:49 +01:00
|
|
|
local function backport_for(name, formspec)
|
|
|
|
local info = get_player_information(name)
|
|
|
|
local formspec_version = info and info.formspec_version or 1
|
2022-01-24 09:14:35 +01:00
|
|
|
if formspec_version >= 4 then return formspec end
|
2021-03-09 08:35:49 +01:00
|
|
|
|
|
|
|
local tree, err = formspec_ast.parse(formspec)
|
|
|
|
if not tree then
|
|
|
|
minetest.log('warning', '[fs51] Error parsing formspec (in ' ..
|
|
|
|
'monkey_patching.lua): ' .. tostring(err))
|
|
|
|
return formspec
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Add some placeholders
|
|
|
|
local modified
|
|
|
|
for node in formspec_ast.walk(tree) do
|
|
|
|
local node_type = node.type
|
2022-02-13 04:37:56 +01:00
|
|
|
if node_type == "dropdown" and node.index_event and node.items then
|
2022-01-24 09:14:35 +01:00
|
|
|
-- Enable the dropdown hack for this player
|
|
|
|
dropdown_hack_enabled[name] = true
|
|
|
|
|
|
|
|
modified = true
|
2022-01-24 20:12:23 +01:00
|
|
|
node.name = "\1fs51\1" .. node.name
|
2022-02-13 04:37:56 +01:00
|
|
|
for i, item in ipairs(node.items) do
|
|
|
|
node.items[i] = "\27(fs51@idx_" .. i .. ")" .. item
|
2022-01-24 09:14:35 +01:00
|
|
|
end
|
|
|
|
node.index_event = nil
|
2022-01-24 09:17:53 +01:00
|
|
|
elseif formspec_version == 3 then -- luacheck: ignore 542
|
2022-01-24 09:14:35 +01:00
|
|
|
-- Don't do anything else
|
|
|
|
elseif formspec_version == 1 and node_type == 'background9' then
|
2021-03-09 08:35:49 +01:00
|
|
|
-- No need to set modified here
|
|
|
|
node.type = 'background'
|
|
|
|
node.middle_x, node.middle_y = nil, nil
|
|
|
|
node.middle_x2, node.middle_y2 = nil, nil
|
|
|
|
elseif node_type == 'animated_image' then
|
|
|
|
modified = true
|
|
|
|
node.type = 'image'
|
|
|
|
local frame_start = node.frame_start or 1
|
|
|
|
node.texture_name = ('(%s)^[verticalframe:%d:%d'):format(
|
|
|
|
node.texture_name, node.frame_count, frame_start - 1)
|
|
|
|
elseif node_type == 'model' and node.textures[1] then
|
|
|
|
modified = true
|
|
|
|
node.type = 'image'
|
|
|
|
node.texture_name = node.textures[1]
|
|
|
|
elseif node_type == 'hypertext' then
|
|
|
|
-- Convert hypertext elements to regular textareas
|
|
|
|
modified = true
|
|
|
|
node.type = 'textarea'
|
|
|
|
node.name = ''
|
|
|
|
node.label = ''
|
2021-03-13 09:08:30 +01:00
|
|
|
node.default = remove_hypertext(node.text)
|
2021-03-09 08:35:49 +01:00
|
|
|
node.text = nil
|
|
|
|
elseif node_type == 'scroll_container' then
|
|
|
|
modified = true
|
|
|
|
node.type = 'container'
|
|
|
|
-- Scroll containers are always going to be broken on older clients
|
|
|
|
for i = #node, 1, -1 do
|
|
|
|
local inner_node = node[i]
|
|
|
|
if inner_node.x and inner_node.y and
|
|
|
|
(inner_node.x >= node.w or inner_node.y >= node.h) then
|
|
|
|
table.remove(node, i)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif formspec_version == 1 and node_type == 'tabheader' then
|
|
|
|
node.w, node.h = nil, nil
|
2021-03-10 06:32:18 +01:00
|
|
|
elseif formspec_version == 2 and node_type == 'bgcolor' then
|
|
|
|
modified = true
|
|
|
|
fixers.bgcolor(node)
|
2021-03-09 08:35:49 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if formspec_version == 1 then
|
|
|
|
modified = true
|
|
|
|
tree = fs51.backport(tree)
|
|
|
|
end
|
|
|
|
|
|
|
|
if modified then
|
|
|
|
return assert(formspec_ast.unparse(tree))
|
|
|
|
end
|
|
|
|
return formspec
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Patch minetest.show_formspec()
|
|
|
|
local show_formspec = minetest.show_formspec
|
|
|
|
function minetest.show_formspec(pname, formname, formspec)
|
|
|
|
return show_formspec(pname, formname, backport_for(pname, formspec))
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Patch player:set_inventory_formspec()
|
|
|
|
local old_set_inventory_formspec
|
|
|
|
local function new_set_inventory_formspec(self, formspec, ...)
|
|
|
|
return old_set_inventory_formspec(self,
|
|
|
|
backport_for(self:get_player_name(), formspec), ...)
|
|
|
|
end
|
|
|
|
|
|
|
|
minetest.register_on_joinplayer(function(player)
|
|
|
|
if old_set_inventory_formspec == nil then
|
|
|
|
assert(type(player) == 'userdata', 'Fake player object?')
|
|
|
|
local cls = getmetatable(player)
|
|
|
|
old_set_inventory_formspec = cls.set_inventory_formspec
|
|
|
|
cls.set_inventory_formspec = new_set_inventory_formspec
|
|
|
|
|
|
|
|
-- In case the inventory formspec has been set in the meantime
|
|
|
|
player:set_inventory_formspec(player:get_inventory_formspec())
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2021-03-10 07:53:55 +01:00
|
|
|
if minetest.settings:get_bool('fs51.disable_meta_override') then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2021-03-09 08:35:49 +01:00
|
|
|
-- Patch minetest.get_meta()
|
|
|
|
-- Inspired by https://gitlab.com/sztest/nodecore/-/blob/master/mods/nc_api
|
|
|
|
local old_nodemeta_set_string
|
|
|
|
local function new_nodemeta_set_string(self, k, v)
|
|
|
|
if k == 'formspec' and type(v) == 'string' then
|
|
|
|
v = fs51.backport_string(v) or v
|
|
|
|
end
|
|
|
|
return old_nodemeta_set_string(self, k, v)
|
|
|
|
end
|
|
|
|
|
|
|
|
local get_meta = minetest.get_meta
|
|
|
|
function minetest.get_meta(...)
|
|
|
|
local meta = get_meta(...)
|
2021-03-10 19:38:07 +01:00
|
|
|
if old_nodemeta_set_string == nil and type(meta) == 'userdata' then
|
2021-03-09 08:35:49 +01:00
|
|
|
minetest.get_meta = get_meta
|
|
|
|
local cls = getmetatable(meta)
|
|
|
|
old_nodemeta_set_string = cls.set_string
|
|
|
|
cls.set_string = new_nodemeta_set_string
|
|
|
|
end
|
|
|
|
return meta
|
|
|
|
end
|