Lots of changes to the plugin and pdeploy to make packaged games run without needing write access or internet access

This commit is contained in:
rdb 2010-09-22 19:52:14 +00:00
parent 57f10c8a37
commit 60973fdb7b
20 changed files with 394 additions and 169 deletions

View File

@ -79,6 +79,7 @@ class AppRunner(DirectObject):
P3DVCNone = 0
P3DVCNormal = 1
P3DVCForce = 2
P3DVCNever = 3
# Also from p3d_plugin.h
P3D_CONTENTS_DEFAULT_MAX_AGE = 5
@ -570,6 +571,11 @@ class AppRunner(DirectObject):
totalSize += packageData.totalSize
self.notify.info("Total Panda3D disk space used: %s MB" % (
(totalSize + 524288) / 1048576))
if self.verifyContents == self.P3DVCNever:
# We're not allowed to delete anything anyway.
return
self.notify.info("Configured max usage is: %s MB" % (
(self.maxDiskUsage + 524288) / 1048576))
if totalSize <= self.maxDiskUsage:

View File

@ -6,7 +6,9 @@ __all__ = ["Standalone", "Installer"]
import os, sys, subprocess, tarfile, shutil, time, zipfile, glob
from direct.directnotify.DirectNotifyGlobal import *
from pandac.PandaModules import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile, readXmlStream
from direct.showbase.AppRunnerGlobal import appRunner
from pandac.PandaModules import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
from pandac.PandaModules import TiXmlDocument, TiXmlDeclaration, TiXmlElement, readXmlStream
from direct.p3d.HostInfo import HostInfo
class CachedFile:
@ -27,7 +29,7 @@ class Standalone:
hostDir = Filename(Filename.getTempDirectory(), 'pdeploy/')
hostDir.makeDir()
self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = base.appRunner, hostDir = hostDir, asMirror = False, perPlatform = True)
self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = hostDir, asMirror = False, perPlatform = True)
self.http = HTTPClient.getGlobalPtr()
if not self.host.hasContentsFile:
@ -61,6 +63,9 @@ class Standalone:
if platform == None:
platform = PandaSystem.getPlatform()
vfs = VirtualFileSystem.getGlobalPtr()
for package in self.host.getPackages(name = "p3dembed", platform = platform):
if not package.downloadDescFile(self.http):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
@ -75,7 +80,7 @@ class Standalone:
else:
p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed" % package.platform)
if not p3dembed.exists():
if not vfs.exists(p3dembed):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
continue
@ -104,7 +109,11 @@ class Standalone:
output.makeDir()
ohandle = open(output.toOsSpecific(), "wb")
ohandle.write(p3dembed_data)
for token in self.tokens.items():
# Write out the tokens. Set log_basename to the basename by default
tokens = {"log_basename" : self.basename}
tokens.update(self.tokens)
for token in tokens.items():
ohandle.write("\0%s=%s" % token)
ohandle.write("\0\0")
@ -134,11 +143,12 @@ class Standalone:
return []
filenames = []
vfs = VirtualFileSystem.getGlobalPtr()
for e in package.extracts:
if e.basename not in ["p3dembed", "p3dembed.exe"]:
filename = Filename(package.getPackageDir(), e.filename)
filename.makeAbsolute()
if filename.exists():
if vfs.exists(filename):
filenames.append(filename)
else:
Standalone.notify.error("%s mentioned in xml, but does not exist" % e.filename)
@ -187,14 +197,16 @@ class Installer:
self.requirements.append((p3dRequires.Attribute('name'), p3dRequires.Attribute('version')))
p3dRequires = p3dRequires.NextSiblingElement('requires')
def installPackagesInto(self, rootDir, platform):
def installPackagesInto(self, hostDir, platform):
""" Installs the packages required by the .p3d file into
the specified root directory, for the given platform. """
the specified directory, for the given platform. """
if not self.includeRequires:
return
host = HostInfo(self.hostUrl, appRunner = base.appRunner, rootDir = rootDir, asMirror = True, perPlatform = False)
packages = []
host = HostInfo(self.hostUrl, appRunner = appRunner, hostDir = hostDir, asMirror = False, perPlatform = False)
if not host.hasContentsFile:
if not host.readContentsFile():
if not host.downloadContentsFile(self.http):
@ -203,15 +215,17 @@ class Installer:
for name, version in self.requirements:
package = host.getPackage(name, version, platform)
package.installed = True # Hack not to let it install itself
packages.append(package)
if not package.downloadDescFile(self.http):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
Installer.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
continue
if not package.downloadPackage(self.http):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
Installer.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
continue
# 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 = appRunner, hostDir = hostDir, asMirror = False, perPlatform = False)
if not host.hasContentsFile:
if not host.readContentsFile():
if not host.downloadContentsFile(self.http):
@ -219,13 +233,71 @@ class Installer:
return
for package in host.getPackages(name = "images"):
package.installed = True # Hack not to let it install itself
packages.append(package)
if not package.downloadDescFile(self.http):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
Installer.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
continue
if not package.downloadPackage(self.http):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
Installer.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
continue
break
# Remove the extracted files from the compressed archive, to save space.
vfs = VirtualFileSystem.getGlobalPtr()
for package in packages:
if package.uncompressedArchive:
archive = Filename(package.getPackageDir(), package.uncompressedArchive.filename)
if not archive.exists():
continue
print archive
mf = Multifile()
# Make sure that it isn't mounted before altering it, just to be safe
vfs.unmount(archive)
if not mf.openRead(archive):
Installer.notify.warning("Failed to open archive " + archive)
continue
# We don't iterate over getNumSubfiles because we're
# removing subfiles while we're iterating over them.
subfiles = mf.getSubfileNames()
for subfile in subfiles:
# We do *NOT* call vfs.exists here in case the package is mounted.
if Filename(package.getPackageDir(), subfile).exists():
mf.removeSubfile(subfile)
# This seems essential for mf.close() not to crash later.
mf.repack()
# If we have no subfiles left, we can just remove the multifile.
if mf.getNumSubfiles() == 0:
Installer.notify.info("Removing empty archive " + package.uncompressedArchive.filename)
mf.close()
archive.unlink()
else:
mf.close()
# Write out our own contents.xml file.
doc = TiXmlDocument()
decl = TiXmlDeclaration("1.0", "utf-8", "")
doc.InsertEndChild(decl)
xcontents = TiXmlElement("contents")
for package in packages:
xpackage = TiXmlElement('package')
xpackage.SetAttribute('name', package.packageName)
if package.platform:
xpackage.SetAttribute('platform', package.platform)
if package.packageVersion:
xpackage.SetAttribute('version', version)
xpackage.SetAttribute('filename', package.packageName + "/" + package.packageVersion + "/" + package.descFileBasename)
else:
xpackage.SetAttribute('filename', package.packageName + "/" + package.descFileBasename)
xcontents.InsertEndChild(xpackage)
doc.InsertEndChild(xcontents)
doc.SaveFile(Filename(hostDir, "contents.xml").toOsSpecific())
def buildAll(self, outputDir = "."):
""" Creates a (graphical) installer for every known platform.
@ -289,45 +361,26 @@ class Installer:
print >>controlfile, "Description: %s" % self.fullname
print >>controlfile, "Depends: libc6, libgcc1, libstdc++6, libx11-6, libssl0.9.8"
controlfile.close()
postinst = open(Filename(tempdir, "postinst").toOsSpecific(), "w")
print >>postinst, "#!/bin/sh"
print >>postinst, "/usr/bin/%s --prep" % self.shortname.lower()
print >>postinst, "chmod -R 777 /usr/share/%s" % self.shortname.lower()
print >>postinst, "chmod -R 555 /usr/share/%s/hosts" % self.shortname.lower()
postinst.close()
os.chmod(Filename(tempdir, "postinst").toOsSpecific(), 0755)
postrmfile = open(Filename(tempdir, "postrm").toOsSpecific(), "w")
print >>postrmfile, "#!/bin/sh"
print >>postrmfile, "rm -rf /usr/share/%s" % self.shortname.lower()
postrmfile.close()
os.chmod(Filename(tempdir, "postrm").toOsSpecific(), 0755)
Filename(tempdir, "usr/bin/").makeDir()
self.standalone.tokens["root_dir"] = "/usr/share/" + self.shortname.lower()
if self.includeRequires:
self.standalone.tokens["host_dir"] = "/usr/lib/" + self.shortname.lower()
elif "host_dir" in self.standalone.tokens:
del self.standalone.tokens["host_dir"]
self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform)
if not self.licensefile.empty():
Filename(tempdir, "usr/share/doc/%s/" % self.shortname.lower()).makeDir()
shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/copyright" % self.shortname.lower()).toOsSpecific())
rootDir = Filename(tempdir, "usr/share/" + self.shortname.lower())
rootDir.makeDir()
Filename(rootDir, "log").makeDir()
Filename(rootDir, "prc").makeDir()
Filename(rootDir, "start").makeDir()
Filename(rootDir, "certs").makeDir()
self.installPackagesInto(rootDir, platform)
hostDir = Filename(tempdir, "usr/lib/" + self.shortname.lower())
hostDir.makeDir()
self.installPackagesInto(hostDir, platform)
# Create a control.tar.gz file in memory
controlfile = Filename(tempdir, "control")
postinstfile = Filename(tempdir, "postinst")
postrmfile = Filename(tempdir, "postrm")
controltargz = CachedFile()
controltarfile = tarfile.TarFile.gzopen("control.tar.gz", "w", controltargz, 9)
controltarfile.add(controlfile.toOsSpecific(), "control")
controltarfile.add(postinstfile.toOsSpecific(), "postinst")
controltarfile.add(postrmfile.toOsSpecific(), "postrm")
controltarfile.close()
controlfile.unlink()
postinstfile.unlink()
postrmfile.unlink()
# Create the data.tar.gz file in the temporary directory
datatargz = CachedFile()
@ -352,7 +405,7 @@ class Installer:
if (len(datatargz.str) & 1): debfile.write("\x0A")
debfile.close()
try:
base.appRunner.rmtree(tempdir)
appRunner.rmtree(tempdir)
except:
try: shutil.rmtree(tempdir.toOsSpecific())
except: pass
@ -367,11 +420,14 @@ class Installer:
# Create the executable for the application bundle
exefile = Filename(output, "Contents/MacOS/" + self.shortname)
exefile.makeDir()
self.standalone.tokens["root_dir"] = "../Resources"
if self.includeRequires:
self.standalone.tokens["host_dir"] = "../Resources"
elif "host_dir" in self.standalone.tokens:
del self.standalone.tokens["host_dir"]
self.standalone.build(exefile, platform)
rootDir = Filename(output, "Contents/Resources/")
rootDir.makeDir()
self.installPackagesInto(rootDir, platform)
hostDir = Filename(output, "Contents/Resources/")
hostDir.makeDir()
self.installPackagesInto(hostDir, platform)
# Create the application plist file.
# Although it might make more sense to use Python's plistlib module here,
@ -510,13 +566,6 @@ class Installer:
plist.write('</plist>\n')
plist.close()
postflight = open(Filename(output, "Contents/Resources/postflight").toOsSpecific(), "w")
print >>postflight, '#!/bin/sh'
print >>postflight, 'chmod -R 777 "%s"' % appname
print >>postflight, 'chmod -R 755 "%s/hosts/"' % appname
postflight.close()
os.chmod(Filename(output, "Contents/Resources/postflight").toOsSpecific(), 0755)
if hasattr(tarfile, "PAX_FORMAT"):
archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz", format = tarfile.PAX_FORMAT)
else:
@ -569,13 +618,16 @@ class Installer:
exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
exefile.unlink()
self.standalone.tokens["root_dir"] = "."
if self.includeRequires:
self.standalone.tokens["host_dir"] = "."
elif "host_dir" in self.standalone.tokens:
del self.standalone.tokens["host_dir"]
self.standalone.build(exefile, platform)
# Temporary directory to store the rootdir in
rootDir = Filename.temporary("", self.shortname.lower() + "_exe_", "") + "/"
rootDir.makeDir()
self.installPackagesInto(rootDir, platform)
# Temporary directory to store the hostdir in
hostDir = Filename.temporary("", self.shortname.lower() + "_exe_", "") + "/"
hostDir.makeDir()
self.installPackagesInto(hostDir, platform)
nsifile = Filename(Filename.getTempDirectory(), self.shortname + ".nsi")
nsifile.unlink()
@ -625,11 +677,11 @@ class Installer:
for f in extrafiles:
nsi.write(' File "%s"\n' % f.toOsSpecific())
curdir = ""
for root, dirs, files in os.walk(rootDir.toOsSpecific()):
for root, dirs, files in os.walk(hostDir.toOsSpecific()):
for name in files:
file = Filename.fromOsSpecific(os.path.join(root, name))
file.makeAbsolute()
file.makeRelativeTo(rootDir)
file.makeRelativeTo(hostDir)
outdir = file.getDirname().replace('/', '\\')
if curdir != outdir:
nsi.write(' SetOutPath "$INSTDIR\\%s"\n' % outdir)
@ -670,8 +722,8 @@ class Installer:
nsifile.unlink()
try:
base.appRunner.rmtree(rootDir)
appRunner.rmtree(hostDir)
except:
try: shutil.rmtree(rootDir.toOsSpecific())
try: shutil.rmtree(hostDir.toOsSpecific())
except: pass
return output

View File

@ -118,6 +118,10 @@ class HostInfo:
# We've already got one.
return True
if self.appRunner.verifyContents == self.appRunner.P3DVCNever:
# Not allowed to.
return False
rf = None
if http:
if not redownload and self.appRunner and self.appRunner.superMirrorUrl:
@ -183,6 +187,10 @@ class HostInfo:
not. """
assert self.hasContentsFile
if self.appRunner.verifyContents == self.appRunner.P3DVCNever:
# Not allowed to.
return False
url = self.hostUrlPrefix + 'contents.xml'
self.notify.info("Redownloading %s" % (url))
@ -215,7 +223,9 @@ class HostInfo:
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 not self.appRunner \
or self.appRunner.verifyContents == self.appRunner.P3DVCNone \
or self.appRunner.verifyContents == self.appRunner.P3DVCNever:
# If we're not asking to verify contents, then
# contents.xml files never expires.
return self.hasContentsFile
@ -342,14 +352,15 @@ class HostInfo:
self.hasContentsFile = True
# Now save the contents.xml file into the standard location.
assert self.hostDir
filename = Filename(self.hostDir, 'contents.xml')
filename.makeDir()
if freshDownload:
doc.SaveFile(filename.toOsSpecific())
else:
if filename != tempFilename:
tempFilename.copyTo(filename)
if self.appRunner.verifyContents != self.appRunner.P3DVCNever:
assert self.hostDir
filename = Filename(self.hostDir, 'contents.xml')
filename.makeDir()
if freshDownload:
doc.SaveFile(filename.toOsSpecific())
else:
if filename != tempFilename:
tempFilename.copyTo(filename)
return True

View File

@ -263,42 +263,44 @@ class PackageInfo:
# We've already got one.
yield self.stepComplete; return
self.http = http
if self.host.appRunner and self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever:
# We're allowed to download it.
self.http = http
func = lambda step, self = self: self.__downloadFile(
None, self.descFile,
urlbase = self.descFile.filename,
filename = self.descFileBasename)
step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc')
for token in step.func():
if token == self.stepContinue:
yield token
else:
break
while token == self.restartDownload:
# Try again.
func = lambda step, self = self: self.__downloadFile(
None, self.descFile,
urlbase = self.descFile.filename,
filename = self.descFileBasename)
step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc')
for token in step.func():
if token == self.stepContinue:
yield token
else:
break
if token == self.stepFailed:
# Couldn't download the desc file.
yield self.stepFailed; return
while token == self.restartDownload:
# Try again.
func = lambda step, self = self: self.__downloadFile(
None, self.descFile,
urlbase = self.descFile.filename,
filename = self.descFileBasename)
step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc')
for token in step.func():
if token == self.stepContinue:
yield token
else:
break
assert token == self.stepComplete
if token == self.stepFailed:
# Couldn't download the desc file.
yield self.stepFailed; return
filename = Filename(self.getPackageDir(), self.descFileBasename)
# Now that we've written the desc file, make it read-only.
os.chmod(filename.toOsSpecific(), 0444)
assert token == self.stepComplete
filename = Filename(self.getPackageDir(), self.descFileBasename)
# Now that we've written the desc file, make it read-only.
os.chmod(filename.toOsSpecific(), 0444)
if not self.__readDescFile():
# Weird, it passed the hash check, but we still can't read
@ -409,6 +411,12 @@ class PackageInfo:
pc.start()
self.hasPackage = False
if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
# We're not allowed to download anything.
self.installPlans = []
pc.stop()
return
if self.asMirror:
# If we're just downloading a mirror archive, we only need
@ -520,6 +528,10 @@ class PackageInfo:
""" Returns true if the archive and all extractable files are
already correct on disk, false otherwise. """
if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
# Assume that everything is just fine.
return True
# Get a list of all of the files in the directory, so we can
# remove files that don't belong.
contents = self.__scanDirectoryRecursively(self.getPackageDir())
@ -604,6 +616,10 @@ class PackageInfo:
# We've already got one.
yield self.stepComplete; return
if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
# We're not allowed to download anything. Assume it's already downloaded.
yield self.stepComplete; return
# We should have an install plan by the time we get here.
assert self.installPlans
@ -715,6 +731,10 @@ class PackageInfo:
packageDir. Yields one of stepComplete, stepFailed,
restartDownload, or stepContinue. """
if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
# We're not allowed to download anything.
yield self.stepFailed; return
self.updated = True
if not urlbase:
@ -895,6 +915,10 @@ class PackageInfo:
archive. Yields one of stepComplete, stepFailed,
restartDownload, or stepContinue. """
if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
# We're not allowed to!
yield self.stepFailed; return
self.updated = True
sourcePathname = Filename(self.getPackageDir(), self.compressedArchive.filename)
@ -945,6 +969,10 @@ class PackageInfo:
self.hasPackage = True
yield self.stepComplete; return
if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
# We're not allowed to!
yield self.stepFailed; return
self.updated = True
mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename)
@ -1090,6 +1118,10 @@ class PackageInfo:
if not hasattr(PandaModules, 'TiXmlDocument'):
return
if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever:
# Not allowed to write any files to the package directory.
return
if self.updated:
# If we've just installed a new version of the package,
# re-measure the actual disk space used.

View File

@ -229,6 +229,8 @@ if deploy_mode == 'standalone':
s.build(Filename(outputDir, platform + "/" + shortname), platform)
elif deploy_mode == 'installer':
if includeRequires:
tokens["verify_contents"] = "never"
i = Installer(shortname, fullname, appFilename, version, tokens = tokens)
i.licensename = licensename
i.licensefile = licensefile

View File

@ -138,7 +138,8 @@ load_plugin(const string &p3d_plugin_filename,
P3D_verify_contents verify_contents, const string &platform,
const string &log_directory, const string &log_basename,
bool trusted_environment, bool console_environment,
const string &root_dir, ostream &logfile) {
const string &root_dir, const string &host_dir,
ostream &logfile) {
if (plugin_loaded) {
return true;
}
@ -254,7 +255,7 @@ load_plugin(const string &p3d_plugin_filename,
verify_contents, platform,
log_directory, log_basename,
trusted_environment, console_environment,
root_dir, logfile)) {
root_dir, host_dir, logfile)) {
unload_dso();
return false;
}
@ -277,7 +278,8 @@ init_plugin(const string &contents_filename, const string &host_url,
P3D_verify_contents verify_contents, const string &platform,
const string &log_directory, const string &log_basename,
bool trusted_environment, bool console_environment,
const string &root_dir, ostream &logfile) {
const string &root_dir, const string &host_dir,
ostream &logfile) {
// Ensure that all of the function pointers have been found.
if (P3D_initialize_ptr == NULL ||
@ -369,7 +371,7 @@ init_plugin(const string &contents_filename, const string &host_url,
host_url.c_str(), verify_contents, platform.c_str(),
log_directory.c_str(), log_basename.c_str(),
trusted_environment, console_environment,
root_dir.c_str())) {
root_dir.c_str(), host_dir.c_str())) {
// Oops, failure to initialize.
logfile
<< "Failed to initialize plugin (passed API version "

View File

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

View File

@ -23,12 +23,13 @@
////////////////////////////////////////////////////////////////////
// Function: P3DHost::Constructor
// Access: Private
// Description: Use P3DInstanceManager::get_host() to construct a new
// P3DHost.
// Description: Use P3DInstanceManager::get_host() to construct a
// new P3DHost.
////////////////////////////////////////////////////////////////////
P3DHost::
P3DHost(const string &host_url) :
_host_url(host_url)
P3DHost(const string &host_url, const string &host_dir) :
_host_url(host_url),
_host_dir(host_dir)
{
// Ensure that the download URL ends with a slash.
_host_url_prefix = _host_url;
@ -116,7 +117,8 @@ get_alt_host(const string &alt_host) {
////////////////////////////////////////////////////////////////////
bool P3DHost::
has_current_contents_file(P3DInstanceManager *inst_mgr) const {
if (inst_mgr->get_verify_contents() == P3D_VC_none) {
if (inst_mgr->get_verify_contents() == P3D_VC_never
|| inst_mgr->get_verify_contents() == P3D_VC_none) {
// If we're not asking to verify contents, then contents.xml files
// never expire.
return has_contents_file();
@ -250,36 +252,41 @@ read_contents_file(const string &contents_filename, bool fresh_download) {
}
}
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (_host_dir.empty()) {
determine_host_dir("");
}
assert(!_host_dir.empty());
mkdir_complete(_host_dir, nout);
string standard_filename = _host_dir + "/contents.xml";
if (fresh_download) {
if (!save_xml_file(&doc, standard_filename)) {
nout << "Couldn't save to " << standard_filename << "\n";
}
} else {
if (standardize_filename(standard_filename) !=
standardize_filename(contents_filename)) {
if (!copy_file(contents_filename, standard_filename)) {
nout << "Couldn't copy to " << standard_filename << "\n";
if (inst_mgr->get_verify_contents() != P3D_VC_never) {
mkdir_complete(_host_dir, nout);
if (fresh_download) {
if (!save_xml_file(&doc, standard_filename)) {
nout << "Couldn't save to " << standard_filename << "\n";
}
} else {
if (standardize_filename(standard_filename) !=
standardize_filename(contents_filename)) {
if (!copy_file(contents_filename, standard_filename)) {
nout << "Couldn't copy to " << standard_filename << "\n";
}
}
}
}
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (_host_url == inst_mgr->get_host_url()) {
// If this is also the plugin host, then copy the contents.xml
// file into the root Panda directory as well, for the next plugin
// iteration.
string top_filename = inst_mgr->get_root_dir() + "/contents.xml";
if (standardize_filename(top_filename) !=
standardize_filename(standard_filename)) {
if (!copy_file(standard_filename, top_filename)) {
nout << "Couldn't copy to " << top_filename << "\n";
if (_host_url == inst_mgr->get_host_url()) {
// If this is also the plugin host, then copy the contents.xml
// file into the root Panda directory as well, for the next plugin
// iteration.
string top_filename = inst_mgr->get_root_dir() + "/contents.xml";
if (standardize_filename(top_filename) !=
standardize_filename(standard_filename)) {
if (!copy_file(standard_filename, top_filename)) {
nout << "Couldn't copy to " << top_filename << "\n";
}
}
}
}
@ -305,7 +312,9 @@ read_xhost(TiXmlElement *xhost) {
if (host_dir_basename == NULL) {
host_dir_basename = "";
}
determine_host_dir(host_dir_basename);
if (_host_dir.empty()) {
determine_host_dir(host_dir_basename);
}
// Get the "download" URL, which is the source from which we
// download everything other than the contents.xml file.
@ -610,6 +619,14 @@ uninstall() {
nout << "Cannot uninstall " << _descriptive_name << ": host directory not yet known.\n";
return;
}
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
// Check if we're even allowed to.
if (inst_mgr->get_verify_contents() == P3D_VC_never) {
nout << "Not allowed to uninstall " << _descriptive_name << ".\n";
return;
}
// First, explicitly uninstall each of our packages.
Packages::iterator mi;
@ -624,8 +641,6 @@ uninstall() {
// Then, uninstall the host itself.
nout << "Uninstalling " << _descriptive_name << " from " << _host_dir << "\n";
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->delete_directory_recursively(_host_dir);
inst_mgr->forget_host(this);
}

View File

@ -30,7 +30,7 @@ class P3DPackage;
////////////////////////////////////////////////////////////////////
class P3DHost {
private:
P3DHost(const string &host_url);
P3DHost(const string &host_url, const string &host_dir = "");
~P3DHost();
public:

View File

@ -505,10 +505,6 @@ set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
////////////////////////////////////////////////////////////////////
void P3DInstance::
set_wparams(const P3DWindowParams &wparams) {
if (is_failed()) {
return;
}
bool prev_got_wparams = _got_wparams;
_got_wparams = true;
_wparams = wparams;
@ -531,7 +527,15 @@ set_wparams(const P3DWindowParams &wparams) {
} else {
make_splash_window();
}
}
// It doesn't make much sense to go further than this point
// if the instance is already in the failed state.
if (is_failed()) {
return;
}
if (_wparams.get_window_type() != P3D_WT_hidden) {
#ifdef __APPLE__
// On Mac, we have to communicate the results of the rendering
// back via shared memory, instead of directly parenting windows
@ -1534,12 +1538,15 @@ uninstall_packages() {
}
// Also clean up the start directory, if we have a custom start dir.
string start_dir_suffix = get_start_dir_suffix();
if (!start_dir_suffix.empty()) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
string start_dir = inst_mgr->get_root_dir() + "/start" + start_dir_suffix;
nout << "Cleaning up start directory " << start_dir << "\n";
inst_mgr->delete_directory_recursively(start_dir);
// We won't do this if verify_contents is 'none'.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (inst_mgr->get_verify_contents() != P3D_VC_never) {
string start_dir_suffix = get_start_dir_suffix();
if (!start_dir_suffix.empty()) {
string start_dir = inst_mgr->get_root_dir() + "/start" + start_dir_suffix;
nout << "Cleaning up start directory " << start_dir << "\n";
inst_mgr->delete_directory_recursively(start_dir);
}
}
return true;

View File

@ -194,7 +194,8 @@ initialize(int api_version, const string &contents_filename,
const string &host_url, P3D_verify_contents verify_contents,
const string &platform, const string &log_directory,
const string &log_basename, bool trusted_environment,
bool console_environment, const string &root_dir) {
bool console_environment,
const string &root_dir, const string &host_dir) {
_api_version = api_version;
_host_url = host_url;
_verify_contents = verify_contents;
@ -236,6 +237,8 @@ initialize(int api_version, const string &contents_filename,
} else {
_root_dir = root_dir;
}
_host_dir = host_dir;
// Allow the caller (e.g. panda3d.exe) to specify a log directory.
// Or, allow the developer to compile one in.
@ -641,7 +644,7 @@ get_host(const string &host_url) {
return (*pi).second;
}
P3DHost *host = new P3DHost(host_url);
P3DHost *host = new P3DHost(host_url, _host_dir);
bool inserted = _hosts.insert(Hosts::value_type(host_url, host)).second;
assert(inserted);
@ -1384,6 +1387,9 @@ create_runtime_environment() {
<< ", host_url = " << _host_url
<< ", verify_contents = " << _verify_contents
<< "\n";
if (!_host_dir.empty()) {
nout << "_host_dir = " << _host_dir << "\n";
}
nout << "api_version = " << _api_version << "\n";
// Make the certificate directory.
@ -1458,3 +1464,4 @@ nt_thread_run() {
}
_notify_ready.release();
}

View File

@ -58,7 +58,8 @@ public:
const string &log_basename,
bool trusted_environment,
bool console_environment,
const string &root_dir = "");
const string &root_dir = "",
const string &host_dir = "");
inline bool is_initialized() const;
inline void reconsider_runtime_environment();
@ -164,6 +165,7 @@ private:
int _api_version;
string _host_url;
string _root_dir;
string _host_dir;
string _certs_dir;
P3D_verify_contents _verify_contents;
string _platform;

View File

@ -229,6 +229,12 @@ remove_instance(P3DInstance *inst) {
////////////////////////////////////////////////////////////////////
void P3DPackage::
mark_used() {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (inst_mgr->get_verify_contents() == P3D_VC_never) {
// We're not allowed to create any files in the package directory.
return;
}
// Unlike the Python variant of this function, we don't mess around
// with updating the disk space or anything.
string filename = get_package_dir() + "/usage.xml";
@ -404,6 +410,12 @@ download_contents_file() {
return;
}
// Don't download it if we're not allowed to.
if (inst_mgr->get_verify_contents() == P3D_VC_never) {
contents_file_download_finished(false);
return;
}
// Download contents.xml to a temporary filename first, in case
// multiple packages are downloading it simultaneously.
if (_temp_contents_file != NULL) {
@ -426,17 +438,24 @@ void P3DPackage::
contents_file_download_finished(bool success) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (!_host->has_current_contents_file(inst_mgr)) {
if (!success || !_host->read_contents_file(_temp_contents_file->get_filename(), true)) {
nout << "Couldn't read " << *_temp_contents_file << "\n";
if (!success || _temp_contents_file == NULL ||
!_host->read_contents_file(_temp_contents_file->get_filename(), true)) {
if (_temp_contents_file) {
nout << "Couldn't read " << *_temp_contents_file << "\n";
}
// Maybe we can read an already-downloaded contents.xml file.
string standard_filename = _host->get_host_dir() + "/contents.xml";
if (_host->get_host_dir().empty() ||
!_host->read_contents_file(standard_filename, false)) {
// Couldn't even read that. Fail.
nout << "Couldn't read " << standard_filename << "\n";
report_done(false);
delete _temp_contents_file;
_temp_contents_file = NULL;
if (_temp_contents_file) {
delete _temp_contents_file;
_temp_contents_file = NULL;
}
return;
}
}
@ -444,8 +463,10 @@ contents_file_download_finished(bool success) {
// The file is correctly installed by now; we can remove the
// temporary file.
delete _temp_contents_file;
_temp_contents_file = NULL;
if (_temp_contents_file) {
delete _temp_contents_file;
_temp_contents_file = NULL;
}
host_got_contents_file();
}
@ -480,6 +501,12 @@ redownload_contents_file(P3DPackage::Download *download) {
return;
}
// Don't download it if we're not allowed to.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (inst_mgr->get_verify_contents() == P3D_VC_never) {
return;
}
set_saved_download(download);
// Download contents.xml to a temporary filename first.
@ -526,8 +553,10 @@ contents_file_redownload_finished(bool success) {
}
// We no longer need the temporary file.
delete _temp_contents_file;
_temp_contents_file = NULL;
if (_temp_contents_file) {
delete _temp_contents_file;
_temp_contents_file = NULL;
}
if (contents_changed) {
// OK, the contents.xml has changed; this means we have to restart
@ -600,7 +629,10 @@ host_got_contents_file() {
}
// Ensure the package directory exists; create it if it does not.
mkdir_complete(_package_dir, nout);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (inst_mgr->get_verify_contents() != P3D_VC_never) {
mkdir_complete(_package_dir, nout);
}
download_desc_file();
}
@ -645,7 +677,8 @@ download_desc_file() {
local_desc_file.set_filename(_desc_file_basename);
_desc_file_pathname = local_desc_file.get_pathname(_package_dir);
if (!local_desc_file.full_verify(_package_dir)) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (!local_desc_file.full_verify(_package_dir) && inst_mgr->get_verify_contents() != P3D_VC_never) {
nout << _desc_file_pathname << " is stale.\n";
} else {
@ -663,6 +696,13 @@ download_desc_file() {
}
}
// Don't download it if we're not allowed to.
if (inst_mgr->get_verify_contents() == P3D_VC_never) {
nout << "Couldn't read " << _desc_file_pathname << "\n";
report_done(false);
return;
}
// The desc file is not current. Go download it.
start_download(DT_desc_file, _desc_file.get_filename(),
_desc_file_pathname, local_desc_file);
@ -680,8 +720,11 @@ desc_file_download_finished(bool success) {
return;
}
// Now that we've downloaded the desc file, make it read-only.
chmod(_desc_file_pathname.c_str(), 0444);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (inst_mgr->get_verify_contents() != P3D_VC_never) {
// Now that we've downloaded the desc file, make it read-only.
chmod(_desc_file_pathname.c_str(), 0444);
}
if (_package_solo) {
// No need to load it: the desc file *is* the package.
@ -785,6 +828,13 @@ got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
xrequires = xrequires->NextSiblingElement("requires");
}
if (inst_mgr->get_verify_contents() == P3D_VC_never) {
// This means we'll just leave it at this
// and assume that we're finished.
report_done(true);
return;
}
// Get a list of all of the files in the directory, so we can remove
// files that don't belong.
vector<string> contents, dirname_contents;
@ -1208,6 +1258,9 @@ start_download(P3DPackage::DownloadType dtype, const string &urlbase,
const string &pathname, const FileSpec &file_spec) {
// Only one download should be active at a time
assert(_active_download == NULL);
// This can't happen! If verify_contents is set to P3D_VC_never,
// we're not allowed to download anything, so we shouldn't get here.
assert(inst_mgr->get_verify_contents() != P3D_VC_never);
// We can't explicitly support partial downloads here, because
// Mozilla provides no interface to ask for one. We have to trust

View File

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

View File

@ -79,7 +79,7 @@ extern "C" {
(below). This number will be incremented whenever there are changes
to any of the interface specifications defined in this header
file. */
#define P3D_API_VERSION 15
#define P3D_API_VERSION 16
/************************ GLOBAL FUNCTIONS **************************/
@ -91,6 +91,7 @@ typedef enum {
P3D_VC_none,
P3D_VC_normal,
P3D_VC_force,
P3D_VC_never,
} P3D_verify_contents;
/* This function should be called immediately after the core API is
@ -110,8 +111,11 @@ typedef enum {
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.
contents.xml has expired for that server. The opposite of
P3D_VC_force is P3D_VC_never, which forces the plugin never to
contact the server and not to verify the contents at all. This
option should only be used if the host directory is prepopulated.
Normally, a web plugin should set this to P3D_VC_normal.
If platform is not NULL or empty, it specifies the current platform
string; otherwise, the compiled-in default is used. This should
@ -137,13 +141,16 @@ typedef enum {
p3d file will be run without checking its signature. Normally, a
browser plugin should set this false.
Finally, console_environment should be set true to indicate that we
are running within a text-based console, and expect to preserve the
current working directory, and also see standard output, or false
Furthermore, console_environment should be set true to indicate that
we are running within a text-based console, and expect to preserve
the current working directory, and also see standard output, or false
to indicate that we are running within a GUI environment, and
expect none of these. Normally, a browser plugin should set this
false.
Finally, root_dir and host_dir can be set to override the default
root and package directories. Normally, you don't need to set them.
This function returns true if the core API is valid and uses a
compatible API, false otherwise. If it returns false, the host
should not call any more functions in this API, and should
@ -154,7 +161,7 @@ P3D_initialize_func(int api_version, const char *contents_filename,
const char *platform,
const char *log_directory, const char *log_basename,
bool trusted_environment, bool console_environment,
const char *root_dir);
const char *root_dir, const char *host_dir);
/* This function should be called to unload the core API. It will
release all internally-allocated memory and return the core API to

View File

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

View File

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

View File

@ -81,6 +81,7 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
string keyword;
string value;
string root_dir;
string host_dir;
while (true) {
if (curchr == EOF) {
cerr << "Truncated stream\n";
@ -109,10 +110,24 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
_win_width = atoi(value.c_str());
} else if (keyword == "height") {
_win_height = atoi(value.c_str());
} else if (keyword == "log_basename") {
_log_basename = value;
} else if (keyword == "root_dir") {
root_dir = value;
} else if (keyword == "host_dir") {
host_dir = value;
} else if (keyword == "verify_contents") {
_verify_contents = (P3D_verify_contents)atoi(value.c_str());
if (value == "never") {
_verify_contents = P3D_VC_never;
} else if (value == "force") {
_verify_contents = P3D_VC_force;
} else if (value == "normal") {
_verify_contents = P3D_VC_normal;
} else if (value == "none") {
_verify_contents = P3D_VC_none;
} else {
_verify_contents = (P3D_verify_contents)atoi(value.c_str());
}
}
}
curstr = "";
@ -139,6 +154,13 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
root_dir_f.make_absolute(f.get_dirname());
_root_dir = root_dir_f.to_os_specific();
}
// Make the host directory absolute
if (!host_dir.empty()) {
Filename host_dir_f(host_dir);
host_dir_f.make_absolute(f.get_dirname());
_host_dir = host_dir_f.to_os_specific();
}
// Initialize the core API by directly assigning all of the function
// pointers.
@ -185,6 +207,7 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
// Calling the executable with --prep just prepares the directory
// structure, this is usually invoked in the installer.
if (argc == 2 && strcmp(argv[1], "--prep") == 0) {
cerr << "Invoking the prepare step is deprecated, please rebuild the application using a more recent version of pdeploy\n";
_window_type = P3D_WT_hidden;
_log_basename = "prep";
P3D_token token;
@ -200,7 +223,7 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
// function pointers. This will also call P3D_initialize().
if (!init_plugin("", _host_url, _verify_contents, _this_platform,
_log_dirname, _log_basename, true, _console_environment,
_root_dir, cerr)) {
_root_dir, _host_dir, cerr)) {
cerr << "Unable to launch core API\n";
return 1;
}

View File

@ -802,7 +802,7 @@ get_core_api() {
if (!load_plugin(pathname, contents_filename.to_os_specific(),
_host_url, _verify_contents, _this_platform, _log_dirname,
_log_basename, trusted_environment, _console_environment,
_root_dir, cerr)) {
_root_dir, "", cerr)) {
cerr << "Unable to launch core API in " << pathname << "\n";
return false;
}

View File

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