Sfoglia il codice sorgente

Implement extracted files for packp3d and pdeploy (only when used together)
This also changes start_dir for pdeployed applications to point to the application directory, no longer to ~/.panda3d/start

rdb 10 anni fa
parent
commit
3e34233b1c

+ 65 - 13
direct/src/p3d/DeploymentTools.py

@@ -444,6 +444,7 @@ class Installer:
     notify = directNotify.newCategory("Installer")
 
     def __init__(self, p3dfile, shortname, fullname, version, tokens = {}):
+        self.p3dFilename = p3dfile
         if not shortname:
             shortname = p3dfile.getBasenameWoExtension()
         self.shortname = shortname
@@ -477,22 +478,16 @@ class Installer:
         if not self.authoremail and ' ' not in uname:
             self.authoremail = "%s@%s" % (uname, socket.gethostname())
 
-        self.standalone = Standalone(p3dfile, tokens)
-        self.tempDir = Filename.temporary("", self.shortname, "") + "/"
-        self.tempDir.makeDir()
-        self.__tempRoots = {}
-
         # Load the p3d file to read out the required packages
         mf = Multifile()
-        if not mf.openRead(p3dfile):
+        if not mf.openRead(self.p3dFilename):
             Installer.notify.error("Not a Panda3D application: %s" % (p3dfile))
             return
 
         # Now load the p3dInfo file.
-        self.hostUrl = PandaSystem.getPackageHostUrl()
-        if not self.hostUrl:
-            self.hostUrl = self.standalone.host.hostUrl
+        self.hostUrl = None
         self.requires = []
+        self.extracts = []
         i = mf.findSubfile('p3d_info.xml')
         if i >= 0:
             stream = mf.openReadSubfile(i)
@@ -511,14 +506,54 @@ class Installer:
                         p3dRequires.Attribute('host')))
                     p3dRequires = p3dRequires.NextSiblingElement('requires')
 
+                p3dExtract = p3dPackage.FirstChildElement('extract')
+                while p3dExtract:
+                    filename = p3dExtract.Attribute('filename')
+                    self.extracts.append(filename)
+                    p3dExtract = p3dExtract.NextSiblingElement('extract')
+
                 if not self.fullname:
                     p3dConfig = p3dPackage.FirstChildElement('config')
                     if p3dConfig:
                         self.fullname = p3dConfig.Attribute('display_name')
+        else:
+            Installer.notify.warning("No p3d_info.xml was found in .p3d archive.")
+
+        mf.close()
+
+        if not self.hostUrl:
+            self.hostUrl = PandaSystem.getPackageHostUrl()
+            if not self.hostUrl:
+                self.hostUrl = self.standalone.host.hostUrl
+            Installer.notify.warning("No host URL was specified by .p3d archive.  Falling back to %s" % (self.hostUrl))
 
         if not self.fullname:
             self.fullname = self.shortname
 
+        self.tempDir = Filename.temporary("", self.shortname, "") + "/"
+        self.tempDir.makeDir()
+        self.__tempRoots = {}
+
+        if self.extracts:
+            # Copy .p3d to a temporary file so we can remove the extracts.
+            p3dfile = Filename(self.tempDir, self.p3dFilename.getBasename())
+            shutil.copyfile(self.p3dFilename.toOsSpecific(), p3dfile.toOsSpecific())
+            mf = Multifile()
+            if not mf.openReadWrite(p3dfile):
+                Installer.notify.error("Failure to open %s for writing." % (p3dfile))
+
+            # We don't really need this silly thing when embedding, anyway.
+            mf.setHeaderPrefix("")
+
+            for fn in self.extracts:
+                if not mf.removeSubfile(fn):
+                    Installer.notify.error("Failure to remove %s from multifile." % (p3dfile))
+
+            mf.repack()
+            mf.close()
+
+        self.standalone = Standalone(p3dfile, tokens)
+
     def __del__(self):
         try:
             appRunner.rmtree(self.tempDir)
@@ -533,6 +568,22 @@ class Installer:
         if not self.includeRequires:
             return
 
+        # Write out the extracts from the original .p3d.
+        if self.extracts:
+            mf = Multifile()
+            if not mf.openRead(self.p3dFilename):
+                Installer.notify.error("Failed to open .p3d archive: %s" % (filename))
+
+            for filename in self.extracts:
+                i = mf.findSubfile(filename)
+                if i < 0:
+                    Installer.notify.error("Cannot find extract in .p3d archive: %s" % (filename))
+                    continue
+
+                if not mf.extractSubfile(i, Filename(hostDir, filename)):
+                    Installer.notify.error("Failed to extract file from .p3d archive: %s" % (filename))
+            mf.close()
+
         pkgTree = PackageTree(platform, hostDir, self.hostUrl)
         pkgTree.installPackage("images", None, self.standalone.host.hostUrl)
 
@@ -660,7 +711,8 @@ class Installer:
 
         Filename(tempdir, "usr/bin/").makeDir()
         if self.includeRequires:
-            extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower()}
+            extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower(),
+                           "start_dir" : "/usr/lib/" + self.shortname.lower()}
         else:
             extraTokens = {}
         self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens)
@@ -712,7 +764,7 @@ class Installer:
         print >>desktop, "Type=Application"
         desktop.close()
 
-        if self.includeRequires:
+        if self.includeRequires or self.extracts:
             hostDir = Filename(tempdir, "usr/lib/%s/" % self.shortname.lower())
             hostDir.makeDir()
             self.installPackagesInto(hostDir, platform)
@@ -853,7 +905,7 @@ class Installer:
         exefile = Filename(output, "Contents/MacOS/" + self.shortname)
         exefile.makeDir()
         if self.includeRequires:
-            extraTokens = {"host_dir" : "../Resources"}
+            extraTokens = {"host_dir": "../Resources", "start_dir": "../Resources"}
         else:
             extraTokens = {}
         self.standalone.build(exefile, platform, extraTokens)
@@ -1064,7 +1116,7 @@ class Installer:
         exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
         exefile.unlink()
         if self.includeRequires:
-            extraTokens = {"host_dir" : "."}
+            extraTokens = {"host_dir": ".", "start_dir": "."}
         else:
             extraTokens = {}
         self.standalone.build(exefile, platform, extraTokens)

+ 4 - 0
direct/src/p3d/Packager.py

@@ -1496,6 +1496,10 @@ class Packager:
                     xhost = he.makeXml(packager = self.packager)
                     xpackage.InsertEndChild(xhost)
 
+            self.extracts.sort()
+            for name, xextract in self.extracts:
+                xpackage.InsertEndChild(xextract)
+
             doc.InsertEndChild(xpackage)
 
             # Write the xml file to a temporary file on disk, so we

+ 10 - 0
direct/src/p3d/packp3d.py

@@ -57,6 +57,14 @@ Options:
      instead of -e for files that are uncompressible by their nature
      (e.g. mpg files).  This option may be repeated as necessary.
 
+  -x ext
+     Marks files with the given extensions of needing to be physically
+     extracted to disk before they can be loaded.  This is used for
+     file types that cannot be loaded via the virtual file system,
+     such as .ico files on Windows.
+     This option is currently only implemented when deploying the
+     application with pdeploy.
+
   -p python_lib_dir
      Adds a directory to search for additional Python modules.  You
      can use this to add your system's Python path, to allow packp3d
@@ -134,6 +142,8 @@ def makePackedApp(args):
             packager.binaryExtensions.append(value)
         elif option == '-n':
             packager.uncompressibleExtensions.append(value)
+        elif option == '-x':
+            packager.extractExtensions.append(value)
         elif option == '-p':
             sys.path.append(value)
         elif option == '-c':

+ 17 - 6
direct/src/plugin/load_plugin.cxx

@@ -140,7 +140,7 @@ load_plugin(const string &p3d_plugin_filename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             const string &root_dir, const string &host_dir,
-            ostream &logfile) {
+            const string &start_dir, ostream &logfile) {
   if (plugin_loaded) {
     return true;
   }
@@ -259,7 +259,7 @@ load_plugin(const string &p3d_plugin_filename,
                    verify_contents, platform,
                    log_directory, log_basename,
                    trusted_environment, console_environment,
-                   root_dir, host_dir, logfile)) {
+                   root_dir, host_dir, start_dir, logfile)) {
     unload_dso();
     return false;
   }
@@ -283,7 +283,7 @@ init_plugin(const string &contents_filename, const string &host_url,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             const string &root_dir, const string &host_dir,
-            ostream &logfile) {
+            const string &start_dir, ostream &logfile) {
 
   // Ensure that all of the function pointers have been found.
   if (P3D_initialize_ptr == NULL ||
@@ -371,15 +371,26 @@ init_plugin(const string &contents_filename, const string &host_url,
     return false;
   }
 
-  if (!P3D_initialize_ptr(P3D_API_VERSION, contents_filename.c_str(),
+  // A bit of extra hand-hacked compatibility for using newer plug-ins
+  // with an older version of the core API.
+  int api_version = P3D_API_VERSION;
+  if (api_version == 17 && start_dir.empty()) {
+    api_version = 16;
+    if (host_dir.empty()) {
+      api_version = 15;
+    }
+  }
+
+  if (!P3D_initialize_ptr(api_version, contents_filename.c_str(),
                           host_url.c_str(), verify_contents, platform.c_str(),
                           log_directory.c_str(), log_basename.c_str(),
                           trusted_environment, console_environment, 
-                          root_dir.c_str(), host_dir.c_str())) {
+                          root_dir.c_str(), host_dir.c_str(),
+                          start_dir.c_str())) {
     // Oops, failure to initialize.
     logfile
       << "Failed to initialize plugin (passed API version " 
-      << P3D_API_VERSION << ")\n";
+      << api_version << ")\n";
     return false;
   }
 

+ 4 - 2
direct/src/plugin/load_plugin.h

@@ -67,13 +67,15 @@ load_plugin(const string &p3d_plugin_filename,
             P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
-            const string &root_dir, const string &host_dir, ostream &logfile);
+            const string &root_dir, const string &host_dir,
+            const string &start_dir, ostream &logfile);
 bool
 init_plugin(const string &contents_filename, const string &host_url, 
             P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
-            const string &root_dir, const string &host_dir, ostream &logfile);
+            const string &root_dir, const string &host_dir,
+            const string &start_dir, ostream &logfile);
 
 void unload_plugin(ostream &logfile);
 bool is_plugin_loaded();

+ 1 - 1
direct/src/plugin/p3dAuthSession.cxx

@@ -163,7 +163,7 @@ start_p3dcert() {
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
-  _start_dir = inst_mgr->get_root_dir() + "/start";
+  _start_dir = inst_mgr->get_start_dir();
 
   string root_dir = _inst->_p3dcert_package->get_package_dir();
   mkdir_complete(_start_dir, nout);

+ 1 - 1
direct/src/plugin/p3dInstance.cxx

@@ -1564,7 +1564,7 @@ uninstall_packages() {
   if (inst_mgr->get_verify_contents() != P3D_VC_never) {
     string start_dir_suffix = get_start_dir_suffix();
     if (!start_dir_suffix.empty()) {
-      string start_dir = inst_mgr->get_root_dir() + "/start" + start_dir_suffix;
+      string start_dir = inst_mgr->get_start_dir() + start_dir_suffix;
       nout << "Cleaning up start directory " << start_dir << "\n";
       inst_mgr->delete_directory_recursively(start_dir);
     }

+ 12 - 0
direct/src/plugin/p3dInstanceManager.I

@@ -111,6 +111,18 @@ get_root_dir() const {
   return _root_dir;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::get_start_dir
+//       Access: Public
+//  Description: Returns the directory that the .p3d file should be
+//               mounted to and run from.  This is usually the
+//               "start" subdirectory of the root_dir.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DInstanceManager::
+get_start_dir() const {
+  return _start_dir;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_platform
 //       Access: Public

+ 14 - 5
direct/src/plugin/p3dInstanceManager.cxx

@@ -201,7 +201,8 @@ initialize(int api_version, const string &contents_filename,
            const string &platform, const string &log_directory,
            const string &log_basename, bool trusted_environment,
            bool console_environment,
-           const string &root_dir, const string &host_dir) {
+           const string &root_dir, const string &host_dir,
+           const string &start_dir) {
   _api_version = api_version;
   _host_url = host_url;
   _verify_contents = verify_contents;
@@ -298,12 +299,18 @@ initialize(int api_version, const string &contents_filename,
   } else {
     _root_dir = root_dir;
   }
-  
+
   _host_dir = host_dir;
 
+  if (start_dir.empty()) {
+    _start_dir = _root_dir + "/start";
+  } else {
+    _start_dir = start_dir;
+  }
+
   // Allow the caller (e.g. panda3d.exe) to specify a log directory.
   // Or, allow the developer to compile one in.
-  
+  //
   // Failing that, we write logfiles to Panda3D/log.
   if (_log_directory.empty()) {
     _log_directory = _root_dir + "/log";
@@ -1515,8 +1522,10 @@ create_runtime_environment() {
 
   // Make the certificate directory.
   _certs_dir = _root_dir + "/certs";
-  if (!mkdir_complete(_certs_dir, nout)) {
-    nout << "Couldn't mkdir " << _certs_dir << "\n";
+  if (!get_trusted_environment()) {
+    if (!mkdir_complete(_certs_dir, nout)) {
+      nout << "Couldn't mkdir " << _certs_dir << "\n";
+    }
   }
 
   _created_runtime_environment = true;

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

@@ -59,7 +59,8 @@ public:
                   bool trusted_environment,
                   bool console_environment,
                   const string &root_dir = "",
-                  const string &host_dir = "");
+                  const string &host_dir = "",
+                  const string &start_dir = "");
 
   inline bool is_initialized() const;
   inline void reconsider_runtime_environment();
@@ -69,6 +70,7 @@ public:
   inline int get_api_version() const;
   inline const string &get_host_url() const;
   inline const string &get_root_dir() const;
+  inline const string &get_start_dir() const;
   inline const string &get_platform() const;
   inline const string &get_temp_directory() const;
   inline const string &get_log_directory() const;
@@ -173,6 +175,7 @@ private:
   string _host_url;
   string _root_dir;
   string _host_dir;
+  string _start_dir;
   string _certs_dir;
   P3D_verify_contents _verify_contents;
   string _platform;

+ 1 - 1
direct/src/plugin/p3dSession.cxx

@@ -717,7 +717,7 @@ start_p3dpython(P3DInstance *inst) {
     _keep_user_env = true;
   }
   if (!_keep_user_env) {
-    _start_dir = inst_mgr->get_root_dir() + "/start" + inst->get_start_dir_suffix();
+    _start_dir = inst_mgr->get_start_dir() + inst->get_start_dir_suffix();
     mkdir_complete(_start_dir, nout);
   }
   replace_slashes(_start_dir);

+ 7 - 2
direct/src/plugin/p3d_plugin.cxx

@@ -39,7 +39,8 @@ P3D_initialize(int api_version, const char *contents_filename,
                const char *platform, const char *log_directory,
                const char *log_basename, bool trusted_environment,
                bool console_environment,
-               const char *root_dir, const char *host_dir) {
+               const char *root_dir, const char *host_dir,
+               const char *start_dir) {
   if (api_version < 10 || api_version > P3D_API_VERSION) {
     // Can't accept an incompatible version.
     return false;
@@ -89,12 +90,16 @@ P3D_initialize(int api_version, const char *contents_filename,
     host_dir = "";
   }
 
+  if (api_version < 17 || start_dir == NULL) {
+    start_dir = "";
+  }
+
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   bool result = inst_mgr->initialize(api_version, contents_filename, host_url,
                                      verify_contents, platform,
                                      log_directory, log_basename,
                                      trusted_environment, console_environment,
-                                     root_dir, host_dir);
+                                     root_dir, host_dir, start_dir);
   RELEASE_LOCK(_api_lock);
   return result;
 }

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

@@ -79,7 +79,7 @@ extern "C" {
    (below). This number will be incremented whenever there are changes
    to any of the interface specifications defined in this header
    file. */
-#define P3D_API_VERSION 16
+#define P3D_API_VERSION 17
 
 /************************ GLOBAL FUNCTIONS **************************/
 
@@ -161,7 +161,8 @@ P3D_initialize_func(int api_version, const char *contents_filename,
                     const char *platform,
                     const char *log_directory, const char *log_basename,
                     bool trusted_environment, bool console_environment,
-                    const char *root_dir, const char *host_dir);
+                    const char *root_dir, const char *host_dir,
+                    const char *start_dir);
 
 /* This function should be called to unload the core API.  It will
    release all internally-allocated memory and return the core API to

+ 3 - 3
direct/src/plugin_activex/PPInstance.cpp

@@ -581,12 +581,12 @@ int PPInstance::LoadPlugin( const std::string& dllFilename )
         pathname = override_filename;
       }
 #endif  // P3D_PLUGIN_P3D_PLUGIN
-      
+
       nout << "Attempting to load core API from " << pathname << "\n";
       string contents_filename = m_rootDir + "/contents.xml";
       if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
-                       P3D_VC_normal, "", "", "", false, false, 
-                       m_rootDir, "", nout)) {
+                       P3D_VC_normal, "", "", "", false, false,
+                       m_rootDir, "", "", nout)) {
         nout << "Unable to launch core API in " << pathname << "\n";
         error = 1;
       } else {

+ 2 - 2
direct/src/plugin_npapi/ppInstance.cxx

@@ -1738,8 +1738,8 @@ do_load_plugin() {
   nout << "Attempting to load core API from " << pathname << "\n";
   string contents_filename = _root_dir + "/contents.xml";
   if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
-                   P3D_VC_normal, "", "", "", false, false, 
-                   _root_dir, "", nout)) {
+                   P3D_VC_normal, "", "", "", false, false,
+                   _root_dir, "", "", nout)) {
     nout << "Unable to launch core API in " << pathname << "\n";
     set_failed();
     return;

+ 23 - 6
direct/src/plugin_standalone/p3dEmbed.cxx

@@ -53,7 +53,8 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   // the + 1 from the test.
   _read_offset_check = read_offset + (streampos)1;
   if (_read_offset_check == (streampos)0xFF3D3D01) {
-    cerr << "This program is not intended to be run directly.\nIt is used by pdeploy to construct an embedded Panda3D application.\n";
+    cerr << "This program is not intended to be run directly.\nIt is used "
+            "by pdeploy to construct an embedded Panda3D application.\n";
     return 1;
   }
 
@@ -86,22 +87,30 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   string value;
   string root_dir;
   string host_dir;
+  string start_dir;
+
   while (true) {
     if (curchr == EOF) {
       cerr << "Truncated stream\n";
-      return(1);
+      return 1;
 
     } else if (curchr == 0) {
       // Two null bytes in a row means we've reached the end of the data.
       if (havenull) {
         break;
       }
-      
+
       // This means we haven't seen an '=' character yet.
       if (keyword == "") {
         if (curstr != "") {
           cerr << "Ignoring token '" << curstr << "' without value\n";
         }
+
+      } else if (keyword == "start_dir") {
+        // Don't pass this on as a token, since it has slightly different
+        // semantics when used as an HTML token.
+        start_dir = curstr;
+
       } else {
         value.assign(curstr);
         P3D_token token;
@@ -118,6 +127,8 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
           _got_win_size = true;
         } else if (keyword == "log_basename") {
           _log_basename = value;
+        } else if (keyword == "log_directory") {
+          _log_dirname = value;
         } else if (keyword == "root_dir") {
           root_dir = value;
         } else if (keyword == "host_dir") {
@@ -168,6 +179,13 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
     _host_dir = host_dir_f.to_os_specific();
   }
 
+  // Make the start directory absolute
+  if (!start_dir.empty()) {
+    Filename start_dir_f(start_dir);
+    start_dir_f.make_absolute(f.get_dirname());
+    _start_dir = start_dir_f.to_os_specific();
+  }
+
   // Initialize the core API by directly assigning all of the function
   // pointers.
   P3D_initialize_ptr = &P3D_initialize;
@@ -229,18 +247,17 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   // function pointers.  This will also call P3D_initialize().
   if (!init_plugin("", _host_url, _verify_contents, _this_platform, 
                    _log_dirname, _log_basename, true, _console_environment,
-                   _root_dir, _host_dir, cerr)) {
+                   _root_dir, _host_dir, _start_dir, cerr)) {
     cerr << "Unable to launch core API\n";
     return 1;
   }
-  
+
   // Create a plugin instance and run the program
   P3D_instance *inst = create_instance(f, true, argv, argc, read_offset);
   _instances.insert(inst);
   
   run_main_loop();
 
-
   unload_plugin(cerr);
   return 0;
 }

+ 98 - 73
direct/src/plugin_standalone/panda3d.cxx

@@ -690,7 +690,6 @@ choose_random_mirrors(vector_string &result, int num_mirrors) {
   }
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::get_core_api
 //       Access: Protected
@@ -700,78 +699,12 @@ choose_random_mirrors(vector_string &result, int num_mirrors) {
 ////////////////////////////////////////////////////////////////////
 bool Panda3D::
 get_core_api() {
+  bool is_fresh = false;
   if (!_coreapi_dll.quick_verify(_root_dir)) {
-    // 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 << _coreapi_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 += _coreapi_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) + _coreapi_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 + _coreapi_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(_coreapi_dll.get_pathname(_root_dir));
-    pathname.make_dir();
-    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 (!_coreapi_dll.full_verify(_root_dir)) {
-        cerr << "Mismatched download for " << url << "\n";
-
-      } else {
-        // successfully downloaded!
-        success = true;
-        break;
-      }
-    }
-
-    if (!success) {
-      cerr << "Couldn't download core API.\n";
+    if (!download_core_api()) {
       return false;
     }
-
-    // Since we had to download some of it, might as well ask the core
-    // API to check all of it.
-    if (_verify_contents == P3D_VC_none) {
-      _verify_contents = P3D_VC_normal;
-    }
+    is_fresh = true;
   }
 
   // Now we've got the DLL.  Load it.
@@ -795,9 +728,19 @@ get_core_api() {
   if (!load_plugin(pathname, contents_filename.to_os_specific(),
                    _host_url, _verify_contents, _this_platform, _log_dirname,
                    _log_basename, trusted_environment, _console_environment,
-                   _root_dir, "", cerr)) {
-    cerr << "Unable to launch core API in " << pathname << "\n";
-    return false;
+                   _root_dir, _host_dir, _start_dir, cerr)) {
+
+    // If we're not sure this is the latest version, make sure it is
+    // up-to-date, and then try again.
+    if (is_fresh || !download_core_api() ||
+        !load_plugin(pathname, contents_filename.to_os_specific(),
+                     _host_url, _verify_contents, _this_platform, _log_dirname,
+                     _log_basename, trusted_environment, _console_environment,
+                     _root_dir, _host_dir, _start_dir, cerr)) {
+
+      cerr << "Unable to launch core API in " << pathname << "\n";
+      return false;
+    }
   }
 
   // Successfully loaded.
@@ -822,6 +765,88 @@ get_core_api() {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::download_core_api
+//       Access: Protected
+//  Description: Downloads the latest version of the core API from
+//               the plug-in server.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+download_core_api() {
+  // 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 << _coreapi_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 += _coreapi_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) + _coreapi_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 + _coreapi_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(_coreapi_dll.get_pathname(_root_dir));
+  pathname.make_dir();
+  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 (!_coreapi_dll.full_verify(_root_dir)) {
+      cerr << "Mismatched download for " << url << "\n";
+
+    } else {
+      // successfully downloaded!
+      success = true;
+      break;
+    }
+  }
+
+  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.
+  if (_verify_contents == P3D_VC_none) {
+    _verify_contents = P3D_VC_normal;
+  }
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::usage
 //       Access: Protected

+ 1 - 0
direct/src/plugin_standalone/panda3d.h

@@ -48,6 +48,7 @@ protected:
   void add_mirror(string mirror_url);
   void choose_random_mirrors(vector_string &result, int num_mirrors);
   bool get_core_api();
+  bool download_core_api();
 
   void usage();
 

+ 1 - 0
direct/src/plugin_standalone/panda3dBase.h

@@ -74,6 +74,7 @@ protected:
   string _host_url;
   string _root_dir;
   string _host_dir;
+  string _start_dir;
   string _log_dirname;
   string _log_basename;
   string _this_platform;