From ea0a0fe6c402d1d026bd7d8f8002bc2b42021d4c Mon Sep 17 00:00:00 2001 From: Luke aka SwissalpS <161979+SwissalpS@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:19:40 +0100 Subject: [PATCH] On move support aka jumpdrive compat (#9) * whitespace and minetest. -> core. - use same whitespace style throughout and fix some indents. - change from minetest. to core. namespace. * Update Readme: Minetest -> Luanti * add luacheck * update optional depends * luacheck cleanups - unused arguments - shaddowed vars - var redeclarations - some whitespace involved in above lines * clean close of file after reading * avoid duplicate code save some lines and some extra method calls. * avoid error: "unknown global" * stray whitespace fixups * whitespace: switch all files to same indent character tabs -> spaces * refactor weird declaration * whitespace: two stray spaces ...that slipped through the cracks on previous whitespace edit * fix shaddowing global table * reduce amount of times transformation string is calculated * use ipairs feature or use the faster repeat-until-loop * cap max amount of layers fixes #5 * fix #5 make transform history non-global fixes #5 transformation history was shared by all users and kept growing as it was never truely reset. Every time a user used a banner a white background was dumped on top of the stack making it possible to crash the server through an overflow to core.serialize() function. * minimize metadata size also changed banners.max_transformations to banners.max_undo_levels and lowered the value substantially. * add versioning * add recoverable per player history with saved colour * rename update_all -> update_preview_inv since it isn't updating everything, only the preview and the inventory item * dynamically update masks to reflect the currently selected colour. * banner always exists even if only "" it exists and there isn't an error. not adding "" or texture string, causes a non critical error. * use same function declaration style throughout * remove set_banner_texture() only used once and is easy enough to have inline * preserve player undo-history over joins player could have timed-out and thus lost their history. It isn't crucial to remove their history, so let's give them this feature. * refactor: reusable transformation string creation unfortunately the git diff is a bit messy on this one. Nothing changed with: - banners.Banner - banners.Banner:new - banners.Banner:push_transform - banners.Banner:pop_transform content of banners.Banner:get_transform_string was moved to banners.transform_table_to_string with very few changes * add transform_string_to_table allows us to add more features such as: - clean existing banners to slim down data - read transforms from items in inventory -> allow players to change existing patterns without having to start from scratch. * cleanup old banners when their entities are generated * read item meta allowing players to edit existing banners * version bump * oopsie fixes * remove stray debug point * add chatcommand banners_fix fixes #7 provides a work-around for the engine problem with entities. * sensitive github luacheck syntax * version bump * cleanup: compact node definitions * adds [jumpdrive] compat github.com/mt-mods/jumpdrive closes https://github.com/mt-mods/jumpdrive/issues/90 * also make factions banners jump-able just for completeness sake --- .luacheckrc | 2 + chatcommands.lua | 53 +++++++++++++++ factions.lua | 18 ++---- init.lua | 164 ++++++++++++++++++++++++++++++++++------------- nodes.lua | 35 +++------- 5 files changed, 189 insertions(+), 83 deletions(-) create mode 100644 chatcommands.lua diff --git a/.luacheckrc b/.luacheckrc index 77837ad..a819f33 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -8,5 +8,7 @@ read_globals = { "dump", "factions", "inventory_plus", + ["table"] = { fields = { "insert_all" } }, "unified_inventory", + "vector", } diff --git a/chatcommands.lua b/chatcommands.lua new file mode 100644 index 0000000..0fdcbd0 --- /dev/null +++ b/chatcommands.lua @@ -0,0 +1,53 @@ +-- Due to some engine troubles there are sometimes stray +-- banner entities and more rarely there are banner nodes without entities. +-- Calling this command fixes both situations. +core.register_chatcommand("banners_fix", { + description = "recreates the banner-visuals in your area", + func = function(name) + local player = core.get_player_by_name(name) + if not player then + return + end + + local pos = player:get_pos() + local t1 = core.get_us_time() + + local radius = 10 + local entity_count = 0 + local objects = core.get_objects_inside_radius(pos, radius) + for _, v in ipairs(objects) do + local e = v:get_luaentity() + if e and e.name == "banners:banner_ent" then + entity_count = entity_count + 1 + v:remove() + end + end + + local pos1 = vector.subtract(pos, radius) + local pos2 = vector.add(pos, radius) + local nodes = { + "banners:wooden_banner", + "banners:steel_banner", + } + if core.get_modpath("factions") then + table.insert_all(nodes, { + "banners:power_banner", + "banners:death_banner", + + }) + end + local pos_list = core.find_nodes_in_area(pos1, pos2, nodes) + + for _, node_pos in ipairs(pos_list) do + core.add_entity(node_pos, "banners:banner_ent") + end + + local t2 = core.get_us_time() + local diff = t2 - t1 + local millis = diff / 1000 + + return true, "Removed " .. entity_count .. " banner entities and restored " + .. #pos_list .. " banners in " .. millis .. " ms" + end +}) + diff --git a/factions.lua b/factions.lua index aab0cf1..1abbd70 100644 --- a/factions.lua +++ b/factions.lua @@ -143,12 +143,8 @@ core.register_node("banners:power_banner", { stack_max = 1, paramtype = "light", paramtype2 = "facedir", - after_place_node = function (pos, player, itemstack, pointed_thing) - banners.after_powerbanner_placed(pos, player, itemstack, pointed_thing) - end, - on_destruct = function(pos) - banners.banner_on_destruct(pos) - end, + after_place_node = banners.after_powerbanner_placed, + on_destruct = banners.banner_on_destruct, on_dig = function(pos, n, p) if core.is_protected(pos, p:get_player_name()) then return @@ -163,6 +159,7 @@ core.register_node("banners:power_banner", { end banners.banner_on_dig(pos, n, p) end, + on_movenode = banners.banner_on_movenode, }) core.register_node("banners:death_banner", { @@ -176,12 +173,8 @@ core.register_node("banners:death_banner", { stack_max = 1, paramtype = "light", paramtype2 = "facedir", - after_place_node = function (pos, player, itemstack, pointed_thing) - banners.after_deathbanner_placed(pos, player, itemstack, pointed_thing) - end, - on_destruct = function(pos) - banners.banner_on_destruct(pos) - end, + after_place_node = banners.after_deathbanner_placed, + on_destruct = banners.banner_on_destruct, -- (pos, node, player) on_dig = function(pos, _, player) if core.is_protected(pos, player:get_player_name()) then @@ -198,6 +191,7 @@ core.register_node("banners:death_banner", { end core.remove_node(pos) end, + on_movenode = banners.banner_on_movenode, }) -- (pos, player, itemstack, pointed_thing) diff --git a/init.lua b/init.lua index 427294c..7c34a1e 100644 --- a/init.lua +++ b/init.lua @@ -2,7 +2,7 @@ local MP = core.get_modpath("banners") .. "/" dofile(MP .. "smartfs.lua") banners = { - version = 20241128.1533 + version = 20241130.1920 } banners.masks = { @@ -47,24 +47,43 @@ banners.colors = { "brown", "darkbrown" } +local valid_masks = {} +local valid_colors = {} +do + local i, s + i = #banners.masks + repeat + s = banners.masks[i] + valid_masks[s .. ".png"] = true + i = i - 1 + until i == 0 + + i = #banners.colors + repeat + s = banners.colors[i] + valid_colors["bg_" .. s .. ".png"] = true + i = i - 1 + until i == 0 +end + banners.base_transform = { texture = "bg_white.png", mask = "mask_background.png" } -banners.creation_form_func = function(state) +function banners.creation_form_func(state) -- helper functions - state.update_player_inv = function(self, transform_string) + function state:update_player_inv(transform_string) local player = core.get_player_by_name(self.player) local newbanner = player:get_wielded_item() newbanner:get_meta():set_string("", transform_string) player:set_wielded_item(newbanner) end - state.update_preview = function(self, transform_string) + function state:update_preview(transform_string) self:get("banner_preview"):setImage(transform_string) self:get("color_indicator"):setImage(self.current_color) end - state.update_preview_inv = function(self) + function state:update_preview_inv() local transform_string = self.banner:get_transform_string() self:update_preview(transform_string) self:update_player_inv(transform_string) @@ -78,6 +97,7 @@ banners.creation_form_func = function(state) state.banner:push_transform(banners.base_transform) histories[state.player] = state.banner end + state.banner:read_item(state.player) state.current_color = state.banner.color state:size(20, 10) state:image(3, 0.4, 4, 2, "banner_preview", nil) @@ -144,34 +164,36 @@ end banners.creation_form = smartfs.create("banners:banner_creation", banners.creation_form_func) - --- banner definition -banners.Banner = {} - -function banners.Banner:new(banner) - banner = banner or { color = "bg_black.png", transforms = {} } - setmetatable(banner, self) - self.__index = self - return banner -end -function banners.Banner.push_transform(self, transform) - table.insert(self.transforms, transform) - if #self.transforms > banners.max_undo_levels then - table.remove(self.transforms, 1) +function banners.transform_string_to_table(transform_string) + local mask, parts, texture + local transforms = {} + for part in transform_string:gmatch("%(([^%)]+)%)") do + parts = part:split("^[") + if 3 == #parts then + texture = parts[1] + mask = parts[2]:sub(6) + if valid_masks[mask] and valid_colors[texture] then + table.insert(transforms, { + texture = texture, + mask = mask + }) + end + end end + return transforms end -function banners.Banner.pop_transform(self) - table.remove(self.transforms) -end -function banners.Banner.get_transform_string(self) + +function banners.transform_table_to_string(transforms) + local i = #transforms + if 0 == i then return "" end + local final = {} local used = {} local transform -- work backwards to keep resulting data small - local i = #self.transforms repeat - transform = self.transforms[i] - -- same mask can be trimmed out only using most recent + transform = transforms[i] + -- duplicate mask can be trimmed out only use most recent if not used[transform.mask] then used[transform.mask] = true table.insert(final, 1, "(" .. transform.texture @@ -183,13 +205,54 @@ function banners.Banner.get_transform_string(self) end i = i - 1 until i == 0 - local ret = table.concat(final, "^") - return ret + return table.concat(final, "^") +end + +-- banner definition +banners.Banner = {} + +function banners.Banner:new(banner) + banner = banner or { color = "bg_black.png", transforms = {} } + setmetatable(banner, self) + self.__index = self + return banner +end + +function banners.Banner:push_transform(transform) + table.insert(self.transforms, transform) + if #self.transforms > banners.max_undo_levels then + table.remove(self.transforms, 1) + end +end + +function banners.Banner:pop_transform() + table.remove(self.transforms) +end + +function banners.Banner:get_transform_string() + return banners.transform_table_to_string(self.transforms) +end + +function banners.Banner:read_item(player_name) + local player = core.get_player_by_name(player_name) + local item = player:get_wielded_item() + if "banners:" ~= item:get_name():sub(1, 8) then return end + + local transforms = banners.transform_string_to_table( + item:get_meta():get_string("")) + local total = #transforms + if 0 == total then return end + + local i = 1 + repeat + self:push_transform(transforms[i]) + i = i + 1 + until i > total end -- helper function for determining the flag's direction -- (pos, pointed_thing) -banners.determine_flag_direction = function(_, pointed_thing) +function banners.determine_flag_direction(_, pointed_thing) local above = pointed_thing.above local under = pointed_thing.under local dir = { @@ -201,13 +264,13 @@ banners.determine_flag_direction = function(_, pointed_thing) end -- (itemstack, player, pointed_thing) -banners.banner_on_use = function(_, player) +function banners.banner_on_use(_, player) if player.is_player then banners.creation_form:show(player:get_player_name()) end end -banners.banner_on_dig = function(pos, node, player) +function banners.banner_on_dig(pos, node, player) if not player or core.is_protected(pos, player:get_player_name()) then return end @@ -223,7 +286,7 @@ banners.banner_on_dig = function(pos, node, player) end -- (pos, node, player) -banners.banner_on_destruct = function(pos) +function banners.banner_on_destruct(pos) local objects = core.get_objects_inside_radius(pos, 0.5) for _, v in ipairs(objects) do local e = v:get_luaentity() @@ -234,21 +297,33 @@ banners.banner_on_destruct = function(pos) end -- (pos, player, itemstack, pointed_thing) -banners.banner_after_place = function(pos, _, itemstack, pointed_thing) +function banners.banner_after_place(pos, _, itemstack, pointed_thing) core.get_node(pos).param2 = banners.determine_flag_direction(pos, pointed_thing) - core.get_meta(pos):set_string("banner", itemstack:get_meta():get_string("")) + local meta = core.get_meta(pos) + meta:set_string("banner", itemstack:get_meta():get_string("")) + meta:set_float("version", banners.version) core.add_entity(pos, "banners:banner_ent") end --- banner entity -local set_banner_texture = function(obj, texture) - obj:set_properties({ textures = { "banner_uv_text.png^" .. texture } }) +-- [jumpdrive] compat +-- (from_pos, to_pos, additional_info) +function banners.banner_on_movenode(_, to_pos) + core.add_entity(to_pos, "banners:banner_ent") end +-- banner entity -banners.banner_on_activate = function(self) +function banners:banner_on_activate() local pos = self.object:get_pos() - local banner = core.get_meta(pos):get_string("banner") + local meta = core.get_meta(pos) + local banner = meta:get_string("banner") + -- cleanup meta of old banners + if meta:get_float("version") < 20241122 then + meta:set_float("version", banners.version) + banner = banners.transform_table_to_string( + banners.transform_string_to_table(banner)) + meta:set_string("banner", banner) + end local banner_face = core.get_node(pos).param2 local yaw = 0. if banner_face == 2 then @@ -261,9 +336,9 @@ banners.banner_on_activate = function(self) yaw = 4.71238898038469 -- 3 * pi / 2 end self.object:set_yaw(yaw) - if banner then - set_banner_texture(self.object, banner) - end + self.object:set_properties({ + textures = { "banner_uv_text.png^" .. banner } + }) end core.register_entity("banners:banner_ent", { @@ -276,10 +351,6 @@ core.register_entity("banners:banner_ent", { on_activate = banners.banner_on_activate, }) -core.register_on_leaveplayer(function(player) - histories[player:get_player_name()] = nil -end) - if core.get_modpath("factions") then dofile(MP .. "factions.lua") end @@ -287,4 +358,5 @@ end dofile(MP .. "items.lua") dofile(MP .. "nodes.lua") dofile(MP .. "crafts.lua") +dofile(MP .. "chatcommands.lua") diff --git a/nodes.lua b/nodes.lua index ad2991c..3dd86aa 100644 --- a/nodes.lua +++ b/nodes.lua @@ -10,18 +10,11 @@ core.register_node("banners:wooden_banner", { stack_max = 1, paramtype = "light", paramtype2 = "facedir", - after_place_node = function (pos, player, itemstack, pointed_thing) - banners.banner_after_place(pos, player, itemstack, pointed_thing) - end, - on_destruct = function(pos) - banners.banner_on_destruct(pos) - end, - on_use = function(i, p, pt) - banners.banner_on_use(i, p, pt) - end, - on_dig = function(pos, n, p) - banners.banner_on_dig(pos, n, p) - end + after_place_node = banners.banner_after_place, + on_destruct = banners.banner_on_destruct, + on_use = banners.banner_on_use, + on_dig = banners.banner_on_dig, + on_movenode = banners.banner_on_movenode, }) -- steel banner @@ -36,18 +29,10 @@ core.register_node("banners:steel_banner", { stack_max = 1, paramtype = "light", paramtype2 = "facedir", - after_place_node = function (pos, player, itemstack, pointed_thing) - banners.banner_after_place(pos, player, itemstack, pointed_thing) - end, - on_destruct = function(pos) - banners.banner_on_destruct(pos) - end, - on_use = function(i, p, pt) - banners.banner_on_use(i, p, pt) - end, - on_dig = function(pos, n, p) - banners.banner_on_dig(pos, n, p) - end - + after_place_node = banners.banner_after_place, + on_destruct = banners.banner_on_destruct, + on_use = banners.banner_on_use, + on_dig = banners.banner_on_dig, + on_movenode = banners.banner_on_movenode, })