super mirror

This commit is contained in:
David Rose 2009-09-23 23:31:38 +00:00
parent d2307eb838
commit 508b92005f
20 changed files with 209 additions and 49 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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