commit 7e4a64c890060674ae9745e78887ed5fa1230500 Author: luk3yx Date: Sat Mar 24 10:56:56 2018 +1300 Initial commit diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..b274816 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +*Copyright © 2018 by luk3yx* + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Progress b/Progress new file mode 100644 index 0000000..24c0cc1 --- /dev/null +++ b/Progress @@ -0,0 +1,23 @@ +http://dev.minetest.net/formspec + +size: Done! +list: Incomplete +image: image +field: textbox +pwdfield: textbox +textarea: textbox +label: text +vertlabel: text +button: button +image_button: button +item_image_button: button +button_exit: button +image_button_exit: button +listcolors: formspeclib:raw_formspec +bgcolor: formspeclib:raw_formspec +background: formspeclib:raw_formspec +textlist: combobox +dropdown: combobox + +Incomplete: +list diff --git a/README.md b/README.md new file mode 100644 index 0000000..de23bd2 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# formspeclib + +A formspec renderer for Minetest. diff --git a/core.lua b/core.lua new file mode 100644 index 0000000..89e1848 --- /dev/null +++ b/core.lua @@ -0,0 +1,117 @@ +-- +-- formspeclib core - Better and easier formspecs for Minetest +-- +-- © 2017 by luk3yx +-- + +-- This is only the core rendering process. + +formspeclib = {} + +local objects = {} + +-- formspeclib.version: The version of formspeclib. +formspeclib.version = 0.1 + +-- +-- formspeclib.render(): Renders a formspeclib object into a formspec. +-- +-- Syntax: formspeclib.render(formspeclib_object, safe_mode, no_iterations) +-- +-- formspeclib_object: The formspeclib object to render. +-- safe_mode: Optional. If it is enabled, the renderer does safety +-- checks so the server does not crash. +-- no_iterations: Optional, not recommended. Only allows formspeclib +-- objects that produce a direct formspec string. +-- +formspeclib.render = function(formspec, safe_mode, no_iterations) + if safe_mode and type(formspec) ~= 'table' then + -- Don't throw an error, just don't render the formspec. + return false + end + local compiled = '' + local i + local width + local height + if type(formspec.width) == 'number' then + width = formspec.width + else + width = 0 + end + if type(formspec.height) == 'number' then + height = formspec.height + else + height = 0 + end + for i = 1, #formspec do + if safe_mode and type(formspec[i]) ~= 'table' then + return false + elseif not formspec[i].type then + -- The formspec is defining the global width/height. + if type(formspec[i].width) == 'number' then + width = obj.width + end + if type(formspec[i].height) == 'number' then + height = obj.height + end + elseif objects[formspec[i].type] then + local a + local o + if safe_mode then + a, o = pcall(objects[formspec[i].type], formspec[i], safe_mode) + if not a then o = false end + else + o = objects[formspec[i].type](formspec[i], safe_mode) + end + if type(o) == 'string' then + compiled = compiled .. o + elseif type(o) == 'table' and not no_iterations then + o = formspeclib.render(o, safe_mode, safe_mode or no_iterations) + if not o then return false end + compiled = compiled .. o + else + return false + end + else + -- On unknown element + return false + end + end + if width > 0 and height > 0 then + if default and default.gui_bg and default.gui_bg_img and default.gui_slots then + compiled = default.gui_bg .. default.gui_bg_img .. default.gui_slots .. compiled + end + compiled = 'size[' .. + tostring(width) .. ',' .. tostring(height) .. + ']' .. compiled + end + return compiled +end + +-- +-- formspeclib.register_object(): Create a formspeclib object +-- +-- Syntax: formspeclib.register_object(name, function) +-- +-- name: The name of the object (set in 'type'). It is recommended to make this +-- 'mod:object' unless you are overriding an object. +-- func: The function to generate the object. This function gets sent the +-- formspec chunk to generate, and will return a string or formspeclib +-- formspec, otherwise false to indicate an error. +-- +formspeclib.register_object = function(name, func) + if type(func) ~= 'function' then + return false + end + objects[name] = func + return true +end + +-- +-- formspeclib.escape(): Escape and stringify text. +-- +-- This may seem useless, but it is actually quite useful. +-- +formspeclib.escape = function(text) + return minetest.formspec_escape(tostring(text)) +end diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..5e78c21 --- /dev/null +++ b/depends.txt @@ -0,0 +1 @@ +default? diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..cfd8b2e --- /dev/null +++ b/init.lua @@ -0,0 +1,10 @@ +-- +-- formspeclib - Better and easier formspecs for Minetest +-- Version 0.1 +-- +-- © 2017 by luk3yx +-- + +local path = minetest.get_modpath('formspeclib') +dofile(path .. '/core.lua') -- formspeclib core +dofile(path .. '/objects.lua') -- Default formspeclib objects diff --git a/objects.lua b/objects.lua new file mode 100644 index 0000000..4ef2624 --- /dev/null +++ b/objects.lua @@ -0,0 +1,240 @@ +-- +-- formspeclib default objects +-- +-- These are unprefixed, however if you create a mod that registers objects, +-- it is recommended to prefix them with your mod name to prevent conflicts. +-- + +-- +-- Raw formspec data +-- +-- Parameters: data +-- +-- Example 'bgcolor[#fff;true;]': +-- { +-- type = 'formspeclib:raw_formspec', +-- data = 'bgcolor[#fff;true;]', +-- } +-- +-- Using this is not recommended, and it is disabled in safe mode as it can be +-- used to crash MT clients, effectively kicking an unsuspecting victim. +-- +formspeclib.register_object('formspeclib:raw_formspec', function(obj, safe_mode) + if safe_mode or type(obj.data) ~= 'string' then + return false + else + return obj.data + end +end) + +-- +-- A label +-- +-- Parameters: x, y, align(?), text +-- +-- Example 'label[1,1;Hello world!]': +-- { +-- type = 'text', +-- x = 1, +-- y = 1, +-- align = 'left', +-- text = 'Hello world!' +-- } +-- +-- align can be one of 'left', 'centre'/'center', or 'vertical'. If align is +-- set to 'centre'/'center', you can also set width and height. +-- +formspeclib.register_object('text', function(obj, safe_mode) + if not obj.text or not obj.x or not obj.y then return false end + if not obj.align then obj.align = 'left' end + local x = formspeclib.escape(obj.x) + local y = formspeclib.escape(obj.y) + local text = formspeclib.escape(obj.text) + if obj.align == 'left' then + return 'label[' .. x .. ',' .. y .. ';' .. text .. ']' + elseif obj.align == 'centre' or obj.align == 'center' then + -- This magical hack lets you centre/center text in formspecs. + -- Make sure your formspec ignores 'formspeclib:ignore'. + local width = formspeclib.escape(obj.width or 8) + local height = formspeclib.escape(obj.height or 0.5) + return 'image_button[' .. x .. ',' .. y .. ';' .. width .. ',' .. + height .. ';' .. formspeclib.escape('default_dirt.png^[colorize:#343434') .. ';formspeclib:ignore;' .. + text .. ']' + elseif obj.align == 'vertical' then + return 'vertlabel[' .. x .. ',' .. y .. ';' .. text .. ']' + else + return false + end +end) + +-- +-- An image +-- +-- Parameters: x, y, width(?), height(?), image +-- +-- Example 'image[1,1;1,1;default_dirt.png]': +-- { +-- type = 'image', +-- x = 1, +-- y = 1, +-- width = 1, +-- height = 1, +-- image = 'default_dirt.png' +-- } +formspeclib.register_object('image', function(obj, safe_mode) + if not obj.image or not obj.x or not obj.y then return false end + local x = formspeclib.escape(obj.x) + local y = formspeclib.escape(obj.y) + local width = formspeclib.escape(obj.width or obj.height or 1) + local height = formspeclib.escape(obj.height or obj.width or 1) + local image = formspeclib.escape(obj.image) + return 'image[' .. x .. ',' .. y .. ';' .. width .. ',' .. height .. ';' .. image .. ']' +end) + +-- +-- A button +-- +-- Parameters: x, y, width, height(?), name, text, quit(?), image(?) +-- +-- Example 'image_button_exit[1,1;3,1;button_cancel.png;cancel;Cancel]': +-- { +-- type = 'button', +-- x = 1, +-- y = 1, +-- width = 3, +-- height = 1, +-- name = 'cancel', +-- text = 'Cancel', +-- quit = true, +-- image = 'button_cancel.png', +-- } +-- +formspeclib.register_object('button', function(obj, safe_mode) + if not obj.text or not obj.x or not obj.y or not obj.width or not obj.name then + return false + end + local x = formspeclib.escape(obj.x) + local y = formspeclib.escape(obj.y) + local w = formspeclib.escape(obj.width) + local h = formspeclib.escape(obj.height or 2) + local name = formspeclib.escape(obj.name) + local text = formspeclib.escape(obj.text) + local t = 'button' + local img = '' + if obj.quit then + t = t .. '_exit' + end + if obj.image then + t = 'image_' .. t + img = ';' .. formspeclib.escape(obj.image) + if not obj.quit and not string.find(img, '.') then + t = 'item_' .. t + end + end + return t .. '[' .. x .. ',' .. y .. ';' .. w .. ',' .. h .. img .. ';' .. name .. ';' .. text .. ']' +end) + +-- +-- A text box +-- +-- Parameters: x, y, width, height(?), name, label(?), default(?), password(?) +-- +-- Example 'textarea[1,1;2,3;message;Yay]': +-- { +-- type = 'textbox', +-- x = 1, +-- y = 1, +-- width = 2, +-- height = 3, +-- default = 'Yay', +-- name = 'message', +-- } +formspeclib.register_object('textbox', function(obj, safe_mode) + if obj.x or not obj.y or not obj.width or not obj.name then + return false + end + local x = formspeclib.escape(obj.x) + local y = formspeclib.escape(obj.y) + local w = formspeclib.escape(obj.width) + local name = formspeclib.escape(obj.name) + local l = formspeclib.escape(obj.label or '') + local def = formspeclib.escape(obj.default or '') + local t = 'field' + local img = '' + def = ';' .. def + local h + if obj.height then + h = formspeclib.escape(obj.height) + t = 'textarea' + else + h = 2 + end + if obj.password then + if obj.default or obj.height then + return false + else + t = 'pwd' .. t + def = '' + end + end + return t .. '[' .. x .. ',' .. y .. ';' .. w .. ',' .. h .. ';' .. name .. ';' .. l .. def .. ']' +end) + +-- +-- A combo box +-- +-- Parameters: x, y, width, height(?), name, label(?), default(?), password(?) +-- +formspeclib.register_object('combobox', function(obj, safe_mode) + if not obj.x or not obj.y or not obj.width or not obj.name or not obj.items then + return false + end + local x = formspeclib.escape(obj.x) + local y = formspeclib.escape(obj.y) + local w = formspeclib.escape(obj.width) + local name = formspeclib.escape(obj.name) + local l = formspeclib.escape(obj.label or '') + local def = formspeclib.escape(obj.default or '') + local img = '' + def = ';' .. def + local h + local t + local i + local items + for i = 1, #obj.items do + if i ~= 1 then i = i .. ';' end + items = items .. obj.items[i] + end + if obj.height then + h = formspeclib.escape(obj.height) + t = 'textlist' + else + h = 2 + t = 'dropdown' + end + return t .. '[' .. x .. ',' .. y .. ';' .. w .. ',' .. h .. ';' .. name .. ';' .. l .. def .. ']' +end) + +-- +-- An inventory list +-- +-- Parameters: location(?), name(?), x, y, width, height, start_at(?) +-- +formspeclib.register_object('inventory', function(obj, safe_mode) + if not obj.x or not obj.y or not obj.width or not obj.height then + return false + end + local x = formspeclib.escape(obj.x) + local y = formspeclib.escape(obj.y) + local w = formspeclib.escape(obj.width) + local h = formspeclib.escape(obj.height) + local location = formspeclib.escape(obj.location or 'current_player') + local name = formspeclib.escape(obj.name or 'main') + local start = formspeclib.escape(obj.start_at or '') + if safe_mode and location ~= 'current_player' and location ~= 'context' then + -- You are not allowed to use other locations in safe mode. + return false + end + return 'list[' .. location .. ';' .. name .. ';' .. x .. ',' .. y .. ';' .. + w .. ',' .. h .. ';' .. start .. ']' +end)