Browse Source

contents seq, package seq

David Rose 15 years ago
parent
commit
547b3abea9

+ 25 - 1
direct/src/p3d/PackageMerger.py

@@ -1,4 +1,5 @@
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.FileSpec import FileSpec
+from direct.p3d.SeqValue import SeqValue
 from pandac.PandaModules import *
 from pandac.PandaModules import *
 import copy
 import copy
 import shutil
 import shutil
@@ -8,7 +9,7 @@ class PackageMergerError(StandardError):
     pass
     pass
 
 
 class PackageMerger:
 class PackageMerger:
-    """ This class will combine two or more separately-build stage
+    """ This class will combine two or more separately-built stage
     directories, the output of Packager.py or the ppackage tool, into
     directories, the output of Packager.py or the ppackage tool, into
     a single output directory.  It assumes that the clocks on all
     a single output directory.  It assumes that the clocks on all
     hosts are in sync, so that the file across all builds with the
     hosts are in sync, so that the file across all builds with the
@@ -41,6 +42,9 @@ class PackageMerger:
             self.descFile = FileSpec()
             self.descFile = FileSpec()
             self.descFile.loadXml(xpackage)
             self.descFile.loadXml(xpackage)
 
 
+            self.packageSeq = SeqValue()
+            self.packageSeq.loadXml(xpackage)
+
             self.importDescFile = None
             self.importDescFile = None
             ximport = xpackage.FirstChildElement('import')
             ximport = xpackage.FirstChildElement('import')
             if ximport:
             if ximport:
@@ -59,6 +63,7 @@ class PackageMerger:
                 xpackage.SetAttribute('solo', '1')
                 xpackage.SetAttribute('solo', '1')
 
 
             self.descFile.storeXml(xpackage)
             self.descFile.storeXml(xpackage)
+            self.packageSeq.storeXml(xpackage)
 
 
             if self.importDescFile:
             if self.importDescFile:
                 ximport = TiXmlElement('import')
                 ximport = TiXmlElement('import')
@@ -72,6 +77,8 @@ class PackageMerger:
         self.installDir = installDir
         self.installDir = installDir
         self.xhost = None
         self.xhost = None
         self.contents = {}
         self.contents = {}
+        self.maxAge = None
+        self.contentsSeq = SeqValue()
 
 
         # We allow the first one to fail quietly.
         # We allow the first one to fail quietly.
         self.__readContentsFile(self.installDir)
         self.__readContentsFile(self.installDir)
@@ -89,6 +96,18 @@ class PackageMerger:
 
 
         xcontents = doc.FirstChildElement('contents')
         xcontents = doc.FirstChildElement('contents')
         if xcontents:
         if xcontents:
+            maxAge = xcontents.Attribute('max_age')
+            if maxAge:
+                maxAge = int(maxAge)
+                if self.maxAge is None:
+                    self.maxAge = maxAge
+                else:
+                    self.maxAge = min(self.maxAge, maxAge)
+
+            contentsSeq = SeqValue()
+            if contentsSeq.loadXml(xcontents):
+                self.contentsSeq = max(self.contentsSeq, contentsSeq)
+
             xhost = xcontents.FirstChildElement('host')
             xhost = xcontents.FirstChildElement('host')
             if xhost:
             if xhost:
                 self.xhost = xhost.Clone()
                 self.xhost = xhost.Clone()
@@ -119,6 +138,10 @@ class PackageMerger:
         if self.xhost:
         if self.xhost:
             xcontents.InsertEndChild(self.xhost)
             xcontents.InsertEndChild(self.xhost)
 
 
+        if self.maxAge is not None:
+            xcontents.SetAttribute('max_age', str(self.maxAge))
+        self.contentsSeq.storeXml(xcontents)
+
         contents = self.contents.items()
         contents = self.contents.items()
         contents.sort()
         contents.sort()
         for key, pe in contents:
         for key, pe in contents:
@@ -203,5 +226,6 @@ class PackageMerger:
                 # Here's a new subdirectory we have to copy in.
                 # Here's a new subdirectory we have to copy in.
                 self.__copySubdirectory(pe)
                 self.__copySubdirectory(pe)
 
 
+        self.contentsSeq += 1
         self.__writeContentsFile()
         self.__writeContentsFile()
         
         

+ 51 - 1
direct/src/p3d/Packager.py

@@ -13,6 +13,7 @@ import types
 import getpass
 import getpass
 import platform
 import platform
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.FileSpec import FileSpec
+from direct.p3d.SeqValue import SeqValue
 from direct.showbase import Loader
 from direct.showbase import Loader
 from direct.showbase import AppRunnerGlobal
 from direct.showbase import AppRunnerGlobal
 from direct.showutil import FreezeTool
 from direct.showutil import FreezeTool
@@ -165,7 +166,7 @@ class Packager:
         file. """
         file. """
         
         
         def __init__(self):
         def __init__(self):
-            pass
+            self.packageSeq = SeqValue()
 
 
         def getKey(self):
         def getKey(self):
             """ Returns a tuple used for sorting the PackageEntry
             """ Returns a tuple used for sorting the PackageEntry
@@ -194,6 +195,9 @@ class Packager:
             solo = xpackage.Attribute('solo')
             solo = xpackage.Attribute('solo')
             self.solo = int(solo or '0')
             self.solo = int(solo or '0')
 
 
+            self.packageSeq = SeqValue()
+            self.packageSeq.loadXml(xpackage)
+
             self.descFile = FileSpec()
             self.descFile = FileSpec()
             self.descFile.loadXml(xpackage)
             self.descFile.loadXml(xpackage)
 
 
@@ -215,6 +219,7 @@ class Packager:
             if self.solo:
             if self.solo:
                 xpackage.SetAttribute('solo', '1')
                 xpackage.SetAttribute('solo', '1')
 
 
+            self.packageSeq.storeXml(xpackage)
             self.descFile.storeXml(xpackage)
             self.descFile.storeXml(xpackage)
 
 
             if self.importDescFile:
             if self.importDescFile:
@@ -658,6 +663,7 @@ class Packager:
                 os.chmod(self.packageFullpath.toOsSpecific(), 0755)
                 os.chmod(self.packageFullpath.toOsSpecific(), 0755)
             else:
             else:
                 self.readDescFile()
                 self.readDescFile()
+                self.packageSeq += 1
                 self.compressMultifile()
                 self.compressMultifile()
                 self.writeDescFile()
                 self.writeDescFile()
                 self.writeImportDescFile()
                 self.writeImportDescFile()
@@ -672,6 +678,7 @@ class Packager:
                 pe.fromFile(self.packageName, self.platform, self.version,
                 pe.fromFile(self.packageName, self.platform, self.version,
                             False, self.packager.installDir,
                             False, self.packager.installDir,
                             self.packageDesc, self.packageImportDesc)
                             self.packageDesc, self.packageImportDesc)
+                pe.packageSeq = self.packageSeq
                 
                 
                 self.packager.contents[pe.getKey()] = pe
                 self.packager.contents[pe.getKey()] = pe
                 self.packager.contentsChanged = True
                 self.packager.contentsChanged = True
@@ -733,6 +740,10 @@ class Packager:
             pe.fromFile(self.packageName, self.platform, self.version,
             pe.fromFile(self.packageName, self.platform, self.version,
                         True, self.packager.installDir,
                         True, self.packager.installDir,
                         Filename(packageDir, file.newName), None)
                         Filename(packageDir, file.newName), None)
+            peOrig = self.packager.contents.get(pe.getKey(), None)
+            if peOrig:
+                pe.packageSeq = peOrig.packageSeq + 1
+
             self.packager.contents[pe.getKey()] = pe
             self.packager.contents[pe.getKey()] = pe
             self.packager.contentsChanged = True
             self.packager.contentsChanged = True
 
 
@@ -1204,6 +1215,7 @@ class Packager:
                 if package.version:
                 if package.version:
                     xrequires.SetAttribute('version', package.version)
                     xrequires.SetAttribute('version', package.version)
                 xrequires.SetAttribute('host', package.host)
                 xrequires.SetAttribute('host', package.host)
+                package.packageSeq.storeXml(xrequires)
                 requireHosts[package.host] = True
                 requireHosts[package.host] = True
                 xpackage.InsertEndChild(xrequires)
                 xpackage.InsertEndChild(xrequires)
 
 
@@ -1249,6 +1261,7 @@ class Packager:
             it.  We need this to preserve the list of patches, and
             it.  We need this to preserve the list of patches, and
             similar historic data, between sessions. """
             similar historic data, between sessions. """
 
 
+            self.packageSeq = SeqValue()
             self.patchVersion = None
             self.patchVersion = None
             self.patches = []
             self.patches = []
 
 
@@ -1263,6 +1276,8 @@ class Packager:
             if not xpackage:
             if not xpackage:
                 return
                 return
 
 
+            self.packageSeq.loadXml(xpackage)
+
             xcompressed = xpackage.FirstChildElement('compressed_archive')
             xcompressed = xpackage.FirstChildElement('compressed_archive')
             if xcompressed:
             if xcompressed:
                 compressedFilename = xcompressed.Attribute('filename')
                 compressedFilename = xcompressed.Attribute('filename')
@@ -1309,6 +1324,8 @@ class Packager:
             if self.patchVersion:
             if self.patchVersion:
                 xpackage.SetAttribute('last_patch_version', self.patchVersion)
                 xpackage.SetAttribute('last_patch_version', self.patchVersion)
 
 
+            self.packageSeq.storeXml(xpackage)
+
             self.__addConfigs(xpackage)
             self.__addConfigs(xpackage)
 
 
             for package in self.requires:
             for package in self.requires:
@@ -1318,6 +1335,7 @@ class Packager:
                     xrequires.SetAttribute('platform', package.platform)
                     xrequires.SetAttribute('platform', package.platform)
                 if package.version:
                 if package.version:
                     xrequires.SetAttribute('version', package.version)
                     xrequires.SetAttribute('version', package.version)
+                package.packageSeq.storeXml(xrequires)
                 xrequires.SetAttribute('host', package.host)
                 xrequires.SetAttribute('host', package.host)
                 xpackage.InsertEndChild(xrequires)
                 xpackage.InsertEndChild(xrequires)
 
 
@@ -1379,6 +1397,8 @@ class Packager:
                 xpackage.SetAttribute('version', self.version)
                 xpackage.SetAttribute('version', self.version)
             xpackage.SetAttribute('host', self.host)
             xpackage.SetAttribute('host', self.host)
 
 
+            self.packageSeq.storeXml(xpackage)
+
             for package in self.requires:
             for package in self.requires:
                 xrequires = TiXmlElement('requires')
                 xrequires = TiXmlElement('requires')
                 xrequires.SetAttribute('name', package.packageName)
                 xrequires.SetAttribute('name', package.packageName)
@@ -1386,6 +1406,7 @@ class Packager:
                     xrequires.SetAttribute('platform', package.platform)
                     xrequires.SetAttribute('platform', package.platform)
                 if package.version:
                 if package.version:
                     xrequires.SetAttribute('version', package.version)
                     xrequires.SetAttribute('version', package.version)
+                package.packageSeq.storeXml(xrequires)
                 xrequires.SetAttribute('host', package.host)
                 xrequires.SetAttribute('host', package.host)
                 xpackage.InsertEndChild(xrequires)
                 xpackage.InsertEndChild(xrequires)
 
 
@@ -1400,6 +1421,8 @@ class Packager:
             """ Reads the import desc file.  Returns True on success,
             """ Reads the import desc file.  Returns True on success,
             False on failure. """
             False on failure. """
 
 
+            self.packageSeq = SeqValue()
+
             doc = TiXmlDocument(filename.toOsSpecific())
             doc = TiXmlDocument(filename.toOsSpecific())
             if not doc.LoadFile():
             if not doc.LoadFile():
                 return False
                 return False
@@ -1412,6 +1435,8 @@ class Packager:
             self.version = xpackage.Attribute('version')
             self.version = xpackage.Attribute('version')
             self.host = xpackage.Attribute('host')
             self.host = xpackage.Attribute('host')
 
 
+            self.packageSeq.loadXml(xpackage)
+
             self.requires = []
             self.requires = []
             xrequires = xpackage.FirstChildElement('requires')
             xrequires = xpackage.FirstChildElement('requires')
             while xrequires:
             while xrequires:
@@ -1868,6 +1893,25 @@ class Packager:
         # contents.xml before re-querying the server, in seconds.
         # contents.xml before re-querying the server, in seconds.
         self.maxAge = 0
         self.maxAge = 0
 
 
+        # The contents seq: a tuple of integers, representing the
+        # current seq value.  The contents seq generally increments
+        # with each modification to the contents.xml file.  There is
+        # also a package seq for each package, which generally
+        # increments with each modification to the package.
+        
+        # The contents seq and package seq are used primarily for
+        # documentation purposes, to note when a new version is
+        # released.  The package seq value can also be used to verify
+        # that the contents.xml, desc.xml, and desc.import.xml files
+        # were all built at the same time.
+
+        # Although the package seqs are used at runtime to verify that
+        # the latest contents.xml file has been downloaded, they are
+        # not otherwise used at runtime, and they are not binding on
+        # the download version.  The md5 hash, not the package seq, is
+        # actually used to differentiate different download versions.
+        self.contentsSeq = SeqValue()
+
         # A search list for previously-built local packages.
         # A search list for previously-built local packages.
 
 
         # We use a bit of caution to read the Filenames out of the
         # We use a bit of caution to read the Filenames out of the
@@ -3163,6 +3207,7 @@ class Packager:
         self.addHost(self.host)
         self.addHost(self.host)
 
 
         self.maxAge = 0
         self.maxAge = 0
+        self.contentsSeq = SeqValue()
         self.contents = {}
         self.contents = {}
         self.contentsChanged = False
         self.contentsChanged = False
 
 
@@ -3181,6 +3226,8 @@ class Packager:
             maxAge = xcontents.Attribute('max_age')
             maxAge = xcontents.Attribute('max_age')
             if maxAge:
             if maxAge:
                 self.maxAge = int(maxAge)
                 self.maxAge = int(maxAge)
+
+            self.contentsSeq.loadXml(xcontents)
                 
                 
             xhost = xcontents.FirstChildElement('host')
             xhost = xcontents.FirstChildElement('host')
             if xhost:
             if xhost:
@@ -3212,6 +3259,9 @@ class Packager:
         xcontents = TiXmlElement('contents')
         xcontents = TiXmlElement('contents')
         if self.maxAge:
         if self.maxAge:
             xcontents.SetAttribute('max_age', str(self.maxAge))
             xcontents.SetAttribute('max_age', str(self.maxAge))
+
+        self.contentsSeq += 1
+        self.contentsSeq.storeXml(xcontents)
             
             
         if self.host:
         if self.host:
             he = self.hosts.get(self.host, None)
             he = self.hosts.get(self.host, None)

+ 40 - 8
direct/src/p3d/PatchMaker.py

@@ -1,4 +1,5 @@
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.FileSpec import FileSpec
+from direct.p3d.SeqValue import SeqValue
 from pandac.PandaModules import *
 from pandac.PandaModules import *
 import copy
 import copy
 
 
@@ -427,14 +428,15 @@ class PatchMaker:
 
 
                 if doProcessing:
                 if doProcessing:
                     newCompressedFilename = '%s.%s.pz' % (self.currentFile.filename, self.patchVersion)
                     newCompressedFilename = '%s.%s.pz' % (self.currentFile.filename, self.patchVersion)
-                    oldCompressedPathname = Filename(self.packageDir, oldCompressedFilename)
-                    newCompressedPathname = Filename(self.packageDir, newCompressedFilename)
-                    if oldCompressedPathname.renameTo(newCompressedPathname):
-                        compressedFile.fromFile(self.packageDir, newCompressedFilename)
-                        compressedFile.storeXml(xcompressed)
+                    if newCompressedFilename != oldCompressedFilename:
+                        oldCompressedPathname = Filename(self.packageDir, oldCompressedFilename)
+                        newCompressedPathname = Filename(self.packageDir, newCompressedFilename)
+                        if oldCompressedPathname.renameTo(newCompressedPathname):
+                            compressedFile.fromFile(self.packageDir, newCompressedFilename)
+                            compressedFile.storeXml(xcompressed)
 
 
-                    self.compressedFilename = newCompressedFilename
-                    self.anyChanges = True
+                        self.compressedFilename = newCompressedFilename
+                        self.anyChanges = True
 
 
             # Get the base_version--the bottom (oldest) of the patch
             # Get the base_version--the bottom (oldest) of the patch
             # chain.
             # chain.
@@ -484,6 +486,11 @@ class PatchMaker:
             if not xpackage:
             if not xpackage:
                 return
                 return
 
 
+            packageSeq = SeqValue()
+            packageSeq.loadXml(xpackage)
+            packageSeq += 1
+            packageSeq.storeXml(xpackage)
+
             # Remove all of the old patch entries from the desc file
             # Remove all of the old patch entries from the desc file
             # we read earlier.
             # we read earlier.
             xremove = []
             xremove = []
@@ -516,6 +523,19 @@ class PatchMaker:
 
 
             self.doc.SaveFile()
             self.doc.SaveFile()
 
 
+            # Also copy the seq to the import desc file, for
+            # documentation purposes.
+
+            importDescFullpath = Filename(self.patchMaker.installDir, self.packageDesc.cStr()[:-3] + 'import.xml')
+            doc = TiXmlDocument(importDescFullpath.toOsSpecific())
+            if doc.LoadFile():
+                xpackage = doc.FirstChildElement('package')
+                if xpackage:
+                    packageSeq.storeXml(xpackage)
+                    doc.SaveFile()
+            else:
+                print "Couldn't read %s" % (importDescFullpath)
+
             if self.contentsDocPackage:
             if self.contentsDocPackage:
                 # Now that we've rewritten the xml file, we have to
                 # Now that we've rewritten the xml file, we have to
                 # change the contents.xml file that references it to
                 # change the contents.xml file that references it to
@@ -523,7 +543,14 @@ class PatchMaker:
                 fileSpec = FileSpec()
                 fileSpec = FileSpec()
                 fileSpec.fromFile(self.patchMaker.installDir, self.packageDesc)
                 fileSpec.fromFile(self.patchMaker.installDir, self.packageDesc)
                 fileSpec.storeXml(self.contentsDocPackage)
                 fileSpec.storeXml(self.contentsDocPackage)
-            
+
+                # Also copy the package seq value into the
+                # contents.xml file, mainly for documentation purposes
+                # (the authoritative seq value is within the desc
+                # file).
+                packageSeq.storeXml(self.contentsDocPackage)
+
+
     # PatchMaker constructor.
     # PatchMaker constructor.
     def __init__(self, installDir):
     def __init__(self, installDir):
         self.installDir = installDir
         self.installDir = installDir
@@ -601,6 +628,11 @@ class PatchMaker:
 
 
         xcontents = doc.FirstChildElement('contents')
         xcontents = doc.FirstChildElement('contents')
         if xcontents:
         if xcontents:
+            contentsSeq = SeqValue()
+            contentsSeq.loadXml(xcontents)
+            contentsSeq += 1
+            contentsSeq.storeXml(xcontents)
+            
             xpackage = xcontents.FirstChildElement('package')
             xpackage = xcontents.FirstChildElement('package')
             while xpackage:
             while xpackage:
                 solo = xpackage.Attribute('solo')
                 solo = xpackage.Attribute('solo')

+ 52 - 0
direct/src/p3d/SeqValue.py

@@ -0,0 +1,52 @@
+class SeqValue:
+
+    """ This represents a sequence value read from a contents.xml
+    file, either from the <contents> or the <package> section.  It's
+    represented as series of dotted integers in the xml file, and
+    stored internally as a tuple of integers.
+
+    It may be incremented, which increments only the last integer in
+    the series; or it may be compared with another SeqValue, which
+    compares all of the integers componentwise. """
+
+    def __init__(self, *value):
+        self.value = value
+        if not self.value:
+            self.value = (0,)
+
+    def loadXml(self, xelement):
+        """ Reads the seq from the indicated XML element.  Returns
+        true if loaded, false if not given or if there was an
+        error. """
+
+        self.value = (0,)
+        value = xelement.Attribute('seq')
+        if value:
+            value = value.split('.')
+            try:
+                value = map(int, value)
+            except ValueError:
+                value = None
+        if value:
+            self.value = tuple(value)
+            return True
+        return False
+
+
+    def storeXml(self, xelement):
+        """ Adds the seq to the indicated XML element. """
+        value = '.'.join(map(str, self.value))
+        xelement.SetAttribute('seq', value)
+
+    def __add__(self, inc):
+        """ Increments the seq value, returning the new value. """
+        value = self.value[:-1] + (self.value[-1] + inc,)
+        return SeqValue(*value)
+
+    def __cmp__(self, other):
+        """ Compares to another seq value. """
+        return cmp(self.value, other.value)
+
+    def __str__(self):
+        return 'SeqValue%s' % (repr(self.value))
+    

+ 9 - 5
direct/src/plugin/p3dHost.I

@@ -91,16 +91,20 @@ has_contents_file() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DHost::get_contents_seq
+//     Function: P3DHost::get_contents_iseq
 //       Access: Public
 //       Access: Public
 //  Description: Returns a number that increments whenever a new
 //  Description: Returns a number that increments whenever a new
 //               version of the contents.xml file has been read.  This
 //               version of the contents.xml file has been read.  This
-//               can be used by packages to determine whether they
-//               need to redownload from scratch.
+//               number is local to the session only; it has nothing
+//               to do with the "seq" value written into the
+//               contents.xml file itself.  
+//
+//               This can be used by packages to determine whether
+//               they need to redownload from scratch.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 inline int P3DHost::
 inline int P3DHost::
-get_contents_seq() const {
-  return _contents_seq;
+get_contents_iseq() const {
+  return _contents_iseq;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 106 - 4
direct/src/plugin/p3dHost.cxx

@@ -41,7 +41,7 @@ P3DHost(const string &host_url) :
 
 
   _xcontents = NULL;
   _xcontents = NULL;
   _contents_expiration = 0;
   _contents_expiration = 0;
-  _contents_seq = 0;
+  _contents_iseq = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -171,7 +171,7 @@ read_contents_file(const string &contents_filename, bool fresh_download) {
     delete _xcontents;
     delete _xcontents;
   }
   }
   _xcontents = (TiXmlElement *)xcontents->Clone();
   _xcontents = (TiXmlElement *)xcontents->Clone();
-  ++_contents_seq;
+  ++_contents_iseq;
   _contents_spec = FileSpec();
   _contents_spec = FileSpec();
 
 
   int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE;
   int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE;
@@ -336,17 +336,24 @@ read_xhost(TiXmlElement *xhost) {
 //       Access: Public
 //       Access: Public
 //  Description: Returns a (possibly shared) pointer to the indicated
 //  Description: Returns a (possibly shared) pointer to the indicated
 //               package.
 //               package.
+//
+//               The package_seq value should be the expected minimum
+//               package_seq value for the indicated package.  If the
+//               given seq value is higher than the package_seq value
+//               in the contents.xml file cached for the host, it is a
+//               sign that the contents.xml file is out of date and
+//               needs to be redownloaded.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3DPackage *P3DHost::
 P3DPackage *P3DHost::
 get_package(const string &package_name, const string &package_version,
 get_package(const string &package_name, const string &package_version,
-            const string &alt_host) {
+            const string &package_seq, const string &alt_host) {
   if (!alt_host.empty()) {
   if (!alt_host.empty()) {
     if (_xcontents != NULL) {
     if (_xcontents != NULL) {
       // If we're asking for an alt host and we've already read our
       // If we're asking for an alt host and we've already read our
       // contents.xml file, then we already know all of our hosts, and
       // contents.xml file, then we already know all of our hosts, and
       // we can start the package off with the correct host immediately.
       // we can start the package off with the correct host immediately.
       P3DHost *new_host = get_alt_host(alt_host);
       P3DHost *new_host = get_alt_host(alt_host);
-      return new_host->get_package(package_name, package_version);
+      return new_host->get_package(package_name, package_version, package_seq);
     }
     }
 
 
     // If we haven't read contents.xml yet, we need to create the
     // If we haven't read contents.xml yet, we need to create the
@@ -379,6 +386,27 @@ get_package(const string &package_name, const string &package_version,
       new P3DPackage(this, package_name, package_version, alt_host);
       new P3DPackage(this, package_name, package_version, alt_host);
     package_map[key] = package;
     package_map[key] = package;
   }
   }
+
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  if (!package_seq.empty() && has_current_contents_file(inst_mgr)) {
+    // If we were given a specific package_seq file to verify, and we
+    // believe we have a valid contents.xml file, then check the seq
+    // value in the contents.
+    FileSpec desc_file;
+    string platform, seq;
+    bool solo;
+    if (get_package_desc_file(desc_file, platform, seq, solo,
+                              package_name, package_version)) {
+      nout << package_name << ": asked for seq " << package_seq
+           << ", we have seq " << seq << "\n";
+      if (compare_seq(package_seq, seq) > 0) {
+        // The requested seq value is higher than the one we have on
+        // file; our contents.xml file must be out of date after all.
+        nout << "expiring contents.xml for " << get_host_url() << "\n";
+        _contents_expiration = 0;
+      }
+    }
+  }
     
     
   return package;
   return package;
 }
 }
@@ -396,6 +424,7 @@ get_package(const string &package_name, const string &package_version,
 bool P3DHost::
 bool P3DHost::
 get_package_desc_file(FileSpec &desc_file,              // out
 get_package_desc_file(FileSpec &desc_file,              // out
                       string &package_platform,         // out
                       string &package_platform,         // out
+                      string &package_seq,              // out
                       bool &package_solo,               // out
                       bool &package_solo,               // out
                       const string &package_name,       // in
                       const string &package_name,       // in
                       const string &package_version) {  // in
                       const string &package_version) {  // in
@@ -412,16 +441,21 @@ get_package_desc_file(FileSpec &desc_file,              // out
     const char *name = xpackage->Attribute("name");
     const char *name = xpackage->Attribute("name");
     const char *platform = xpackage->Attribute("platform");
     const char *platform = xpackage->Attribute("platform");
     const char *version = xpackage->Attribute("version");
     const char *version = xpackage->Attribute("version");
+    const char *seq = xpackage->Attribute("seq");
     const char *solo = xpackage->Attribute("solo");
     const char *solo = xpackage->Attribute("solo");
     if (version == NULL) {
     if (version == NULL) {
       version = "";
       version = "";
     }
     }
+    if (seq == NULL) {
+      seq = "";
+    }
     if (name != NULL && platform != NULL &&
     if (name != NULL && platform != NULL &&
         package_name == name && 
         package_name == name && 
         inst_mgr->get_platform() == platform &&
         inst_mgr->get_platform() == platform &&
         package_version == version) {
         package_version == version) {
       // Here's the matching package definition.
       // Here's the matching package definition.
       desc_file.load_xml(xpackage);
       desc_file.load_xml(xpackage);
+      package_seq = seq;
       package_platform = platform;
       package_platform = platform;
       package_solo = false;
       package_solo = false;
       if (solo != NULL) {
       if (solo != NULL) {
@@ -439,6 +473,7 @@ get_package_desc_file(FileSpec &desc_file,              // out
     const char *name = xpackage->Attribute("name");
     const char *name = xpackage->Attribute("name");
     const char *platform = xpackage->Attribute("platform");
     const char *platform = xpackage->Attribute("platform");
     const char *version = xpackage->Attribute("version");
     const char *version = xpackage->Attribute("version");
+    const char *seq = xpackage->Attribute("seq");
     const char *solo = xpackage->Attribute("solo");
     const char *solo = xpackage->Attribute("solo");
     if (platform == NULL) {
     if (platform == NULL) {
       platform = "";
       platform = "";
@@ -446,12 +481,16 @@ get_package_desc_file(FileSpec &desc_file,              // out
     if (version == NULL) {
     if (version == NULL) {
       version = "";
       version = "";
     }
     }
+    if (seq == NULL) {
+      seq = "";
+    }
     if (name != NULL &&
     if (name != NULL &&
         package_name == name && 
         package_name == name && 
         *platform == '\0' &&
         *platform == '\0' &&
         package_version == version) {
         package_version == version) {
       // Here's the matching package definition.
       // Here's the matching package definition.
       desc_file.load_xml(xpackage);
       desc_file.load_xml(xpackage);
+      package_seq = seq;
       package_platform = platform;
       package_platform = platform;
       package_solo = false;
       package_solo = false;
       if (solo != NULL) {
       if (solo != NULL) {
@@ -805,3 +844,66 @@ save_xml_file(TiXmlDocument *doc, const string &to_filename) {
   unlink(temp_filename.c_str());
   unlink(temp_filename.c_str());
   return false;
   return false;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DHost::compare_seq
+//       Access: Private, Static
+//  Description: Compares the two dotted-integer sequence values
+//               numerically.  Returns -1 if seq_a sorts first, 1 if
+//               seq_b sorts first, 0 if they are equivalent.
+////////////////////////////////////////////////////////////////////
+int P3DHost::
+compare_seq(const string &seq_a, const string &seq_b) {
+  const char *num_a = seq_a.c_str();
+  const char *num_b = seq_b.c_str();
+  int comp = compare_seq_int(num_a, num_b);
+  while (comp == 0) {
+    if (*num_a != '.') {
+      if (*num_b != '.') {
+        // Both strings ran out together.
+        return 0;
+      }
+      // a ran out first.
+      return -1;
+    } else if (*num_b != '.') {
+      // b ran out first.
+      return 1;
+    }
+
+    // Increment past the dot.
+    ++num_a;
+    ++num_b;
+    comp = compare_seq(num_a, num_b);
+  }
+
+  return comp;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DHost::compare_seq_int
+//       Access: Private, Static
+//  Description: Numerically compares the formatted integer value at
+//               num_a with num_b.  Increments both num_a and num_b to
+//               the next character following the valid integer.
+////////////////////////////////////////////////////////////////////
+int P3DHost::
+compare_seq_int(const char *&num_a, const char *&num_b) {
+  long int a;
+  char *next_a;
+  long int b;
+  char *next_b;
+
+  a = strtol((char *)num_a, &next_a, 10);
+  b = strtol((char *)num_b, &next_b, 10);
+
+  num_a = next_a;
+  num_b = next_b;
+
+  if (a < b) {
+    return -1;
+  } else if (b < a) {
+    return 1;
+  } else {
+    return 0;
+  }
+}

+ 6 - 2
direct/src/plugin/p3dHost.h

@@ -44,7 +44,7 @@ public:
 
 
   inline bool has_contents_file() const;
   inline bool has_contents_file() const;
   bool has_current_contents_file(P3DInstanceManager *inst_mgr) const;
   bool has_current_contents_file(P3DInstanceManager *inst_mgr) const;
-  inline int get_contents_seq() const;
+  inline int get_contents_iseq() const;
   inline bool check_contents_hash(const string &pathname) const;
   inline bool check_contents_hash(const string &pathname) const;
 
 
   bool read_contents_file();
   bool read_contents_file();
@@ -53,9 +53,11 @@ public:
 
 
   P3DPackage *get_package(const string &package_name, 
   P3DPackage *get_package(const string &package_name, 
                           const string &package_version,
                           const string &package_version,
+                          const string &package_seq,
                           const string &alt_host = "");
                           const string &alt_host = "");
   bool get_package_desc_file(FileSpec &desc_file, 
   bool get_package_desc_file(FileSpec &desc_file, 
                              string &package_platform,
                              string &package_platform,
+                             string &package_seq,
                              bool &package_solo,
                              bool &package_solo,
                              const string &package_name,
                              const string &package_name,
                              const string &package_version);
                              const string &package_version);
@@ -74,6 +76,8 @@ private:
   static string standardize_filename(const string &filename);
   static string standardize_filename(const string &filename);
   static bool copy_file(const string &from_filename, const string &to_filename);
   static bool copy_file(const string &from_filename, const string &to_filename);
   static bool save_xml_file(TiXmlDocument *doc, const string &to_filename);
   static bool save_xml_file(TiXmlDocument *doc, const string &to_filename);
+  static int compare_seq(const string &seq_a, const string &seq_b);
+  static int compare_seq_int(const char *&num_a, const char *&num_b);
 
 
 private:
 private:
   string _host_dir;
   string _host_dir;
@@ -83,7 +87,7 @@ private:
   string _descriptive_name;
   string _descriptive_name;
   TiXmlElement *_xcontents;
   TiXmlElement *_xcontents;
   time_t _contents_expiration;
   time_t _contents_expiration;
-  int _contents_seq;
+  int _contents_iseq;
   FileSpec _contents_spec;
   FileSpec _contents_spec;
 
 
   typedef vector<string> Mirrors;
   typedef vector<string> Mirrors;

+ 21 - 8
direct/src/plugin/p3dInstance.cxx

@@ -232,7 +232,7 @@ P3DInstance(P3D_request_ready_func *func,
   // put up a splash image (for instance, the above IT_download image)
   // put up a splash image (for instance, the above IT_download image)
   // while we download the real contents.
   // while we download the real contents.
   P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
   P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
-  _image_package = host->get_package("images", "");
+  _image_package = host->get_package("images", "", "");
   if (_image_package != NULL) {
   if (_image_package != NULL) {
     _image_package->add_instance(this);
     _image_package->add_instance(this);
   }
   }
@@ -758,7 +758,7 @@ get_request() {
         P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
         P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
         P3DHost *host = inst_mgr->get_host(host_url);
         P3DHost *host = inst_mgr->get_host(host_url);
         if (package_name != NULL) {
         if (package_name != NULL) {
-          P3DPackage *package = host->get_package(package_name, package_version);
+          P3DPackage *package = host->get_package(package_name, package_version, "");
           host->forget_package(package);
           host->forget_package(package);
         } else {
         } else {
           // If a NULL package name is given, forget the whole host.
           // If a NULL package name is given, forget the whole host.
@@ -1021,9 +1021,17 @@ get_log_pathname() const {
 //               instance.  The instance will share responsibility for
 //               instance.  The instance will share responsibility for
 //               downloading the package with any of the other
 //               downloading the package with any of the other
 //               instances that use the same package.
 //               instances that use the same package.
+//
+//               The seq value should be the expected minimum
+//               package_seq value for the indicated package.  If the
+//               given seq value is higher than the package_seq value
+//               in the contents.xml file cached for the host, it is a
+//               sign that the contents.xml file is out of date and
+//               needs to be redownloaded.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 void P3DInstance::
-add_package(const string &name, const string &version, P3DHost *host) {
+add_package(const string &name, const string &version, const string &seq,
+            P3DHost *host) {
   string alt_host = _fparams.lookup_token("alt_host");
   string alt_host = _fparams.lookup_token("alt_host");
 
 
   // Look up in the p3d_info.xml file to see if this p3d file has
   // Look up in the p3d_info.xml file to see if this p3d file has
@@ -1043,7 +1051,7 @@ add_package(const string &name, const string &version, P3DHost *host) {
     get_host_info(host);
     get_host_info(host);
   }
   }
   
   
-  P3DPackage *package = host->get_package(name, version, alt_host);
+  P3DPackage *package = host->get_package(name, version, seq, alt_host);
   add_package(package);
   add_package(package);
 }
 }
     
     
@@ -1859,7 +1867,7 @@ check_p3d_signature() {
     // We have to go download this package.
     // We have to go download this package.
     P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
     P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
     P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
     P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
-    _certlist_package = host->get_package("certlist", "");
+    _certlist_package = host->get_package("certlist", "", "");
     if (_certlist_package != NULL) {
     if (_certlist_package != NULL) {
       _certlist_package->add_instance(this);
       _certlist_package->add_instance(this);
     }
     }
@@ -1900,7 +1908,7 @@ mark_p3d_untrusted() {
     // We have to go download this package.
     // We have to go download this package.
     P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
     P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
     P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
     P3DHost *host = inst_mgr->get_host(inst_mgr->get_host_url());
-    _p3dcert_package = host->get_package("p3dcert", "");
+    _p3dcert_package = host->get_package("p3dcert", "", "");
     if (_p3dcert_package != NULL) {
     if (_p3dcert_package != NULL) {
       _p3dcert_package->add_instance(this);
       _p3dcert_package->add_instance(this);
     }
     }
@@ -2123,8 +2131,12 @@ add_packages() {
       if (version == NULL) {
       if (version == NULL) {
         version = "";
         version = "";
       }
       }
+      const char *seq = xrequires->Attribute("seq");
+      if (seq == NULL) {
+        seq = "";
+      }
       P3DHost *host = inst_mgr->get_host(host_url);
       P3DHost *host = inst_mgr->get_host(host_url);
-      add_package(name, version, host);
+      add_package(name, version, seq, host);
     }
     }
 
 
     xrequires = xrequires->NextSiblingElement("requires");
     xrequires = xrequires->NextSiblingElement("requires");
@@ -2876,7 +2888,8 @@ report_package_info_ready(P3DPackage *package) {
   P3DPackage::Requires::const_iterator ri;
   P3DPackage::Requires::const_iterator ri;
   for (ri = package->_requires.begin(); ri != package->_requires.end(); ++ri) {
   for (ri = package->_requires.begin(); ri != package->_requires.end(); ++ri) {
     const P3DPackage::RequiredPackage &rp = (*ri);
     const P3DPackage::RequiredPackage &rp = (*ri);
-    add_package(rp._package_name, rp._package_version, rp._host);
+    add_package(rp._package_name, rp._package_version, rp._package_seq, 
+                rp._host);
   }
   }
 
 
   if (get_packages_info_ready()) {
   if (get_packages_info_ready()) {

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

@@ -93,7 +93,8 @@ 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(const string &name, const string &version, 
+                   const string &seq, P3DHost *host);
   void add_package(P3DPackage *package);
   void add_package(P3DPackage *package);
   void remove_package(P3DPackage *package);
   void remove_package(P3DPackage *package);
   bool get_packages_info_ready() const;
   bool get_packages_info_ready() const;

+ 2 - 1
direct/src/plugin/p3dPackage.I

@@ -210,9 +210,10 @@ report_step_progress() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 inline P3DPackage::RequiredPackage::
 inline P3DPackage::RequiredPackage::
 RequiredPackage(const string &package_name, const string &package_version,
 RequiredPackage(const string &package_name, const string &package_version,
-                P3DHost *host) :
+                const string &package_seq, P3DHost *host) :
   _package_name(package_name),
   _package_name(package_name),
   _package_version(package_version),
   _package_version(package_version),
+  _package_seq(package_seq),
   _host(host)
   _host(host)
 {
 {
 }
 }

+ 15 - 10
direct/src/plugin/p3dPackage.cxx

@@ -59,7 +59,7 @@ P3DPackage(P3DHost *host, const string &package_name,
   // file, instead of an xml file and a multifile to unpack.
   // file, instead of an xml file and a multifile to unpack.
   _package_solo = false;
   _package_solo = false;
 
 
-  _host_contents_seq = 0;
+  _host_contents_iseq = 0;
 
 
   _xconfig = NULL;
   _xconfig = NULL;
   _temp_contents_file = NULL;
   _temp_contents_file = NULL;
@@ -474,8 +474,8 @@ redownload_contents_file(P3DPackage::Download *download) {
   assert(_active_download == NULL);
   assert(_active_download == NULL);
   assert(_saved_download == NULL);
   assert(_saved_download == NULL);
   
   
-  if (_host->get_contents_seq() != _host_contents_seq) {
-    // If the contents_seq number has changed, we don't even need to
+  if (_host->get_contents_iseq() != _host_contents_iseq) {
+    // If the contents_iseq number has changed, we don't even need to
     // download anything--just go restart the download.
     // download anything--just go restart the download.
     host_got_contents_file();
     host_got_contents_file();
     return;
     return;
@@ -504,8 +504,8 @@ void P3DPackage::
 contents_file_redownload_finished(bool success) {
 contents_file_redownload_finished(bool success) {
   bool contents_changed = false;
   bool contents_changed = false;
   
   
-  if (_host->get_contents_seq() != _host_contents_seq) {
-    // If the contents_seq number has changed, we don't even need to
+  if (_host->get_contents_iseq() != _host_contents_iseq) {
+    // If the contents_iseq number has changed, we don't even need to
     // bother reading what we just downloaded.
     // bother reading what we just downloaded.
     contents_changed = true;
     contents_changed = true;
   }
   }
@@ -592,7 +592,7 @@ host_got_contents_file() {
   // Record this now, so we'll know later whether the host has been
   // Record this now, so we'll know later whether the host has been
   // reloaded (e.g. due to some other package, from some other
   // reloaded (e.g. due to some other package, from some other
   // instance, reloading it).
   // instance, reloading it).
-  _host_contents_seq = _host->get_contents_seq();
+  _host_contents_iseq = _host->get_contents_iseq();
 
 
   // Now that we have a valid host, we can define the _package_dir.
   // Now that we have a valid host, we can define the _package_dir.
   _package_dir = _host->get_host_dir() + string("/") + _package_name;
   _package_dir = _host->get_host_dir() + string("/") + _package_name;
@@ -619,8 +619,9 @@ download_desc_file() {
   // Attempt to check the desc file for freshness.  If it already
   // Attempt to check the desc file for freshness.  If it already
   // exists, and is consistent with the server contents file, we don't
   // exists, and is consistent with the server contents file, we don't
   // need to re-download it.
   // need to re-download it.
+  string package_seq;
   if (!_host->get_package_desc_file(_desc_file, _package_platform, 
   if (!_host->get_package_desc_file(_desc_file, _package_platform, 
-                                    _package_solo,
+                                    package_seq, _package_solo,
                                     _package_name, _package_version)) {
                                     _package_name, _package_version)) {
     nout << "Couldn't find package " << _package_fullname
     nout << "Couldn't find package " << _package_fullname
          << " in contents file.\n";
          << " in contents file.\n";
@@ -768,14 +769,18 @@ got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
   TiXmlElement *xrequires = xpackage->FirstChildElement("requires");
   TiXmlElement *xrequires = xpackage->FirstChildElement("requires");
   while (xrequires != NULL) {
   while (xrequires != NULL) {
     const char *package_name = xrequires->Attribute("name");
     const char *package_name = xrequires->Attribute("name");
-    const char *version = xrequires->Attribute("version");
     const char *host_url = xrequires->Attribute("host");
     const char *host_url = xrequires->Attribute("host");
     if (package_name != NULL && host_url != NULL) {
     if (package_name != NULL && host_url != NULL) {
-      P3DHost *host = inst_mgr->get_host(host_url);
+      const char *version = xrequires->Attribute("version");
       if (version == NULL) {
       if (version == NULL) {
         version = "";
         version = "";
       }
       }
-      _requires.push_back(RequiredPackage(package_name, version, host));
+      const char *seq = xrequires->Attribute("seq");
+      if (seq == NULL) {
+        seq = "";
+      }
+      P3DHost *host = inst_mgr->get_host(host_url);
+      _requires.push_back(RequiredPackage(package_name, version, seq, host));
     }
     }
 
 
     xrequires = xrequires->NextSiblingElement("requires");
     xrequires = xrequires->NextSiblingElement("requires");

+ 3 - 1
direct/src/plugin/p3dPackage.h

@@ -245,9 +245,11 @@ public:
   public:
   public:
     inline RequiredPackage(const string &package_name,
     inline RequiredPackage(const string &package_name,
                            const string &package_version,
                            const string &package_version,
+                           const string &package_seq,
                            P3DHost *host);
                            P3DHost *host);
     string _package_name;
     string _package_name;
     string _package_version;
     string _package_version;
+    string _package_seq;
     P3DHost *_host;
     P3DHost *_host;
   };
   };
   typedef vector<RequiredPackage> Requires;
   typedef vector<RequiredPackage> Requires;
@@ -255,7 +257,7 @@ public:
 
 
 private:
 private:
   P3DHost *_host;
   P3DHost *_host;
-  int _host_contents_seq;
+  int _host_contents_iseq;
 
 
   string _package_name;
   string _package_name;
   string _package_version;
   string _package_version;