mirror of
https://github.com/minetest/minetest.git
synced 2024-11-23 08:03:45 +01:00
Enable dynamic_add_media to take the file data instead of a path
This commit is contained in:
parent
c90ebad46b
commit
d4b107e2e8
@ -36,6 +36,7 @@ core.features = {
|
|||||||
item_specific_pointabilities = true,
|
item_specific_pointabilities = true,
|
||||||
blocking_pointability_type = true,
|
blocking_pointability_type = true,
|
||||||
dynamic_add_media_startup = true,
|
dynamic_add_media_startup = true,
|
||||||
|
dynamic_add_media_filepath = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function core.has_feature(arg)
|
function core.has_feature(arg)
|
||||||
|
@ -5306,6 +5306,8 @@ Utilities
|
|||||||
blocking_pointability_type = true,
|
blocking_pointability_type = true,
|
||||||
-- dynamic_add_media can be called at startup when leaving callback as `nil` (5.9.0)
|
-- dynamic_add_media can be called at startup when leaving callback as `nil` (5.9.0)
|
||||||
dynamic_add_media_startup = true,
|
dynamic_add_media_startup = true,
|
||||||
|
-- dynamic_add_media supports `filename` and `filedata` parameters (5.9.0)
|
||||||
|
dynamic_add_media_filepath = true,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -6602,12 +6604,14 @@ Server
|
|||||||
* `minetest.dynamic_add_media(options, callback)`
|
* `minetest.dynamic_add_media(options, callback)`
|
||||||
* `options`: table containing the following parameters
|
* `options`: table containing the following parameters
|
||||||
* `filename`: name the media file will be usable as
|
* `filename`: name the media file will be usable as
|
||||||
(optional, default taken from path)
|
(optional if `filepath` present)
|
||||||
* `filepath`: path to the file on the filesystem
|
* `filepath`: path to the file on the filesystem [*]
|
||||||
|
* `filedata`: the data of the file to be sent [*]
|
||||||
* `to_player`: name of the player the media should be sent to instead of
|
* `to_player`: name of the player the media should be sent to instead of
|
||||||
all players (optional)
|
all players (optional)
|
||||||
* `ephemeral`: boolean that marks the media as ephemeral,
|
* `ephemeral`: boolean that marks the media as ephemeral,
|
||||||
it will not be cached on the client (optional, default false)
|
it will not be cached on the client (optional, default false)
|
||||||
|
* Exactly one of the paramters marked [*] must be specified.
|
||||||
* `callback`: function with arguments `name`, which is a player name
|
* `callback`: function with arguments `name`, which is a player name
|
||||||
* Pushes the specified media file to client(s). (details below)
|
* Pushes the specified media file to client(s). (details below)
|
||||||
The file must be a supported image, sound or model format.
|
The file must be a supported image, sound or model format.
|
||||||
|
@ -151,7 +151,7 @@ checker = nil
|
|||||||
|
|
||||||
do
|
do
|
||||||
-- we used to write the textures to our mod folder. in order to avoid
|
-- we used to write the textures to our mod folder. in order to avoid
|
||||||
-- duplicate errors delete them if they still exist.
|
-- duplication errors delete them if they still exist.
|
||||||
local path = core.get_modpath(core.get_current_modname()) .. "/textures/"
|
local path = core.get_modpath(core.get_current_modname()) .. "/textures/"
|
||||||
os.remove(path .. "testnodes_generated_mb.png")
|
os.remove(path .. "testnodes_generated_mb.png")
|
||||||
os.remove(path .. "testnodes_generated_ck.png")
|
os.remove(path .. "testnodes_generated_ck.png")
|
||||||
@ -162,15 +162,15 @@ core.safe_file_write(
|
|||||||
textures_path .. "testnodes1.png",
|
textures_path .. "testnodes1.png",
|
||||||
encode_and_check(512, 512, "rgb", data_mb)
|
encode_and_check(512, 512, "rgb", data_mb)
|
||||||
)
|
)
|
||||||
core.safe_file_write(
|
local png_ck = encode_and_check(512, 512, "gray", data_ck)
|
||||||
textures_path .. "testnodes_generated_ck.png",
|
|
||||||
encode_and_check(512, 512, "gray", data_ck)
|
|
||||||
)
|
|
||||||
core.dynamic_add_media({
|
core.dynamic_add_media({
|
||||||
filename = "testnodes_generated_mb.png",
|
filename = "testnodes_generated_mb.png",
|
||||||
filepath = textures_path .. "testnodes1.png"
|
filepath = textures_path .. "testnodes1.png"
|
||||||
})
|
})
|
||||||
core.dynamic_add_media(textures_path .. "testnodes_generated_ck.png")
|
core.dynamic_add_media({
|
||||||
|
filename = "testnodes_generated_ck.png",
|
||||||
|
filedata = png_ck,
|
||||||
|
})
|
||||||
|
|
||||||
minetest.register_node("testnodes:generated_png_mb", {
|
minetest.register_node("testnodes:generated_png_mb", {
|
||||||
description = S("Generated Mandelbrot PNG Test Node"),
|
description = S("Generated Mandelbrot PNG Test Node"),
|
||||||
@ -213,6 +213,8 @@ minetest.register_node("testnodes:generated_png_dst_emb", {
|
|||||||
groups = { dig_immediate = 2 },
|
groups = { dig_immediate = 2 },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
png_ck = nil
|
||||||
|
png_emb = nil
|
||||||
data_emb = nil
|
data_emb = nil
|
||||||
data_mb = nil
|
data_mb = nil
|
||||||
data_ck = nil
|
data_ck = nil
|
||||||
|
@ -483,6 +483,17 @@ bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname
|
|||||||
|
|
||||||
bool getstringfield(lua_State *L, int table,
|
bool getstringfield(lua_State *L, int table,
|
||||||
const char *fieldname, std::string &result)
|
const char *fieldname, std::string &result)
|
||||||
|
{
|
||||||
|
std::string_view sv;
|
||||||
|
if (getstringfield(L, table, fieldname, sv)) {
|
||||||
|
result = sv;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getstringfield(lua_State *L, int table,
|
||||||
|
const char *fieldname, std::string_view &result)
|
||||||
{
|
{
|
||||||
lua_getfield(L, table, fieldname);
|
lua_getfield(L, table, fieldname);
|
||||||
bool got = false;
|
bool got = false;
|
||||||
@ -491,7 +502,7 @@ bool getstringfield(lua_State *L, int table,
|
|||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
const char *ptr = lua_tolstring(L, -1, &len);
|
const char *ptr = lua_tolstring(L, -1, &len);
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
result.assign(ptr, len);
|
result = std::string_view(ptr, len);
|
||||||
got = true;
|
got = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <string_view>
|
||||||
|
|
||||||
#include "irrlichttypes_bloated.h"
|
#include "irrlichttypes_bloated.h"
|
||||||
#include "common/c_types.h"
|
#include "common/c_types.h"
|
||||||
@ -67,11 +67,11 @@ v3s16 getv3s16field_default(lua_State *L, int table,
|
|||||||
|
|
||||||
bool getstringfield(lua_State *L, int table,
|
bool getstringfield(lua_State *L, int table,
|
||||||
const char *fieldname, std::string &result);
|
const char *fieldname, std::string &result);
|
||||||
|
bool getstringfield(lua_State *L, int table,
|
||||||
|
const char *fieldname, std::string_view &result);
|
||||||
size_t getstringlistfield(lua_State *L, int table,
|
size_t getstringlistfield(lua_State *L, int table,
|
||||||
const char *fieldname,
|
const char *fieldname,
|
||||||
std::vector<std::string> *result);
|
std::vector<std::string> *result);
|
||||||
void read_groups(lua_State *L, int index,
|
|
||||||
std::unordered_map<std::string, int> &result);
|
|
||||||
bool getboolfield(lua_State *L, int table,
|
bool getboolfield(lua_State *L, int table,
|
||||||
const char *fieldname, bool &result);
|
const char *fieldname, bool &result);
|
||||||
bool getfloatfield(lua_State *L, int table,
|
bool getfloatfield(lua_State *L, int table,
|
||||||
|
@ -548,19 +548,22 @@ int ModApiServer::l_dynamic_add_media(lua_State *L)
|
|||||||
Server *server = getServer(L);
|
Server *server = getServer(L);
|
||||||
const bool at_startup = !getEnv(L);
|
const bool at_startup = !getEnv(L);
|
||||||
|
|
||||||
std::string filename, filepath, to_player;
|
std::string tmp;
|
||||||
bool ephemeral = false;
|
Server::DynamicMediaArgs args;
|
||||||
|
|
||||||
if (lua_istable(L, 1)) {
|
if (lua_istable(L, 1)) {
|
||||||
getstringfield(L, 1, "filename", filename);
|
getstringfield(L, 1, "filename", args.filename);
|
||||||
getstringfield(L, 1, "filepath", filepath);
|
if (getstringfield(L, 1, "filepath", tmp))
|
||||||
getstringfield(L, 1, "to_player", to_player);
|
args.filepath = tmp;
|
||||||
getboolfield(L, 1, "ephemeral", ephemeral);
|
args.data.emplace();
|
||||||
|
if (!getstringfield(L, 1, "filedata", *args.data))
|
||||||
|
args.data.reset();
|
||||||
|
getstringfield(L, 1, "to_player", args.to_player);
|
||||||
|
getboolfield(L, 1, "ephemeral", args.ephemeral);
|
||||||
} else {
|
} else {
|
||||||
filepath = readParam<std::string>(L, 1);
|
tmp = readParam<std::string>(L, 1);
|
||||||
|
args.filepath = tmp;
|
||||||
}
|
}
|
||||||
if (filepath.empty())
|
|
||||||
luaL_typerror(L, 1, "non-empty string");
|
|
||||||
if (at_startup) {
|
if (at_startup) {
|
||||||
if (!lua_isnoneornil(L, 2))
|
if (!lua_isnoneornil(L, 2))
|
||||||
throw LuaError("must be called without callback at load-time");
|
throw LuaError("must be called without callback at load-time");
|
||||||
@ -572,13 +575,27 @@ int ModApiServer::l_dynamic_add_media(lua_State *L)
|
|||||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_SECURE_PATH(L, filepath.c_str(), false);
|
// validate
|
||||||
|
if (args.filepath) {
|
||||||
|
if (args.filepath->empty())
|
||||||
|
throw LuaError("filepath must be non-empty");
|
||||||
|
if (args.data)
|
||||||
|
throw LuaError("cannot provide both filepath and filedata");
|
||||||
|
} else if (args.data) {
|
||||||
|
if (args.filename.empty())
|
||||||
|
throw LuaError("filename required");
|
||||||
|
} else {
|
||||||
|
throw LuaError("either filepath or filedata must be provided");
|
||||||
|
}
|
||||||
|
|
||||||
u32 token = server->getScriptIface()->allocateDynamicMediaCallback(L, 2);
|
if (args.filepath)
|
||||||
|
CHECK_SECURE_PATH(L, args.filepath->c_str(), false);
|
||||||
|
|
||||||
bool ok = server->dynamicAddMedia(filename, filepath, token, to_player, ephemeral);
|
args.token = server->getScriptIface()->allocateDynamicMediaCallback(L, 2);
|
||||||
|
|
||||||
|
bool ok = server->dynamicAddMedia(args);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
server->getScriptIface()->freeDynamicMediaCallback(token);
|
server->getScriptIface()->freeDynamicMediaCallback(args.token);
|
||||||
lua_pushboolean(L, ok);
|
lua_pushboolean(L, ok);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
114
src/server.cpp
114
src/server.cpp
@ -381,6 +381,13 @@ Server::~Server()
|
|||||||
if (m_mod_storage_database)
|
if (m_mod_storage_database)
|
||||||
m_mod_storage_database->endSave();
|
m_mod_storage_database->endSave();
|
||||||
|
|
||||||
|
// Clean up files
|
||||||
|
for (auto &it : m_media) {
|
||||||
|
if (it.second.delete_at_shutdown) {
|
||||||
|
fs::DeleteSingleFileOrEmptyDirectory(it.second.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete things in the reverse order of creation
|
// Delete things in the reverse order of creation
|
||||||
delete m_emerge;
|
delete m_emerge;
|
||||||
delete m_env;
|
delete m_env;
|
||||||
@ -3568,22 +3575,65 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id)
|
|||||||
SendDeleteParticleSpawner(peer_id, id);
|
SendDeleteParticleSpawner(peer_id, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::dynamicAddMedia(std::string filename, std::string filepath,
|
namespace {
|
||||||
const u32 token, const std::string &to_player, bool ephemeral)
|
std::string writeToTempFile(std::string_view content)
|
||||||
|
{
|
||||||
|
auto filepath = fs::CreateTempFile();
|
||||||
|
if (filepath.empty())
|
||||||
|
return "";
|
||||||
|
std::ofstream os(filepath, std::ios::binary);
|
||||||
|
if (!os.good())
|
||||||
|
return "";
|
||||||
|
os << content;
|
||||||
|
os.close();
|
||||||
|
if (os.fail()) {
|
||||||
|
fs::DeleteSingleFileOrEmptyDirectory(filepath);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return filepath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::dynamicAddMedia(const DynamicMediaArgs &a)
|
||||||
{
|
{
|
||||||
|
std::string filename = a.filename;
|
||||||
|
std::string filepath;
|
||||||
|
|
||||||
|
// Deal with file -or- data, as provided
|
||||||
|
// (Note: caller must ensure validity, so sanity_check is okay)
|
||||||
|
if (a.filepath) {
|
||||||
|
sanity_check(!a.data);
|
||||||
|
filepath = *a.filepath;
|
||||||
if (filename.empty())
|
if (filename.empty())
|
||||||
filename = fs::GetFilenameFromPath(filepath.c_str());
|
filename = fs::GetFilenameFromPath(filepath.c_str());
|
||||||
|
} else {
|
||||||
|
sanity_check(a.data);
|
||||||
|
sanity_check(!filename.empty());
|
||||||
|
|
||||||
|
// Write the file to disk. addMediaFile() will read it right back but this
|
||||||
|
// is the best way without turning the media loading code into spaghetti.
|
||||||
|
filepath = writeToTempFile(*a.data);
|
||||||
|
if (filepath.empty()) {
|
||||||
|
errorstream << "Server: failed writing media file \""
|
||||||
|
<< filename << "\" to disk" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
verbosestream << "Server: \"" << filename << "\" temporarily written to "
|
||||||
|
<< filepath << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do some checks
|
||||||
auto it = m_media.find(filename);
|
auto it = m_media.find(filename);
|
||||||
if (it != m_media.end()) {
|
if (it != m_media.end()) {
|
||||||
// Allow the same path to be "added" again in certain conditions
|
// Allow the same path to be "added" again in certain conditions
|
||||||
if (ephemeral || it->second.path != filepath) {
|
if (a.ephemeral || it->second.path != filepath) {
|
||||||
errorstream << "Server::dynamicAddMedia(): file \"" << filename
|
errorstream << "Server::dynamicAddMedia(): file \"" << filename
|
||||||
<< "\" already exists in media cache" << std::endl;
|
<< "\" already exists in media cache" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_env && (!to_player.empty() || ephemeral)) {
|
if (!m_env && (!a.to_player.empty() || a.ephemeral)) {
|
||||||
errorstream << "Server::dynamicAddMedia(): "
|
errorstream << "Server::dynamicAddMedia(): "
|
||||||
"adding ephemeral or player-specific media at startup is nonsense"
|
"adding ephemeral or player-specific media at startup is nonsense"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
@ -3595,35 +3645,37 @@ bool Server::dynamicAddMedia(std::string filename, std::string filepath,
|
|||||||
bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
|
bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
return false;
|
return false;
|
||||||
|
assert(!filedata.empty());
|
||||||
|
|
||||||
if (ephemeral) {
|
const auto &media_it = m_media.find(filename);
|
||||||
|
assert(media_it != m_media.end());
|
||||||
|
|
||||||
|
if (a.ephemeral) {
|
||||||
|
if (!a.data) {
|
||||||
// Create a copy of the file and swap out the path, this removes the
|
// Create a copy of the file and swap out the path, this removes the
|
||||||
// requirement that mods keep the file accessible at the original path.
|
// requirement that mods keep the file accessible at the original path.
|
||||||
filepath = fs::CreateTempFile();
|
filepath = writeToTempFile(filedata);
|
||||||
bool ok = ([&] () -> bool {
|
if (filepath.empty()) {
|
||||||
if (filepath.empty())
|
errorstream << "Server: failed creating a copy of media file \""
|
||||||
return false;
|
<< filename << "\"" << std::endl;
|
||||||
std::ofstream os(filepath.c_str(), std::ios::binary);
|
|
||||||
if (!os.good())
|
|
||||||
return false;
|
|
||||||
os << filedata;
|
|
||||||
os.close();
|
|
||||||
return !os.fail();
|
|
||||||
})();
|
|
||||||
if (!ok) {
|
|
||||||
errorstream << "Server: failed to create a copy of media file "
|
|
||||||
<< "\"" << filename << "\"" << std::endl;
|
|
||||||
m_media.erase(filename);
|
m_media.erase(filename);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
verbosestream << "Server: \"" << filename << "\" temporarily copied to "
|
verbosestream << "Server: \"" << filename << "\" temporarily copied to "
|
||||||
<< filepath << std::endl;
|
<< filepath << std::endl;
|
||||||
|
media_it->second.path = filepath;
|
||||||
|
}
|
||||||
|
|
||||||
m_media[filename].path = filepath;
|
media_it->second.no_announce = true;
|
||||||
m_media[filename].no_announce = true;
|
// stepPendingDynMediaCallbacks will clean the file up later
|
||||||
// stepPendingDynMediaCallbacks will clean this up later.
|
} else if (a.data) {
|
||||||
} else if (!to_player.empty()) {
|
// data is in a temporary file but not ephemeral, so the cleanup point
|
||||||
m_media[filename].no_announce = true;
|
// is different.
|
||||||
|
media_it->second.delete_at_shutdown = true;
|
||||||
|
}
|
||||||
|
if (!a.to_player.empty()) {
|
||||||
|
// only sent to one player (who must be online), so shouldn't announce.
|
||||||
|
media_it->second.no_announce = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_set<session_t> delivered, waiting;
|
std::unordered_set<session_t> delivered, waiting;
|
||||||
@ -3631,18 +3683,18 @@ bool Server::dynamicAddMedia(std::string filename, std::string filepath,
|
|||||||
// Push file to existing clients
|
// Push file to existing clients
|
||||||
if (m_env) {
|
if (m_env) {
|
||||||
NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
|
NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
|
||||||
pkt << raw_hash << filename << static_cast<bool>(ephemeral);
|
pkt << raw_hash << filename << static_cast<bool>(a.ephemeral);
|
||||||
|
|
||||||
NetworkPacket legacy_pkt = pkt;
|
NetworkPacket legacy_pkt = pkt;
|
||||||
|
|
||||||
// Newer clients get asked to fetch the file (asynchronous)
|
// Newer clients get asked to fetch the file (asynchronous)
|
||||||
pkt << token;
|
pkt << a.token;
|
||||||
// Older clients have an awful hack that just throws the data at them
|
// Older clients have an awful hack that just throws the data at them
|
||||||
legacy_pkt.putLongString(filedata);
|
legacy_pkt.putLongString(filedata);
|
||||||
|
|
||||||
ClientInterface::AutoLock clientlock(m_clients);
|
ClientInterface::AutoLock clientlock(m_clients);
|
||||||
for (auto &pair : m_clients.getClientList()) {
|
for (auto &pair : m_clients.getClientList()) {
|
||||||
if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
|
if (pair.second->getState() == CS_DefinitionsSent && !a.ephemeral) {
|
||||||
/*
|
/*
|
||||||
If a client is in the DefinitionsSent state it is too late to
|
If a client is in the DefinitionsSent state it is too late to
|
||||||
transfer the file via sendMediaAnnouncement() but at the same
|
transfer the file via sendMediaAnnouncement() but at the same
|
||||||
@ -3663,7 +3715,7 @@ bool Server::dynamicAddMedia(std::string filename, std::string filepath,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const session_t peer_id = pair.second->peer_id;
|
const session_t peer_id = pair.second->peer_id;
|
||||||
if (!to_player.empty() && getPlayerName(peer_id) != to_player)
|
if (!a.to_player.empty() && getPlayerName(peer_id) != a.to_player)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (proto_ver < 40) {
|
if (proto_ver < 40) {
|
||||||
@ -3686,15 +3738,15 @@ bool Server::dynamicAddMedia(std::string filename, std::string filepath,
|
|||||||
// Run callback for players that already had the file delivered (legacy-only)
|
// Run callback for players that already had the file delivered (legacy-only)
|
||||||
for (session_t peer_id : delivered) {
|
for (session_t peer_id : delivered) {
|
||||||
if (auto player = m_env->getPlayer(peer_id))
|
if (auto player = m_env->getPlayer(peer_id))
|
||||||
getScriptIface()->on_dynamic_media_added(token, player->getName());
|
getScriptIface()->on_dynamic_media_added(a.token, player->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save all others in our pending state
|
// Save all others in our pending state
|
||||||
auto &state = m_pending_dyn_media[token];
|
auto &state = m_pending_dyn_media[a.token];
|
||||||
state.waiting_players = std::move(waiting);
|
state.waiting_players = std::move(waiting);
|
||||||
// regardless of success throw away the callback after a while
|
// regardless of success throw away the callback after a while
|
||||||
state.expiry_timer = 60.0f;
|
state.expiry_timer = 60.0f;
|
||||||
if (ephemeral)
|
if (a.ephemeral)
|
||||||
state.filename = filename;
|
state.filename = filename;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
21
src/server.h
21
src/server.h
@ -44,6 +44,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
class ChatEvent;
|
class ChatEvent;
|
||||||
struct ChatEventChat;
|
struct ChatEventChat;
|
||||||
@ -87,13 +89,17 @@ struct MediaInfo
|
|||||||
{
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string sha1_digest; // base64-encoded
|
std::string sha1_digest; // base64-encoded
|
||||||
bool no_announce; // true: not announced in TOCLIENT_ANNOUNCE_MEDIA (at player join)
|
// true = not announced in TOCLIENT_ANNOUNCE_MEDIA (at player join)
|
||||||
|
bool no_announce;
|
||||||
|
// does what it says. used by some cases of dynamic media.
|
||||||
|
bool delete_at_shutdown;
|
||||||
|
|
||||||
MediaInfo(const std::string &path_="",
|
MediaInfo(const std::string &path_="",
|
||||||
const std::string &sha1_digest_=""):
|
const std::string &sha1_digest_=""):
|
||||||
path(path_),
|
path(path_),
|
||||||
sha1_digest(sha1_digest_),
|
sha1_digest(sha1_digest_),
|
||||||
no_announce(false)
|
no_announce(false),
|
||||||
|
delete_at_shutdown(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -258,8 +264,15 @@ public:
|
|||||||
|
|
||||||
void deleteParticleSpawner(const std::string &playername, u32 id);
|
void deleteParticleSpawner(const std::string &playername, u32 id);
|
||||||
|
|
||||||
bool dynamicAddMedia(std::string filename, std::string filepath, u32 token,
|
struct DynamicMediaArgs {
|
||||||
const std::string &to_player, bool ephemeral);
|
std::string filename;
|
||||||
|
std::optional<std::string> filepath;
|
||||||
|
std::optional<std::string_view> data;
|
||||||
|
u32 token;
|
||||||
|
std::string to_player;
|
||||||
|
bool ephemeral = false;
|
||||||
|
};
|
||||||
|
bool dynamicAddMedia(const DynamicMediaArgs &args);
|
||||||
|
|
||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user