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.
This commit is contained in:
Nils Dagsson Moskopp 2022-02-04 01:03:19 +01:00
parent 82a5708144
commit 16a68af119
No known key found for this signature in database
GPG Key ID: A3BC671C35191080
1 changed files with 115 additions and 1 deletions

View File

@ -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)