From 9ce075d30bebe91f8d8f93af72031b7f073dce80 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Mon, 3 Dec 2018 16:27:08 +0100 Subject: [PATCH] Depreciation of old display groups and display_lib global + warnings --- deprecation.lua | 72 ++++++++++++++ display.lua | 236 ++++++++++++++++++++++++++++++++++++++++++++ init.lua | 253 +++++------------------------------------------- 3 files changed, 330 insertions(+), 231 deletions(-) create mode 100644 deprecation.lua create mode 100644 display.lua diff --git a/deprecation.lua b/deprecation.lua new file mode 100644 index 0000000..29bbd50 --- /dev/null +++ b/deprecation.lua @@ -0,0 +1,72 @@ +--[[ + display_api mod for Minetest - Library to add dynamic display + capabilities to nodes + (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 . +--]] + +-- Deprecation + +function deprecated_group(deprecated_group, replacement_group) + for name, ndef in pairs(minetest.registered_nodes) do + if ndef.groups and ndef.groups[deprecated_group] then + minetest.log("warning", string.format('Node %s belongs to deprecated "%s" group which should be replaced with new "%s" group.', + name, deprecated_group, replacement_group)) + end + end +end + +function deprecated_global_table(deprecated_global_name, replacement_global_name) + assert(type(deprecated_global_name) == 'string', "deprecated_global_name should be a string.") + assert(type(replacement_global_name) == 'string', "replacement_global_name should be a string.") + assert(deprecated_global_name ~= '', "deprecated_global_name should not be empty.") + assert(replacement_global_name ~= '', "replacement_global_name should not be empty.") + assert(rawget(_G, deprecated_global_name) == nil, "replacement global already exists.") + if _G[replacement_global_name] == nil then + print('warn_deprecated_functions: Warning, replacement global "'..replacement_global_name..'" does not exists.') + return + end + local meta = { + deprecated = deprecated_global_name, + replacement = replacement_global_name, + __index = function(table, key) + local meta = getmetatable(table) + local dbg = debug.getinfo(2, "lS") + minetest.log("warning", string.format('Warning: Accessing deprecated "%s" table, "%s" should be used instead (%s:%d).', + meta.deprecated, meta.replacement, (dbg.short_src or 'unknown'), (dbg.currentline or 0))) + return _G[meta.replacement][key] + end, + __newindex = function(table, key, value) + local meta = getmetatable(table) + local dbg = debug.getinfo(2, "lS") + minetest.log("warning", string.format('Warning: Accessing deprecated "%s" table, "%s" should be used instead (%s:%d).', + meta.deprecated, meta.replacement, (dbg.short_src or 'unknown'), (dbg.currentline or 0))) + _G[meta.replacement][key]=value + end, + } + rawset(_G, deprecated_global_name, {}) + setmetatable(_G[deprecated_global_name], meta) +end + + +-- deprecated(1) -- December 2018 - Deprecation of groups display_modpack_node and display_lib_node +-- Group to be removed from display API register_lbm +minetest.after(0, function() + deprecated_group("display_modpack_node", "display_api") + deprecated_group("display_lib_node", "display_api") +end) + +-- deprecated(2) -- December 2018 - Deprecation of display_lib +deprecated_global_table('display_lib', 'display_api') diff --git a/display.lua b/display.lua new file mode 100644 index 0000000..53fd749 --- /dev/null +++ b/display.lua @@ -0,0 +1,236 @@ +--[[ + display_api mod for Minetest - Library to add dynamic display + capabilities to nodes + (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 . +--]] + +-- Prefered gap between node and entity +-- Entity positionment is up to mods but it is a good practice to use this +-- variable as spacing between entity and node +display_api.entity_spacing = 0.002 + +-- Maximum entity position relative to the node pos +local max_entity_pos = 1.5 + +-- Miscelaneous values depending on wallmounted param2 +local wallmounted_values = { + [2]={dx=-1, dz=0, rx=0, rz=-1, yaw=-math.pi/2}, + [3]={dx=1, dz=0, rx=0, rz=1, yaw=math.pi/2 }, + [4]={dx=0, dz=-1, rx=1, rz=0, yaw=0 }, + [5]={dx=0, dz=1, rx=-1, rz=0, yaw=math.pi } +} + +-- Miscelaneous values depending on facedir param2 +local facedir_values = { + [0]={dx=0, dz=-1, rx=1, rz=0, yaw=0 }, + [1]={dx=-1, dz=0, rx=0, rz=-1, yaw=-math.pi/2}, + [2]={dx=0, dz=1, rx=-1, rz=0, yaw=math.pi }, + [3]={dx=1, dz=0, rx=0, rz=1, yaw=math.pi/2 } +} + +-- dx/dy = depth vector, rx/ly = right vector, yaw = yaw of entity, +local function get_values(node) + local ndef = minetest.registered_nodes[node.name] + + if ndef then + local paramtype2 = ndef.paramtype2 + if paramtype2 == "wallmounted" or paramtype2 == "colorwallmounted" then + return wallmounted_values[node.param2 % 8] + elseif paramtype2 == "facedir" or paramtype2 == "colorfacedir" then + return facedir_values[node.param2 % 32] + end + end +end + +--- Gets the display entities attached with a node. Removes extra ones +local function get_entities(pos) + local objrefs = {} + local ndef = minetest.registered_nodes[minetest.get_node(pos).name] + if ndef and ndef.display_entities then + for _, objref in + ipairs(minetest.get_objects_inside_radius(pos, max_entity_pos)) do + local entity = objref:get_luaentity() + if entity and ndef.display_entities[entity.name] and + entity.nodepos and vector.equals(pos, entity.nodepos) then + if objrefs[entity.name] then + objref:remove() -- Remove duplicates + else + objrefs[entity.name] = objref + end + end + end + end + return objrefs +end + +local function clip_pos_prop(posprop) + if posprop then + return math.max(-max_entity_pos, math.min(max_entity_pos, posprop)) + else + return 0 + end +end + +--- (Create and) place display entities according to the node orientation +local function place_entities(pos) + local node = minetest.get_node(pos) + local ndef = minetest.registered_nodes[node.name] + local values = get_values(node) + local objrefs = get_entities(pos) + + if values and ndef and ndef.display_entities then + for entity_name, props in pairs(ndef.display_entities) do + local depth = clip_pos_prop(props.depth) + local right = clip_pos_prop(props.right) + local top = clip_pos_prop(props.top) + if not objrefs[entity_name] then + objrefs[entity_name] = minetest.add_entity(pos, entity_name, + minetest.serialize({ nodepos = pos })) + end + + objrefs[entity_name]:setpos({ + x = pos.x - values.dx * depth + values.rx * right, + y = pos.y - top, + z = pos.z - values.dz * depth + values.rz * right}) + + objrefs[entity_name]:setyaw(values.yaw) + end + end + return objrefs +end + + +--- Entity update +function update_entity(entity) + if not entity then + return + end + + if not entity.nodepos then + entity.object:remove() -- Remove old/buggy entity + return + end + + local node = minetest.get_node(entity.nodepos) + local ndef = minetest.registered_nodes[node.name] + if ndef and ndef.display_entities and + ndef.display_entities[entity.name] and + ndef.display_entities[entity.name].on_display_update + then + -- Call on_display_update callback of a node for one of its display entities + ndef.display_entities[entity.name].on_display_update(entity.nodepos, + entity.object) + end +end + +--- Force entity update +function display_api.update_entities(pos) + for _, objref in pairs(place_entities(pos)) do + update_entity(objref:get_luaentity()) + end +end + +--- On_activate callback for display_api entities. Calls on_display_update callbacks +--- of corresponding node for each entity. +function display_api.on_activate(entity, staticdata) + if entity then + if string.sub(staticdata, 1, string.len("return")) == "return" then + local data = minetest.deserialize(staticdata) + if data and type(data) == "table" then + entity.nodepos = data.nodepos + end + entity.object:set_armor_groups({immortal=1}) + end + update_entity(entity) + end +end + +--- On_place callback for display_api items. +-- Does nothing more than preventing node from being placed on ceiling or ground +function display_api.on_place(itemstack, placer, pointed_thing, override_param2) + local ndef = itemstack:get_definition() + local above = pointed_thing.above + local under = pointed_thing.under + local dir = {x = under.x - above.x, y = 0, z = under.z - above.z} + + -- If item is not placed on a wall, use the player's view direction instead + if dir.x == 0 and dir.z == 0 then + dir = placer:get_look_dir() + dir.y = 0 + end + + local param2 = 0 + if ndef then + local paramtype2 = ndef.paramtype2 + if paramtype2 == "wallmounted" or paramtype2 == "colorwallmounted" then + param2 = minetest.dir_to_wallmounted(dir) + elseif paramtype2 == "facedir" or paramtype2 == "colorfacedir" then + param2 = minetest.dir_to_facedir(dir) + end + end + return minetest.item_place(itemstack, placer, pointed_thing, + param2 + (override_param2 or 0)) +end + +--- On_construct callback for display_api items. +-- Creates entities and update them. +function display_api.on_construct(pos) + display_api.update_entities(pos) +end + +--- On_destruct callback for display_api items. +-- Removes entities. +function display_api.on_destruct(pos) + for _, objref in pairs(get_entities(pos)) do + objref:remove() + end +end + +-- On_rotate (screwdriver) callback for display_api items. Prevents invalid rotations and reorients entities. +function display_api.on_rotate(pos, node, user, _, new_param2) + node.param2 = new_param2 + if get_values(node) then + minetest.swap_node(pos, node) + place_entities(pos) + return true + else + return false + end +end + +--- Creates display entity with some fields and the on_activate callback +function display_api.register_display_entity(entity_name) + if not minetest.registered_entity then + minetest.register_entity(':'..entity_name, { + collisionbox = { 0, 0, 0, 0, 0, 0 }, + visual = "upright_sprite", + textures = {}, + on_activate = display_api.on_activate, + get_staticdata = function(self) + return minetest.serialize({ nodepos = self.nodepos }) + end, + }) + end +end + +minetest.register_lbm({ + label = "Update display_api entities", + name = "display_api:update_entities", + run_at_every_load = true, + nodenames = {"group:display_api", + "group:display_modpack_node", "group:display_lib_node"}, -- See deprecated(1) + action = function(pos, node) display_api.update_entities(pos) end, +}) diff --git a/init.lua b/init.lua index bf95ded..f1e54e8 100644 --- a/init.lua +++ b/init.lua @@ -1,240 +1,31 @@ --[[ - display_api mod for Minetest - Library to add dynamic display - capabilities to nodes - (c) Pierre-Yves Rollo + display_api mod for Minetest - Library to add dynamic display + capabilities to nodes + (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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . --]] +-- Global variables +------------------- + display_api = {} +display_api.name = minetest.get_current_modname() +display_api.path = minetest.get_modpath(display_api.name) --- Prefered gap between node and entity --- Entity positionment is up to mods but it is a good practice to use this --- variable as spacing between entity and node -display_api.entity_spacing = 0.002 +-- Inclusions +------------- --- Maximum entity position relative to the node pos -local max_entity_pos = 1.5 - --- Miscelaneous values depending on wallmounted param2 -local wallmounted_values = { - [2]={dx=-1, dz=0, rx=0, rz=-1, yaw=-math.pi/2}, - [3]={dx=1, dz=0, rx=0, rz=1, yaw=math.pi/2 }, - [4]={dx=0, dz=-1, rx=1, rz=0, yaw=0 }, - [5]={dx=0, dz=1, rx=-1, rz=0, yaw=math.pi } -} - --- Miscelaneous values depending on facedir param2 -local facedir_values = { - [0]={dx=0, dz=-1, rx=1, rz=0, yaw=0 }, - [1]={dx=-1, dz=0, rx=0, rz=-1, yaw=-math.pi/2}, - [2]={dx=0, dz=1, rx=-1, rz=0, yaw=math.pi }, - [3]={dx=1, dz=0, rx=0, rz=1, yaw=math.pi/2 } -} - --- dx/dy = depth vector, rx/ly = right vector, yaw = yaw of entity, -local function get_values(node) - local ndef = minetest.registered_nodes[node.name] - - if ndef then - local paramtype2 = ndef.paramtype2 - if paramtype2 == "wallmounted" or paramtype2 == "colorwallmounted" then - return wallmounted_values[node.param2 % 8] - elseif paramtype2 == "facedir" or paramtype2 == "colorfacedir" then - return facedir_values[node.param2 % 32] - end - end -end - ---- Gets the display entities attached with a node. Removes extra ones -local function get_entities(pos) - local objrefs = {} - local ndef = minetest.registered_nodes[minetest.get_node(pos).name] - if ndef and ndef.display_entities then - for _, objref in - ipairs(minetest.get_objects_inside_radius(pos, max_entity_pos)) do - local entity = objref:get_luaentity() - if entity and ndef.display_entities[entity.name] and - entity.nodepos and vector.equals(pos, entity.nodepos) then - if objrefs[entity.name] then - objref:remove() -- Remove duplicates - else - objrefs[entity.name] = objref - end - end - end - end - return objrefs -end - -local function clip_pos_prop(posprop) - if posprop then - return math.max(-max_entity_pos, math.min(max_entity_pos, posprop)) - else - return 0 - end -end - ---- (Create and) place display entities according to the node orientation -local function place_entities(pos) - local node = minetest.get_node(pos) - local ndef = minetest.registered_nodes[node.name] - local values = get_values(node) - local objrefs = get_entities(pos) - - if values and ndef and ndef.display_entities then - for entity_name, props in pairs(ndef.display_entities) do - local depth = clip_pos_prop(props.depth) - local right = clip_pos_prop(props.right) - local top = clip_pos_prop(props.top) - if not objrefs[entity_name] then - objrefs[entity_name] = minetest.add_entity(pos, entity_name, - minetest.serialize({ nodepos = pos })) - end - - objrefs[entity_name]:setpos({ - x = pos.x - values.dx * depth + values.rx * right, - y = pos.y - top, - z = pos.z - values.dz * depth + values.rz * right}) - - objrefs[entity_name]:setyaw(values.yaw) - end - end - return objrefs -end - - ---- Entity update -function update_entity(entity) - if not entity then - return - end - - if not entity.nodepos then - entity.object:remove() -- Remove old/buggy entity - return - end - - local node = minetest.get_node(entity.nodepos) - local ndef = minetest.registered_nodes[node.name] - if ndef and ndef.display_entities and - ndef.display_entities[entity.name] and - ndef.display_entities[entity.name].on_display_update - then - -- Call on_display_update callback of a node for one of its display entities - ndef.display_entities[entity.name].on_display_update(entity.nodepos, - entity.object) - end -end - ---- Force entity update -function display_api.update_entities(pos) - for _, objref in pairs(place_entities(pos)) do - update_entity(objref:get_luaentity()) - end -end - ---- On_activate callback for display_api entities. Calls on_display_update callbacks ---- of corresponding node for each entity. -function display_api.on_activate(entity, staticdata) - if entity then - if string.sub(staticdata, 1, string.len("return")) == "return" then - local data = minetest.deserialize(staticdata) - if data and type(data) == "table" then - entity.nodepos = data.nodepos - end - entity.object:set_armor_groups({immortal=1}) - end - update_entity(entity) - end -end - ---- On_place callback for display_api items. --- Does nothing more than preventing node from being placed on ceiling or ground -function display_api.on_place(itemstack, placer, pointed_thing, override_param2) - local ndef = itemstack:get_definition() - local above = pointed_thing.above - local under = pointed_thing.under - local dir = {x = under.x - above.x, y = 0, z = under.z - above.z} - - -- If item is not placed on a wall, use the player's view direction instead - if dir.x == 0 and dir.z == 0 then - dir = placer:get_look_dir() - dir.y = 0 - end - - local param2 = 0 - if ndef then - local paramtype2 = ndef.paramtype2 - if paramtype2 == "wallmounted" or paramtype2 == "colorwallmounted" then - param2 = minetest.dir_to_wallmounted(dir) - elseif paramtype2 == "facedir" or paramtype2 == "colorfacedir" then - param2 = minetest.dir_to_facedir(dir) - end - end - return minetest.item_place(itemstack, placer, pointed_thing, - param2 + (override_param2 or 0)) -end - ---- On_construct callback for display_api items. --- Creates entities and update them. -function display_api.on_construct(pos) - display_api.update_entities(pos) -end - ---- On_destruct callback for display_api items. --- Removes entities. -function display_api.on_destruct(pos) - for _, objref in pairs(get_entities(pos)) do - objref:remove() - end -end - --- On_rotate (screwdriver) callback for display_api items. Prevents invalid rotations and reorients entities. -function display_api.on_rotate(pos, node, user, _, new_param2) - node.param2 = new_param2 - if get_values(node) then - minetest.swap_node(pos, node) - place_entities(pos) - return true - else - return false - end -end - ---- Creates display entity with some fields and the on_activate callback -function display_api.register_display_entity(entity_name) - if not minetest.registered_entity then - minetest.register_entity(':'..entity_name, { - collisionbox = { 0, 0, 0, 0, 0, 0 }, - visual = "upright_sprite", - textures = {}, - on_activate = display_api.on_activate, - get_staticdata = function(self) - return minetest.serialize({ nodepos = self.nodepos }) - end, - }) - end -end - -minetest.register_lbm({ - label = "Update display_api entities", - name = "display_api:update_entities", - run_at_every_load = true, - nodenames = {"group:display_modpack_node", "group:display_lib_node"}, - action = function(pos, node) display_api.update_entities(pos) end, -}) - --- Compatibility -display_lib = display_api +dofile(display_api.path.."/display.lua") +dofile(display_api.path.."/deprecation.lua")