Browse Source

super mirror

David Rose 16 years ago
parent
commit
508b92005f

+ 15 - 10
direct/src/p3d/AppRunner.py

@@ -102,6 +102,13 @@ class AppRunner(DirectObject):
         # the instance starts up.
         # the instance starts up.
         self.rootDir = None
         self.rootDir = None
 
 
+        # self.superMirrorUrl, if nonempty, is the "super mirror" URL
+        # that should be contacted first before trying the actual
+        # host.  This is primarily used for "downloading" from a
+        # locally-stored Panda3D installation.  This is also filled in
+        # when the instance starts up.
+        self.superMirrorUrl = None
+
         # A list of the Panda3D packages that have been loaded.
         # A list of the Panda3D packages that have been loaded.
         self.installedPackages = []
         self.installedPackages = []
 
 
@@ -430,11 +437,7 @@ class AppRunner(DirectObject):
             interactiveConsole = self.interactiveConsole
             interactiveConsole = self.interactiveConsole
             self.interactiveConsole = False
             self.interactiveConsole = False
 
 
-            try:
-                __import__(moduleName)
-            except ImportError:
-                message = "No %s found in application." % (moduleName)
-                raise StandardError, message
+            __import__(moduleName)
             main = sys.modules[moduleName]
             main = sys.modules[moduleName]
             if hasattr(main, 'main') and callable(main.main):
             if hasattr(main, 'main') and callable(main.main):
                 main.main(self)
                 main.main(self)
@@ -469,15 +472,17 @@ class AppRunner(DirectObject):
                                needsResponse = False)
                                needsResponse = False)
         self.deferredEvals = []
         self.deferredEvals = []
 
 
-    def setInstanceInfo(self, rootDir):
+    def setInstanceInfo(self, rootDir, superMirrorUrl):
         """ Called by the browser to set some global information about
         """ Called by the browser to set some global information about
         the instance. """
         the instance. """
 
 
-        # At the present, this only includes rootDir, which is the
-        # root Panda3D install directory on the local machine.
-        
+        # rootDir is the root Panda3D install directory on the local
+        # machine.
         self.rootDir = Filename.fromOsSpecific(rootDir)
         self.rootDir = Filename.fromOsSpecific(rootDir)
 
 
+        # The "super mirror" URL, generally used only by panda3d.exe.
+        self.superMirrorUrl = superMirrorUrl
+
     def addPackageInfo(self, name, platform, version, hostUrl):
     def addPackageInfo(self, name, platform, version, hostUrl):
         """ Called by the browser to list all of the "required"
         """ Called by the browser to list all of the "required"
         packages that were preloaded before starting the
         packages that were preloaded before starting the
@@ -809,7 +814,7 @@ def dummyAppRunner(tokens = [], argv = None):
 
 
     if AppRunnerGlobal.appRunner:
     if AppRunnerGlobal.appRunner:
         print "Already have AppRunner, not creating a new one."
         print "Already have AppRunner, not creating a new one."
-        return
+        return AppRunnerGlobal.appRunner
 
 
     appRunner = AppRunner()
     appRunner = AppRunner()
     appRunner.dummy = True
     appRunner.dummy = True

+ 1 - 0
direct/src/p3d/HostInfo.py

@@ -25,6 +25,7 @@ class HostInfo:
         assert appRunner or hostDir
         assert appRunner or hostDir
         
         
         self.hostUrl = hostUrl
         self.hostUrl = hostUrl
+        self.appRunner = appRunner
         self.hostDir = hostDir
         self.hostDir = hostDir
         self.asMirror = asMirror
         self.asMirror = asMirror
 
 

+ 12 - 4
direct/src/p3d/PackageInfo.py

@@ -532,16 +532,24 @@ class PackageInfo:
         if not urlbase:
         if not urlbase:
             urlbase = self.descFileDirname + '/' + fileSpec.filename
             urlbase = self.descFileDirname + '/' + fileSpec.filename
 
 
-        # Build up a list of URL's to try downloading from.
+        # Build up a list of URL's to try downloading from.  Unlike
+        # the C++ implementation in P3DPackage.cxx, here we build the
+        # URL's in forward order.
         tryUrls = []
         tryUrls = []
+
+        if self.host.appRunner and self.host.appRunner.superMirrorUrl:
+            # We start with the "super mirror", if it's defined.
+            url = self.host.appRunner.superMirrorUrl + urlbase
+            tryUrls.append(url)
+
         if self.host.mirrors:
         if self.host.mirrors:
-            # Choose a mirror at random first, then a different
-            # mirror.
+            # Choose two mirrors at random.
             mirrors = self.host.mirrors[:]
             mirrors = self.host.mirrors[:]
             for i in range(2):
             for i in range(2):
                 mirror = random.choice(mirrors)
                 mirror = random.choice(mirrors)
                 mirrors.remove(mirror)
                 mirrors.remove(mirror)
-                tryUrls.append(mirror + urlbase)
+                url = mirror + urlbase
+                tryUrls.append(url)
                 if not mirrors:
                 if not mirrors:
                     break
                     break
 
 

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

@@ -458,7 +458,7 @@ class Packager:
                     continue
                     continue
 
 
                 if mdef.exclude and mdef.implicit:
                 if mdef.exclude and mdef.implicit:
-                    # Don't bother mentioning implicity-excluded
+                    # Don't bother mentioning implicitly-excluded
                     # (i.e. missing) modules.
                     # (i.e. missing) modules.
                     continue
                     continue
 
 
@@ -1529,10 +1529,8 @@ class Packager:
         # The following are config settings that the caller may adjust
         # The following are config settings that the caller may adjust
         # before calling any of the command methods.
         # before calling any of the command methods.
 
 
-        # These should each be a Filename, or None if they are not
-        # filled in.
+        # This should be set to a Filename.
         self.installDir = None
         self.installDir = None
-        self.persistDir = None
 
 
         # The download URL at which these packages will eventually be
         # The download URL at which these packages will eventually be
         # hosted.
         # hosted.

+ 7 - 0
direct/src/p3d/panda3d.pdef

@@ -14,6 +14,12 @@ from pandac.PandaModules import getModelPath, Filename, ConfigVariableFilename
 # and then a number of smaller, optional packages, which may or may
 # and then a number of smaller, optional packages, which may or may
 # not be needed by any one particular application.
 # not be needed by any one particular application.
 
 
+packager.setHost('file:///home/drose/p3dstage',
+                 mirrors = ['file:///home/drose/p3dstage_mirror1',
+                            'file:///home/drose/p3dstage_mirror2',
+                            'file:///home/drose/p3dstage_mirror3',
+                            'file:///home/drose/p3dstage_mirror4'])
+
 class coreapi(solo):
 class coreapi(solo):
     # The special "coreapi" package.  As a "solo", this is just a
     # The special "coreapi" package.  As a "solo", this is just a
     # single .dll (or dylib, or whatever).
     # single .dll (or dylib, or whatever).
@@ -102,6 +108,7 @@ class panda3d(package):
            'direct.gui.*',
            'direct.gui.*',
            'direct.interval.*',
            'direct.interval.*',
            'direct.particles.*',
            'direct.particles.*',
+           'direct.p3d.*',
            'direct.showbase.*',
            'direct.showbase.*',
            'direct.showutil.*',
            'direct.showutil.*',
            'direct.stdpy.*')
            'direct.stdpy.*')

+ 1 - 12
direct/src/p3d/ppackage.py

@@ -61,15 +61,6 @@ Options:
      This option may be repeated as necessary.  These directories may
      This option may be repeated as necessary.  These directories may
      also be specified with the pdef-path Config.prc variable.
      also be specified with the pdef-path Config.prc variable.
 
 
-  -d persist_dir
-     The full path to a local directory that retains persistant state
-     between publishes.  This directory structure keeps files that are
-     used to build patches for future releases.  You should keep this
-     directory structure around for as long as you plan to support
-     this package.  If this directory structure does not exist or is
-     empty, patches will not be created for this publish; but the
-     directory structure will be populated for the next publish.
-
   -u host_url
   -u host_url
      Specifies the URL to the download server that will eventually
      Specifies the URL to the download server that will eventually
      host these packages (that is, the public URL of the install
      host these packages (that is, the public URL of the install
@@ -119,7 +110,7 @@ packager = Packager.Packager()
 buildPatches = False
 buildPatches = False
 
 
 try:
 try:
-    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:d:DP:u:n:h')
+    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:DP:u:n:h')
 except getopt.error, msg:
 except getopt.error, msg:
     usage(1, msg)
     usage(1, msg)
 
 
@@ -130,8 +121,6 @@ for opt, arg in opts:
         buildPatches = True
         buildPatches = True
     elif opt == '-s':
     elif opt == '-s':
         packager.installSearch.appendDirectory(Filename.fromOsSpecific(arg))
         packager.installSearch.appendDirectory(Filename.fromOsSpecific(arg))
-    elif opt == '-d':
-        packager.persistDir = Filename.fromOsSpecific(arg)
     elif opt == '-D':
     elif opt == '-D':
         packager.allowPythonDev = True
         packager.allowPythonDev = True
     elif opt == '-P':
     elif opt == '-P':

+ 5 - 0
direct/src/plugin/load_plugin.cxx

@@ -36,6 +36,7 @@ static const string default_plugin_filename = "p3d_plugin";
 
 
 P3D_initialize_func *P3D_initialize;
 P3D_initialize_func *P3D_initialize;
 P3D_finalize_func *P3D_finalize;
 P3D_finalize_func *P3D_finalize;
+P3D_set_super_mirror_func *P3D_set_super_mirror;
 P3D_new_instance_func *P3D_new_instance;
 P3D_new_instance_func *P3D_new_instance;
 P3D_instance_start_func *P3D_instance_start;
 P3D_instance_start_func *P3D_instance_start;
 P3D_instance_finish_func *P3D_instance_finish;
 P3D_instance_finish_func *P3D_instance_finish;
@@ -187,6 +188,7 @@ load_plugin(const string &p3d_plugin_filename,
   // Now get all of the function pointers.
   // Now get all of the function pointers.
   P3D_initialize = (P3D_initialize_func *)get_func(module, "P3D_initialize");  
   P3D_initialize = (P3D_initialize_func *)get_func(module, "P3D_initialize");  
   P3D_finalize = (P3D_finalize_func *)get_func(module, "P3D_finalize");  
   P3D_finalize = (P3D_finalize_func *)get_func(module, "P3D_finalize");  
+  P3D_set_super_mirror = (P3D_set_super_mirror_func *)get_func(module, "P3D_set_super_mirror");  
   P3D_new_instance = (P3D_new_instance_func *)get_func(module, "P3D_new_instance");  
   P3D_new_instance = (P3D_new_instance_func *)get_func(module, "P3D_new_instance");  
   P3D_instance_start = (P3D_instance_start_func *)get_func(module, "P3D_instance_start");  
   P3D_instance_start = (P3D_instance_start_func *)get_func(module, "P3D_instance_start");  
   P3D_instance_finish = (P3D_instance_finish_func *)get_func(module, "P3D_instance_finish");  
   P3D_instance_finish = (P3D_instance_finish_func *)get_func(module, "P3D_instance_finish");  
@@ -226,6 +228,7 @@ load_plugin(const string &p3d_plugin_filename,
   // Ensure that all of the function pointers have been found.
   // Ensure that all of the function pointers have been found.
   if (P3D_initialize == NULL ||
   if (P3D_initialize == NULL ||
       P3D_finalize == NULL ||
       P3D_finalize == NULL ||
+      P3D_set_super_mirror == NULL ||
       P3D_new_instance == NULL ||
       P3D_new_instance == NULL ||
       P3D_instance_start == NULL ||
       P3D_instance_start == NULL ||
       P3D_instance_finish == NULL ||
       P3D_instance_finish == NULL ||
@@ -265,6 +268,7 @@ load_plugin(const string &p3d_plugin_filename,
       << "Some function pointers not found:"
       << "Some function pointers not found:"
       << "\nP3D_initialize = " << P3D_initialize
       << "\nP3D_initialize = " << P3D_initialize
       << "\nP3D_finalize = " << P3D_finalize
       << "\nP3D_finalize = " << P3D_finalize
+      << "\nP3D_set_super_mirror = " << P3D_set_super_mirror
       << "\nP3D_new_instance = " << P3D_new_instance
       << "\nP3D_new_instance = " << P3D_new_instance
       << "\nP3D_instance_start = " << P3D_instance_start
       << "\nP3D_instance_start = " << P3D_instance_start
       << "\nP3D_instance_finish = " << P3D_instance_finish
       << "\nP3D_instance_finish = " << P3D_instance_finish
@@ -358,6 +362,7 @@ unload_dso() {
   
   
   P3D_initialize = NULL;
   P3D_initialize = NULL;
   P3D_finalize = NULL;
   P3D_finalize = NULL;
+  P3D_set_super_mirror = NULL;
   P3D_new_instance = NULL;
   P3D_new_instance = NULL;
   P3D_instance_start = NULL;
   P3D_instance_start = NULL;
   P3D_instance_finish = NULL;
   P3D_instance_finish = NULL;

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

@@ -22,6 +22,7 @@ using namespace std;
 
 
 extern P3D_initialize_func *P3D_initialize;
 extern P3D_initialize_func *P3D_initialize;
 extern P3D_finalize_func *P3D_finalize;
 extern P3D_finalize_func *P3D_finalize;
+extern P3D_set_super_mirror_func *P3D_set_super_mirror;
 extern P3D_new_instance_func *P3D_new_instance;
 extern P3D_new_instance_func *P3D_new_instance;
 extern P3D_instance_start_func *P3D_instance_start;
 extern P3D_instance_start_func *P3D_instance_start;
 extern P3D_instance_finish_func *P3D_instance_finish;
 extern P3D_instance_finish_func *P3D_instance_finish;

+ 3 - 4
direct/src/plugin/p3dInstance.cxx

@@ -847,10 +847,6 @@ start_download(P3DDownload *download) {
 
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
 
-  // Since we're downloading something, we might as well check all
-  // contents files from this point on.
-  inst_mgr->reset_verify_contents();
-
   int download_id = inst_mgr->get_unique_id();
   int download_id = inst_mgr->get_unique_id();
   download->set_download_id(download_id);
   download->set_download_id(download_id);
 
 
@@ -912,6 +908,9 @@ make_xml() {
 
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   xinstance->SetAttribute("root_dir", inst_mgr->get_root_dir());
   xinstance->SetAttribute("root_dir", inst_mgr->get_root_dir());
+  if (!inst_mgr->get_super_mirror().empty()) {
+    xinstance->SetAttribute("super_mirror", inst_mgr->get_super_mirror());
+  }
 
 
   TiXmlElement *xfparams = _fparams.make_xml();
   TiXmlElement *xfparams = _fparams.make_xml();
   xinstance->LinkEndChild(xfparams);
   xinstance->LinkEndChild(xfparams);

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

@@ -122,6 +122,16 @@ get_trusted_environment() const {
   return _trusted_environment;
   return _trusted_environment;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::get_super_mirror
+//       Access: Public
+//  Description: Returns the "super mirror" URL.  See p3d_plugin.h.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DInstanceManager::
+get_super_mirror() const {
+  return _super_mirror_url;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_num_instances
 //     Function: P3DInstanceManager::get_num_instances
 //       Access: Public
 //       Access: Public

+ 15 - 0
direct/src/plugin/p3dInstanceManager.cxx

@@ -296,6 +296,21 @@ initialize(const string &contents_filename, const string &download_url,
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::set_super_mirror
+//       Access: Public
+//  Description: Specifies the "super mirror" URL.  See p3d_plugin.h.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+set_super_mirror(const string &super_mirror_url) {
+  _super_mirror_url = super_mirror_url;
+
+  // Make sure it ends with a slash.
+  if (!_super_mirror_url.empty() && _super_mirror_url[_super_mirror_url.size() - 1] != '/') {
+    _super_mirror_url += '/';
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::create_instance
 //     Function: P3DInstanceManager::create_instance
 //       Access: Public
 //       Access: Public

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

@@ -67,6 +67,9 @@ public:
   inline const string &get_log_pathname() const;
   inline const string &get_log_pathname() const;
   inline bool get_trusted_environment() const;
   inline bool get_trusted_environment() const;
 
 
+  void set_super_mirror(const string &super_mirror_url);
+  inline const string &get_super_mirror() const;
+
   P3DInstance *
   P3DInstance *
   create_instance(P3D_request_ready_func *func, 
   create_instance(P3D_request_ready_func *func, 
                   const P3D_token tokens[], size_t num_tokens, 
                   const P3D_token tokens[], size_t num_tokens, 
@@ -129,6 +132,7 @@ private:
   string _log_pathname;
   string _log_pathname;
   string _temp_directory;
   string _temp_directory;
   bool _trusted_environment;
   bool _trusted_environment;
+  string _super_mirror_url;
 
 
   P3D_object *_undefined_object;
   P3D_object *_undefined_object;
   P3D_object *_none_object;
   P3D_object *_none_object;

+ 16 - 0
direct/src/plugin/p3dPackage.cxx

@@ -945,6 +945,22 @@ start_download(P3DPackage::DownloadType dtype, const string &urlbase,
     }
     }
   }
   }
 
 
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+
+  if (dtype == DT_contents_file && inst_mgr->get_verify_contents()) {
+    // When we're dowloading the contents file with verify_contents
+    // true, we always go straight to the authoritative host, not even
+    // to the super-mirror.
+
+  } else {
+    // In other cases, if the "super mirror" is enabled, we try that
+    // first.
+    if (!inst_mgr->get_super_mirror().empty()) {
+      string url = inst_mgr->get_super_mirror() + urlbase;
+      download->_try_urls.push_back(url);
+    }
+  }
+
   // OK, start the download.
   // OK, start the download.
   assert(!download->_try_urls.empty());
   assert(!download->_try_urls.empty());
   url = download->_try_urls.back();
   url = download->_try_urls.back();

+ 6 - 1
direct/src/plugin/p3dPythonRun.cxx

@@ -1116,8 +1116,13 @@ set_instance_info(P3DCInstance *inst, TiXmlElement *xinstance) {
     root_dir = "";
     root_dir = "";
   }
   }
 
 
+  const char *super_mirror = xinstance->Attribute("super_mirror");
+  if (super_mirror == NULL) {
+    super_mirror = "";
+  }
+
   PyObject *result = PyObject_CallMethod
   PyObject *result = PyObject_CallMethod
-    (_runner, (char *)"setInstanceInfo", (char *)"s", root_dir);
+    (_runner, (char *)"setInstanceInfo", (char *)"ss", root_dir, super_mirror);
 
 
   if (result == NULL) {
   if (result == NULL) {
     PyErr_Print();
     PyErr_Print();

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

@@ -850,8 +850,9 @@ start_p3dpython(P3DInstance *inst) {
   _env += python_path;
   _env += python_path;
   _env += '\0';
   _env += '\0';
 
 
+  // Let's leave PYTHONHOME empty.  Setting it adds junk to our
+  // carefully-constructed PYTHONPATH.
   _env += "PYTHONHOME=";
   _env += "PYTHONHOME=";
-  _env += _python_root_dir;
   _env += '\0';
   _env += '\0';
 
 
   _env += "PRC_PATH=";
   _env += "PRC_PATH=";

+ 13 - 3
direct/src/plugin/p3d_plugin.cxx

@@ -84,11 +84,23 @@ P3D_finalize() {
   P3DInstanceManager::delete_global_ptr();
   P3DInstanceManager::delete_global_ptr();
 }
 }
 
 
+void
+P3D_set_super_mirror(const char *super_mirror_url) {
+  assert(P3DInstanceManager::get_global_ptr()->is_initialized());
+  if (super_mirror_url == NULL) {
+    super_mirror_url = "";
+  }
+
+  ACQUIRE_LOCK(_api_lock);
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  inst_mgr->set_super_mirror(super_mirror_url);
+  RELEASE_LOCK(_api_lock);
+}
+
 P3D_instance *
 P3D_instance *
 P3D_new_instance(P3D_request_ready_func *func, 
 P3D_new_instance(P3D_request_ready_func *func, 
                  const P3D_token tokens[], size_t num_tokens,
                  const P3D_token tokens[], size_t num_tokens,
                  int argc, const char *argv[], void *user_data) {
                  int argc, const char *argv[], void *user_data) {
-  nout << "new_instance\n";
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   ACQUIRE_LOCK(_api_lock);
   ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
@@ -101,7 +113,6 @@ P3D_new_instance(P3D_request_ready_func *func,
 bool
 bool
 P3D_instance_start(P3D_instance *instance, bool is_local, 
 P3D_instance_start(P3D_instance *instance, bool is_local, 
                    const char *p3d_filename) {
                    const char *p3d_filename) {
-  nout << "instance_start\n";
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   if (p3d_filename == NULL) {
   if (p3d_filename == NULL) {
     p3d_filename = "";
     p3d_filename = "";
@@ -138,7 +149,6 @@ P3D_instance_setup_window(P3D_instance *instance,
                           int win_x, int win_y,
                           int win_x, int win_y,
                           int win_width, int win_height,
                           int win_width, int win_height,
                           P3D_window_handle parent_window) {
                           P3D_window_handle parent_window) {
-  nout << "setup_window\n";
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   P3DWindowParams wparams(window_type, win_x, win_y,
   P3DWindowParams wparams(window_type, win_x, win_y,
                           win_width, win_height, parent_window);
                           win_width, win_height, parent_window);

+ 17 - 0
direct/src/plugin/p3d_plugin.h

@@ -145,6 +145,22 @@ P3D_initialize_func(int api_version, const char *contents_filename,
 typedef void
 typedef void
 P3D_finalize_func();
 P3D_finalize_func();
 
 
+/* This function defines a "super mirror" URL: a special URL that is
+   consulted first whenever downloading any package referenced by a
+   p3d file.  This setting is global, and affects all package
+   downloads across all instances.  The main purpose of this is to
+   facilitate local distribution of the Panda3D runtime build, to
+   allow applications to ship themselves totally self-contained.  If
+   you install the appropriate Panda3D package files into a directory
+   on disk, and set the "super mirror" to a file:// URL that
+   references that directory, then users will be able to run your p3d
+   file without necessarily having an internet connection.
+
+   This normally should be set only by the panda3d standalone runtime
+   executable, not by a web plugin. */
+typedef void
+P3D_set_super_mirror_func(const char *super_mirror_url);
+
 /********************** INSTANCE MANAGEMENT **************************/
 /********************** INSTANCE MANAGEMENT **************************/
 
 
 /* The following interfaces define the API to manage individual
 /* The following interfaces define the API to manage individual
@@ -852,6 +868,7 @@ P3D_instance_handle_event_func(P3D_instance *instance, P3D_event_data event);
 /* Define all of the actual prototypes for the above functions. */
 /* Define all of the actual prototypes for the above functions. */
 EXPCL_P3D_PLUGIN P3D_initialize_func P3D_initialize;
 EXPCL_P3D_PLUGIN P3D_initialize_func P3D_initialize;
 EXPCL_P3D_PLUGIN P3D_finalize_func P3D_finalize;
 EXPCL_P3D_PLUGIN P3D_finalize_func P3D_finalize;
+EXPCL_P3D_PLUGIN P3D_set_super_mirror_func P3D_set_super_mirror;
 
 
 EXPCL_P3D_PLUGIN P3D_new_instance_func P3D_new_instance;
 EXPCL_P3D_PLUGIN P3D_new_instance_func P3D_new_instance;
 EXPCL_P3D_PLUGIN P3D_instance_start_func P3D_instance_start;
 EXPCL_P3D_PLUGIN P3D_instance_start_func P3D_instance_start;

+ 76 - 10
direct/src/plugin_standalone/panda3d.cxx

@@ -22,6 +22,7 @@
 // definition, even though we don't link with dtool.
 // definition, even though we don't link with dtool.
 #include "dtool_platform.h"
 #include "dtool_platform.h"
 
 
+#include <ctype.h>
 #include <sstream>
 #include <sstream>
 #ifdef _WIN32
 #ifdef _WIN32
 #include <windows.h>
 #include <windows.h>
@@ -66,10 +67,11 @@ run(int argc, char *argv[]) {
   // We prefix a "+" sign to tell gnu getopt not to parse options
   // We prefix a "+" sign to tell gnu getopt not to parse options
   // following the first not-option parameter.  (These will be passed
   // following the first not-option parameter.  (These will be passed
   // into the sub-process.)
   // into the sub-process.)
-  const char *optstr = "+mu:p:fw:t:s:o:l:ih";
+  const char *optstr = "+mu:M:p:fw:t:s:o:l:ih";
 
 
   bool allow_multiple = false;
   bool allow_multiple = false;
   string download_url = PANDA_PACKAGE_HOST_URL;
   string download_url = PANDA_PACKAGE_HOST_URL;
+  string super_mirror_url;
   string this_platform = DTOOL_PLATFORM;
   string this_platform = DTOOL_PLATFORM;
   bool verify_contents = false;
   bool verify_contents = false;
 
 
@@ -89,6 +91,10 @@ run(int argc, char *argv[]) {
       download_url = optarg;
       download_url = optarg;
       break;
       break;
 
 
+    case 'M':
+      super_mirror_url = optarg;
+      break;
+
     case 'p':
     case 'p':
       this_platform = optarg;
       this_platform = optarg;
       break;
       break;
@@ -181,16 +187,36 @@ run(int argc, char *argv[]) {
     return 1;
     return 1;
   }
   }
 
 
-  // Make sure it ends with a slash.
+  // Make sure the download URL ends with a slash.
   if (!download_url.empty() && download_url[download_url.length() - 1] != '/') {
   if (!download_url.empty() && download_url[download_url.length() - 1] != '/') {
     download_url += '/';
     download_url += '/';
   }
   }
 
 
+  // If the "super mirror" URL is a filename, convert it to a file:// url.
+  if (!super_mirror_url.empty()) {
+    if (!is_url(super_mirror_url)) {
+      Filename filename = Filename::from_os_specific(super_mirror_url);
+      filename.make_absolute();
+      string path = filename.to_os_generic();
+      if (!path.empty() && path[0] != '/') {
+        // On Windows, a leading drive letter must be preceded by an
+        // additional slash.
+        path = "/" + path;
+      }
+      super_mirror_url = "file://" + path;
+    }
+  }
+
   if (!get_plugin(download_url, this_platform, verify_contents)) {
   if (!get_plugin(download_url, this_platform, verify_contents)) {
     cerr << "Unable to load Panda3D plugin.\n";
     cerr << "Unable to load Panda3D plugin.\n";
     return 1;
     return 1;
   }
   }
 
 
+  // Set up the "super mirror" URL, if specified.
+  if (!super_mirror_url.empty()) {
+    P3D_set_super_mirror(super_mirror_url.c_str());
+  }
+
   int num_instance_filenames, num_instance_args;
   int num_instance_filenames, num_instance_args;
   char **instance_filenames, **instance_args;
   char **instance_filenames, **instance_args;
 
 
@@ -682,16 +708,14 @@ P3D_instance *Panda3D::
 create_instance(const string &p3d, P3D_window_type window_type,
 create_instance(const string &p3d, P3D_window_type window_type,
                 int win_x, int win_y, int win_width, int win_height,
                 int win_x, int win_y, int win_width, int win_height,
                 P3D_window_handle parent_window, char **args, int num_args) {
                 P3D_window_handle parent_window, char **args, int num_args) {
-  // If the supplied parameter name is a real file, pass it in on the
-  // parameter list.  Otherwise, assume it's a URL and let the plugin
-  // download it.
+  // Check to see if the p3d filename we were given is a URL, or a
+  // local file.
   Filename p3d_filename = Filename::from_os_specific(p3d);
   Filename p3d_filename = Filename::from_os_specific(p3d);
   string os_p3d_filename = p3d;
   string os_p3d_filename = p3d;
-  bool is_local = false;
-  if (p3d_filename.exists()) {
+  bool is_local = !is_url(p3d);
+  if (is_local) {
     p3d_filename.make_absolute();
     p3d_filename.make_absolute();
     os_p3d_filename = p3d_filename.to_os_specific();
     os_p3d_filename = p3d_filename.to_os_specific();
-    is_local = true;
   } 
   } 
 
 
   // Build up the token list.
   // Build up the token list.
@@ -832,8 +856,17 @@ usage() {
     << "    code.\n\n"
     << "    code.\n\n"
 
 
     << "  -u url\n"
     << "  -u url\n"
-    << "    Specify the URL of the Panda3D download server.  The default is\n"
-    << "    \"" << PANDA_PACKAGE_HOST_URL << "\" .\n\n"
+
+    << "    Specify the URL of the Panda3D download server.  This is the host\n"
+    << "    from which the plugin itself will be downloaded if necessary.  The\n"
+    << "    default is\n \"" << PANDA_PACKAGE_HOST_URL << "\" .\n\n"
+
+    << "  -M super_mirror_url\n"
+    << "    Specifies the \"super mirror\" URL, the special URL that is consulted\n"
+    << "    first before downloading any package file referenced by a p3d file.\n"
+    << "    This is primarily intended to support pre-installing a downloadable\n"
+    << "    Panda3D tree on the local machine, to allow p3d applications to\n"
+    << "    execute without requiring an internet connection.\n\n"
 
 
     << "  -p platform\n"
     << "  -p platform\n"
     << "    Specify the platform to masquerade as.  The default is \""
     << "    Specify the platform to masquerade as.  The default is \""
@@ -888,6 +921,39 @@ parse_int_pair(char *arg, int &x, int &y) {
   return false;
   return false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::is_url
+//       Access: Private, Static
+//  Description: Returns true if the indicated string appears to be a
+//               URL, with a leading http:// or file:// or whatever,
+//               or false if it must be a local filename instead.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+is_url(const string &param) {
+  // We define a URL prefix as a sequence of at least two letters,
+  // followed by a colon, followed by at least one slash.
+  size_t p = 0;
+  while (p < param.size() && isalpha(param[p])) {
+    ++p;
+  }
+  if (p < 2) {
+    // Not enough letters.
+    return false;
+  }
+  if (p >= param.size() || param[p] != ':') {
+    // No colon.
+    return false;
+  }
+  ++p;
+  if (p >= param.size() || param[p] != '/') {
+    // No slash.
+    return false;
+  }
+
+  // It matches the rules.
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::report_downloading_package
 //     Function: Panda3D::report_downloading_package
 //       Access: Private
 //       Access: Private

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

@@ -65,6 +65,7 @@ private:
   void usage();
   void usage();
   bool parse_token(char *arg);
   bool parse_token(char *arg);
   bool parse_int_pair(char *arg, int &x, int &y);
   bool parse_int_pair(char *arg, int &x, int &y);
+  static bool is_url(const string &param);
 
 
   void report_downloading_package(P3D_instance *instance);
   void report_downloading_package(P3D_instance *instance);
   void report_download_complete(P3D_instance *instance);
   void report_download_complete(P3D_instance *instance);

+ 2 - 0
direct/src/showutil/FreezeTool.py

@@ -786,6 +786,8 @@ class Freezer:
                 continue
                 continue
             if origName in self.previousModules:
             if origName in self.previousModules:
                 continue
                 continue
+            if origName in self.modules:
+                continue
 
 
             # This module is missing.  Let it be missing in the
             # This module is missing.  Let it be missing in the
             # runtime also.
             # runtime also.