From 4a57d08f2b2b2e98a803cb010762862fdf800c72 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sat, 10 Nov 2018 22:35:47 +0100 Subject: [PATCH 1/4] New font selection formspec --- fontform.lua | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++ init.lua | 8 +-- 2 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 fontform.lua diff --git a/fontform.lua b/fontform.lua new file mode 100644 index 0000000..118d92d --- /dev/null +++ b/fontform.lua @@ -0,0 +1,171 @@ +--[[ + font_api mod for Minetest - Library to add font display capability + to display_api mod. + (c) Pierre-Yves Rollo + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +--]] + +local modname = minetest.get_current_modname() + +-- Context management functions (surely many improvements to do) + +local contexts = {} + +local function get_player_name(player) + if type(player) == 'string' then return player end + if type(player) == 'userdata' and player.get_player_name then + return player:get_player_name() + end + minetest.log('warning', '['..modname..'] get_player_name could not identify player.') +end + +minetest.register_on_leaveplayer(function(player) + local playername = get_player_name(player) + if playername then contexts[playername] = nil end +end) + +local function new_context(player, context) + local playername = get_player_name(player) + if playername then + contexts[playername] = context + contexts[playername].playername = playername + return contexts[playername] + end +end + +local function get_context(player) + local playername = get_player_name(player) + if playername then + if contexts[playername] then + return contexts[playername] + else + minetest.log('warning', '['..modname..'] Context not found for player "'..playername..'"') + end + end +end + +local function update_context(player, changes) + local playername = get_player_name(player) + if playername then + if not contexts[playername] then + contexts[playername] = { playername = playername } + end + for key, value in pairs(changes) do + contexts[playername][key] = value + end + end +end + +-- Show node formspec functions + +local function show_node_formspec(player, pos) + local meta = minetest.get_meta(pos) + local playername = get_player_name(player) + + -- Decontextualize formspec + local fs = meta:get_string('formspec') + + -- Change context and currrent_name references to nodemeta references + fs = fs:gsub("current_name", "nodemeta:"..pos.x..","..pos.y..","..pos.z) + fs = fs:gsub("context", "nodemeta:"..pos.x..","..pos.y..","..pos.z) + + -- Change all ${} to their corresponding metadata values + local s, e + repeat + s, e = fs:find('%${.*}') + if s and e then + fs = fs:sub(1, s-1).. + minetest.formspec_escape(meta:get_string(fs:sub(s+2,e-1))).. + fs:sub(e+1) + end + until s == nil + + -- Find node on_receive_fields + local ndef = minetest.registered_nodes[minetest.get_node(pos).name] + + if ndef and ndef.on_receive_fields then + update_context(player, { on_receive_fields = ndef.on_receive_fields } ) + end + update_context(player, { node_pos = pos } ) + + -- Show formspec + minetest.show_formspec(playername, modname..':context_formspec', fs) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == modname..':context_formspec' then + local context = get_context(player) + if context == nil then return end + + if context.on_receive_fields then + context.on_receive_fields(context.pos, '', fields, player) + end + end +end) + +-- Specific functions + +local function font_list_prepare() + local list = {} + for name, _ in pairs(font_api.registered_fonts) do + list[#list+1] = name + end + table.sort(list) + return list +end + +local function show_fs(player) + local context = get_context(player) + if context == nil then return end + local fonts = font_list_prepare() + + local fs = 'size[4,'..(#fonts + 0.8)..']' + ..default.gui_bg..default.gui_bg_img..default.gui_slots + ..'button_exit[0,'..(#fonts)..';4,1;cancel;Cancel]' + + for line = 1, #fonts do + local font = font_api.get_font(fonts[line]) + fs = fs..'image[0.1,'..(line-0.9)..';4.5,0.8;' + ..font:make_text_texture(font.name, font:get_height()*5, + font:get_height()*1.2, 1, "center", "top", "#fff") + ..']button_exit[0,'..(line-1)..';4,1;font_'..font.name..';]' + end + minetest.show_formspec(context.playername, modname..':font_list', fs) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == modname..':font_list' then + local context = get_context(player) + if context == nil then return end + + if fields.quit == 'true' then + for name, _ in pairs(font_api.registered_fonts) do + if fields['font_'..name] then + local meta = minetest.get_meta(context.pos) + meta:set_string("font", name) + display_api.update_entities(context.pos) + end + end + + -- Using after to avoid the "double close" bug + minetest.after(0, show_node_formspec, player, context.pos) + end + end +end) + +function font_api.show_font_list(player, pos) + new_context(player, { pos = pos }) + show_fs(player) +end diff --git a/init.lua b/init.lua index dc3a3e0..27cba99 100644 --- a/init.lua +++ b/init.lua @@ -29,9 +29,10 @@ font_api.path = minetest.get_modpath(font_api.name) dofile(font_api.path.."/font.lua") dofile(font_api.path.."/registry.lua") +dofile(font_api.path.."/fontform.lua") --- Standard on_display_update entity callback. --- Node should have a corresponding display_entity with size, resolution and +-- Node should have a corresponding display_entity with size, resolution and -- maxlines fields and optionally halign, valign and color fields -- @param pos Node position -- @param objref Object reference of entity @@ -49,8 +50,8 @@ function font_api.on_display_update(pos, objref) local maxlines = def.maxlines or 1 -- TODO:How to do w/o maxlines ? - objref:set_properties({ - textures={font:make_text_texture(text, + objref:set_properties({ + textures={font:make_text_texture(text, font:get_height(maxlines) * def.size.x / def.size.y / (def.aspect_ratio or 1), font:get_height(maxlines), @@ -62,4 +63,3 @@ end -- Compatibility font_lib = font_api - From 81fa9f60cf567e108cb4cdfe43f2e9507a26fbfa Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 18 Nov 2018 15:29:59 +0100 Subject: [PATCH 2/4] Code simplification and securisation --- fontform.lua | 155 +++++++++++++++++++++++---------------------------- 1 file changed, 69 insertions(+), 86 deletions(-) diff --git a/fontform.lua b/fontform.lua index 118d92d..01929dd 100644 --- a/fontform.lua +++ b/fontform.lua @@ -19,67 +19,37 @@ local modname = minetest.get_current_modname() --- Context management functions (surely many improvements to do) - local contexts = {} -local function get_player_name(player) - if type(player) == 'string' then return player end - if type(player) == 'userdata' and player.get_player_name then - return player:get_player_name() - end - minetest.log('warning', '['..modname..'] get_player_name could not identify player.') -end - minetest.register_on_leaveplayer(function(player) - local playername = get_player_name(player) - if playername then contexts[playername] = nil end + if minetest.is_player(player) then + contexts[player:get_player_name()] = nil + end end) -local function new_context(player, context) - local playername = get_player_name(player) - if playername then - contexts[playername] = context - contexts[playername].playername = playername - return contexts[playername] - end -end - -local function get_context(player) - local playername = get_player_name(player) - if playername then - if contexts[playername] then - return contexts[playername] - else - minetest.log('warning', '['..modname..'] Context not found for player "'..playername..'"') - end - end -end - -local function update_context(player, changes) - local playername = get_player_name(player) - if playername then - if not contexts[playername] then - contexts[playername] = { playername = playername } - end - for key, value in pairs(changes) do - contexts[playername][key] = value - end +local function get_context(playername) + if not contexts[playername] then + contexts[playername] = { playername = playername } end + return contexts[playername] end -- Show node formspec functions - -local function show_node_formspec(player, pos) +local function show_node_formspec(playername, pos) local meta = minetest.get_meta(pos) - local playername = get_player_name(player) -- Decontextualize formspec local fs = meta:get_string('formspec') + if not fs then + return + end + -- Change context and currrent_name references to nodemeta references - fs = fs:gsub("current_name", "nodemeta:"..pos.x..","..pos.y..","..pos.z) - fs = fs:gsub("context", "nodemeta:"..pos.x..","..pos.y..","..pos.z) + -- Change context and currrent_name references to nodemeta references + local nodemeta = string.format("nodemeta:%i,%i,%i", pos.x, pos.y ,pos.z) + fs = fs:gsub("current_name", nodemeta) + fs = fs:gsub("context", nodemeta) -- Change all ${} to their corresponding metadata values local s, e @@ -92,44 +62,44 @@ local function show_node_formspec(player, pos) end until s == nil + local context = get_context(playername) + context.node_pos = pos + -- Find node on_receive_fields local ndef = minetest.registered_nodes[minetest.get_node(pos).name] - if ndef and ndef.on_receive_fields then - update_context(player, { on_receive_fields = ndef.on_receive_fields } ) + context.on_receive_fields = ndef.on_receive_fields end - update_context(player, { node_pos = pos } ) -- Show formspec minetest.show_formspec(playername, modname..':context_formspec', fs) end minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname == modname..':context_formspec' then - local context = get_context(player) - if context == nil then return end - - if context.on_receive_fields then - context.on_receive_fields(context.pos, '', fields, player) - end + if formname ~= modname..':context_formspec' then + return end + + if not minetest.is_player(player) then + return true + end + + local context = get_context(player:get_player_name()) + if context.on_receive_fields then + context.on_receive_fields(context.pos, '', fields, player) + end + return true end) -- Specific functions -local function font_list_prepare() - local list = {} +local function show_font_formspec(playername) + local context = get_context(playername) + local fonts = {} for name, _ in pairs(font_api.registered_fonts) do - list[#list+1] = name + fonts[#fonts+1] = name end - table.sort(list) - return list -end - -local function show_fs(player) - local context = get_context(player) - if context == nil then return end - local fonts = font_list_prepare() + table.sort(fonts) local fs = 'size[4,'..(#fonts + 0.8)..']' ..default.gui_bg..default.gui_bg_img..default.gui_slots @@ -146,26 +116,39 @@ local function show_fs(player) end minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname == modname..':font_list' then - local context = get_context(player) - if context == nil then return end - - if fields.quit == 'true' then - for name, _ in pairs(font_api.registered_fonts) do - if fields['font_'..name] then - local meta = minetest.get_meta(context.pos) - meta:set_string("font", name) - display_api.update_entities(context.pos) - end - end - - -- Using after to avoid the "double close" bug - minetest.after(0, show_node_formspec, player, context.pos) - end + if formname ~= modname..':font_list' then + return end + + if not minetest.is_player(player) then + return true + end + + local playername = player:get_player_name() + local context = get_context(playername) + + if minetest.is_protected(context.pos, playername) then + return true + end + + if fields.quit == 'true' then + for name, _ in pairs(font_api.registered_fonts) do + if fields['font_'..name] then + local meta = minetest.get_meta(context.pos) + meta:set_string("font", name) + display_api.update_entities(context.pos) + end + end + -- Using after to avoid the "double close" bug + minetest.after(0, show_node_formspec, playername, context.pos) + end + return true end) -function font_api.show_font_list(player, pos) - new_context(player, { pos = pos }) - show_fs(player) +function font_api.show_font_list_from_pos(player, pos) + if minetest.is_player(player) then + local context = get_context(player:get_player_name()) + context.pos = pos + show_font_formspec(player:get_player_name()) + end end From 6d9e45f43deff645f05f07f350a505e7c321daeb Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 18 Nov 2018 15:30:37 +0100 Subject: [PATCH 3/4] Added font formspec textures --- textures/font_api_center.png | Bin 0 -> 224 bytes textures/font_api_font.png | Bin 0 -> 238 bytes textures/font_api_left.png | Bin 0 -> 220 bytes textures/font_api_right.png | Bin 0 -> 222 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 textures/font_api_center.png create mode 100644 textures/font_api_font.png create mode 100644 textures/font_api_left.png create mode 100644 textures/font_api_right.png diff --git a/textures/font_api_center.png b/textures/font_api_center.png new file mode 100644 index 0000000000000000000000000000000000000000..967db6aded151e4f0d2e47878b4430e70164d795 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|t_%$6K<5Ad|IeP;yA#M_ zED7=pW^j0RBMrn!@^*J&=wOxg04Xoqg z4pt^Mb~ZLQjg48NJq_KKYoo4mC{DPNxwb*Dz;Hq!gTEr5{xg|-tst{JUHx3vIVCg! E092tkF#rGn literal 0 HcmV?d00001 diff --git a/textures/font_api_font.png b/textures/font_api_font.png new file mode 100644 index 0000000000000000000000000000000000000000..e45033859af8885cabdca76db63acc5f1e228527 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|t_%$6K<5Ad|IeP;yA#M_ zED7=pW^j0RBMrn!@^*J&=wOxg04XooU649-QVi6yBi3gww484B*6z5ywEsq8>;Q%@Ji5RU7~ z9IQ-i>}+gq3I|z~Ei9xOS;Qw?2nuKs>XwpFYzaQhq_C08qrx!5fpY_+!eWL`PQ31y Se3PbtO!jp3b6Mw<&;$TshdSW^ literal 0 HcmV?d00001 diff --git a/textures/font_api_left.png b/textures/font_api_left.png new file mode 100644 index 0000000000000000000000000000000000000000..727410bd23c9f1f4d337f8875f76d87eef8a5e58 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|t_%$6K<5Ad|IeP;yA#M_ zED7=pW^j0RBMrn!@^*J&=wOxg04XoLV0FMhJw4NZ$OG(DmzeI+0(@_gyVWL z2P+dBI~$uD$HT2zj8h!MqFGfZ98F+x;zWVZS literal 0 HcmV?d00001 diff --git a/textures/font_api_right.png b/textures/font_api_right.png new file mode 100644 index 0000000000000000000000000000000000000000..16401d30ba6d6ad425a5a3a8f8e6655300f3d763 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|t_%$6K<5Ad|IeP;yA#M_ zED7=pW^j0RBMrn!@^*J&=wOxg04XooU649-QVi6yBi3gww484B*6z5ywEsq8>;RZkbk5RU7~ z9IQ-i>}+gq8V9##DNb?dSj$uxzOc)%VW&ot!eWLrfBxuq(L0xbO!9Q~b6Mw<&;$Sh Cb2vBv literal 0 HcmV?d00001 From 19f2da52ee6313778462a8708c3c617f47191c84 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Fri, 23 Nov 2018 11:34:55 +0100 Subject: [PATCH 4/4] Fonts configurable in signs, posters and steles --- fontform.lua | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/fontform.lua b/fontform.lua index 01929dd..e4952ed 100644 --- a/fontform.lua +++ b/fontform.lua @@ -101,16 +101,18 @@ local function show_font_formspec(playername) end table.sort(fonts) - local fs = 'size[4,'..(#fonts + 0.8)..']' - ..default.gui_bg..default.gui_bg_img..default.gui_slots - ..'button_exit[0,'..(#fonts)..';4,1;cancel;Cancel]' + local fs = string.format( + "size[4,%s]%s%s%sbutton_exit[0,%s;4,1;cancel;Cancel]", + #fonts + 0.8, default.gui_bg, default.gui_bg_img, default.gui_slots, + #fonts) for line = 1, #fonts do local font = font_api.get_font(fonts[line]) - fs = fs..'image[0.1,'..(line-0.9)..';4.5,0.8;' - ..font:make_text_texture(font.name, font:get_height()*5, + local texture = font:make_text_texture(font.name, font:get_height()*5, font:get_height()*1.2, 1, "center", "top", "#fff") - ..']button_exit[0,'..(line-1)..';4,1;font_'..font.name..';]' + fs = string.format( + "%simage[0.1,%s;4.5,0.8;%s]button_exit[0,%s;4,1;font_%s;]", + fs, line-0.9, texture, line-1, font.name) end minetest.show_formspec(context.playername, modname..':font_list', fs) end @@ -127,7 +129,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local playername = player:get_player_name() local context = get_context(playername) - if minetest.is_protected(context.pos, playername) then + if not context.pos + or minetest.is_protected(context.pos, playername) then return true end @@ -139,16 +142,26 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) display_api.update_entities(context.pos) end end - -- Using after to avoid the "double close" bug - minetest.after(0, show_node_formspec, playername, context.pos) + + if context.callback and type(context.callback) == "function" then + -- Using after to avoid the "double close" bug + minetest.after(0, context.callback, playername, context.pos) + else + -- Using after to avoid the "double close" bug + minetest.after(0, show_node_formspec, playername, context.pos) + end end return true end) -function font_api.show_font_list_from_pos(player, pos) +-- @param player Player viewing the form +-- @param pos Node pos +-- @param callback function(playername, pos) to be called on form close +function font_api.show_font_list(player, pos, callback) if minetest.is_player(player) then local context = get_context(player:get_player_name()) context.pos = pos + context.callback = callback show_font_formspec(player:get_player_name()) end end