Browse Source

more max_age stuff

David Rose 15 years ago
parent
commit
1b02e24408

+ 33 - 13
direct/src/p3d/AppRunner.py

@@ -74,6 +74,14 @@ class AppRunner(DirectObject):
 
 
     # Default values for parameters that are absent from the config file:
     # Default values for parameters that are absent from the config file:
     maxDiskUsage = 2048 * 1048576  # 2 GB
     maxDiskUsage = 2048 * 1048576  # 2 GB
+
+    # Values for verifyContents, from p3d_plugin.h
+    P3DVCNone = 0
+    P3DVCNormal = 1
+    P3DVCForce = 2
+
+    # Also from p3d_plugin.h
+    P3D_CONTENTS_DEFAULT_MAX_AGE = 5
     
     
     def __init__(self):
     def __init__(self):
         DirectObject.__init__(self)
         DirectObject.__init__(self)
@@ -96,6 +104,8 @@ class AppRunner(DirectObject):
         self.initialAppImport = False
         self.initialAppImport = False
         self.trueFileIO = False
         self.trueFileIO = False
 
 
+        self.verifyContents = self.P3DVCNone
+
         self.sessionId = 0
         self.sessionId = 0
         self.packedAppEnvironmentInitialized = False
         self.packedAppEnvironmentInitialized = False
         self.gotWindow = False
         self.gotWindow = False
@@ -272,6 +282,7 @@ class AppRunner(DirectObject):
         parameter, nested, is a list of packages that we are
         parameter, nested, is a list of packages that we are
         recursively calling this from, to avoid recursive loops. """
         recursively calling this from, to avoid recursive loops. """
 
 
+        package.checkStatus()
         if not package.downloadDescFile(self.http):
         if not package.downloadDescFile(self.http):
             return False
             return False
 
 
@@ -334,7 +345,7 @@ class AppRunner(DirectObject):
         # No shenanigans, just return the requested host.
         # No shenanigans, just return the requested host.
         return host
         return host
         
         
-    def getHost(self, hostUrl):
+    def getHost(self, hostUrl, hostDir = None):
         """ Returns a new HostInfo object corresponding to the
         """ Returns a new HostInfo object corresponding to the
         indicated host URL.  If we have already seen this URL
         indicated host URL.  If we have already seen this URL
         previously, returns the same object.
         previously, returns the same object.
@@ -348,7 +359,7 @@ class AppRunner(DirectObject):
 
 
         host = self.hosts.get(hostUrl, None)
         host = self.hosts.get(hostUrl, None)
         if not host:
         if not host:
-            host = HostInfo(hostUrl, appRunner = self)
+            host = HostInfo(hostUrl, appRunner = self, hostDir = hostDir)
             self.hosts[hostUrl] = host
             self.hosts[hostUrl] = host
         return host
         return host
 
 
@@ -361,9 +372,10 @@ class AppRunner(DirectObject):
         host directory cannot be read or doesn't seem consistent. """
         host directory cannot be read or doesn't seem consistent. """
 
 
         host = HostInfo(None, hostDir = hostDir, appRunner = self)
         host = HostInfo(None, hostDir = hostDir, appRunner = self)
-        if not host.readContentsFile():
-            # Couldn't read the contents.xml file
-            return None
+        if not host.hasContentsFile:
+            if not host.readContentsFile():
+                # Couldn't read the contents.xml file
+                return None
 
 
         if not host.hostUrl:
         if not host.hostUrl:
             # The contents.xml file there didn't seem to indicate the
             # The contents.xml file there didn't seem to indicate the
@@ -754,7 +766,8 @@ class AppRunner(DirectObject):
                                needsResponse = False)
                                needsResponse = False)
         self.deferredEvals = []
         self.deferredEvals = []
 
 
-    def setInstanceInfo(self, rootDir, logDirectory, superMirrorUrl):
+    def setInstanceInfo(self, rootDir, logDirectory, superMirrorUrl,
+                        verifyContents):
         """ Called by the browser to set some global information about
         """ Called by the browser to set some global information about
         the instance. """
         the instance. """
 
 
@@ -772,6 +785,10 @@ class AppRunner(DirectObject):
         # The "super mirror" URL, generally used only by panda3d.exe.
         # The "super mirror" URL, generally used only by panda3d.exe.
         self.superMirrorUrl = superMirrorUrl
         self.superMirrorUrl = superMirrorUrl
 
 
+        # How anxious should we be about contacting the server for
+        # the latest code?
+        self.verifyContents = verifyContents
+
         # Now that we have rootDir, we can read the config file.
         # Now that we have rootDir, we can read the config file.
         self.readConfigXml()
         self.readConfigXml()
         
         
@@ -782,14 +799,17 @@ class AppRunner(DirectObject):
         If for some reason the package isn't already downloaded, this
         If for some reason the package isn't already downloaded, this
         will download it on the spot.  Raises OSError on failure. """
         will download it on the spot.  Raises OSError on failure. """
 
 
-        host = self.getHost(hostUrl)
-        if hostDir and not host.hostDir:
-            host.hostDir = Filename.fromOsSpecific(hostDir)
+        host = self.getHost(hostUrl, hostDir = hostDir)
+
+        if not host.hasContentsFile:
+            # Always pre-read these hosts' contents.xml files, even if
+            # we have P3DVCForce in effect, since presumably we've
+            # already forced them on the plugin side.
+            host.readContentsFile()
 
 
-        if not host.readContentsFile():
-            if not host.downloadContentsFile(self.http):
-                message = "Host %s cannot be downloaded, cannot preload %s." % (hostUrl, name)
-                raise OSError, message
+        if not host.downloadContentsFile(self.http):
+            message = "Host %s cannot be downloaded, cannot preload %s." % (hostUrl, name)
+            raise OSError, message
 
 
         if not platform:
         if not platform:
             platform = None
             platform = None

+ 15 - 12
direct/src/p3d/DeploymentTools.py

@@ -30,10 +30,11 @@ class Standalone:
         self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = base.appRunner, hostDir = hostDir, asMirror = False, perPlatform = True)
         self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = base.appRunner, hostDir = hostDir, asMirror = False, perPlatform = True)
         
         
         self.http = HTTPClient.getGlobalPtr()
         self.http = HTTPClient.getGlobalPtr()
-        if not self.host.readContentsFile():
-            if not self.host.downloadContentsFile(self.http):
-                Standalone.notify.error("couldn't read host")
-                return False
+        if not self.host.hasContentsFile:
+            if not self.host.readContentsFile():
+                if not self.host.downloadContentsFile(self.http):
+                    Standalone.notify.error("couldn't read host")
+                    return False
     
     
     def buildAll(self, outputDir = "."):
     def buildAll(self, outputDir = "."):
         """ Builds standalone executables for every known platform,
         """ Builds standalone executables for every known platform,
@@ -194,10 +195,11 @@ class Installer:
             return
             return
         
         
         host = HostInfo(self.hostUrl, appRunner = base.appRunner, rootDir = rootDir, asMirror = True, perPlatform = False)
         host = HostInfo(self.hostUrl, appRunner = base.appRunner, rootDir = rootDir, asMirror = True, perPlatform = False)
-        if not host.readContentsFile():
-            if not host.downloadContentsFile(self.http):
-                Installer.notify.error("couldn't read host")
-                return
+        if not host.hasContentsFile:
+            if not host.readContentsFile():
+                if not host.downloadContentsFile(self.http):
+                    Installer.notify.error("couldn't read host")
+                    return
         
         
         for name, version in self.requirements:
         for name, version in self.requirements:
             package = host.getPackage(name, version, platform)
             package = host.getPackage(name, version, platform)
@@ -210,10 +212,11 @@ class Installer:
         
         
         # Also install the 'images' package from the same host that p3dembed was downloaded from.
         # Also install the 'images' package from the same host that p3dembed was downloaded from.
         host = HostInfo(self.standalone.host.hostUrl, appRunner = base.appRunner, rootDir = rootDir, asMirror = False, perPlatform = False)
         host = HostInfo(self.standalone.host.hostUrl, appRunner = base.appRunner, rootDir = rootDir, asMirror = False, perPlatform = False)
-        if not host.readContentsFile():
-            if not host.downloadContentsFile(self.http):
-                Installer.notify.error("couldn't read host")
-                return
+        if not host.hasContentsFile:
+            if not host.readContentsFile():
+                if not host.downloadContentsFile(self.http):
+                    Installer.notify.error("couldn't read host")
+                    return
         
         
         for package in host.getPackages(name = "images"):
         for package in host.getPackages(name = "images"):
             if not package.downloadDescFile(self.http):
             if not package.downloadDescFile(self.http):

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

@@ -9,6 +9,10 @@ class FileSpec:
 
 
     def __init__(self):
     def __init__(self):
         self.actualFile = None
         self.actualFile = None
+        self.filename = None
+        self.size = 0
+        self.timestamp = 0
+        self.hash = None
 
 
     def fromFile(self, packageDir, filename, pathname = None, st = None):
     def fromFile(self, packageDir, filename, pathname = None, st = None):
         """ Reads the file information from the indicated file.  If st
         """ Reads the file information from the indicated file.  If st
@@ -28,9 +32,15 @@ class FileSpec:
         self.size = st.st_size
         self.size = st.st_size
         self.timestamp = st.st_mtime
         self.timestamp = st.st_mtime
 
 
+        self.readHash(pathname)
+
+    def readHash(self, pathname):
+        """ Reads the hash only from the indicated pathname. """
+
         hv = HashVal()
         hv = HashVal()
         hv.hashFile(pathname)
         hv.hashFile(pathname)
         self.hash = hv.asHex()
         self.hash = hv.asHex()
+                 
 
 
     def loadXml(self, xelement):
     def loadXml(self, xelement):
         """ Reads the file information from the indicated XML
         """ Reads the file information from the indicated XML

+ 102 - 22
direct/src/p3d/HostInfo.py

@@ -33,7 +33,7 @@ class HostInfo:
         the host directory directly, without an intervening
         the host directory directly, without an intervening
         platform-specific directory name.  If asMirror is True, then
         platform-specific directory name.  If asMirror is True, then
         the default is perPlatform = True. """
         the default is perPlatform = True. """
-        
+
         assert appRunner or rootDir or hostDir
         assert appRunner or rootDir or hostDir
 
 
         self.__setHostUrl(hostUrl)
         self.__setHostUrl(hostUrl)
@@ -41,7 +41,10 @@ class HostInfo:
         self.rootDir = rootDir
         self.rootDir = rootDir
         if rootDir is None and appRunner:
         if rootDir is None and appRunner:
             self.rootDir = appRunner.rootDir
             self.rootDir = appRunner.rootDir
-        
+
+        if hostDir and not isinstance(hostDir, Filename):
+            hostDir = Filename.fromOsSpecific(hostDir)
+            
         self.hostDir = hostDir
         self.hostDir = hostDir
         self.asMirror = asMirror
         self.asMirror = asMirror
         self.perPlatform = perPlatform
         self.perPlatform = perPlatform
@@ -52,6 +55,13 @@ class HostInfo:
         # successfully read.
         # successfully read.
         self.hasContentsFile = False
         self.hasContentsFile = False
 
 
+        # This is the time value at which the current contents file is
+        # no longer valid.
+        self.contentsExpiration = 0
+
+        # Contains the md5 hash of the original contents.xml file.
+        self.contentsSpec = FileSpec()
+
         # descriptiveName will be filled in later, when the
         # descriptiveName will be filled in later, when the
         # contents file is read.
         # contents file is read.
         self.descriptiveName = None
         self.descriptiveName = None
@@ -70,6 +80,11 @@ class HostInfo:
         # will be filled in when the contents file is read.
         # will be filled in when the contents file is read.
         self.packages = {}
         self.packages = {}
 
 
+        if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCForce:
+            # Attempt to pre-read the existing contents.xml; maybe it
+            # will be current enough for our purposes.
+            self.readContentsFile()
+
     def __setHostUrl(self, hostUrl):
     def __setHostUrl(self, hostUrl):
         """ Assigns self.hostUrl, and related values. """
         """ Assigns self.hostUrl, and related values. """
         self.hostUrl = hostUrl
         self.hostUrl = hostUrl
@@ -96,7 +111,7 @@ class HostInfo:
         synchronously, and then reads it.  Returns true on success,
         synchronously, and then reads it.  Returns true on success,
         false on failure. """
         false on failure. """
 
 
-        if self.hasContentsFile:
+        if self.hasCurrentContentsFile():
             # We've already got one.
             # We've already got one.
             return True
             return True
 
 
@@ -145,11 +160,12 @@ class HostInfo:
             f.write(rf.getData())
             f.write(rf.getData())
             f.close()
             f.close()
 
 
-            if not self.readContentsFile(tempFilename):
+            if not self.readContentsFile(tempFilename, freshDownload = True):
                 self.notify.warning("Failure reading %s" % (url))
                 self.notify.warning("Failure reading %s" % (url))
                 tempFilename.unlink()
                 tempFilename.unlink()
                 return False
                 return False
 
 
+            tempFilename.unlink()
             return True
             return True
 
 
         # Couldn't download the file.  Maybe we should look for a
         # Couldn't download the file.  Maybe we should look for a
@@ -167,9 +183,12 @@ class HostInfo:
 
 
         # Get the hash of the original file.
         # Get the hash of the original file.
         assert self.hostDir
         assert self.hostDir
-        filename = Filename(self.hostDir, 'contents.xml')
         hv1 = HashVal()
         hv1 = HashVal()
-        hv1.hashFile(filename)
+        if self.contentsSpec.hash:
+            hv1.setFromHex(self.contentsSpec.hash)
+        else:
+            filename = Filename(self.hostDir, 'contents.xml')
+            hv1.hashFile(filename)
 
 
         # Now download it again.
         # Now download it again.
         self.hasContentsFile = False
         self.hasContentsFile = False
@@ -186,8 +205,18 @@ class HostInfo:
             self.notify.info("%s has not changed." % (url))
             self.notify.info("%s has not changed." % (url))
             return False
             return False
 
 
+    def hasCurrentContentsFile(self):
+        """ Returns true if a contents.xml file has been successfully
+        read for this host and is still current, false otherwise. """
+        if not self.appRunner or self.appRunner.verifyContents == self.appRunner.P3DVCNone:
+            # If we're not asking to verify contents, then
+            # contents.xml files never expires.
+            return self.hasContentsFile
 
 
-    def readContentsFile(self, tempFilename = None):
+        now = int(time.time())
+        return now < self.contentsExpiration and self.hasContentsFile
+        
+    def readContentsFile(self, tempFilename = None, freshDownload = False):
         """ Reads the contents.xml file for this particular host, once
         """ Reads the contents.xml file for this particular host, once
         it has been downloaded into the indicated temporary file.
         it has been downloaded into the indicated temporary file.
         Returns true on success, false if the contents file is not
         Returns true on success, false if the contents file is not
@@ -198,29 +227,77 @@ class HostInfo:
         there already.  If tempFilename is not specified, the standard
         there already.  If tempFilename is not specified, the standard
         filename is read if it is known. """
         filename is read if it is known. """
 
 
-        if self.hasContentsFile:
-            # No need to read it again.
-            return True
-
         if not hasattr(PandaModules, 'TiXmlDocument'):
         if not hasattr(PandaModules, 'TiXmlDocument'):
             return False
             return False
 
 
         if not tempFilename:
         if not tempFilename:
-            if not self.hostDir:
+            if self.hostDir:
                 # If the filename is not specified, we can infer it
                 # If the filename is not specified, we can infer it
-                # only if we already know our hostDir.
-                return False
-            
-            tempFilename = Filename(self.hostDir, 'contents.xml')
-        
+                # if we already know our hostDir
+                hostDir = self.hostDir
+            else:
+                # Otherwise, we have to guess the hostDir.
+                hostDir = self.__determineHostDir(None, self.hostUrl)
+
+            tempFilename = Filename(hostDir, 'contents.xml')
+
         doc = PandaModules.TiXmlDocument(tempFilename.toOsSpecific())
         doc = PandaModules.TiXmlDocument(tempFilename.toOsSpecific())
         if not doc.LoadFile():
         if not doc.LoadFile():
             return False
             return False
-        
+
         xcontents = doc.FirstChildElement('contents')
         xcontents = doc.FirstChildElement('contents')
         if not xcontents:
         if not xcontents:
             return False
             return False
 
 
+        maxAge = xcontents.Attribute('max_age')
+        if maxAge:
+            try:
+                maxAge = int(maxAge)
+            except:
+                maxAge = None
+        if maxAge is None:
+            # Default max_age if unspecified (see p3d_plugin.h).
+            from direct.p3d.AppRunner import AppRunner
+            maxAge = AppRunner.P3D_CONTENTS_DEFAULT_MAX_AGE
+
+        # Get the latest possible expiration time, based on the max_age
+        # indication.  Any expiration time later than this is in error.
+        now = int(time.time())
+        self.contentsExpiration = now + maxAge
+
+        if freshDownload:
+            self.contentsSpec.readHash(tempFilename)
+
+            # Update the XML with the new download information.
+            xorig = xcontents.FirstChildElement('orig')
+            while xorig:
+                xcontents.RemoveChild(xorig)
+                xorig = xcontents.FirstChildElement('orig')
+
+            xorig = PandaModules.TiXmlElement('orig')
+            self.contentsSpec.storeXml(xorig)
+            xorig.SetAttribute('expiration', str(self.contentsExpiration))
+
+            xcontents.InsertEndChild(xorig)
+            
+        else:
+            # Read the download hash and expiration time from the XML.
+            expiration = None
+            xorig = xcontents.FirstChildElement('orig')
+            if xorig:
+                self.contentsSpec.loadXml(xorig)
+                expiration = xorig.Attribute('expiration')
+                if expiration:
+                    try:
+                        expiration = int(expiration)
+                    except:
+                        expiration = None
+            if not self.contentsSpec.hash:
+                self.contentsSpec.readHash(tempFilename)
+
+            if expiration is not None:
+                self.contentsExpiration = min(self.contentsExpiration, expiration)
+
         # Look for our own entry in the hosts table.
         # Look for our own entry in the hosts table.
         if self.hostUrl:
         if self.hostUrl:
             self.__findHostXml(xcontents)
             self.__findHostXml(xcontents)
@@ -257,12 +334,15 @@ class HostInfo:
 
 
         self.hasContentsFile = True
         self.hasContentsFile = True
 
 
-        # Now copy the contents.xml file into the standard location.
+        # Now save the contents.xml file into the standard location.
         assert self.hostDir
         assert self.hostDir
         filename = Filename(self.hostDir, 'contents.xml')
         filename = Filename(self.hostDir, 'contents.xml')
-        if filename != tempFilename:
-            filename.makeDir()
-            tempFilename.copyTo(filename)
+        filename.makeDir()
+        if freshDownload:
+            doc.SaveFile(filename.toOsSpecific())
+        else:
+            if filename != tempFilename:
+                tempFilename.copyTo(filename)
 
 
         return True
         return True
 
 

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

@@ -217,6 +217,16 @@ class PackageInfo:
 
 
         return self.hasPackage
         return self.hasPackage
 
 
+    def hasCurrentDescFile(self):
+        """ Returns true if a desc file file has been successfully
+        read for this package and is still current, false
+        otherwise. """
+
+        if not self.host.hasCurrentContentsFile():
+            return False
+
+        return self.hasDescFile
+
     def downloadDescFile(self, http):
     def downloadDescFile(self, http):
         """ Downloads the desc file for this particular package,
         """ Downloads the desc file for this particular package,
         synchronously, and then reads it.  Returns true on success,
         synchronously, and then reads it.  Returns true on success,

+ 1 - 1
direct/src/p3d/PackageInstaller.py

@@ -98,7 +98,7 @@ class PackageInstaller(DirectObject):
             """ Returns true if the desc file is already downloaded
             """ Returns true if the desc file is already downloaded
             and good, or false if it needs to be downloaded. """
             and good, or false if it needs to be downloaded. """
 
 
-            if not self.host.hasContentsFile:
+            if not self.host.hasCurrentContentsFile():
                 # If the contents file isn't ready yet, we can't check
                 # If the contents file isn't ready yet, we can't check
                 # the desc file yet.
                 # the desc file yet.
                 return False
                 return False

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

@@ -135,7 +135,7 @@ static void unload_dso();
 bool
 bool
 load_plugin(const string &p3d_plugin_filename, 
 load_plugin(const string &p3d_plugin_filename, 
             const string &contents_filename, const string &host_url, 
             const string &contents_filename, const string &host_url, 
-            bool verify_contents, const string &platform,
+            P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             bool trusted_environment, bool console_environment,
             const string &root_dir, ostream &logfile) {
             const string &root_dir, ostream &logfile) {
@@ -274,7 +274,7 @@ load_plugin(const string &p3d_plugin_filename,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool
 bool
 init_plugin(const string &contents_filename, const string &host_url, 
 init_plugin(const string &contents_filename, const string &host_url, 
-            bool verify_contents, const string &platform,
+            P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             bool trusted_environment, bool console_environment,
             const string &root_dir, ostream &logfile) {
             const string &root_dir, ostream &logfile) {

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

@@ -64,13 +64,13 @@ string get_plugin_basename();
 bool 
 bool 
 load_plugin(const string &p3d_plugin_filename, 
 load_plugin(const string &p3d_plugin_filename, 
             const string &contents_filename, const string &host_url,
             const string &contents_filename, const string &host_url,
-            bool verify_contents, const string &platform,
+            P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             bool trusted_environment, bool console_environment,
             const string &root_dir, ostream &logfile);
             const string &root_dir, ostream &logfile);
 bool
 bool
 init_plugin(const string &contents_filename, const string &host_url, 
 init_plugin(const string &contents_filename, const string &host_url, 
-            bool verify_contents, const string &platform,
+            P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             bool trusted_environment, bool console_environment,
             const string &root_dir, ostream &logfile);
             const string &root_dir, ostream &logfile);

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

@@ -116,7 +116,7 @@ get_alt_host(const string &alt_host) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool P3DHost::
 bool P3DHost::
 has_current_contents_file(P3DInstanceManager *inst_mgr) const {
 has_current_contents_file(P3DInstanceManager *inst_mgr) const {
-  if (!inst_mgr->get_verify_contents()) {
+  if (inst_mgr->get_verify_contents() == P3D_VC_none) {
     // If we're not asking to verify contents, then contents.xml files
     // If we're not asking to verify contents, then contents.xml files
     // never expire.
     // never expire.
     return has_contents_file();
     return has_contents_file();

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

@@ -1336,6 +1336,7 @@ 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());
   xinstance->SetAttribute("log_directory", inst_mgr->get_log_directory());
   xinstance->SetAttribute("log_directory", inst_mgr->get_log_directory());
+  xinstance->SetAttribute("verify_contents", (int)inst_mgr->get_verify_contents());
 
 
   if (!inst_mgr->get_super_mirror().empty()) {
   if (!inst_mgr->get_super_mirror().empty()) {
     xinstance->SetAttribute("super_mirror", inst_mgr->get_super_mirror());
     xinstance->SetAttribute("super_mirror", inst_mgr->get_super_mirror());

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

@@ -42,12 +42,13 @@ reconsider_runtime_environment() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::verify_contents
 //     Function: P3DInstanceManager::verify_contents
 //       Access: Public
 //       Access: Public
-//  Description: Returns the verify_contents flag.  When this is set
-//               false, it indicates that we don't need to contact the
-//               server to verify that a contents.xml file is fresh
-//               before using it; we should just use it as it is.
+//  Description: Returns the verify_contents setting.  When this is
+//               set to P3D_VC_none, it indicates that we don't need
+//               to contact the server to verify that a contents.xml
+//               file is fresh before using it; we should just use it
+//               as it is.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-inline bool P3DInstanceManager::
+inline P3D_verify_contents P3DInstanceManager::
 get_verify_contents() const {
 get_verify_contents() const {
   return _verify_contents;
   return _verify_contents;
 }
 }
@@ -55,14 +56,16 @@ get_verify_contents() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::reset_verify_contents
 //     Function: P3DInstanceManager::reset_verify_contents
 //       Access: Public
 //       Access: Public
-//  Description: Resets the verify_contents flag to true.  This should
-//               be done whenever we discover anything needs to be
-//               downloaded.  At this point, we might as well verify
-//               everything.
+//  Description: Resets the verify_contents flag to P3D_VC_normal, if
+//               it is P3D_VC_none.  This should be done whenever we
+//               discover anything needs to be downloaded.  At this
+//               point, we might as well verify everything.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 inline void P3DInstanceManager::
 inline void P3DInstanceManager::
 reset_verify_contents() {
 reset_verify_contents() {
-  _verify_contents = true;
+  if (_verify_contents == P3D_VC_none) {
+    _verify_contents = P3D_VC_normal;
+  }
 }
 }
 
 
 
 

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

@@ -191,7 +191,7 @@ P3DInstanceManager::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool P3DInstanceManager::
 bool P3DInstanceManager::
 initialize(int api_version, const string &contents_filename, 
 initialize(int api_version, const string &contents_filename, 
-           const string &host_url, bool verify_contents,
+           const string &host_url, P3D_verify_contents verify_contents,
            const string &platform, const string &log_directory,
            const string &platform, const string &log_directory,
            const string &log_basename, bool trusted_environment,
            const string &log_basename, bool trusted_environment,
            bool console_environment, const string &root_dir) {
            bool console_environment, const string &root_dir) {

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

@@ -52,7 +52,7 @@ private:
 public:
 public:
   bool initialize(int api_version, const string &contents_filename,
   bool initialize(int api_version, const string &contents_filename,
                   const string &host_url,
                   const string &host_url,
-                  bool verify_contents,
+                  P3D_verify_contents verify_contents,
                   const string &platform,
                   const string &platform,
                   const string &log_directory,
                   const string &log_directory,
                   const string &log_basename,
                   const string &log_basename,
@@ -62,7 +62,7 @@ public:
 
 
   inline bool is_initialized() const;
   inline bool is_initialized() const;
   inline void reconsider_runtime_environment();
   inline void reconsider_runtime_environment();
-  inline bool get_verify_contents() const;
+  inline P3D_verify_contents get_verify_contents() const;
   inline void reset_verify_contents();
   inline void reset_verify_contents();
 
 
   inline int get_api_version() const;
   inline int get_api_version() const;
@@ -163,7 +163,7 @@ private:
   string _host_url;
   string _host_url;
   string _root_dir;
   string _root_dir;
   string _certs_dir;
   string _certs_dir;
-  bool _verify_contents;
+  P3D_verify_contents _verify_contents;
   string _platform;
   string _platform;
   string _log_directory;
   string _log_directory;
   string _log_basename;
   string _log_basename;

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

@@ -392,7 +392,7 @@ begin_info_download() {
 void P3DPackage::
 void P3DPackage::
 download_contents_file() {
 download_contents_file() {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (!_host->has_contents_file()) {
+  if (!_host->has_contents_file() && inst_mgr->get_verify_contents() != P3D_VC_force) {
     // First, read whatever contents file is already on disk.  Maybe
     // First, read whatever contents file is already on disk.  Maybe
     // it's current enough.
     // it's current enough.
     _host->read_contents_file();
     _host->read_contents_file();

+ 5 - 2
direct/src/plugin/p3dPythonRun.cxx

@@ -1219,14 +1219,17 @@ set_instance_info(P3DCInstance *inst, TiXmlElement *xinstance) {
     log_directory = "";
     log_directory = "";
   }
   }
 
 
+  int verify_contents = 0;
+  xinstance->Attribute("verify_contents", &verify_contents);
+
   const char *super_mirror = xinstance->Attribute("super_mirror");
   const char *super_mirror = xinstance->Attribute("super_mirror");
   if (super_mirror == NULL) {
   if (super_mirror == NULL) {
     super_mirror = "";
     super_mirror = "";
   }
   }
 
 
   PyObject *result = PyObject_CallMethod
   PyObject *result = PyObject_CallMethod
-    (_runner, (char *)"setInstanceInfo", (char *)"sss", root_dir, 
-     log_directory, super_mirror);
+    (_runner, (char *)"setInstanceInfo", (char *)"sssi", root_dir, 
+     log_directory, super_mirror, verify_contents);
 
 
   if (result == NULL) {
   if (result == NULL) {
     PyErr_Print();
     PyErr_Print();

+ 11 - 1
direct/src/plugin/p3d_plugin.cxx

@@ -35,7 +35,7 @@ LOCK _api_lock;
 
 
 bool 
 bool 
 P3D_initialize(int api_version, const char *contents_filename,
 P3D_initialize(int api_version, const char *contents_filename,
-               const char *host_url, bool verify_contents,
+               const char *host_url, P3D_verify_contents verify_contents,
                const char *platform, const char *log_directory,
                const char *platform, const char *log_directory,
                const char *log_basename, bool trusted_environment,
                const char *log_basename, bool trusted_environment,
                bool console_environment, const char *root_dir) {
                bool console_environment, const char *root_dir) {
@@ -44,6 +44,16 @@ P3D_initialize(int api_version, const char *contents_filename,
     return false;
     return false;
   }
   }
 
 
+  if (api_version < 13) {
+    // Prior to version 13, verify_contents was a bool.  Convert
+    // "true" to P3D_VC_normal and "false" to P3D_VC_none.
+    if ((int)verify_contents != 0) {
+      verify_contents = P3D_VC_normal;
+    } else {
+      verify_contents = P3D_VC_none;
+    }
+  }
+
   if (!initialized_lock) {
   if (!initialized_lock) {
     INIT_LOCK(_api_lock);
     INIT_LOCK(_api_lock);
     initialized_lock = true;
     initialized_lock = true;

+ 16 - 9
direct/src/plugin/p3d_plugin.h

@@ -79,13 +79,20 @@ extern "C" {
    (below). This number will be incremented whenever there are changes
    (below). This number will be incremented whenever there are changes
    to any of the interface specifications defined in this header
    to any of the interface specifications defined in this header
    file. */
    file. */
-#define P3D_API_VERSION 12
+#define P3D_API_VERSION 13
 
 
 /************************ GLOBAL FUNCTIONS **************************/
 /************************ GLOBAL FUNCTIONS **************************/
 
 
 /* The following interfaces are global to the core API space, as
 /* The following interfaces are global to the core API space, as
    opposed to being specific to a particular instance. */
    opposed to being specific to a particular instance. */
 
 
+/* This is passed for verify_contents, below. */
+typedef enum {
+  P3D_VC_none,
+  P3D_VC_normal,
+  P3D_VC_force,
+} P3D_verify_contents;
+
 /* This function should be called immediately after the core API is
 /* This function should be called immediately after the core API is
    loaded.  You should pass P3D_API_VERSION as the first parameter, so
    loaded.  You should pass P3D_API_VERSION as the first parameter, so
    the DLL can verify that it has been built with the same version of
    the DLL can verify that it has been built with the same version of
@@ -98,13 +105,13 @@ extern "C" {
    If host_url is not NULL or empty, it specifies the root URL of
    If host_url is not NULL or empty, it specifies the root URL of
    the download server that provided the contents_filename.
    the download server that provided the contents_filename.
 
 
-   If verify_contents is true, it means that the download server will
-   be contacted to verify that contents.xml is current, before
-   continuing, for any contents.xml file that is loaded.  If it is
-   false, it means that the contents.xml will be loaded without
-   checking the download server, if possible.  This can be used to
-   minimize unnecessary network operations for standalone
-   applications.  For a web plugin, it should be set true.
+   If verify_contents is P3D_VC_none, then the server will not be
+   contacted unless the current contents.xml cannot be read at all.
+   If it is P3D_VC_normal, the server will be contacted whenever the
+   contents.xml has expired.  If it is P3D_VC_force, each server will
+   be contacted initially in all cases, and subseqeuntly only whenever
+   contents.xml has expired for that server.  Normally, a web plugin
+   should set this to P3D_VC_normal.
 
 
    If platform is not NULL or empty, it specifies the current platform
    If platform is not NULL or empty, it specifies the current platform
    string; otherwise, the compiled-in default is used.  This should
    string; otherwise, the compiled-in default is used.  This should
@@ -143,7 +150,7 @@ extern "C" {
    immediately unload the DLL and (if possible) download a new one. */
    immediately unload the DLL and (if possible) download a new one. */
 typedef bool 
 typedef bool 
 P3D_initialize_func(int api_version, const char *contents_filename,
 P3D_initialize_func(int api_version, const char *contents_filename,
-                    const char *host_url, bool verify_contents,
+                    const char *host_url, P3D_verify_contents verify_contents,
                     const char *platform,
                     const char *platform,
                     const char *log_directory, const char *log_basename,
                     const char *log_directory, const char *log_basename,
                     bool trusted_environment, bool console_environment,
                     bool trusted_environment, bool console_environment,

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

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

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

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

+ 1 - 1
direct/src/plugin_standalone/p3dEmbed.cxx

@@ -112,7 +112,7 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
         } else if (keyword == "root_dir") {
         } else if (keyword == "root_dir") {
           root_dir = value;
           root_dir = value;
         } else if (keyword == "verify_contents") {
         } else if (keyword == "verify_contents") {
-          _verify_contents = (bool) atoi(value.c_str());
+          _verify_contents = (P3D_verify_contents)atoi(value.c_str());
         }
         }
       }
       }
       curstr = "";
       curstr = "";

+ 28 - 13
direct/src/plugin_standalone/panda3d.cxx

@@ -62,7 +62,7 @@ run_command_line(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:M:Sp:fw:t:s:o:l:iVUPh";
+  const char *optstr = "+mu:M:Sp:nfw:t:s:o:l:iVUPh";
 
 
   bool allow_multiple = false;
   bool allow_multiple = false;
 
 
@@ -90,8 +90,12 @@ run_command_line(int argc, char *argv[]) {
       _this_platform = optarg;
       _this_platform = optarg;
       break;
       break;
 
 
+    case 'n':
+      _verify_contents = P3D_VC_normal;
+      break;
+
     case 'f':
     case 'f':
-      _verify_contents = true;
+      _verify_contents = P3D_VC_force;
       break;
       break;
 
 
     case 'w':
     case 'w':
@@ -356,10 +360,12 @@ get_plugin() {
   bool success = false;
   bool success = false;
 
 
   Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
   Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
-  if (read_contents_file(contents_filename, false)) {
-    if (!_verify_contents || time(NULL) < _contents_expiration) {
-      // Got the file, and it's good.
-      success = true;
+  if (_verify_contents != P3D_VC_force) {
+    if (read_contents_file(contents_filename, false)) {
+      if (_verify_contents == P3D_VC_none || time(NULL) < _contents_expiration) {
+        // Got the file, and it's good.
+        success = true;
+      }
     }
     }
   }
   }
 
 
@@ -409,7 +415,9 @@ get_plugin() {
       
       
       // Since we have to download some of it, might as well ask the core
       // Since we have to download some of it, might as well ask the core
       // API to check all of it.
       // API to check all of it.
-      _verify_contents = true;
+      if (_verify_contents == P3D_VC_none) {
+        _verify_contents = P3D_VC_normal;
+      }
       
       
       // First, download it to a temporary file.
       // First, download it to a temporary file.
       Filename tempfile = Filename::temporary("", "p3d_");
       Filename tempfile = Filename::temporary("", "p3d_");
@@ -529,7 +537,6 @@ read_contents_file(const Filename &contents_filename, bool fresh_download) {
       return false;
       return false;
     }
     }
     tempfile.rename_to(standard_filename);
     tempfile.rename_to(standard_filename);
-    nout << "rewrote " << standard_filename << "\n";
 
 
   } else {
   } else {
     if (contents_filename != standard_filename) {
     if (contents_filename != standard_filename) {
@@ -538,7 +545,6 @@ read_contents_file(const Filename &contents_filename, bool fresh_download) {
         contents_filename.unlink();
         contents_filename.unlink();
         return false;
         return false;
       }
       }
-      nout << "moved to " << standard_filename << "\n";
     }
     }
   }
   }
 
 
@@ -742,7 +748,9 @@ get_core_api() {
 
 
     // Since we had to download some of it, might as well ask the core
     // Since we had to download some of it, might as well ask the core
     // API to check all of it.
     // API to check all of it.
-    _verify_contents = true;
+    if (_verify_contents == P3D_VC_none) {
+      _verify_contents = P3D_VC_normal;
+    }
   }
   }
 
 
   // Now we've got the DLL.  Load it.
   // Now we've got the DLL.  Load it.
@@ -844,10 +852,17 @@ usage() {
     << "    to be written.  If this is not specified, the default is to send\n"
     << "    to be written.  If this is not specified, the default is to send\n"
     << "    the application output to the console.\n\n"
     << "    the application output to the console.\n\n"
 
 
+    << "  -n\n"
+    << "    Allow a network connect to the Panda3D download server, to check\n"
+    << "    if a new version is available (but only if the current version\n"
+    << "    appears to be out-of-date).  The default behavior, if both -n\n"
+    << "    and -f are omitted, is not to contact the server at all, unless\n"
+    << "    the local contents do not exist or cannot be read.\n\n"
+
     << "  -f\n"
     << "  -f\n"
-    << "    Force an initial contact of the Panda3D download server, to check\n"
-    << "    if a new version is available.  Normally, this is done only\n"
-    << "    if contents.xml cannot be read.\n\n"
+    << "    Force an initial contact of the Panda3D download server, even\n"
+    << "    if the local contents appear to be current.  This is mainly\n"
+    << "    useful when testing local republishes.\n\n"
 
 
     << "  -i\n"
     << "  -i\n"
     << "    Runs the application interactively.  This requires that the application\n"
     << "    Runs the application interactively.  This requires that the application\n"

+ 1 - 1
direct/src/plugin_standalone/panda3dBase.cxx

@@ -64,7 +64,7 @@ Panda3DBase(bool console_environment) {
   _exit_with_last_instance = true;
   _exit_with_last_instance = true;
   _host_url = PANDA_PACKAGE_HOST_URL;
   _host_url = PANDA_PACKAGE_HOST_URL;
   _this_platform = DTOOL_PLATFORM;
   _this_platform = DTOOL_PLATFORM;
-  _verify_contents = false;
+  _verify_contents = P3D_VC_none;
   _contents_expiration = 0;
   _contents_expiration = 0;
 
 
   // Seed the lame random number generator in rand(); we use it to
   // Seed the lame random number generator in rand(); we use it to

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

@@ -73,7 +73,7 @@ protected:
   string _log_dirname;
   string _log_dirname;
   string _log_basename;
   string _log_basename;
   string _this_platform;
   string _this_platform;
-  bool _verify_contents;
+  P3D_verify_contents _verify_contents;
   time_t _contents_expiration;
   time_t _contents_expiration;
 
 
   P3D_window_type _window_type;
   P3D_window_type _window_type;