Просмотр исходного кода

panda3d.exe should check mirrors too

David Rose 16 лет назад
Родитель
Сommit
6c6f498ebb

+ 2 - 7
direct/src/p3d/Packager.py

@@ -2700,15 +2700,11 @@ class Packager:
         xcontents = doc.FirstChildElement('contents')
         if xcontents:
             xhost = xcontents.FirstChildElement('host')
-            while xhost:
+            if xhost:
                 he = self.HostEntry()
                 he.loadXml(xhost, self)
                 self.hosts[he.url] = he
-                xhost = xhost.NextSiblingElement('host')
-
-            host = xcontents.Attribute('host')
-            if host:
-                self.host = host
+                self.host = he.url
             
             xpackage = xcontents.FirstChildElement('package')
             while xpackage:
@@ -2736,7 +2732,6 @@ class Packager:
 
         xcontents = TiXmlElement('contents')
         if self.host:
-            xcontents.SetAttribute('host', self.host)
             he = self.hosts.get(self.host, None)
             if he:
                 xhost = he.makeXml(packager = self)

+ 2 - 2
direct/src/plugin/load_plugin.cxx

@@ -125,7 +125,7 @@ static void unload_dso();
 ////////////////////////////////////////////////////////////////////
 bool
 load_plugin(const string &p3d_plugin_filename, 
-            const string &contents_filename, const string &download_url, 
+            const string &contents_filename, const string &host_url, 
             bool verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
@@ -311,7 +311,7 @@ load_plugin(const string &p3d_plugin_filename,
   plugin_loaded = true;
 
   if (!P3D_initialize(P3D_API_VERSION, contents_filename.c_str(),
-                      download_url.c_str(), verify_contents, platform.c_str(),
+                      host_url.c_str(), verify_contents, platform.c_str(),
                       log_directory.c_str(), log_basename.c_str(),
                       trusted_environment, console_environment)) {
     // Oops, failure to initialize.

+ 1 - 1
direct/src/plugin/load_plugin.h

@@ -61,7 +61,7 @@ extern P3D_instance_handle_event_func *P3D_instance_handle_event;
 string get_plugin_basename();
 bool 
 load_plugin(const string &p3d_plugin_filename, 
-            const string &contents_filename, const string &download_url,
+            const string &contents_filename, const string &host_url,
             bool verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,

+ 4 - 4
direct/src/plugin/p3dInstanceManager.cxx

@@ -169,7 +169,7 @@ P3DInstanceManager::
 //               redownloaded.
 ////////////////////////////////////////////////////////////////////
 bool P3DInstanceManager::
-initialize(const string &contents_filename, const string &download_url,
+initialize(const string &contents_filename, const string &host_url,
            bool verify_contents,
            const string &platform, const string &log_directory,
            const string &log_basename, bool trusted_environment,
@@ -312,7 +312,7 @@ initialize(const string &contents_filename, const string &download_url,
        << ", _temp_directory = " << _temp_directory
        << ", platform = " << _platform
        << ", contents_filename = " << contents_filename
-       << ", download_url = " << download_url
+       << ", host_url = " << host_url
        << ", verify_contents = " << verify_contents
        << "\n";
 
@@ -330,10 +330,10 @@ initialize(const string &contents_filename, const string &download_url,
   _is_initialized = true;
 
   if (!_verify_contents &&
-      !download_url.empty() && !contents_filename.empty()) {
+      !host_url.empty() && !contents_filename.empty()) {
     // Attempt to pre-read the supplied contents.xml file, to avoid an
     // unnecessary download later.
-    P3DHost *host = get_host(download_url);
+    P3DHost *host = get_host(host_url);
     if (!host->read_contents_file(contents_filename)) {
       nout << "Couldn't read " << contents_filename << "\n";
     }

+ 1 - 1
direct/src/plugin/p3dInstanceManager.h

@@ -50,7 +50,7 @@ private:
 
 public:
   bool initialize(const string &contents_filename,
-                  const string &download_url,
+                  const string &host_url,
                   bool verify_contents,
                   const string &platform,
                   const string &log_directory,

+ 4 - 4
direct/src/plugin/p3d_plugin.cxx

@@ -35,7 +35,7 @@ LOCK _api_lock;
 
 bool 
 P3D_initialize(int api_version, const char *contents_filename,
-               const char *download_url, bool verify_contents,
+               const char *host_url, bool verify_contents,
                const char *platform,
                const char *log_directory, const char *log_basename,
                bool trusted_environment, bool console_environment) {
@@ -54,8 +54,8 @@ P3D_initialize(int api_version, const char *contents_filename,
     contents_filename = "";
   }
 
-  if (download_url == NULL){ 
-    download_url = "";
+  if (host_url == NULL){ 
+    host_url = "";
   }
 
   if (platform == NULL) {
@@ -71,7 +71,7 @@ P3D_initialize(int api_version, const char *contents_filename,
   }
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  bool result = inst_mgr->initialize(contents_filename, download_url,
+  bool result = inst_mgr->initialize(contents_filename, host_url,
                                      verify_contents, platform,
                                      log_directory, log_basename,
                                      trusted_environment, console_environment);

+ 2 - 2
direct/src/plugin/p3d_plugin.h

@@ -95,7 +95,7 @@ extern "C" {
    file that has already been downloaded and verified from the server.
    If this is NULL, a new file will be downloaded as needed.
 
-   If download_url is not NULL or empty, it specifies the root URL of
+   If host_url is not NULL or empty, it specifies the root URL of
    the download server that provided the contents_filename.
 
    If verify_contents is true, it means that the download server will
@@ -141,7 +141,7 @@ extern "C" {
    immediately unload the DLL and (if possible) download a new one. */
 typedef bool 
 P3D_initialize_func(int api_version, const char *contents_filename,
-                    const char *download_url, bool verify_contents,
+                    const char *host_url, bool verify_contents,
                     const char *platform,
                     const char *log_directory, const char *log_basename,
                     bool trusted_environment, bool console_environment);

+ 38 - 22
direct/src/plugin_npapi/ppInstance.cxx

@@ -686,6 +686,43 @@ variant_to_p3dobj(const NPVariant *variant) {
   return P3D_new_none_object();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::find_host
+//       Access: Private
+//  Description: Scans the <contents> element for the matching <host>
+//               element.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+find_host(TiXmlElement *xcontents) {
+  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);
+      return;
+      
+    } 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);
+          return;
+        }
+        xalthost = xalthost->NextSiblingElement("alt_host");
+      }
+    }
+
+    // Hmm, didn't find the URL we used mentioned.  Assume we're the
+    // primary host.
+    read_xhost(xhost);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::read_xhost
 //       Access: Private
@@ -841,28 +878,7 @@ read_contents_file(const string &contents_filename) {
   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");
-        }
-      }
-    }
+    find_host(xcontents);
 
     // Now look for the core API package.
     TiXmlElement *xpackage = xcontents->FirstChildElement("package");

+ 1 - 0
direct/src/plugin_npapi/ppInstance.h

@@ -64,6 +64,7 @@ public:
   static void output_np_variant(ostream &out, const NPVariant &result);
 
 private:
+  void find_host(TiXmlElement *xcontents);
   void read_xhost(TiXmlElement *xhost);
   void add_mirror(string mirror_url);
   void choose_random_mirrors(vector<string> &result, int num_mirrors);

+ 255 - 49
direct/src/plugin_standalone/panda3d.cxx

@@ -24,6 +24,7 @@
 
 #include <ctype.h>
 #include <sstream>
+#include <algorithm>
 #ifdef _WIN32
 #include <windows.h>
 #else
@@ -52,6 +53,10 @@ Panda3D() {
   _root_dir = find_root_dir();
   _reporting_download = false;
   _enable_security = false;
+
+  // Seed the lame random number generator in rand(); we use it to
+  // select a mirror for downloading.
+  srand((unsigned int)time(NULL));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -71,10 +76,10 @@ run(int argc, char *argv[]) {
   const char *optstr = "+mu:M:Sp:fw:t:s:o:l:ih";
 
   bool allow_multiple = false;
-  string download_url = PANDA_PACKAGE_HOST_URL;
+  _host_url = PANDA_PACKAGE_HOST_URL;
   string super_mirror_url;
-  string this_platform = DTOOL_PLATFORM;
-  bool verify_contents = false;
+  _this_platform = DTOOL_PLATFORM;
+  _verify_contents = false;
 
   P3D_window_type window_type = P3D_WT_toplevel;
   int win_x = 0, win_y = 0;
@@ -89,7 +94,7 @@ run(int argc, char *argv[]) {
       break;
 
     case 'u':
-      download_url = optarg;
+      _host_url = optarg;
       break;
 
     case 'M':
@@ -101,11 +106,11 @@ run(int argc, char *argv[]) {
       break;
 
     case 'p':
-      this_platform = optarg;
+      _this_platform = optarg;
       break;
 
     case 'f':
-      verify_contents = true;
+      _verify_contents = true;
       break;
 
     case 'w':
@@ -192,10 +197,12 @@ run(int argc, char *argv[]) {
     return 1;
   }
 
-  // Make sure the download URL ends with a slash.
-  if (!download_url.empty() && download_url[download_url.length() - 1] != '/') {
-    download_url += '/';
+  // Set host_url_prefix to end with a slash.
+  _host_url_prefix = _host_url;
+  if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.length() - 1] != '/') {
+    _host_url_prefix += '/';
   }
+  _download_url_prefix = _host_url_prefix;
 
   // If the "super mirror" URL is a filename, convert it to a file:// url.
   if (!super_mirror_url.empty()) {
@@ -210,9 +217,15 @@ run(int argc, char *argv[]) {
       }
       super_mirror_url = "file://" + path;
     }
+
+    // And make sure the super_mirror_url_prefix ends with a slash.
+    _super_mirror_url_prefix = super_mirror_url;
+    if (!_super_mirror_url_prefix.empty() && _super_mirror_url_prefix[_super_mirror_url_prefix.length() - 1] != '/') {
+      _super_mirror_url_prefix += '/';
+    }
   }
 
-  if (!get_plugin(download_url, this_platform, verify_contents)) {
+  if (!get_plugin()) {
     cerr << "Unable to load Panda3D plugin.\n";
     return 1;
   }
@@ -403,18 +416,48 @@ run(int argc, char *argv[]) {
 //               true on success, false on failure.
 ////////////////////////////////////////////////////////////////////
 bool Panda3D::
-get_plugin(const string &download_url, const string &this_platform, 
-           bool verify_contents) {
+get_plugin() {
   // First, look for the existing contents.xml file.
   Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
-  if (!verify_contents && read_contents_file(contents_filename, download_url, this_platform, verify_contents)) {
+  if (!_verify_contents && read_contents_file(contents_filename)) {
     // Got the file, and it's good.
     return true;
   }
 
   // Couldn't read it, so go get it.
+  HTTPClient *http = HTTPClient::get_global_ptr();
+
+  // Try the super_mirror first.
+  if (!_super_mirror_url_prefix.empty()) {
+    // We don't bother putting a uniquifying query string when we're
+    // downloading this file from the super_mirror.  The super_mirror
+    // is by definition a cache, so it doesn't make sense to bust
+    // caches here.
+    string url = _super_mirror_url_prefix + "contents.xml";
+    PT(HTTPChannel) channel = http->make_channel(false);
+    channel->get_document(url);
+
+    Filename tempfile = Filename::temporary("", "p3d_");
+    if (!channel->download_to_file(tempfile)) {
+      cerr << "Unable to download " << url << "\n";
+      tempfile.unlink();
+    } else {
+      // Successfully downloaded from the super_mirror; move it into
+      // place and try to read it.
+      contents_filename.make_dir();
+      contents_filename.unlink();
+      tempfile.rename_to(contents_filename);
+      if (read_contents_file(contents_filename)) {
+        return true;
+      }
+    }
+
+    // Failed to download from the super_mirror.
+  }
+
+  // Go download contents.xml from the actual host.
   ostringstream strm;
-  strm << download_url << "contents.xml";
+  strm << _host_url_prefix << "contents.xml";
   // Append a uniquifying query string to the URL to force the
   // download to go all the way through any caches.  We use the time
   // in seconds; that's unique enough.
@@ -425,8 +468,7 @@ get_plugin(const string &download_url, const string &this_platform,
   // since we have an interface for that via HTTPChannel.
   DocumentSpec request(url);
   request.set_cache_control(DocumentSpec::CC_no_cache);
-  
-  HTTPClient *http = HTTPClient::get_global_ptr();
+
   PT(HTTPChannel) channel = http->make_channel(false);
   channel->get_document(request);
 
@@ -447,9 +489,9 @@ get_plugin(const string &download_url, const string &this_platform,
 
   // Since we had to download some of it, might as well ask the core
   // API to check all of it.
-  verify_contents = true;
+  _verify_contents = true;
 
-  return read_contents_file(contents_filename, download_url, this_platform, verify_contents);
+  return read_contents_file(contents_filename);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -460,40 +502,158 @@ get_plugin(const string &download_url, const string &this_platform,
 //               possible.  Returns true on success, false on failure.
 ////////////////////////////////////////////////////////////////////
 bool Panda3D::
-read_contents_file(Filename contents_filename, const string &download_url, 
-                   const string &this_platform, bool verify_contents) {
-  ifstream in;
-  contents_filename.set_text();
-  if (!contents_filename.open_read(in)) {
+read_contents_file(const Filename &contents_filename) {
+  string os_contents_filename = contents_filename.to_os_specific();
+  TiXmlDocument doc(os_contents_filename.c_str());
+  if (!doc.LoadFile()) {
     return false;
   }
 
-  TiXmlDocument doc;
-  in >> doc;
-
   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.
+    find_host(xcontents);
+
+    // 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 *xplatform = xpackage->Attribute("platform");
-        if (xplatform != NULL && strcmp(xplatform, this_platform.c_str()) == 0) {
-          return get_core_api(contents_filename, download_url, 
-                              this_platform, verify_contents, xpackage);
+        const char *platform = xpackage->Attribute("platform");
+        if (platform != NULL && _this_platform == string(platform)) {
+          return get_core_api(contents_filename, xpackage);
         }
       }
-      
+    
       xpackage = xpackage->NextSiblingElement("package");
     }
   }
 
-  // Couldn't find the coreapi plugin description.
-  cerr << "No coreapi plugin defined in contents file for "
-       << this_platform << "\n";
+  // Couldn't find the coreapi package description.
+  nout << "No coreapi package defined in contents file for "
+       << _this_platform << "\n";
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::find_host
+//       Access: Private
+//  Description: Scans the <contents> element for the matching <host>
+//               element.
+////////////////////////////////////////////////////////////////////
+void Panda3D::
+find_host(TiXmlElement *xcontents) {
+  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);
+      return;
+      
+    } 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);
+          return;
+        }
+        xalthost = xalthost->NextSiblingElement("alt_host");
+      }
+    }
+
+    // Hmm, didn't find the URL we used mentioned.  Assume we're the
+    // primary host.
+    read_xhost(xhost);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::read_xhost
+//       Access: Private
+//  Description: Reads the host data from the <host> (or <alt_host>)
+//               entry in the contents.xml file.
+////////////////////////////////////////////////////////////////////
+void Panda3D::
+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 = xhost->Attribute("url");
+  }
+
+  if (download_url != NULL) {
+    _download_url_prefix = download_url;
+  } else {
+    _download_url_prefix = _host_url_prefix;
+  }
+  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: Panda3D::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 Panda3D::
+add_mirror(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 (find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) {
+    _mirrors.push_back(mirror_url);
+  }
+}
+    
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::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 Panda3D::
+choose_random_mirrors(vector_string &result, int num_mirrors) {
+  pvector<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 (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;
+  }
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::get_core_api
@@ -503,33 +663,79 @@ read_contents_file(Filename contents_filename, const string &download_url,
 //               if necessary.
 ////////////////////////////////////////////////////////////////////
 bool Panda3D::
-get_core_api(const Filename &contents_filename, const string &download_url,
-             const string &this_platform, bool verify_contents,
-             TiXmlElement *xpackage) {
+get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) {
   _core_api_dll.load_xml(xpackage);
 
   if (!_core_api_dll.quick_verify(_root_dir)) {
-    // The DLL file needs to be downloaded.  Go get it.
-    string url = download_url;
+    // The DLL file needs to be downloaded.  Build up our list of
+    // URL's to attempt to download it from, in reverse order.
+    string url;
+    vector_string core_urls;
+
+    // Our last act of desperation: hit the original host, with a
+    // query uniquifier, to break through any caches.
+    ostringstream strm;
+    strm << _download_url_prefix << _core_api_dll.get_filename()
+         << "?" << time(NULL);
+    url = strm.str();
+    core_urls.push_back(url);
+
+    // Before we try that, we'll hit the original host, without a
+    // uniquifier.
+    url = _download_url_prefix;
     url += _core_api_dll.get_filename();
-    
+    core_urls.push_back(url);
+
+    // And before we try that, we'll try two mirrors, at random.
+    vector_string mirrors;
+    choose_random_mirrors(mirrors, 2);
+    for (vector_string::iterator si = mirrors.begin();
+         si != mirrors.end(); 
+         ++si) {
+      url = (*si) + _core_api_dll.get_filename();
+      core_urls.push_back(url);
+    }
+
+    // The very first thing we'll try is the super_mirror, if we have
+    // one.
+    if (!_super_mirror_url_prefix.empty()) {
+      url = _super_mirror_url_prefix + _core_api_dll.get_filename();
+      core_urls.push_back(url);
+    }
+
+    // Now pick URL's off the list, and try them, until we have
+    // success.
     Filename pathname = Filename::from_os_specific(_core_api_dll.get_pathname(_root_dir));
-    HTTPClient *http = HTTPClient::get_global_ptr();
-    PT(HTTPChannel) channel = http->get_document(url);
     pathname.make_dir();
-    if (!channel->download_to_file(pathname)) {
-      cerr << "Unable to download " << url << "\n";
-      return false;
+    HTTPClient *http = HTTPClient::get_global_ptr();
+
+    bool success = false;
+    while (!core_urls.empty()) {
+      url = core_urls.back();
+      core_urls.pop_back();
+    
+      PT(HTTPChannel) channel = http->get_document(url);
+      if (!channel->download_to_file(pathname)) {
+        cerr << "Unable to download " << url << "\n";
+
+      } else if (!_core_api_dll.full_verify(_root_dir)) {
+        cerr << "Mismatched download for " << url << "\n";
+
+      } else {
+        // successfully downloaded!
+        success = true;
+        break;
+      }
     }
 
-    if (!_core_api_dll.full_verify(_root_dir)) {
-      cerr << "Mismatched download for " << url << "\n";
+    if (!success) {
+      cerr << "Couldn't download core API.\n";
       return false;
     }
 
     // Since we had to download some of it, might as well ask the core
     // API to check all of it.
-    verify_contents = true;
+    _verify_contents = true;
   }
 
   // Now we've got the DLL.  Load it.
@@ -556,7 +762,7 @@ get_core_api(const Filename &contents_filename, const string &download_url,
 #endif
 
   if (!load_plugin(pathname, contents_filename.to_os_specific(),
-                   download_url, verify_contents, this_platform, _log_dirname,
+                   _host_url, _verify_contents, _this_platform, _log_dirname,
                    _log_basename, trusted_environment, console_environment,
                    cerr)) {
     cerr << "Unable to launch core API in " << pathname << "\n" << flush;

+ 18 - 8
direct/src/plugin_standalone/panda3d.h

@@ -29,6 +29,7 @@
 #include "ramfile.h"
 #include "fileSpec.h"
 #include "pset.h"
+#include "vector_string.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Panda3D
@@ -42,14 +43,13 @@ public:
   int run(int argc, char *argv[]);
 
 private:
-  bool get_plugin(const string &download_url, const string &this_platform,
-                  bool verify_contents);
-  bool read_contents_file(Filename contents_filename,
-                          const string &download_url,
-                          const string &this_platform, bool verify_contents);
-  bool get_core_api(const Filename &contents_filename, 
-                    const string &download_url, const string &this_platform,
-                    bool verify_contents, TiXmlElement *xplugin);
+  bool get_plugin();
+  bool read_contents_file(const Filename &contents_filename);
+  void find_host(TiXmlElement *xcontents);
+  void read_xhost(TiXmlElement *xhost);
+  void add_mirror(string mirror_url);
+  void choose_random_mirrors(vector_string &result, int num_mirrors);
+  bool get_core_api(const Filename &contents_filename, TiXmlElement *xplugin);
   void run_getters();
   void handle_request(P3D_request *request);
   void make_parent_window(P3D_window_handle &parent_window, 
@@ -79,6 +79,16 @@ private:
   string _root_dir;
   string _log_dirname;
   string _log_basename;
+  string _this_platform;
+  bool _verify_contents;
+
+  string _host_url;
+  string _super_mirror_url_prefix;
+  string _host_url_prefix;
+  string _download_url_prefix;
+  typedef pvector<string> Mirrors;
+  Mirrors _mirrors;
+  
   FileSpec _core_api_dll;
   bool _reporting_download;
   bool _enable_security;