From 16a68af1199180e402b05574d0fb284409c49e30 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Fri, 4 Feb 2022 01:03:19 +0100 Subject: [PATCH] Store mcl_maps textures in map item meta Map textures are stored as files in a world directory and in item meta. For a typical forest landscape, this adds up to 10kb to map item meta. The main purpose of this is to allow maps to work in world downloads: Maps are automatically restored when no textures are found on disk. As a side effect, players will probably lag a bit if they fill their inventory with many (shulkers full of) maps, but enchanted items or books can be used for the same purpose already, so this is not new. Saving PNGs can lead to a few kb of savings here, but would hinder enhancements to mcl_maps that are based on updating the stored map, as reading and writing TGA images is stupidly easy compared to PNG. (Really, any bitmap format is about the same size zlib compressed.) Note that any hypothetical future version of mcl_maps that changes the storage format (e.g. to compressed PNM) or sends PNG maps to clients has to include a TGA parser, to avoid breaking existing maps in user worlds. --- mods/ITEMS/mcl_maps/init.lua | 116 ++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_maps/init.lua b/mods/ITEMS/mcl_maps/init.lua index ad40f4c6..b6315127 100644 --- a/mods/ITEMS/mcl_maps/init.lua +++ b/mods/ITEMS/mcl_maps/init.lua @@ -153,8 +153,122 @@ function mcl_maps.load_map(id) return texture end +local function encode_item_meta(input) + return minetest.encode_base64( + minetest.compress( + input, + "deflate", + 9 + ) + ) +end + +local function decode_item_meta(input) + return minetest.decompress( + minetest.decode_base64(input), + "deflate", + 9 + ) +end + +result_original = "foo\0\01\02\x03\n\rbar" +result_roundtrip = decode_item_meta( + encode_item_meta(result_original) +) +assert( + result_original == result_roundtrip, + "mcl_maps: mismatch between encode_item_meta() and decode_item_meta()" +) + function mcl_maps.load_map_item(itemstack) - return mcl_maps.load_map(itemstack:get_meta():get_string("mcl_maps:id")) + local meta = itemstack:get_meta() + local map_id = meta:get_string("mcl_maps:id") + + if "" == map_id or creating_maps[map_id] then + -- wait for map content to be created + return + end + + if loaded_maps[map_id] then + -- texture has already been sent + return mcl_maps.load_map(map_id) + end + + local texture_file_name = "mcl_maps_map_texture_" .. map_id .. ".tga" + local texture_file_path = map_textures_path .. texture_file_name + + -- does the texture file exist? + local texture_file_handle_read = io.open( + texture_file_path, + "rb" + ) + local texture_file_exists = true + local texture_data_from_file + if nil == texture_file_handle_read then + texture_file_exists = false + else + texture_data_from_file = texture_file_handle_read:read("*a") + texture_file_handle_read:close() + end + + -- does the texture item meta exist? + local tga_deflate_base64 = meta:get_string("mcl_maps:tga_deflate_base64") + local texture_item_meta_exists = true + if "" == tga_deflate_base64 then + texture_item_meta_exists = false + end + + if texture_file_exists and nil ~= texture_data_from_file then + if texture_item_meta_exists then + -- sanity check: do we have the same textures? + -- if server-side texture has changed, take it + if decode_item_meta(tga_deflate_base64) ~= texture_data_from_file then + minetest.log( + "action", + "mcl_maps: update item meta from file content for map " .. map_id + ) + meta:set_string( + "mcl_maps:tga_deflate_base64", + encode_item_meta(texture_data_from_file) + ) + end + else + -- probably an older map from mcl2 or mcl5, so + -- we now write the file contents to item meta + minetest.log( + "action", + "mcl_maps: create item meta from file content for map " .. map_id + ) + meta:set_string( + "mcl_maps:tga_deflate_base64", + encode_item_meta(texture_data_from_file) + ) + end + else + -- no texture file → could be a world download + -- so we look for missing texture in item meta + -- and write that to the map texture file here + if texture_item_meta_exists then + minetest.log( + "action", + "mcl_maps: create file content from item meta for map " .. map_id + ) + assert( + minetest.safe_file_write( + texture_file_path, + decode_item_meta(tga_deflate_base64) + ) + ) + else + minetest.log( + "error", + "no data for map " .. map_id + ) + return + end + end + + return mcl_maps.load_map(map_id) end local function fill_map(itemstack, placer, pointed_thing)