Revise dynamic_add_media API to better accomodate future changes

This commit is contained in:
sfan5 2021-01-30 14:35:34 +01:00
parent a01a02f7a1
commit 40ad976753
6 changed files with 68 additions and 22 deletions

@ -266,3 +266,26 @@ end
function core.cancel_shutdown_requests() function core.cancel_shutdown_requests()
core.request_shutdown("", false, -1) core.request_shutdown("", false, -1)
end end
-- Callback handling for dynamic_add_media
local dynamic_add_media_raw = core.dynamic_add_media_raw
core.dynamic_add_media_raw = nil
function core.dynamic_add_media(filepath, callback)
local ret = dynamic_add_media_raw(filepath)
if ret == false then
return ret
end
if callback == nil then
core.log("deprecated", "Calling minetest.dynamic_add_media without "..
"a callback is deprecated and will stop working in future versions.")
else
-- At the moment async loading is not actually implemented, so we
-- immediately call the callback ourselves
for _, name in ipairs(ret) do
callback(name)
end
end
return true
end

@ -5446,20 +5446,22 @@ Server
* Returns a code (0: successful, 1: no such player, 2: player is connected) * Returns a code (0: successful, 1: no such player, 2: player is connected)
* `minetest.remove_player_auth(name)`: remove player authentication data * `minetest.remove_player_auth(name)`: remove player authentication data
* Returns boolean indicating success (false if player nonexistant) * Returns boolean indicating success (false if player nonexistant)
* `minetest.dynamic_add_media(filepath)` * `minetest.dynamic_add_media(filepath, callback)`
* Adds the file at the given path to the media sent to clients by the server * `filepath`: path to a media file on the filesystem
on startup and also pushes this file to already connected clients. * `callback`: function with arguments `name`, where name is a player name
(previously there was no callback argument; omitting it is deprecated)
* Adds the file to the media sent to clients by the server on startup
and also pushes this file to already connected clients.
The file must be a supported image, sound or model format. It must not be The file must be a supported image, sound or model format. It must not be
modified, deleted, moved or renamed after calling this function. modified, deleted, moved or renamed after calling this function.
The list of dynamically added media is not persisted. The list of dynamically added media is not persisted.
* Returns boolean indicating success (duplicate files count as error) * Returns false on error, true if the request was accepted
* The media will be ready to use (in e.g. entity textures, sound_play) * The given callback will be called for every player as soon as the
immediately after calling this function. media is available on the client.
Old clients that lack support for this feature will not see the media Old clients that lack support for this feature will not see the media
unless they reconnect to the server. unless they reconnect to the server. (callback won't be called)
* Since media transferred this way does not use client caching or HTTP * Since media transferred this way currently does not use client caching
transfers, dynamic media should not be used with big files or performance or HTTP transfers, dynamic media should not be used with big files.
will suffer.
Bans Bans
---- ----

@ -452,19 +452,30 @@ int ModApiServer::l_sound_fade(lua_State *L)
} }
// dynamic_add_media(filepath) // dynamic_add_media(filepath)
int ModApiServer::l_dynamic_add_media(lua_State *L) int ModApiServer::l_dynamic_add_media_raw(lua_State *L)
{ {
NO_MAP_LOCK_REQUIRED; NO_MAP_LOCK_REQUIRED;
// Reject adding media before the server has started up
if (!getEnv(L)) if (!getEnv(L))
throw LuaError("Dynamic media cannot be added before server has started up"); throw LuaError("Dynamic media cannot be added before server has started up");
std::string filepath = readParam<std::string>(L, 1); std::string filepath = readParam<std::string>(L, 1);
CHECK_SECURE_PATH(L, filepath.c_str(), false); CHECK_SECURE_PATH(L, filepath.c_str(), false);
bool ok = getServer(L)->dynamicAddMedia(filepath); std::vector<RemotePlayer*> sent_to;
lua_pushboolean(L, ok); bool ok = getServer(L)->dynamicAddMedia(filepath, sent_to);
if (ok) {
// (see wrapper code in builtin)
lua_createtable(L, sent_to.size(), 0);
int i = 0;
for (RemotePlayer *player : sent_to) {
lua_pushstring(L, player->getName());
lua_rawseti(L, -2, ++i);
}
} else {
lua_pushboolean(L, false);
}
return 1; return 1;
} }
@ -532,7 +543,7 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(sound_play); API_FCT(sound_play);
API_FCT(sound_stop); API_FCT(sound_stop);
API_FCT(sound_fade); API_FCT(sound_fade);
API_FCT(dynamic_add_media); API_FCT(dynamic_add_media_raw);
API_FCT(get_player_information); API_FCT(get_player_information);
API_FCT(get_player_privs); API_FCT(get_player_privs);

@ -71,7 +71,7 @@ private:
static int l_sound_fade(lua_State *L); static int l_sound_fade(lua_State *L);
// dynamic_add_media(filepath) // dynamic_add_media(filepath)
static int l_dynamic_add_media(lua_State *L); static int l_dynamic_add_media_raw(lua_State *L);
// get_player_privs(name, text) // get_player_privs(name, text)
static int l_get_player_privs(lua_State *L); static int l_get_player_privs(lua_State *L);

@ -3465,7 +3465,8 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id)
SendDeleteParticleSpawner(peer_id, id); SendDeleteParticleSpawner(peer_id, id);
} }
bool Server::dynamicAddMedia(const std::string &filepath) bool Server::dynamicAddMedia(const std::string &filepath,
std::vector<RemotePlayer*> &sent_to)
{ {
std::string filename = fs::GetFilenameFromPath(filepath.c_str()); std::string filename = fs::GetFilenameFromPath(filepath.c_str());
if (m_media.find(filename) != m_media.end()) { if (m_media.find(filename) != m_media.end()) {
@ -3485,9 +3486,17 @@ bool Server::dynamicAddMedia(const std::string &filepath)
pkt << raw_hash << filename << (bool) true; pkt << raw_hash << filename << (bool) true;
pkt.putLongString(filedata); pkt.putLongString(filedata);
auto client_ids = m_clients.getClientIDs(CS_DefinitionsSent); m_clients.lock();
for (session_t client_id : client_ids) { for (auto &pair : m_clients.getClientList()) {
if (pair.second->getState() < CS_DefinitionsSent)
continue;
if (pair.second->net_proto_version < 39)
continue;
if (auto player = m_env->getPlayer(pair.second->peer_id))
sent_to.emplace_back(player);
/* /*
FIXME: this is a very awful hack
The network layer only guarantees ordered delivery inside a channel. The network layer only guarantees ordered delivery inside a channel.
Since the very next packet could be one that uses the media, we have Since the very next packet could be one that uses the media, we have
to push the media over ALL channels to ensure it is processed before to push the media over ALL channels to ensure it is processed before
@ -3496,9 +3505,10 @@ bool Server::dynamicAddMedia(const std::string &filepath)
- channel 1 (HUD) - channel 1 (HUD)
- channel 0 (everything else: e.g. play_sound, object messages) - channel 0 (everything else: e.g. play_sound, object messages)
*/ */
m_clients.send(client_id, 1, &pkt, true); m_clients.send(pair.second->peer_id, 1, &pkt, true);
m_clients.send(client_id, 0, &pkt, true); m_clients.send(pair.second->peer_id, 0, &pkt, true);
} }
m_clients.unlock();
return true; return true;
} }

@ -257,7 +257,7 @@ public:
void deleteParticleSpawner(const std::string &playername, u32 id); void deleteParticleSpawner(const std::string &playername, u32 id);
bool dynamicAddMedia(const std::string &filepath); bool dynamicAddMedia(const std::string &filepath, std::vector<RemotePlayer*> &sent_to);
ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); } ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); }
void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id); void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id);