Fix dynamic media race condition

Wait for *all* players to receive the texture before using it.
Previously this would wait for *any* player to receive the texture.
This works fine in singleplayer (or on pre-5.5,
where dynamic_add_media would block until all players have received the texture),
but it may fail otherwise:
Either entity textures won't be available in time,
or, even worse, the HUD texture is unavailable.
Both produce an error in chat for clients.
This commit is contained in:
Lars Mueller 2023-04-08 20:02:30 +02:00
parent 4fa5c94515
commit df5bd3c4ec
2 changed files with 68 additions and 17 deletions

63
dynamic_add_media_all.lua Normal file
View File

@ -0,0 +1,63 @@
local on_leaves = {} -- functions(name)
local timeout = 5 -- seconds
xmaps.dynamic_add_media_all = function(path, on_all_received)
if not minetest.features.dynamic_add_media_table then
-- minetest.dynamic_add_media() blocks in
-- Minetest 5.3 and 5.4 until media loads
minetest.dynamic_add_media(path, function() end)
on_all_received()
return
end
-- minetest.dynamic_add_media() never blocks
-- in Minetest 5.5, callback runs *for each client*
-- who has received & loaded the media
-- all players currently online must still receive it
local to_receive = {}
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
-- Only clients with protocol version >= 39 can receive dynamic media.
if minetest.get_player_information(name).protocol_version >= 39 then
to_receive[name] = true
end
end
local function remove_to_receive(name)
to_receive[name] = nil
if next(to_receive) == nil then
on_leaves[remove_to_receive] = nil
on_all_received()
end
end
on_leaves[remove_to_receive] = true
minetest.dynamic_add_media(
{filepath = path},
function(name)
assert(name)
if not to_receive[name] then
minetest.log("warning", ("xmaps: %s received media despite not being connected"):format(name))
return
end
remove_to_receive(name)
end
)
minetest.after(timeout, function()
if next(to_receive) ~= nil then
local names = {}
for name in pairs(to_receive) do
table.insert(names, name)
end
table.sort(names)
minetest.log("warning", ("xmaps: sending media to %s timed out"):format(table.concat(names, ", ")))
end
on_leaves[remove_to_receive] = nil
on_all_received()
end)
end
minetest.register_on_leaveplayer(
function(player)
local player_name = player:get_player_name()
for on_leave in pairs(on_leaves) do
on_leave(player_name)
end
end
)

View File

@ -48,6 +48,8 @@ end
xmaps = {}
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/dynamic_add_media_all.lua")
xmaps.dark = {} -- key: player name; value: is it dark?
xmaps.huds = {} -- key: player name; value: player huds
xmaps.maps = {} -- key: player name; value: map texture
@ -393,26 +395,12 @@ xmaps.load_map = function(map_id)
end
local filename = xmaps.get_map_filename(map_id)
local path = textures_dir .. filename
if not xmaps.sent[map_id] then
if not minetest.features.dynamic_add_media_table then
-- minetest.dynamic_add_media() blocks in
-- Minetest 5.3 and 5.4 until media loads
minetest.dynamic_add_media(
textures_dir .. filename,
function() end
)
xmaps.dynamic_add_media_all(path, function()
xmaps.load[map_id] = true
else
-- minetest.dynamic_add_media() never blocks
-- in Minetest 5.5, callback runs after load
minetest.dynamic_add_media(
textures_dir .. filename,
function()
xmaps.load[map_id] = true
end
)
end
end)
xmaps.sent[map_id] = true
end