Skip to content

Commit

Permalink
Revise dynamic_add_media API to better accomodate future changes
Browse files Browse the repository at this point in the history
  • Loading branch information
sfan5 committed Feb 1, 2021
1 parent a01a02f commit 40ad976
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 22 deletions.
23 changes: 23 additions & 0 deletions builtin/game/misc.lua
Expand Up @@ -266,3 +266,26 @@ end
function core.cancel_shutdown_requests()
core.request_shutdown("", false, -1)
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
22 changes: 12 additions & 10 deletions doc/lua_api.txt
Expand Up @@ -5446,20 +5446,22 @@ Server
* Returns a code (0: successful, 1: no such player, 2: player is connected)
* `minetest.remove_player_auth(name)`: remove player authentication data
* Returns boolean indicating success (false if player nonexistant)
* `minetest.dynamic_add_media(filepath)`
* Adds the file at the given path to the media sent to clients by the server
on startup and also pushes this file to already connected clients.
* `minetest.dynamic_add_media(filepath, callback)`
* `filepath`: path to a media file on the filesystem
* `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
modified, deleted, moved or renamed after calling this function.
The list of dynamically added media is not persisted.
* Returns boolean indicating success (duplicate files count as error)
* The media will be ready to use (in e.g. entity textures, sound_play)
immediately after calling this function.
* Returns false on error, true if the request was accepted
* The given callback will be called for every player as soon as the
media is available on the client.
Old clients that lack support for this feature will not see the media
unless they reconnect to the server.
* Since media transferred this way does not use client caching or HTTP
transfers, dynamic media should not be used with big files or performance
will suffer.
unless they reconnect to the server. (callback won't be called)
* Since media transferred this way currently does not use client caching
or HTTP transfers, dynamic media should not be used with big files.

Bans
----
Expand Down
21 changes: 16 additions & 5 deletions src/script/lua_api/l_server.cpp
Expand Up @@ -452,19 +452,30 @@ int ModApiServer::l_sound_fade(lua_State *L)
}

// 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;

// Reject adding media before the server has started up
if (!getEnv(L))
throw LuaError("Dynamic media cannot be added before server has started up");

std::string filepath = readParam<std::string>(L, 1);
CHECK_SECURE_PATH(L, filepath.c_str(), false);

bool ok = getServer(L)->dynamicAddMedia(filepath);
lua_pushboolean(L, ok);
std::vector<RemotePlayer*> sent_to;
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;
}

Expand Down Expand Up @@ -532,7 +543,7 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(sound_play);
API_FCT(sound_stop);
API_FCT(sound_fade);
API_FCT(dynamic_add_media);
API_FCT(dynamic_add_media_raw);

API_FCT(get_player_information);
API_FCT(get_player_privs);
Expand Down
2 changes: 1 addition & 1 deletion src/script/lua_api/l_server.h
Expand Up @@ -71,7 +71,7 @@ class ModApiServer : public ModApiBase
static int l_sound_fade(lua_State *L);

// 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)
static int l_get_player_privs(lua_State *L);
Expand Down
20 changes: 15 additions & 5 deletions src/server.cpp
Expand Up @@ -3465,7 +3465,8 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 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());
if (m_media.find(filename) != m_media.end()) {
Expand All @@ -3485,9 +3486,17 @@ bool Server::dynamicAddMedia(const std::string &filepath)
pkt << raw_hash << filename << (bool) true;
pkt.putLongString(filedata);

auto client_ids = m_clients.getClientIDs(CS_DefinitionsSent);
for (session_t client_id : client_ids) {
m_clients.lock();
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.
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
Expand All @@ -3496,9 +3505,10 @@ bool Server::dynamicAddMedia(const std::string &filepath)
- channel 1 (HUD)
- channel 0 (everything else: e.g. play_sound, object messages)
*/
m_clients.send(client_id, 1, &pkt, true);
m_clients.send(client_id, 0, &pkt, true);
m_clients.send(pair.second->peer_id, 1, &pkt, true);
m_clients.send(pair.second->peer_id, 0, &pkt, true);
}
m_clients.unlock();

return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/server.h
Expand Up @@ -257,7 +257,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,

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(); }
void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id);
Expand Down

0 comments on commit 40ad976

Please sign in to comment.