diff options
Diffstat (limited to 'src/gui/guiScrollBar.h')
0 files changed, 0 insertions, 0 deletions
![]() |
index : minetest.git | |
modified minetest for gpcfs purposes | gpcf |
aboutsummaryrefslogtreecommitdiff |
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "clientmedia.h"
#include "httpfetch.h"
#include "client.h"
#include "filecache.h"
#include "filesys.h"
#include "debug.h"
#include "log.h"
#include "porting.h"
#include "settings.h"
#include "network/networkprotocol.h"
#include "util/hex.h"
#include "util/serialize.h"
#include "util/sha1.h"
#include "util/string.h"
static std::string getMediaCacheDir()
{
return porting::path_cache + DIR_DELIM + "media";
}
/*
ClientMediaDownloader
*/
ClientMediaDownloader::ClientMediaDownloader():
m_media_cache(getMediaCacheDir())
{
m_initial_step_done = false;
m_name_bound = ""; // works because "" is an invalid file name
m_uncached_count = 0;
m_uncached_received_count = 0;
m_httpfetch_caller = HTTPFETCH_DISCARD;
m_httpfetch_active = 0;
m_httpfetch_active_limit = 0;
m_httpfetch_next_id = 0;
m_httpfetch_timeout = 0;
m_outstanding_hash_sets = 0;
}
ClientMediaDownloader::~ClientMediaDownloader()
{
if (m_httpfetch_caller != HTTPFETCH_DISCARD)
httpfetch_caller_free(m_httpfetch_caller);
for (std::map<std::string, FileStatus*>::iterator it = m_files.begin();
it != m_files.end(); ++it)
delete it->second;
for (u32 i = 0; i < m_remotes.size(); ++i)
delete m_remotes[i];
}
void ClientMediaDownloader::addFile(std::string name, std::string sha1)
{
assert(!m_initial_step_done); // pre-condition
// if name was already announced, ignore the new announcement
if (m_files.count(name) != 0) {
errorstream << "Client: ignoring duplicate media announcement "
<< "sent by server: \"" << name << "\""
<< std::endl;
return;
}
// if name is empty or contains illegal characters, ignore the file
if (name.empty() || !string_allowed(name, TEXTURENAME_ALLOWED_CHARS)) {
errorstream << "Client: ignoring illegal file name "
<< "sent by server: \"" << name << "\""
<< std::endl;
return;
}
// length of sha1 must be exactly 20 (160 bits), else ignore the file
if (sha1.size() != 20) {
errorstream << "Client: ignoring illegal SHA1 sent by server: "
<< hex_encode(sha1) << " \"" << name << "\""
<< std::endl;
return;
}
FileStatus *filestatus = new FileStatus;
filestatus->received = false;
filestatus->sha1 = sha1;
filestatus->current_remote = -1;
m_files.insert(std::make_pair(name, filestatus));
}
void ClientMediaDownloader::addRemoteServer(std::string baseurl)
{
assert(!m_initial_step_done); // pre-condition
#ifdef USE_CURL
if (g_settings->getBool("enable_remote_media_server")) {
infostream << "Client: Adding remote server \""
<< baseurl << "\" for media download" << std::endl;
RemoteServerStatus *remote = new RemoteServerStatus;
remote->baseurl = baseurl;
remote->active_count = 0;
remote->request_by_filename = false;
m_remotes.push_back(remote);
}
#else
infostream << "Client: Ignoring remote server \""
<< baseurl << "\" because cURL support is not compiled in"
<< std::endl;
#endif
}
void ClientMediaDownloader::step(Client *client)
{
if (!m_initial_step_done) {
initialStep(client);
m_initial_step_done = true;
}
// Remote media: check for completion of fetches
if (m_httpfetch_active) {
bool fetched_something = false;
HTTPFetchResult fetch_result;
while (httpfetch_async_get(m_httpfetch_caller, fetch_result)) {
m_httpfetch_active--;
fetched_something = true;
// Is this a hashset (index.mth) or a media file?
if (fetch_result.request_id < m_remotes.size())
remoteHashSetReceived(fetch_result);
else
remoteMediaReceived(fetch_result, client);
}
if (fetched_something)
startRemoteMediaTransfers();
// Did all remote transfers end and no new ones can be started?
// If so, request still missing files from the minetest server
// (Or report that we have all files.)
if (m_httpfetch_active == 0) {
if (m_uncached_received_count < m_uncached_count) {
infostream << "Client: Failed to remote-fetch "
<< (m_uncached_count-m_uncached_received_count)
<< " files. Requesting them"
<< " the usual way." << std::endl;
}
startConventionalTransfers(client);
}
}
}
void ClientMediaDownloader::initialStep(Client *client)
{
// Check media cache
m_uncached_count = m_files.size();
for (std::map<std::string, FileStatus*>::iterator
it = m_files.begin();
it != m_files.end(); ++it) {
std::string name = it->first;
FileStatus *filestatus = it->second;
const std::string &sha1 = filestatus->sha1;
std::ostringstream tmp_os(std::ios_base::binary);
bool found_in_cache = m_media_cache.load(hex_encode(sha1), tmp_os);
// If found in cache, try to load it from there
if (found_in_cache) {
bool success = checkAndLoad(name, sha1,
tmp_os.str(), true, client);
if (success) {
filestatus->received = true;
m_uncached_count--;
}
}
}
assert(m_uncached_received_count == 0);
// Create the media cache dir if we are likely to write to it
if (m_uncached_count != 0) {
bool did = fs::CreateAllDirs(getMediaCacheDir());
if (!did) {
errorstream << "Client: "
<< "Could not create media cache directory: "
<< getMediaCacheDir()
<< std::endl;
}
}
// If we found all files in the cache, report this fact to the server.
// If the server reported no remote servers, immediately start
// conventional transfers. Note: if cURL support is not compiled in,
// m_remotes is always empty, so "!USE_CURL" is redundant but may
// reduce the size of the compiled code
if (!USE_CURL || m_uncached_count == 0 || m_remotes.empty()) {
startConventionalTransfers(client);
}
else {
// Otherwise start off by requesting each server's sha1 set
// This is the first time we use httpfetch, so alloc a caller ID
m_httpfetch_caller = httpfetch_caller_alloc();
m_httpfetch_timeout = g_settings->getS32("curl_timeout");
// Set the active fetch limit to curl_parallel_limit or 84,
// whichever is greater. This gives us some leeway so that
// inefficiencies in communicating with the httpfetch thread
// don't slow down fetches too much. (We still want some limit
// so that when the first remote server returns its hash set,
// not all files are requested from that server immediately.)
// One such inefficiency is that ClientMediaDownloader::step()
// is only called a couple times per second, while httpfetch
// might return responses much faster than that.
// Note that httpfetch strictly enforces curl_parallel_limit
// but at no inter-thread communication cost. This however
// doesn't help with the aforementioned inefficiencies.
// The signifance of 84 is that it is 2*6*9 in base 13.
m_httpfetch_active_limit = g_settings->getS32("curl_parallel_limit");
m_httpfetch_active_limit = MYMAX(m_httpfetch_active_limit, 84);
// Write a list of hashes that we need. This will be POSTed
// to the server using Content-Type: application/octet-stream
std::string required_hash_set = serializeRequiredHashSet();
// minor fixme: this loop ignores m_httpfetch_active_limit
// another minor fixme, unlikely to matter in normal usage:
// these index.mth fetches do (however) count against
// m_httpfetch_active_limit when starting actual media file
// requests, so if there are lots of remote servers that are
// not responding, those will stall new media file transfers.
for (u32 i = 0; i < m_remotes.size(); ++i) {
assert(m_httpfetch_next_id == i);
RemoteServerStatus *remote = m_remotes[i];
actionstream << "Client: Contacting remote server \""
<< remote->baseurl << "\"" << std::endl;
HTTPFetchRequest fetch_request;
fetch_request.url =
remote->baseurl + MTHASHSET_FILE_NAME;
fetch_request.caller = m_httpfetch_caller;
fetch_request.request_id = m_httpfetch_next_id; // == i
fetch_request.timeout = m_httpfetch_timeout;
fetch_request.connect_timeout = m_httpfetch_timeout;
fetch_request.post_data = required_hash_set;
fetch_request.extra_headers.push_back(
"Content-Type: application/octet-stream");
httpfetch_async(fetch_request);
m_httpfetch_active++;
m_httpfetch_next_id++;
m_outstanding_hash_sets++;
}
}
}
void ClientMediaDownloader::remoteHashSetReceived(
const HTTPFetchResult &fetch_result)
{
u32 remote_id = fetch_result.request_id;
assert(remote_id < m_remotes.size());
RemoteServerStatus *remote = m_remotes[remote_id];
m_outstanding_hash_sets--;
if (fetch_result.succeeded) {
try {
// Server sent a list of file hashes that are
// available on it, try to parse the list
std::set<std::string> sha1_set;
deSerializeHashSet(fetch_result.data, sha1_set);
// Parsing succeeded: For every file that is
// available on this server, add this server
// to the available_remotes array
for(std::map<std::string, FileStatus*>::iterator
it = m_files.upper_bound(m_name_bound);
it != m_files.end(); ++it) {
FileStatus *f = it->second;
if (!f->received && sha1_set.count(f->sha1))
f->available_remotes.push_back(remote_id);
}
}
catch (SerializationError &e) {
infostream << "Client: Remote server \""
<< remote->baseurl << "\" sent invalid hash set: "
<< e.what() << std::endl;
}
}
// For compatibility: If index.mth is not found, assume that the
// server contains files named like the original files (not their sha1)
// Do NOT check for any particular response code (e.g. 404) here,
// because different servers respond differently
if (!fetch_result.succeeded && !fetch_result.timeout) {
infostream << "Client: Enabling compatibility mode for remote "
<< "server \"" << remote->baseurl << "\"" << std::endl;
remote->request_by_filename = true;
// Assume every file is available on this server
for(std::map<std::string, FileStatus*>::iterator
it = m_files.upper_bound(m_name_bound);
it != m_files.end(); ++it) {
FileStatus *f = it->second;
if (!f->received)
f->available_remotes.push_back(remote_id);
}
}
}
void ClientMediaDownloader::remoteMediaReceived(
const HTTPFetchResult &fetch_result,