1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-11 17:51:04 +00:00

Server pushing media at runtime (#9961)

This commit is contained in:
sfan5 2020-06-13 19:03:26 +02:00 committed by GitHub
parent 982a030f33
commit 2424dfe007
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 263 additions and 85 deletions

View file

@ -2405,9 +2405,87 @@ bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
return true;
}
bool Server::addMediaFile(const std::string &filename,
const std::string &filepath, std::string *filedata_to,
std::string *digest_to)
{
// If name contains illegal characters, ignore the file
if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
infostream << "Server: ignoring illegal file name: \""
<< filename << "\"" << std::endl;
return false;
}
// If name is not in a supported format, ignore it
const char *supported_ext[] = {
".png", ".jpg", ".bmp", ".tga",
".pcx", ".ppm", ".psd", ".wal", ".rgb",
".ogg",
".x", ".b3d", ".md2", ".obj",
// Custom translation file format
".tr",
NULL
};
if (removeStringEnd(filename, supported_ext).empty()) {
infostream << "Server: ignoring unsupported file extension: \""
<< filename << "\"" << std::endl;
return false;
}
// Ok, attempt to load the file and add to cache
// Read data
std::ifstream fis(filepath.c_str(), std::ios_base::binary);
if (!fis.good()) {
errorstream << "Server::addMediaFile(): Could not open \""
<< filename << "\" for reading" << std::endl;
return false;
}
std::string filedata;
bool bad = false;
for (;;) {
char buf[1024];
fis.read(buf, sizeof(buf));
std::streamsize len = fis.gcount();
filedata.append(buf, len);
if (fis.eof())
break;
if (!fis.good()) {
bad = true;
break;
}
}
if (bad) {
errorstream << "Server::addMediaFile(): Failed to read \""
<< filename << "\"" << std::endl;
return false;
} else if (filedata.empty()) {
errorstream << "Server::addMediaFile(): Empty file \""
<< filepath << "\"" << std::endl;
return false;
}
SHA1 sha1;
sha1.addBytes(filedata.c_str(), filedata.length());
unsigned char *digest = sha1.getDigest();
std::string sha1_base64 = base64_encode(digest, 20);
std::string sha1_hex = hex_encode((char*) digest, 20);
if (digest_to)
*digest_to = std::string((char*) digest, 20);
free(digest);
// Put in list
m_media[filename] = MediaInfo(filepath, sha1_base64);
verbosestream << "Server: " << sha1_hex << " is " << filename
<< std::endl;
if (filedata_to)
*filedata_to = std::move(filedata);
return true;
}
void Server::fillMediaCache()
{
infostream<<"Server: Calculating media file checksums"<<std::endl;
infostream << "Server: Calculating media file checksums" << std::endl;
// Collect all media file paths
std::vector<std::string> paths;
@ -2419,80 +2497,15 @@ void Server::fillMediaCache()
for (const std::string &mediapath : paths) {
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
for (const fs::DirListNode &dln : dirlist) {
if (dln.dir) // Ignode dirs
if (dln.dir) // Ignore dirs
continue;
std::string filename = dln.name;
// If name contains illegal characters, ignore the file
if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
infostream<<"Server: ignoring illegal file name: \""
<< filename << "\"" << std::endl;
continue;
}
// If name is not in a supported format, ignore it
const char *supported_ext[] = {
".png", ".jpg", ".bmp", ".tga",
".pcx", ".ppm", ".psd", ".wal", ".rgb",
".ogg",
".x", ".b3d", ".md2", ".obj",
// Custom translation file format
".tr",
NULL
};
if (removeStringEnd(filename, supported_ext).empty()){
infostream << "Server: ignoring unsupported file extension: \""
<< filename << "\"" << std::endl;
continue;
}
// Ok, attempt to load the file and add to cache
std::string filepath;
filepath.append(mediapath).append(DIR_DELIM).append(filename);
// Read data
std::ifstream fis(filepath.c_str(), std::ios_base::binary);
if (!fis.good()) {
errorstream << "Server::fillMediaCache(): Could not open \""
<< filename << "\" for reading" << std::endl;
continue;
}
std::ostringstream tmp_os(std::ios_base::binary);
bool bad = false;
for(;;) {
char buf[1024];
fis.read(buf, 1024);
std::streamsize len = fis.gcount();
tmp_os.write(buf, len);
if (fis.eof())
break;
if (!fis.good()) {
bad = true;
break;
}
}
if(bad) {
errorstream<<"Server::fillMediaCache(): Failed to read \""
<< filename << "\"" << std::endl;
continue;
}
if(tmp_os.str().length() == 0) {
errorstream << "Server::fillMediaCache(): Empty file \""
<< filepath << "\"" << std::endl;
continue;
}
SHA1 sha1;
sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
unsigned char *digest = sha1.getDigest();
std::string sha1_base64 = base64_encode(digest, 20);
std::string sha1_hex = hex_encode((char*)digest, 20);
free(digest);
// Put in list
m_media[filename] = MediaInfo(filepath, sha1_base64);
verbosestream << "Server: " << sha1_hex << " is " << filename
<< std::endl;
std::string filepath = mediapath;
filepath.append(DIR_DELIM).append(dln.name);
addMediaFile(dln.name, filepath);
}
}
infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
}
void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
@ -3428,6 +3441,44 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id)
SendDeleteParticleSpawner(peer_id, id);
}
bool Server::dynamicAddMedia(const std::string &filepath)
{
std::string filename = fs::GetFilenameFromPath(filepath.c_str());
if (m_media.find(filename) != m_media.end()) {
errorstream << "Server::dynamicAddMedia(): file \"" << filename
<< "\" already exists in media cache" << std::endl;
return false;
}
// Load the file and add it to our media cache
std::string filedata, raw_hash;
bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
if (!ok)
return false;
// Push file to existing clients
NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
pkt << raw_hash << filename << (bool) true;
pkt.putLongString(filedata);
auto client_ids = m_clients.getClientIDs(CS_DefinitionsSent);
for (session_t client_id : client_ids) {
/*
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
it is used.
In practice this means we have to send it twice:
- 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);
}
return true;
}
// actions: time-reversed list
// Return value: success/failure
bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,