Browse Source

add audio libraries, and download transitive package requirements

David Rose 16 years ago
parent
commit
d7b2de8029

+ 24 - 2
direct/src/p3d/AppRunner.py

@@ -238,16 +238,39 @@ class AppRunner(DirectObject):
                 packageName, version, hostUrl)
                 packageName, version, hostUrl)
             return False
             return False
 
 
+        return self.__rInstallPackage(package, [])
+
+    def __rInstallPackage(self, package, nested):
+        """ The recursive implementation of installPackage().  The new
+        parameter, nested, is a list of packages that we are
+        recursively calling this from, to avoid recursive loops. """
+
         if not package.downloadDescFile(self.http):
         if not package.downloadDescFile(self.http):
             return False
             return False
 
 
+        # Now that we've downloaded and read the desc file, we can
+        # install all of the required packages first.
+        nested = nested[:] + [self]
+        for packageName, version, host in package.requires:
+            if host.downloadContentsFile(self.http):
+                p2 = host.getPackage(packageName, version)
+                if not p2:
+                    print "Couldn't find %s %s on %s" % (packageName, version, host.hostUrl)
+                else:
+                    if p2 not in nested:
+                        self.__rInstallPackage(p2, nested)
+
+        # Now that all of the required packages are installed, carry
+        # on to download and install this package.
         if not package.downloadPackage(self.http):
         if not package.downloadPackage(self.http):
             return False
             return False
 
 
         if not package.installPackage(self):
         if not package.installPackage(self):
             return False
             return False
 
 
-        print "Package %s %s installed." % (packageName, version)
+        print "Package %s %s installed." % (
+            package.packageName, package.packageVersion)
+        return True
 
 
     def getHostWithAlt(self, hostUrl):
     def getHostWithAlt(self, hostUrl):
         """ Returns a suitable HostInfo object for downloading
         """ Returns a suitable HostInfo object for downloading
@@ -290,7 +313,6 @@ class AppRunner(DirectObject):
         mapped host, which is the one we should actually download
         mapped host, which is the one we should actually download
         from, see getHostWithAlt().  """
         from, see getHostWithAlt().  """
 
 
-
         if hostUrl is None:
         if hostUrl is None:
             hostUrl = PandaSystem.getPackageHostUrl()
             hostUrl = PandaSystem.getPackageHostUrl()
 
 

+ 15 - 0
direct/src/p3d/PackageInfo.py

@@ -79,6 +79,7 @@ class PackageInfo:
         self.uncompressedArchive = None
         self.uncompressedArchive = None
         self.compressedArchive = None
         self.compressedArchive = None
         self.extracts = []
         self.extracts = []
+        self.requires = []
         self.installPlans = None
         self.installPlans = None
  
  
         # This is updated during downloadPackage().  It is in the
         # This is updated during downloadPackage().  It is in the
@@ -253,6 +254,7 @@ class PackageInfo:
             self.compressedArchive.loadXml(xcompressedArchive)
             self.compressedArchive.loadXml(xcompressedArchive)
 
 
         # The list of files that should be extracted to disk.
         # The list of files that should be extracted to disk.
+        self.extracts = []
         xextract = xpackage.FirstChildElement('extract')
         xextract = xpackage.FirstChildElement('extract')
         while xextract:
         while xextract:
             file = FileSpec()
             file = FileSpec()
@@ -260,6 +262,19 @@ class PackageInfo:
             self.extracts.append(file)
             self.extracts.append(file)
             xextract = xextract.NextSiblingElement('extract')
             xextract = xextract.NextSiblingElement('extract')
 
 
+        # The list of additional packages that must be installed for
+        # this package to function properly.
+        self.requires = []
+        xrequires = xpackage.FirstChildElement('requires')
+        while xrequires:
+            packageName = xrequires.Attribute('name')
+            version = xrequires.Attribute('version')
+            hostUrl = xrequires.Attribute('host')
+            if packageName and hostUrl:
+                host = self.host.appRunner.getHostWithAlt(hostUrl)
+                self.requires.append((packageName, version, host))
+            xrequires = xrequires.NextSiblingElement('requires')
+
         self.hasDescFile = True
         self.hasDescFile = True
 
 
         # Now that we've read the desc file, go ahead and use it to
         # Now that we've read the desc file, go ahead and use it to

+ 42 - 13
direct/src/p3d/PackageInstaller.py

@@ -76,6 +76,13 @@ class PackageInstaller(DirectObject):
             # getDescFile().
             # getDescFile().
             self.downloadEffort = 0
             self.downloadEffort = 0
 
 
+        def __cmp__(self, pp):
+            """ Python comparision function.  This makes all
+            PendingPackages withe same (packageName, version, host)
+            combination be deemed equivalent. """
+            return cmp((self.packageName, self.version, self.host),
+                       (pp.packageName, pp.version, pp.host))
+
         def getProgress(self):
         def getProgress(self):
             """ Returns the download progress of this package in the
             """ Returns the download progress of this package in the
             range 0..1. """
             range 0..1. """
@@ -230,16 +237,37 @@ class PackageInstaller(DirectObject):
 
 
         self.packageLock.acquire()
         self.packageLock.acquire()
         try:
         try:
-            self.packages.append(pp)
-            if not pp.checkDescFile():
-                # Still need to download the desc file.
-                self.needsDescFile.append(pp)
-                if not self.descFileTask:
-                    self.descFileTask = taskMgr.add(
-                        self.__getDescFileTask, 'getDescFile',
-                        taskChain = self.taskChain)
-
-            elif not pp.package.hasPackage:
+            self.__internalAddPackage(pp)
+        finally:
+            self.packageLock.release()
+
+    def __internalAddPackage(self, pp):
+        """ Adds the indicated "pending package" to the appropriate
+        list(s) for downloading and installing.  Assumes packageLock
+        is already held."""
+
+        if pp in self.packages:
+            # Already added.
+            return
+        
+        self.packages.append(pp)
+        if not pp.checkDescFile():
+            # Still need to download the desc file.
+            self.needsDescFile.append(pp)
+            if not self.descFileTask:
+                self.descFileTask = taskMgr.add(
+                    self.__getDescFileTask, 'getDescFile',
+                    taskChain = self.taskChain)
+
+        else:
+            # The desc file is ready, which means so are the requirements.
+            for packageName, version, host in pp.package.requires:
+                pp2 = self.PendingPackage(packageName, version, host)
+                self.__internalAddPackage(pp2)
+
+            # Now that we've added the required packages, add the
+            # package itself.
+            if not pp.package.hasPackage:
                 # The desc file is good, but the package itself needs
                 # The desc file is good, but the package itself needs
                 # to be downloaded.
                 # to be downloaded.
                 self.needsDownload.append(pp)
                 self.needsDownload.append(pp)
@@ -247,9 +275,6 @@ class PackageInstaller(DirectObject):
             else:
             else:
                 # The package is already fully downloaded.
                 # The package is already fully downloaded.
                 self.earlyDone.append(pp)
                 self.earlyDone.append(pp)
-                    
-        finally:
-            self.packageLock.release()
 
 
     def donePackages(self):
     def donePackages(self):
         """ After calling addPackage() for each package to be
         """ After calling addPackage() for each package to be
@@ -494,6 +519,10 @@ class PackageInstaller(DirectObject):
         # This package is now ready to be downloaded.
         # This package is now ready to be downloaded.
         self.packageLock.acquire()
         self.packageLock.acquire()
         try:
         try:
+            # Also add any packages required by this one.
+            for packageName, version, host in pp.package.requires:
+                pp2 = self.PendingPackage(packageName, version, host)
+                self.__internalAddPackage(pp2)
             self.needsDownload.append(pp)
             self.needsDownload.append(pp)
         finally:
         finally:
             self.packageLock.release()
             self.packageLock.release()

+ 9 - 9
direct/src/p3d/Packager.py

@@ -1545,15 +1545,15 @@ class Packager:
         def requirePackage(self, package):
         def requirePackage(self, package):
             """ Indicates a dependency on the given package.  This
             """ Indicates a dependency on the given package.  This
             also implicitly requires all of the package's requirements
             also implicitly requires all of the package's requirements
-            as well. """
-
-            for p2 in package.requires + [package]:
-                if p2 not in self.requires:
-                    self.requires.append(p2)
-                    for filename in p2.targetFilenames.keys():
-                        self.skipFilenames[filename] = True
-                    for moduleName, mdef in p2.moduleNames.items():
-                        self.skipModules[moduleName] = mdef
+            as well (though this transitive requirement happens at
+            runtime, not here at build time). """
+
+            if package not in self.requires:
+                self.requires.append(package)
+                for filename in package.targetFilenames.keys():
+                    self.skipFilenames[filename] = True
+                for moduleName, mdef in package.moduleNames.items():
+                    self.skipModules[moduleName] = mdef
 
 
     # Packager constructor
     # Packager constructor
     def __init__(self):
     def __init__(self):

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

@@ -99,7 +99,43 @@ aux-display tinydisplay
 plugin-path $PANDA3D_ROOT
 plugin-path $PANDA3D_ROOT
 default-model-extension .bam
 default-model-extension .bam
 """ + auxDisplays)
 """ + auxDisplays)
+    
+class fmod(package):
+    # This package includes the FMod audio library.  This is
+    # full-featured and robust, but it is closed-source and the
+    # licensing is cost-free only for non-commercial products.
+
+    config(display_name = "FMod audio library")
+    require('panda3d')
+
+    file('libp3fmod_audio.dll')
+
+    file('fmod.prc', extract = True, text = """
+plugin-path $FMOD_ROOT
+audio-library-name p3fmod_audio
+""")
+
+class openal(package):
+    # This package includes the OpenAL audio libraries.  This is free
+    # in both senses, but there are some technical issues, especially
+    # on certain platforms.
 
 
+    config(display_name = "OpenAL audio library")
+    require('panda3d')
+
+    file('libp3openal_audio.dll')
+
+    file('openal.prc', extract = True, text = """
+plugin-path $OPENAL_ROOT
+audio-library-name p3openal_audio
+""")
+
+class audio(package):
+    # This package includes the best audio library for the given platform.
+    if platform.startswith('osx'):
+        require('fmod')
+    else:
+        require('openal')
 
 
 class egg(package):
 class egg(package):
     # This package contains the code for reading and operating on egg
     # This package contains the code for reading and operating on egg

+ 49 - 22
direct/src/plugin/p3dInstance.cxx

@@ -85,6 +85,7 @@ P3DInstance(P3D_request_ready_func *func,
   _got_fparams = false;
   _got_fparams = false;
   _got_wparams = false;
   _got_wparams = false;
   _p3d_trusted = false;
   _p3d_trusted = false;
+  _xpackage = NULL;
   _p3dcert_package = NULL;
   _p3dcert_package = NULL;
 
 
   _fparams.set_tokens(tokens, num_tokens);
   _fparams.set_tokens(tokens, num_tokens);
@@ -224,6 +225,11 @@ P3DInstance::
     delete _temp_p3d_filename;
     delete _temp_p3d_filename;
     _temp_p3d_filename = NULL;
     _temp_p3d_filename = NULL;
   }
   }
+  
+  if (_xpackage != NULL) {
+    delete _xpackage;
+    _xpackage = NULL;
+  }
 
 
 #ifdef __APPLE__
 #ifdef __APPLE__
   if (_frame_timer != NULL) {
   if (_frame_timer != NULL) {
@@ -761,6 +767,33 @@ handle_event(P3D_event_data event) {
   return retval;
   return retval;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::add_package
+//       Access: Public
+//  Description: Adds the package to the list of packages used by this
+//               instance.  The instance will share responsibility for
+//               downloading the package with any of the other
+//               instances that use the same package.
+////////////////////////////////////////////////////////////////////
+void P3DInstance::
+add_package(const string &name, const string &version, P3DHost *host) {
+  string alt_host = _fparams.lookup_token("alt_host");
+
+  // Look up in the p3d_info.xml file to see if this p3d file has
+  // a specific alt_host indication for this host_url.
+  string alt_host_url = find_alt_host_url(host->get_host_url(), alt_host);
+  if (!alt_host_url.empty()) {
+    // If it does, we go ahead and switch to that host now,
+    // instead of bothering to contact the original host.
+    P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+    host = inst_mgr->get_host(alt_host_url);
+    alt_host.clear();
+  }
+  
+  P3DPackage *package = host->get_package(name, version, alt_host);
+  add_package(package);
+}
+    
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::add_package
 //     Function: P3DInstance::add_package
 //       Access: Public
 //       Access: Public
@@ -1259,13 +1292,15 @@ scan_app_desc_file(TiXmlDocument *doc) {
   if (xpackage == NULL) {
   if (xpackage == NULL) {
     return;
     return;
   }
   }
+  assert(_xpackage == NULL);
+  _xpackage = (TiXmlElement *)xpackage->Clone();
 
 
-  const char *log_basename = xpackage->Attribute("log_basename");
+  const char *log_basename = _xpackage->Attribute("log_basename");
   if (log_basename != NULL) {
   if (log_basename != NULL) {
     _log_basename = log_basename;
     _log_basename = log_basename;
   }
   }
 
 
-  TiXmlElement *xconfig = xpackage->FirstChildElement("config");
+  TiXmlElement *xconfig = _xpackage->FirstChildElement("config");
   if (xconfig != NULL) {
   if (xconfig != NULL) {
     int hidden = 0;
     int hidden = 0;
     if (xconfig->QueryIntAttribute("hidden", &hidden) == TIXML_SUCCESS) {
     if (xconfig->QueryIntAttribute("hidden", &hidden) == TIXML_SUCCESS) {
@@ -1308,9 +1343,7 @@ scan_app_desc_file(TiXmlDocument *doc) {
     _wparams.set_window_type(P3D_WT_hidden);
     _wparams.set_window_type(P3D_WT_hidden);
   }
   }
 
 
-  string alt_host = _fparams.lookup_token("alt_host");
-
-  TiXmlElement *xrequires = xpackage->FirstChildElement("requires");
+  TiXmlElement *xrequires = _xpackage->FirstChildElement("requires");
   while (xrequires != NULL) {
   while (xrequires != NULL) {
     const char *name = xrequires->Attribute("name");
     const char *name = xrequires->Attribute("name");
     const char *host_url = xrequires->Attribute("host");
     const char *host_url = xrequires->Attribute("host");
@@ -1320,20 +1353,7 @@ scan_app_desc_file(TiXmlDocument *doc) {
         version = "";
         version = "";
       }
       }
       P3DHost *host = inst_mgr->get_host(host_url);
       P3DHost *host = inst_mgr->get_host(host_url);
-      string this_alt_host = alt_host;
-
-      // Look up in the p3d_info.xml file to see if this p3d file has
-      // a specific alt_host indication for this host_url.
-      string alt_host_url = find_alt_host_url(xpackage, host_url, alt_host);
-      if (!alt_host_url.empty()) {
-        // If it does, we go ahead and switch to that host now,
-        // instead of bothering to contact the original host.
-        host = inst_mgr->get_host(alt_host_url);
-        this_alt_host.clear();
-      }
-        
-      P3DPackage *package = host->get_package(name, version, this_alt_host);
-      add_package(package);
+      add_package(name, version, host);
     }
     }
 
 
     xrequires = xrequires->NextSiblingElement("requires");
     xrequires = xrequires->NextSiblingElement("requires");
@@ -1348,9 +1368,8 @@ scan_app_desc_file(TiXmlDocument *doc) {
 //               Returns empty string if there is no match.
 //               Returns empty string if there is no match.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string P3DInstance::
 string P3DInstance::
-find_alt_host_url(TiXmlElement *xpackage, 
-                  const string &host_url, const string &alt_host) {
-  TiXmlElement *xhost = xpackage->FirstChildElement("host");
+find_alt_host_url(const string &host_url, const string &alt_host) {
+  TiXmlElement *xhost = _xpackage->FirstChildElement("host");
   while (xhost != NULL) {
   while (xhost != NULL) {
     const char *url = xhost->Attribute("url");
     const char *url = xhost->Attribute("url");
     if (url != NULL && host_url == url) {
     if (url != NULL && host_url == url) {
@@ -1845,6 +1864,14 @@ report_package_info_ready(P3DPackage *package) {
     return;
     return;
   }
   }
 
 
+  // Now that the package's info is ready, we know its set of required
+  // packages, and we can add these to the list.
+  P3DPackage::Requires::const_iterator ri;
+  for (ri = package->_requires.begin(); ri != package->_requires.end(); ++ri) {
+    const P3DPackage::RequiredPackage &rp = (*ri);
+    add_package(rp._package_name, rp._package_version, rp._host);
+  }
+
   if (get_packages_info_ready()) {
   if (get_packages_info_ready()) {
     // All packages are ready to go.  Let's start some download
     // All packages are ready to go.  Let's start some download
     // action.
     // action.

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

@@ -90,6 +90,7 @@ public:
 
 
   inline P3D_request_ready_func *get_request_ready_func() const;
   inline P3D_request_ready_func *get_request_ready_func() const;
 
 
+  void add_package(const string &name, const string &version, P3DHost *host);
   void add_package(P3DPackage *package);
   void add_package(P3DPackage *package);
   bool get_packages_info_ready() const;
   bool get_packages_info_ready() const;
   bool get_packages_ready() const;
   bool get_packages_ready() const;
@@ -154,8 +155,7 @@ private:
   void mark_p3d_untrusted();
   void mark_p3d_untrusted();
   void mark_p3d_trusted();
   void mark_p3d_trusted();
   void scan_app_desc_file(TiXmlDocument *doc);
   void scan_app_desc_file(TiXmlDocument *doc);
-  string find_alt_host_url(TiXmlElement *xpackage, 
-                           const string &host_url, const string &alt_host);
+  string find_alt_host_url(const string &host_url, const string &alt_host);
 
 
   void send_browser_script_object();
   void send_browser_script_object();
   P3D_request *make_p3d_request(TiXmlElement *xrequest);
   P3D_request *make_p3d_request(TiXmlElement *xrequest);
@@ -218,6 +218,7 @@ private:
   P3DWindowParams _wparams;
   P3DWindowParams _wparams;
 
 
   bool _p3d_trusted;
   bool _p3d_trusted;
+  TiXmlElement *_xpackage;
   
   
   // For downloading the p3dcert authorization program.
   // For downloading the p3dcert authorization program.
   P3DPackage *_p3dcert_package;
   P3DPackage *_p3dcert_package;

+ 15 - 0
direct/src/plugin/p3dPackage.I

@@ -202,3 +202,18 @@ inline void P3DPackage::InstallStep::
 report_step_progress() {
 report_step_progress() {
   _package->report_progress(this);
   _package->report_progress(this);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DPackage::RequiredPackage::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+inline P3DPackage::RequiredPackage::
+RequiredPackage(const string &package_name, const string &package_version,
+                P3DHost *host) :
+  _package_name(package_name),
+  _package_version(package_version),
+  _host(host)
+{
+}
+

+ 53 - 26
direct/src/plugin/p3dPackage.cxx

@@ -169,8 +169,8 @@ get_formatted_name() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DPackage::add_instance
 //     Function: P3DPackage::add_instance
 //       Access: Public
 //       Access: Public
-//  Description: Specifies an instance that may be responsible for
-//               downloading this package.
+//  Description: Specifies an instance that that will be using this
+//               package, and may be responsible for downloading it.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DPackage::
 void P3DPackage::
 add_instance(P3DInstance *inst) {
 add_instance(P3DInstance *inst) {
@@ -182,8 +182,9 @@ add_instance(P3DInstance *inst) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DPackage::remove_instance
 //     Function: P3DPackage::remove_instance
 //       Access: Public
 //       Access: Public
-//  Description: Indicates that the given instance will no longer be
-//               responsible for downloading this package.
+//  Description: Indicates that the given instance is no longer
+//               interested in this package and will not be
+//               responsible for downloading it.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DPackage::
 void P3DPackage::
 remove_instance(P3DInstance *inst) {
 remove_instance(P3DInstance *inst) {
@@ -567,27 +568,34 @@ desc_file_download_finished(bool success) {
 void P3DPackage::
 void P3DPackage::
 got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
 got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
   TiXmlElement *xpackage = doc->FirstChildElement("package");
   TiXmlElement *xpackage = doc->FirstChildElement("package");
-  TiXmlElement *xuncompressed_archive = NULL;
-  TiXmlElement *xcompressed_archive = NULL;
+  if (xpackage == NULL) {
+    nout << _package_name << " desc file contains no <package>\n";
+    if (!freshly_downloaded) {
+      download_desc_file();
+      return;
+    }
+    report_done(false);
+    return;
+  }
   
   
-  if (xpackage != NULL) {
-    xpackage->Attribute("patch_version", &_patch_version);
-
-    xuncompressed_archive = xpackage->FirstChildElement("uncompressed_archive");
-    xcompressed_archive = xpackage->FirstChildElement("compressed_archive");
-
-    TiXmlElement *xconfig = xpackage->FirstChildElement("config");
-    if (xconfig != NULL) {
-      const char *display_name_cstr = xconfig->Attribute("display_name");
-      if (display_name_cstr != NULL) {
-        _package_display_name = display_name_cstr;
-      }
-
-      // Save the config entry within this class for others to query.
-      _xconfig = (TiXmlElement *)xconfig->Clone();
+  xpackage->Attribute("patch_version", &_patch_version);
+  
+  TiXmlElement *xconfig = xpackage->FirstChildElement("config");
+  if (xconfig != NULL) {
+    const char *display_name_cstr = xconfig->Attribute("display_name");
+    if (display_name_cstr != NULL) {
+      _package_display_name = display_name_cstr;
     }
     }
+    
+    // Save the config entry within this class for others to query.
+    _xconfig = (TiXmlElement *)xconfig->Clone();
   }
   }
 
 
+  TiXmlElement *xuncompressed_archive = 
+    xpackage->FirstChildElement("uncompressed_archive");
+  TiXmlElement *xcompressed_archive = 
+    xpackage->FirstChildElement("compressed_archive");
+
   if (xuncompressed_archive == NULL || xcompressed_archive == NULL) {
   if (xuncompressed_archive == NULL || xcompressed_archive == NULL) {
     // The desc file didn't include the archive file itself, weird.
     // The desc file didn't include the archive file itself, weird.
     if (!freshly_downloaded) {
     if (!freshly_downloaded) {
@@ -604,18 +612,37 @@ got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
   // Now get all the extractable components.
   // Now get all the extractable components.
   _unpack_size = 0;
   _unpack_size = 0;
   _extracts.clear();
   _extracts.clear();
-  TiXmlElement *extract = xpackage->FirstChildElement("extract");
-  while (extract != NULL) {
+  TiXmlElement *xextract = xpackage->FirstChildElement("extract");
+  while (xextract != NULL) {
     FileSpec file;
     FileSpec file;
-    file.load_xml(extract);
+    file.load_xml(xextract);
     _extracts.push_back(file);
     _extracts.push_back(file);
     _unpack_size += file.get_size();
     _unpack_size += file.get_size();
-    extract = extract->NextSiblingElement("extract");
+    xextract = xextract->NextSiblingElement("extract");
+  }
+
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+
+  // Get the required packages.
+  _requires.clear();
+  TiXmlElement *xrequires = xpackage->FirstChildElement("requires");
+  while (xrequires != NULL) {
+    const char *package_name = xrequires->Attribute("name");
+    const char *version = xrequires->Attribute("version");
+    const char *host_url = xrequires->Attribute("host");
+    if (package_name != NULL && host_url != NULL) {
+      P3DHost *host = inst_mgr->get_host(host_url);
+      if (version == NULL) {
+        version = "";
+      }
+      _requires.push_back(RequiredPackage(package_name, version, host));
+    }
+
+    xrequires = xrequires->NextSiblingElement("requires");
   }
   }
 
 
   // Get a list of all of the files in the directory, so we can remove
   // Get a list of all of the files in the directory, so we can remove
   // files that don't belong.
   // files that don't belong.
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   vector<string> contents;
   vector<string> contents;
   inst_mgr->scan_directory_recursively(_package_dir, contents);
   inst_mgr->scan_directory_recursively(_package_dir, contents);
 
 

+ 13 - 0
direct/src/plugin/p3dPackage.h

@@ -211,6 +211,19 @@ private:
 
 
   bool is_extractable(FileSpec &file, const string &filename) const;
   bool is_extractable(FileSpec &file, const string &filename) const;
 
 
+public:
+  class RequiredPackage {
+  public:
+    inline RequiredPackage(const string &package_name,
+                           const string &package_version,
+                           P3DHost *host);
+    string _package_name;
+    string _package_version;
+    P3DHost *_host;
+  };
+  typedef vector<RequiredPackage> Requires;
+  Requires _requires;
+
 private:
 private:
   P3DHost *_host;
   P3DHost *_host;
   int _host_contents_seq;
   int _host_contents_seq;