mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 01:07:51 -04:00
robustify initial download
This commit is contained in:
parent
3557012e6e
commit
6bd5ec1c5a
@ -59,11 +59,14 @@ set_filename(const string &filename) {
|
||||
bool P3DFileDownload::
|
||||
open_file() {
|
||||
if (!mkfile_complete(_filename, nout)) {
|
||||
nout << "Failed to create " << _filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
_file.open(_filename.c_str(), ios::out | ios::ate | ios::binary);
|
||||
|
||||
_file.clear();
|
||||
_file.open(_filename.c_str(), ios::out | ios::trunc | ios::binary);
|
||||
if (!_file) {
|
||||
nout << "Failed to open " << _filename << " in write mode\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -91,13 +91,11 @@ P3DPackage::
|
||||
// Cancel any pending download.
|
||||
if (_active_download != NULL) {
|
||||
_active_download->cancel();
|
||||
delete _active_download;
|
||||
_active_download = NULL;
|
||||
set_active_download(NULL);
|
||||
}
|
||||
if (_saved_download != NULL) {
|
||||
_saved_download->cancel();
|
||||
delete _saved_download;
|
||||
_saved_download = NULL;
|
||||
set_saved_download(NULL);
|
||||
}
|
||||
|
||||
if (_temp_contents_file != NULL) {
|
||||
@ -196,8 +194,7 @@ remove_instance(P3DInstance *inst) {
|
||||
// move to the next instance.
|
||||
if (_active_download != NULL) {
|
||||
_active_download->cancel();
|
||||
delete _active_download;
|
||||
_active_download = NULL;
|
||||
set_active_download(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,9 +352,8 @@ redownload_contents_file(P3DPackage::Download *download) {
|
||||
host_got_contents_file();
|
||||
return;
|
||||
}
|
||||
|
||||
_saved_download = download;
|
||||
_saved_download->ref();
|
||||
|
||||
set_saved_download(download);
|
||||
|
||||
// Download contents.xml to a temporary filename first.
|
||||
if (_temp_contents_file != NULL) {
|
||||
@ -410,8 +406,7 @@ contents_file_redownload_finished(bool success) {
|
||||
if (contents_changed) {
|
||||
// OK, the contents.xml has changed; this means we have to restart
|
||||
// the whole download process from the beginning.
|
||||
unref_delete(_saved_download);
|
||||
_saved_download = NULL;
|
||||
set_saved_download(NULL);
|
||||
host_got_contents_file();
|
||||
|
||||
} else {
|
||||
@ -1045,13 +1040,51 @@ start_download(P3DPackage::DownloadType dtype, const string &urlbase,
|
||||
download->set_url(url);
|
||||
download->set_filename(pathname);
|
||||
|
||||
_active_download = download;
|
||||
set_active_download(download);
|
||||
assert(!_instances.empty());
|
||||
|
||||
_instances[0]->start_download(download);
|
||||
return download;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::set_active_download
|
||||
// Access: Private
|
||||
// Description: Changes _active_download to point to the indicated
|
||||
// object, respecting reference counts.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
set_active_download(Download *download) {
|
||||
if (_active_download != download) {
|
||||
if (_active_download != NULL) {
|
||||
unref_delete(_active_download);
|
||||
}
|
||||
_active_download = download;
|
||||
if (_active_download != NULL) {
|
||||
_active_download->ref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::set_saved_download
|
||||
// Access: Private
|
||||
// Description: Changes _saved_download to point to the indicated
|
||||
// object, respecting reference counts.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
set_saved_download(Download *download) {
|
||||
if (_saved_download != download) {
|
||||
if (_saved_download != NULL) {
|
||||
unref_delete(_saved_download);
|
||||
}
|
||||
_saved_download = download;
|
||||
if (_saved_download != NULL) {
|
||||
_saved_download->ref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::is_extractable
|
||||
// Access: Private
|
||||
@ -1129,7 +1162,15 @@ void P3DPackage::Download::
|
||||
download_finished(bool success) {
|
||||
P3DFileDownload::download_finished(success);
|
||||
assert(_package->_active_download == this);
|
||||
_package->_active_download = NULL;
|
||||
if (get_ref_count() == 1) {
|
||||
// No one cares anymore.
|
||||
nout << "No one cares about " << get_url() << "\n";
|
||||
_package->set_active_download(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
_package->set_active_download(NULL);
|
||||
assert(get_ref_count() > 0);
|
||||
|
||||
if (success && !_file_spec.get_filename().empty()) {
|
||||
// We think we downloaded it correctly. Check the hash to be
|
||||
@ -1175,7 +1216,7 @@ resume_download_finished(bool success) {
|
||||
clear();
|
||||
set_url(url);
|
||||
set_filename(get_filename());
|
||||
_package->_active_download = this;
|
||||
_package->set_active_download(this);
|
||||
|
||||
assert(!_package->_instances.empty());
|
||||
_package->_instances[0]->start_download(this);
|
||||
|
@ -206,6 +206,8 @@ private:
|
||||
void report_done(bool success);
|
||||
Download *start_download(DownloadType dtype, const string &urlbase,
|
||||
const string &pathname, const FileSpec &file_spec);
|
||||
void set_active_download(Download *download);
|
||||
void set_saved_download(Download *download);
|
||||
|
||||
bool is_extractable(FileSpec &file, const string &filename) const;
|
||||
|
||||
|
@ -45,7 +45,7 @@ ref() const {
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DReferenceCount::unref
|
||||
// Access: Published, Virtual
|
||||
// Access: Public
|
||||
// Description: Explicitly decrements the reference count. Usually,
|
||||
// you should call unref_delete() instead.
|
||||
//
|
||||
@ -57,6 +57,16 @@ unref() const {
|
||||
return --(((P3DReferenceCount *)this)->_ref_count) != 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DReferenceCount::get_ref_count
|
||||
// Access: Public
|
||||
// Description: Returns the current reference count.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline int P3DReferenceCount::
|
||||
get_ref_count() const {
|
||||
return _ref_count;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: unref_delete
|
||||
// Description: This global helper function will unref the given
|
||||
|
@ -31,6 +31,7 @@ public:
|
||||
|
||||
inline void ref() const;
|
||||
inline bool unref() const;
|
||||
inline int get_ref_count() const;
|
||||
|
||||
private:
|
||||
int _ref_count;
|
||||
|
@ -167,6 +167,10 @@ BOOL CP3DActiveXApp::InitInstance()
|
||||
if (bInit)
|
||||
{
|
||||
// TODO: Add your own module initialization code here.
|
||||
|
||||
// Seed the lame random number generator in rand(); we use it to
|
||||
// select a mirror for downloading.
|
||||
srand((unsigned int)time(NULL));
|
||||
}
|
||||
|
||||
return bInit;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@ -39,6 +40,10 @@
|
||||
#include "find_root_dir.h"
|
||||
#include "mkdir_complete.h"
|
||||
|
||||
// We can include this header file to get the DTOOL_PLATFORM
|
||||
// definition, even though we don't link with dtool.
|
||||
#include "dtool_platform.h"
|
||||
|
||||
#define P3D_CONTENTS_FILENAME "contents.xml"
|
||||
#define P3D_DEFAULT_PLUGIN_FILENAME "p3d_plugin.dll"
|
||||
|
||||
@ -101,11 +106,11 @@ PPInstance::~PPInstance( )
|
||||
int PPInstance::DownloadFile( const std::string& from, const std::string& to )
|
||||
{
|
||||
int error( 0 );
|
||||
PPDownloadRequest p3dContentsDownloadRequest( *this, to );
|
||||
PPDownloadCallback dcForContents( p3dContentsDownloadRequest );
|
||||
PPDownloadRequest p3dFileDownloadRequest( *this, to );
|
||||
PPDownloadCallback dcForFile( p3dFileDownloadRequest );
|
||||
|
||||
nout << "Downloading " << from << " into " << to << "\n";
|
||||
HRESULT hr = ::URLOpenStream( m_parentCtrl.GetControllingUnknown(), from.c_str(), 0, &dcForContents );
|
||||
HRESULT hr = ::URLOpenStream( m_parentCtrl.GetControllingUnknown(), from.c_str(), 0, &dcForFile );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
error = 1;
|
||||
@ -140,35 +145,144 @@ int PPInstance::CopyFile( const std::string& from, const std::string& to )
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PPInstance::ReadContents( const std::string& contentsFilename, FileSpec& p3dDllFile )
|
||||
{
|
||||
int error(1);
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PPInstance::read_contents_file
|
||||
// Access: Private
|
||||
// Description: Reads the contents.xml file and starts the core API
|
||||
// DLL downloading, if necessary.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool PPInstance::
|
||||
read_contents_file(const string &contents_filename) {
|
||||
TiXmlDocument doc(contents_filename.c_str());
|
||||
if (!doc.LoadFile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TiXmlDocument doc( contentsFilename.c_str( ) );
|
||||
if ( doc.LoadFile( ) )
|
||||
{
|
||||
TiXmlElement *xcontents = doc.FirstChildElement( "contents" );
|
||||
if ( xcontents != NULL )
|
||||
{
|
||||
TiXmlElement *xpackage = xcontents->FirstChildElement( "package" );
|
||||
while ( xpackage != NULL )
|
||||
{
|
||||
const char *name = xpackage->Attribute( "name" );
|
||||
if ( name != NULL && strcmp( name, "coreapi" ) == 0 )
|
||||
{
|
||||
const char *platform = xpackage->Attribute( "platform" );
|
||||
if ( platform != NULL && !strcmp(platform, "win32") )
|
||||
{
|
||||
p3dDllFile.load_xml(xpackage);
|
||||
error = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xpackage = xpackage->NextSiblingElement( "package" );
|
||||
}
|
||||
TiXmlElement *xcontents = doc.FirstChildElement("contents");
|
||||
if (xcontents != NULL) {
|
||||
// Look for the <host> entry; it might point us at a different
|
||||
// download URL, and it might mention some mirrors.
|
||||
string host_url = PANDA_PACKAGE_HOST_URL;
|
||||
TiXmlElement *xhost = xcontents->FirstChildElement("host");
|
||||
if (xhost != NULL) {
|
||||
const char *url = xhost->Attribute("url");
|
||||
if (url != NULL && host_url == string(url)) {
|
||||
// We're the primary host. This is the normal case.
|
||||
read_xhost(xhost);
|
||||
|
||||
} else {
|
||||
// We're not the primary host; perhaps we're an alternate host.
|
||||
TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
|
||||
while (xalthost != NULL) {
|
||||
const char *url = xalthost->Attribute("url");
|
||||
if (url != NULL && host_url == string(url)) {
|
||||
// Yep, we're this alternate host.
|
||||
read_xhost(xhost);
|
||||
break;
|
||||
}
|
||||
xalthost = xalthost->NextSiblingElement("alt_host");
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
|
||||
// Now look for the core API package.
|
||||
TiXmlElement *xpackage = xcontents->FirstChildElement("package");
|
||||
while (xpackage != NULL) {
|
||||
const char *name = xpackage->Attribute("name");
|
||||
if (name != NULL && strcmp(name, "coreapi") == 0) {
|
||||
const char *platform = xpackage->Attribute("platform");
|
||||
if (platform != NULL && strcmp(platform, DTOOL_PLATFORM) == 0) {
|
||||
_core_api_dll.load_xml(xpackage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
xpackage = xpackage->NextSiblingElement("package");
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find the coreapi package description.
|
||||
nout << "No coreapi package defined in contents file for "
|
||||
<< DTOOL_PLATFORM << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PPInstance::read_xhost
|
||||
// Access: Private
|
||||
// Description: Reads the host data from the <host> (or <alt_host>)
|
||||
// entry in the contents.xml file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void PPInstance::
|
||||
read_xhost(TiXmlElement *xhost) {
|
||||
// Get the "download" URL, which is the source from which we
|
||||
// download everything other than the contents.xml file.
|
||||
const char *download_url = xhost->Attribute("download_url");
|
||||
if (download_url != NULL) {
|
||||
_download_url_prefix = download_url;
|
||||
} else {
|
||||
_download_url_prefix = PANDA_PACKAGE_HOST_URL;
|
||||
}
|
||||
if (!_download_url_prefix.empty()) {
|
||||
if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') {
|
||||
_download_url_prefix += "/";
|
||||
}
|
||||
}
|
||||
|
||||
TiXmlElement *xmirror = xhost->FirstChildElement("mirror");
|
||||
while (xmirror != NULL) {
|
||||
const char *url = xmirror->Attribute("url");
|
||||
if (url != NULL) {
|
||||
add_mirror(url);
|
||||
}
|
||||
xmirror = xmirror->NextSiblingElement("mirror");
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PPInstance::add_mirror
|
||||
// Access: Private
|
||||
// Description: Adds a new URL to serve as a mirror for this host.
|
||||
// The mirrors will be consulted first, before
|
||||
// consulting the host directly.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void PPInstance::
|
||||
add_mirror(std::string mirror_url) {
|
||||
// Ensure the URL ends in a slash.
|
||||
if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') {
|
||||
mirror_url += '/';
|
||||
}
|
||||
|
||||
// Add it to the _mirrors list, but only if it's not already
|
||||
// there.
|
||||
if (std::find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) {
|
||||
_mirrors.push_back(mirror_url);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PPInstance::choose_random_mirrors
|
||||
// Access: Public
|
||||
// Description: Selects num_mirrors elements, chosen at random, from
|
||||
// the _mirrors list. Adds the selected mirrors to
|
||||
// result. If there are fewer than num_mirrors elements
|
||||
// in the list, adds only as many mirrors as we can get.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void PPInstance::
|
||||
choose_random_mirrors(std::vector<std::string> &result, int num_mirrors) {
|
||||
std::vector<size_t> selected;
|
||||
|
||||
size_t num_to_select = min(_mirrors.size(), (size_t)num_mirrors);
|
||||
while (num_to_select > 0) {
|
||||
size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
|
||||
while (std::find(selected.begin(), selected.end(), i) != selected.end()) {
|
||||
// Already found this i, find a new one.
|
||||
i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
|
||||
}
|
||||
selected.push_back(i);
|
||||
result.push_back(_mirrors[i]);
|
||||
--num_to_select;
|
||||
}
|
||||
}
|
||||
|
||||
int PPInstance::DownloadP3DComponents( std::string& p3dDllFilename )
|
||||
@ -204,17 +318,18 @@ int PPInstance::DownloadP3DComponents( std::string& p3dDllFilename )
|
||||
strm << hostUrl << P3D_CONTENTS_FILENAME << "?" << time(NULL);
|
||||
std::string remoteContentsUrl( strm.str() );
|
||||
|
||||
FileSpec p3dDllFile;
|
||||
error = DownloadFile( remoteContentsUrl, localContentsFileName );
|
||||
if ( !error )
|
||||
{
|
||||
error = ReadContents( localContentsFileName, p3dDllFile );
|
||||
if ( !read_contents_file( localContentsFileName ) )
|
||||
error = 1;
|
||||
}
|
||||
|
||||
if ( error ) {
|
||||
// If we couldn't download or read the contents.xml file, check
|
||||
// to see if there's a good one on disk already, as a fallback.
|
||||
error = ReadContents( finalContentsFileName, p3dDllFile );
|
||||
if ( !read_contents_file( finalContentsFileName ) )
|
||||
error = 1;
|
||||
|
||||
} else {
|
||||
// If we have successfully read the downloaded version,
|
||||
@ -226,34 +341,63 @@ int PPInstance::DownloadP3DComponents( std::string& p3dDllFilename )
|
||||
// We don't need the temporary file any more.
|
||||
::DeleteFile( localContentsFileName.c_str() );
|
||||
|
||||
if ( !error )
|
||||
{
|
||||
// OK, at this point we have successfully read contents.xml,
|
||||
// and we have a good file spec in p3dDllFile.
|
||||
if ( p3dDllFile.quick_verify( m_rootDir ) )
|
||||
{
|
||||
// The DLL is already on-disk, and is good.
|
||||
p3dDllFilename = p3dDllFile.get_pathname( m_rootDir );
|
||||
if (!error) {
|
||||
// OK, at this point we have successfully read contents.xml,
|
||||
// and we have a good file spec in _core_api_dll.
|
||||
if (_core_api_dll.quick_verify(m_rootDir)) {
|
||||
// The DLL is already on-disk, and is good.
|
||||
p3dDllFilename = _core_api_dll.get_pathname(m_rootDir);
|
||||
} else {
|
||||
// The DLL is not already on-disk, or it's stale. Go get it.
|
||||
std::string p3dLocalModuleFileName(_core_api_dll.get_pathname(m_rootDir));
|
||||
mkfile_complete(p3dLocalModuleFileName, nout);
|
||||
|
||||
// Try one of the mirrors first.
|
||||
std::vector<std::string> mirrors;
|
||||
choose_random_mirrors(mirrors, 2);
|
||||
|
||||
error = 1;
|
||||
for (std::vector<std::string>::iterator si = mirrors.begin();
|
||||
si != mirrors.end() && error;
|
||||
++si) {
|
||||
std::string url = (*si) + _core_api_dll.get_filename();
|
||||
error = DownloadFile(url, p3dLocalModuleFileName);
|
||||
if (!error && !_core_api_dll.full_verify(m_rootDir)) {
|
||||
// If it's not right after downloading, it's an error.
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The DLL is not already on-disk, or it's stale.
|
||||
std::string p3dLocalModuleFileName( p3dDllFile.get_pathname( m_rootDir ) );
|
||||
mkfile_complete( p3dLocalModuleFileName, nout );
|
||||
std::string p3dRemoteModuleUrl( hostUrl );
|
||||
p3dRemoteModuleUrl += p3dDllFile.get_filename();
|
||||
error = DownloadFile( p3dRemoteModuleUrl, p3dLocalModuleFileName );
|
||||
if ( !error )
|
||||
{
|
||||
error = 1;
|
||||
if ( p3dDllFile.full_verify( m_rootDir ) )
|
||||
{
|
||||
// Downloaded successfully.
|
||||
p3dDllFilename = p3dDllFile.get_pathname( m_rootDir );
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If that failed, go get it from the authoritative host.
|
||||
if (error) {
|
||||
std::string url = _download_url_prefix + _core_api_dll.get_filename();
|
||||
error = DownloadFile(url, p3dLocalModuleFileName);
|
||||
if (!error && !_core_api_dll.full_verify(m_rootDir)) {
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If *that* failed, go get it again from the same URL, this
|
||||
// time with a query prefix to bust through any caches.
|
||||
if (error) {
|
||||
std::ostringstream strm;
|
||||
strm << _download_url_prefix << _core_api_dll.get_filename();
|
||||
strm << "?" << time(NULL);
|
||||
|
||||
std::string url = strm.str();
|
||||
error = DownloadFile(url, p3dLocalModuleFileName);
|
||||
if (!error && !_core_api_dll.full_verify(m_rootDir)) {
|
||||
nout << "After download, " << _core_api_dll.get_filename()
|
||||
<< " is no good.\n";
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
// Downloaded successfully.
|
||||
p3dDllFilename = _core_api_dll.get_pathname(m_rootDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
@ -438,22 +582,18 @@ void PPInstance::HandleRequestGetUrl( void* data )
|
||||
PPDownloadRequest p3dObjectDownloadRequest( parent->m_instance, request );
|
||||
PPDownloadCallback bsc( p3dObjectDownloadRequest );
|
||||
HRESULT hr = ::URLOpenStream( parent->GetControllingUnknown(), url.c_str(), 0, &bsc );
|
||||
|
||||
P3D_result_code result_code = P3D_RC_done;
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
nout << "Error handling P3D_RT_get_url request" << " :" << hr << "\n";
|
||||
return;
|
||||
result_code = P3D_RC_generic_error;
|
||||
}
|
||||
//inet_InternetSession inet(parent->m_pythonEmbed.m_threadData.m_parent);
|
||||
//std::string outdata;
|
||||
//if ( !inet.getURLMemory( url, outdata, request ) )
|
||||
//{
|
||||
// handled = false;
|
||||
//}
|
||||
|
||||
P3D_instance_feed_url_stream(
|
||||
request->_instance,
|
||||
request->_request._get_url._unique_id,
|
||||
P3D_RC_done,
|
||||
result_code,
|
||||
0,
|
||||
0,
|
||||
(const void*)NULL,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
#include "afxmt.h"
|
||||
|
||||
@ -69,7 +70,11 @@ protected:
|
||||
|
||||
int DownloadFile( const std::string& from, const std::string& to );
|
||||
int CopyFile( const std::string& from, const std::string& to );
|
||||
int ReadContents( const std::string& contentsFilename, FileSpec& p3dDllFile );
|
||||
|
||||
bool read_contents_file(const std::string &contents_filename);
|
||||
void read_xhost(TiXmlElement *xhost);
|
||||
void add_mirror(std::string mirror_url);
|
||||
void choose_random_mirrors(std::vector<std::string> &result, int num_mirrors);
|
||||
|
||||
void HandleRequest( P3D_request *request );
|
||||
static void HandleRequestGetUrl( void *data );
|
||||
@ -82,5 +87,10 @@ protected:
|
||||
bool m_isInit;
|
||||
bool m_pluginLoaded;
|
||||
|
||||
std::string _download_url_prefix;
|
||||
typedef std::vector<std::string> Mirrors;
|
||||
Mirrors _mirrors;
|
||||
FileSpec _core_api_dll;
|
||||
|
||||
std::string m_rootDir;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user