mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 09:23:03 -04:00
contents.xml max_age; internal cache busting
This commit is contained in:
parent
9d14c7867b
commit
0b80f17c68
@ -1864,6 +1864,10 @@ class Packager:
|
||||
self.host = PandaSystem.getPackageHostUrl()
|
||||
self.addHost(self.host)
|
||||
|
||||
# The maximum amount of time a client should cache the
|
||||
# contents.xml before re-querying the server, in seconds.
|
||||
self.maxAge = 0
|
||||
|
||||
# A search list for previously-built local packages.
|
||||
|
||||
# We use a bit of caution to read the Filenames out of the
|
||||
@ -1918,10 +1922,10 @@ class Packager:
|
||||
# client and is therefore readily available to any hacker.
|
||||
# Not only is this feature useless, but using it also
|
||||
# increases the size of your patchfiles, since encrypted files
|
||||
# don't patch as tightly as unencrypted files. But it's here
|
||||
# if you really want it.
|
||||
self.encryptExtensions = ['ptf', 'dna', 'txt', 'dc']
|
||||
self.encryptFiles = []
|
||||
# can't really be patched. But it's here if you really want
|
||||
# it. ** Note: Actually, this isn't implemented yet.
|
||||
#self.encryptExtensions = []
|
||||
#self.encryptFiles = []
|
||||
|
||||
# This is the list of DC import suffixes that should be
|
||||
# available to the client. Other suffixes, like AI and UD,
|
||||
@ -2220,6 +2224,8 @@ class Packager:
|
||||
# Set up the namespace dictionary for exec.
|
||||
globals = {}
|
||||
globals['__name__'] = packageDef.getBasenameWoExtension()
|
||||
globals['__dir__'] = Filename(packageDef.getDirname()).toOsSpecific()
|
||||
globals['packageDef'] = packageDef
|
||||
|
||||
globals['platform'] = self.platform
|
||||
globals['packager'] = self
|
||||
@ -3156,6 +3162,7 @@ class Packager:
|
||||
# sure that our own host at least is added to the map.
|
||||
self.addHost(self.host)
|
||||
|
||||
self.maxAge = 0
|
||||
self.contents = {}
|
||||
self.contentsChanged = False
|
||||
|
||||
@ -3171,6 +3178,10 @@ class Packager:
|
||||
|
||||
xcontents = doc.FirstChildElement('contents')
|
||||
if xcontents:
|
||||
maxAge = xcontents.Attribute('max_age')
|
||||
if maxAge:
|
||||
self.maxAge = int(maxAge)
|
||||
|
||||
xhost = xcontents.FirstChildElement('host')
|
||||
if xhost:
|
||||
he = self.HostEntry()
|
||||
@ -3199,6 +3210,9 @@ class Packager:
|
||||
doc.InsertEndChild(decl)
|
||||
|
||||
xcontents = TiXmlElement('contents')
|
||||
if self.maxAge:
|
||||
xcontents.SetAttribute('max_age', str(self.maxAge))
|
||||
|
||||
if self.host:
|
||||
he = self.hosts.get(self.host, None)
|
||||
if he:
|
||||
|
@ -67,6 +67,17 @@ get_timestamp() const {
|
||||
return _timestamp;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: FileSpec::has_hash
|
||||
// Access: Public
|
||||
// Description: Returns true if we have successfully read a hash
|
||||
// value, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline bool FileSpec::
|
||||
has_hash() const {
|
||||
return _got_hash;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: FileSpec::get_actual_file
|
||||
// Access: Public
|
||||
|
@ -123,6 +123,30 @@ load_xml(TiXmlElement *xelement) {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: FileSpec::store_xml
|
||||
// Access: Public
|
||||
// Description: Stores the data to the indicated XML file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void FileSpec::
|
||||
store_xml(TiXmlElement *xelement) {
|
||||
if (!_filename.empty()) {
|
||||
xelement->SetAttribute("filename", _filename);
|
||||
}
|
||||
if (_size != 0) {
|
||||
xelement->SetAttribute("size", _size);
|
||||
}
|
||||
if (_timestamp != 0) {
|
||||
xelement->SetAttribute("timestamp", _timestamp);
|
||||
}
|
||||
if (_got_hash) {
|
||||
char hash[hash_size * 2 + 1];
|
||||
encode_hex(hash, _hash, hash_size);
|
||||
hash[hash_size * 2] = '\0';
|
||||
xelement->SetAttribute("hash", hash);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: FileSpec::quick_verify
|
||||
// Access: Public
|
||||
@ -272,6 +296,7 @@ check_hash(const string &pathname) const {
|
||||
bool FileSpec::
|
||||
read_hash(const string &pathname) {
|
||||
memset(_hash, 0, hash_size);
|
||||
_got_hash = false;
|
||||
|
||||
ifstream stream(pathname.c_str(), ios::in | ios::binary);
|
||||
if (!stream) {
|
||||
@ -294,6 +319,7 @@ read_hash(const string &pathname) {
|
||||
}
|
||||
|
||||
MD5_Final(_hash, &ctx);
|
||||
_got_hash = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -34,12 +34,14 @@ public:
|
||||
~FileSpec();
|
||||
|
||||
void load_xml(TiXmlElement *xelement);
|
||||
void store_xml(TiXmlElement *xelement);
|
||||
|
||||
inline const string &get_filename() const;
|
||||
inline void set_filename(const string &filename);
|
||||
inline string get_pathname(const string &package_dir) const;
|
||||
inline size_t get_size() const;
|
||||
inline time_t get_timestamp() const;
|
||||
inline bool has_hash() const;
|
||||
|
||||
bool quick_verify(const string &package_dir);
|
||||
bool quick_verify_pathname(const string &pathname);
|
||||
|
@ -107,7 +107,8 @@ get_contents_seq() const {
|
||||
// Function: P3DHost::check_contents_hash
|
||||
// Access: Public
|
||||
// Description: Returns true if the indicated pathname has the same
|
||||
// md5 hash as the contents.xml file, false otherwise.
|
||||
// md5 hash as the contents.xml file (as provided by the
|
||||
// server), false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline bool P3DHost::
|
||||
check_contents_hash(const string &pathname) const {
|
||||
|
@ -40,6 +40,7 @@ P3DHost(const string &host_url) :
|
||||
_descriptive_name = _host_url;
|
||||
|
||||
_xcontents = NULL;
|
||||
_contents_expiration = 0;
|
||||
_contents_seq = 0;
|
||||
}
|
||||
|
||||
@ -106,6 +107,25 @@ get_alt_host(const string &alt_host) {
|
||||
return this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DHost::has_current_contents_file
|
||||
// Access: Public
|
||||
// Description: Returns true if a contents.xml file has been
|
||||
// successfully read for this host and is still current,
|
||||
// false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DHost::
|
||||
has_current_contents_file(P3DInstanceManager *inst_mgr) const {
|
||||
if (!inst_mgr->get_verify_contents()) {
|
||||
// If we're not asking to verify contents, then contents.xml files
|
||||
// never expire.
|
||||
return has_contents_file();
|
||||
}
|
||||
|
||||
time_t now = time(NULL);
|
||||
return now < _contents_expiration && (_xcontents != NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DHost::read_contents_file
|
||||
// Access: Public
|
||||
@ -122,21 +142,21 @@ read_contents_file() {
|
||||
}
|
||||
|
||||
string standard_filename = _host_dir + "/contents.xml";
|
||||
return read_contents_file(standard_filename);
|
||||
return read_contents_file(standard_filename, false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DHost::read_contents_file
|
||||
// Access: Public
|
||||
// Description: Reads the contents.xml file in the indicated
|
||||
// filename. On success, copies the contents.xml file
|
||||
// filename. On success, writes the contents.xml file
|
||||
// into the standard location (if it's not there
|
||||
// already).
|
||||
//
|
||||
// Returns true on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DHost::
|
||||
read_contents_file(const string &contents_filename) {
|
||||
read_contents_file(const string &contents_filename, bool fresh_download) {
|
||||
TiXmlDocument doc(contents_filename.c_str());
|
||||
if (!doc.LoadFile()) {
|
||||
return false;
|
||||
@ -152,8 +172,51 @@ read_contents_file(const string &contents_filename) {
|
||||
}
|
||||
_xcontents = (TiXmlElement *)xcontents->Clone();
|
||||
++_contents_seq;
|
||||
_contents_spec = FileSpec();
|
||||
|
||||
int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE;
|
||||
xcontents->Attribute("max_age", &max_age);
|
||||
|
||||
// Get the latest possible expiration time, based on the max_age
|
||||
// indication. Any expiration time later than this is in error.
|
||||
time_t now = time(NULL);
|
||||
_contents_expiration = now + (time_t)max_age;
|
||||
|
||||
if (fresh_download) {
|
||||
_contents_spec.read_hash(contents_filename);
|
||||
|
||||
// Update the XML with the new download information.
|
||||
TiXmlElement *xorig = xcontents->FirstChildElement("orig");
|
||||
while (xorig != NULL) {
|
||||
xcontents->RemoveChild(xorig);
|
||||
xorig = xcontents->FirstChildElement("orig");
|
||||
}
|
||||
|
||||
xorig = new TiXmlElement("orig");
|
||||
xcontents->LinkEndChild(xorig);
|
||||
_contents_spec.store_xml(xorig);
|
||||
|
||||
xorig->SetAttribute("expiration", (int)_contents_expiration);
|
||||
|
||||
} else {
|
||||
// Read the download hash and expiration time from the XML.
|
||||
int expiration = 0;
|
||||
TiXmlElement *xorig = xcontents->FirstChildElement("orig");
|
||||
if (xorig != NULL) {
|
||||
_contents_spec.load_xml(xorig);
|
||||
xorig->Attribute("expiration", &expiration);
|
||||
}
|
||||
if (!_contents_spec.has_hash()) {
|
||||
_contents_spec.read_hash(contents_filename);
|
||||
}
|
||||
|
||||
_contents_expiration = min(_contents_expiration, (time_t)expiration);
|
||||
}
|
||||
|
||||
nout << "read contents.xml, max_age = " << max_age
|
||||
<< ", expires in " << max(_contents_expiration, now) - now
|
||||
<< " s\n";
|
||||
|
||||
TiXmlElement *xhost = _xcontents->FirstChildElement("host");
|
||||
if (xhost != NULL) {
|
||||
const char *url = xhost->Attribute("url");
|
||||
@ -194,12 +257,18 @@ read_contents_file(const string &contents_filename) {
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
if (_host_url == inst_mgr->get_host_url()) {
|
||||
@ -208,14 +277,13 @@ read_contents_file(const string &contents_filename) {
|
||||
// iteration.
|
||||
string top_filename = inst_mgr->get_root_dir() + "/contents.xml";
|
||||
if (standardize_filename(top_filename) !=
|
||||
standardize_filename(contents_filename)) {
|
||||
if (!copy_file(contents_filename, top_filename)) {
|
||||
standardize_filename(standard_filename)) {
|
||||
if (!copy_file(standard_filename, top_filename)) {
|
||||
nout << "Couldn't copy to " << top_filename << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -288,24 +356,29 @@ get_package(const string &package_name, const string &package_version,
|
||||
|
||||
PackageMap &package_map = _packages[alt_host];
|
||||
|
||||
P3DPackage *package = NULL;
|
||||
|
||||
string key = package_name + "_" + package_version;
|
||||
PackageMap::iterator pi = package_map.find(key);
|
||||
if (pi != package_map.end()) {
|
||||
P3DPackage *package = (*pi).second;
|
||||
if (!package->get_failed()) {
|
||||
return package;
|
||||
}
|
||||
// We've previously installed this package.
|
||||
package = (*pi).second;
|
||||
|
||||
if (package->get_failed()) {
|
||||
// If the package has previously failed, move it aside and try
|
||||
// again (maybe it just failed because the user interrupted it).
|
||||
nout << "Package " << key << " has previously failed; trying again.\n";
|
||||
_failed_packages.push_back(package);
|
||||
(*pi).second = NULL;
|
||||
package = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
P3DPackage *package =
|
||||
if (package == NULL) {
|
||||
package =
|
||||
new P3DPackage(this, package_name, package_version, alt_host);
|
||||
package_map[key] = package;
|
||||
}
|
||||
|
||||
return package;
|
||||
}
|
||||
@ -694,3 +767,41 @@ copy_file(const string &from_filename, const string &to_filename) {
|
||||
unlink(temp_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DHost::save_xml_file
|
||||
// Access: Private, Static
|
||||
// Description: Stores the XML document to the file named by
|
||||
// to_filename, safely.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DHost::
|
||||
save_xml_file(TiXmlDocument *doc, const string &to_filename) {
|
||||
// Save to a temporary file first, in case (a) we have different
|
||||
// processes writing to the same file, and (b) to prevent partially
|
||||
// overwriting the file should something go wrong.
|
||||
ostringstream strm;
|
||||
strm << to_filename << ".t";
|
||||
#ifdef _WIN32
|
||||
strm << GetCurrentProcessId() << "_" << GetCurrentThreadId();
|
||||
#else
|
||||
strm << getpid();
|
||||
#endif
|
||||
string temp_filename = strm.str();
|
||||
|
||||
if (!doc->SaveFile(temp_filename.c_str())) {
|
||||
unlink(temp_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
unlink(to_filename.c_str());
|
||||
if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
unlink(temp_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
@ -43,11 +43,12 @@ public:
|
||||
P3DHost *get_alt_host(const string &alt_host);
|
||||
|
||||
inline bool has_contents_file() const;
|
||||
bool has_current_contents_file(P3DInstanceManager *inst_mgr) const;
|
||||
inline int get_contents_seq() const;
|
||||
inline bool check_contents_hash(const string &pathname) const;
|
||||
|
||||
bool read_contents_file();
|
||||
bool read_contents_file(const string &contents_filename);
|
||||
bool read_contents_file(const string &contents_filename, bool fresh_download);
|
||||
void read_xhost(TiXmlElement *xhost);
|
||||
|
||||
P3DPackage *get_package(const string &package_name,
|
||||
@ -72,6 +73,7 @@ private:
|
||||
|
||||
static string standardize_filename(const string &filename);
|
||||
static bool copy_file(const string &from_filename, const string &to_filename);
|
||||
static bool save_xml_file(TiXmlDocument *doc, const string &to_filename);
|
||||
|
||||
private:
|
||||
string _host_dir;
|
||||
@ -80,6 +82,7 @@ private:
|
||||
string _download_url_prefix;
|
||||
string _descriptive_name;
|
||||
TiXmlElement *_xcontents;
|
||||
time_t _contents_expiration;
|
||||
int _contents_seq;
|
||||
FileSpec _contents_spec;
|
||||
|
||||
|
@ -1141,6 +1141,11 @@ get_packages_info_ready() const {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DInstance::
|
||||
get_packages_ready() const {
|
||||
if (!_packages_specified) {
|
||||
// We haven't even specified the full set of required packages yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
Packages::const_iterator pi;
|
||||
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
|
||||
if (!(*pi)->get_ready()) {
|
||||
@ -1384,6 +1389,7 @@ void P3DInstance::
|
||||
splash_button_clicked_main_thread() {
|
||||
if (is_failed()) {
|
||||
// Can't click the button after we've failed.
|
||||
nout << "Ignoring click for failed instance\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1391,6 +1397,8 @@ splash_button_clicked_main_thread() {
|
||||
auth_button_clicked();
|
||||
} else if (_session == NULL) {
|
||||
play_button_clicked();
|
||||
} else {
|
||||
nout << "Ignoring click for already-started instance\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -2360,9 +2368,6 @@ handle_notify_request(const string &message) {
|
||||
// Once Python is up and running, we can get the actual main
|
||||
// object from the Python side, and merge it with our own.
|
||||
|
||||
// But only if this web page is allowed to call our scripting
|
||||
// functions.
|
||||
if (_matches_script_origin) {
|
||||
TiXmlDocument *doc = new TiXmlDocument;
|
||||
TiXmlElement *xcommand = new TiXmlElement("command");
|
||||
xcommand->SetAttribute("cmd", "pyobj");
|
||||
@ -2383,9 +2388,16 @@ handle_notify_request(const string &message) {
|
||||
}
|
||||
|
||||
if (result != NULL) {
|
||||
if (_matches_script_origin) {
|
||||
// We only actually merge the objects if this web page is
|
||||
// allowed to call our scripting functions.
|
||||
_panda_script_object->set_pyobj(result);
|
||||
P3D_OBJECT_DECREF(result);
|
||||
} else {
|
||||
// Otherwise, we just do a one-time application of the
|
||||
// toplevel properties down to Python.
|
||||
_panda_script_object->apply_properties(result);
|
||||
}
|
||||
P3D_OBJECT_DECREF(result);
|
||||
}
|
||||
|
||||
_panda_script_object->set_string_property("status", "starting");
|
||||
@ -3006,7 +3018,6 @@ mark_download_complete() {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
ready_to_start() {
|
||||
nout << "_instance_started = " << _instance_started << "\n";
|
||||
if (_instance_started || is_failed()) {
|
||||
// Already started--or never mind.
|
||||
return;
|
||||
|
@ -261,12 +261,11 @@ initialize(int api_version, const string &contents_filename,
|
||||
create_runtime_environment();
|
||||
_is_initialized = true;
|
||||
|
||||
if (!_verify_contents &&
|
||||
!host_url.empty() && !contents_filename.empty()) {
|
||||
if (!host_url.empty() && !contents_filename.empty()) {
|
||||
// Attempt to pre-read the supplied contents.xml file, to avoid an
|
||||
// unnecessary download later.
|
||||
P3DHost *host = get_host(host_url);
|
||||
if (!host->read_contents_file(contents_filename)) {
|
||||
if (!host->read_contents_file(contents_filename, false)) {
|
||||
nout << "Couldn't read " << contents_filename << "\n";
|
||||
}
|
||||
}
|
||||
|
@ -289,12 +289,7 @@ set_pyobj(P3D_object *pyobj) {
|
||||
|
||||
// Now that we have a pyobj, we have to transfer down all of the
|
||||
// properties we'd set locally.
|
||||
Properties::const_iterator pi;
|
||||
for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
|
||||
const string &property_name = (*pi).first;
|
||||
P3D_object *value = (*pi).second;
|
||||
P3D_OBJECT_SET_PROPERTY(_pyobj, property_name.c_str(), false, value);
|
||||
}
|
||||
apply_properties(_pyobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -310,6 +305,41 @@ get_pyobj() const {
|
||||
return _pyobj;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMainObject::apply_properties
|
||||
// Access: Public
|
||||
// Description: Applies the locally-set properties onto the indicated
|
||||
// Python object, but does not store the object. This
|
||||
// is a one-time copy of the locally-set properties
|
||||
// (like "coreapiHostUrl" and the like) onto the
|
||||
// indicated Python object.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DMainObject::
|
||||
apply_properties(P3D_object *pyobj) {
|
||||
P3DPythonObject *p3dpyobj = NULL;
|
||||
if (pyobj->_class == &P3DObject::_object_class) {
|
||||
p3dpyobj = ((P3DObject *)pyobj)->as_python_object();
|
||||
}
|
||||
|
||||
Properties::const_iterator pi;
|
||||
for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
|
||||
const string &property_name = (*pi).first;
|
||||
P3D_object *value = (*pi).second;
|
||||
if (p3dpyobj != NULL && P3D_OBJECT_GET_TYPE(value) != P3D_OT_object) {
|
||||
// If we know we have an actual P3DPythonObject (we really
|
||||
// expect this), then we can call set_property_insecure()
|
||||
// directly, because we want to allow setting the initial
|
||||
// properties even if Javascript has no permissions to write
|
||||
// into Python. But we don't allow setting objects this way in
|
||||
// any event.
|
||||
p3dpyobj->set_property_insecure(property_name, false, value);
|
||||
} else {
|
||||
// Otherwise, we go through the generic interface.
|
||||
P3D_OBJECT_SET_PROPERTY(pyobj, property_name.c_str(), false, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMainObject::set_instance
|
||||
// Access: Public
|
||||
|
@ -65,6 +65,7 @@ public:
|
||||
|
||||
void set_pyobj(P3D_object *pyobj);
|
||||
P3D_object *get_pyobj() const;
|
||||
void apply_properties(P3D_object *pyobj);
|
||||
|
||||
void set_instance(P3DInstance *inst);
|
||||
|
||||
|
@ -352,7 +352,7 @@ fill_xml(TiXmlElement *xvalue, P3DSession *session) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DObject::get_object_array
|
||||
// Access: Public
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns a pointer to the array of objects represented
|
||||
// by this object, if any, or NULL if the object does
|
||||
// not represent an array of objects. This may also
|
||||
@ -366,7 +366,7 @@ get_object_array() {
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DObject::get_object_array_size
|
||||
// Access: Public
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns the number of elements in the array returned
|
||||
// by get_object_array(), or -1 if this object does not
|
||||
// representan array of objects.
|
||||
@ -376,6 +376,18 @@ get_object_array_size() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DObject::as_python_object
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns this object, downcast to a P3DPythonObject,
|
||||
// if it is in fact an object of that type; or NULL if
|
||||
// it is not.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3DPythonObject *P3DObject::
|
||||
as_python_object() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DObject::get_bool_property
|
||||
// Access: Public
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
#include "p3d_plugin_common.h"
|
||||
|
||||
class P3DPythonObject;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : P3DObject
|
||||
// Description : The C++ implementation of P3D_value, corresponding
|
||||
@ -56,6 +58,8 @@ public:
|
||||
virtual P3D_object **get_object_array();
|
||||
virtual int get_object_array_size();
|
||||
|
||||
virtual P3DPythonObject *as_python_object();
|
||||
|
||||
// Convenience functions.
|
||||
bool get_bool_property(const string &property);
|
||||
void set_bool_property(const string &property, bool value);
|
||||
|
@ -181,6 +181,17 @@ void P3DPackage::
|
||||
add_instance(P3DInstance *inst) {
|
||||
_instances.push_back(inst);
|
||||
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
if (!_host->has_current_contents_file(inst_mgr)) {
|
||||
// If the host needs to update its contents file, we're no longer
|
||||
// sure that we're current.
|
||||
_info_ready = false;
|
||||
_ready = false;
|
||||
_failed = false;
|
||||
_allow_data_download = false;
|
||||
nout << "No longer current: " << get_package_name() << "\n";
|
||||
}
|
||||
|
||||
begin_info_download();
|
||||
}
|
||||
|
||||
@ -381,13 +392,13 @@ begin_info_download() {
|
||||
void P3DPackage::
|
||||
download_contents_file() {
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
if (!_host->has_contents_file() && !inst_mgr->get_verify_contents()) {
|
||||
// If we're allowed to read a contents file without checking the
|
||||
// server first, try it now.
|
||||
if (!_host->has_contents_file()) {
|
||||
// First, read whatever contents file is already on disk. Maybe
|
||||
// it's current enough.
|
||||
_host->read_contents_file();
|
||||
}
|
||||
|
||||
if (_host->has_contents_file()) {
|
||||
if (_host->has_current_contents_file(inst_mgr)) {
|
||||
// We've already got a contents.xml file; go straight to the
|
||||
// package desc file.
|
||||
host_got_contents_file();
|
||||
@ -414,14 +425,15 @@ download_contents_file() {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
contents_file_download_finished(bool success) {
|
||||
if (!_host->has_contents_file()) {
|
||||
if (!success || !_host->read_contents_file(_temp_contents_file->get_filename())) {
|
||||
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";
|
||||
|
||||
// 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)) {
|
||||
!_host->read_contents_file(standard_filename, false)) {
|
||||
// Couldn't even read that. Fail.
|
||||
report_done(false);
|
||||
delete _temp_contents_file;
|
||||
@ -503,7 +515,7 @@ contents_file_redownload_finished(bool success) {
|
||||
// from what we had before.
|
||||
if (!_host->check_contents_hash(_temp_contents_file->get_filename())) {
|
||||
// It changed! Now see if we can read the new contents.
|
||||
if (!_host->read_contents_file(_temp_contents_file->get_filename())) {
|
||||
if (!_host->read_contents_file(_temp_contents_file->get_filename(), true)) {
|
||||
// Huh, appears to have changed to something bad. Never mind.
|
||||
nout << "Couldn't read " << *_temp_contents_file << "\n";
|
||||
|
||||
@ -569,7 +581,8 @@ host_got_contents_file() {
|
||||
// host.
|
||||
_alt_host.clear();
|
||||
|
||||
if (!_host->has_contents_file()) {
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
if (!_host->has_current_contents_file(inst_mgr)) {
|
||||
// Now go back and get the contents.xml file for the new host.
|
||||
download_contents_file();
|
||||
return;
|
||||
@ -1434,6 +1447,12 @@ download_finished(bool success) {
|
||||
if (!_file_spec.full_verify(_package->_package_dir)) {
|
||||
nout << "After downloading " << get_url()
|
||||
<< ", failed hash check\n";
|
||||
nout << "expected: ";
|
||||
_file_spec.output_hash(nout);
|
||||
nout << "\n got: ";
|
||||
_file_spec.get_actual_file()->output_hash(nout);
|
||||
nout << "\n";
|
||||
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +159,19 @@ set_property(const string &property, bool needs_response, P3D_object *value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return set_property_insecure(property, needs_response, value);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPythonObject::set_property_insecure
|
||||
// Access: Public
|
||||
// Description: Works as set_property(), but does not check the
|
||||
// matches_script_origin flag. Intended to be called
|
||||
// internally only, never to be called from Javascript.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DPythonObject::
|
||||
set_property_insecure(const string &property, bool needs_response,
|
||||
P3D_object *value) {
|
||||
bool bresult = !needs_response;
|
||||
|
||||
P3D_object *params[2];
|
||||
@ -168,12 +181,12 @@ set_property(const string &property, bool needs_response, P3D_object *value) {
|
||||
|
||||
if (value == NULL) {
|
||||
// Delete an attribute.
|
||||
result = call("__del_property__", needs_response, params, 1);
|
||||
result = call_insecure("__del_property__", needs_response, params, 1);
|
||||
|
||||
} else {
|
||||
// Set a new attribute.
|
||||
params[1] = value;
|
||||
result = call("__set_property__", needs_response, params, 2);
|
||||
result = call_insecure("__set_property__", needs_response, params, 2);
|
||||
}
|
||||
|
||||
P3D_OBJECT_DECREF(params[0]);
|
||||
@ -244,6 +257,19 @@ call(const string &method_name, bool needs_response,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return call_insecure(method_name, needs_response, params, num_params);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPythonObject::call_insecure
|
||||
// Access: Public
|
||||
// Description: Works as call(), but does not check the
|
||||
// matches_script_origin flag. Intended to be called
|
||||
// internally only, never to be called from Javascript.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3D_object *P3DPythonObject::
|
||||
call_insecure(const string &method_name, bool needs_response,
|
||||
P3D_object *params[], int num_params) {
|
||||
TiXmlDocument *doc = new TiXmlDocument;
|
||||
TiXmlElement *xcommand = new TiXmlElement("command");
|
||||
xcommand->SetAttribute("cmd", "pyobj");
|
||||
@ -331,6 +357,18 @@ fill_xml(TiXmlElement *xvalue, P3DSession *session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPythonObject::as_python_object
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns this object, downcast to a P3DPythonObject,
|
||||
// if it is in fact an object of that type; or NULL if
|
||||
// it is not.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3DPythonObject *P3DPythonObject::
|
||||
as_python_object() {
|
||||
return this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPythonObject::get_session
|
||||
// Access: Public
|
||||
|
@ -43,14 +43,20 @@ public:
|
||||
|
||||
virtual P3D_object *get_property(const string &property);
|
||||
virtual bool set_property(const string &property, bool needs_response, P3D_object *value);
|
||||
bool set_property_insecure(const string &property, bool needs_response,
|
||||
P3D_object *value);
|
||||
|
||||
virtual bool has_method(const string &method_name);
|
||||
virtual P3D_object *call(const string &method_name, bool needs_response,
|
||||
P3D_object *params[], int num_params);
|
||||
P3D_object *call_insecure(const string &method_name, bool needs_response,
|
||||
P3D_object *params[], int num_params);
|
||||
|
||||
virtual void output(ostream &out);
|
||||
virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session);
|
||||
|
||||
virtual P3DPythonObject *as_python_object();
|
||||
|
||||
P3DSession *get_session();
|
||||
int get_object_id();
|
||||
|
||||
|
@ -1096,6 +1096,12 @@ EXPCL_P3D_PLUGIN P3D_instance_handle_event_func P3D_instance_handle_event;
|
||||
|
||||
#endif /* P3D_FUNCTION_PROTOTYPES */
|
||||
|
||||
// The default max_age, if none is specified in a particular
|
||||
// contents.xml, is 5 seconds. This gives us enough time to start a
|
||||
// few packages downloading, without re-querying the host for a new
|
||||
// contents.xml at each operation.
|
||||
#define P3D_CONTENTS_DEFAULT_MAX_AGE 5
|
||||
|
||||
#ifdef __cplusplus
|
||||
}; /* end of extern "C" */
|
||||
#endif
|
||||
|
@ -58,6 +58,7 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode,
|
||||
_window_handle_type = window_handle_type;
|
||||
_event_type = event_type;
|
||||
_script_object = NULL;
|
||||
_contents_expiration = 0;
|
||||
_failed = false;
|
||||
_started = false;
|
||||
|
||||
@ -171,15 +172,30 @@ begin() {
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
if (!is_plugin_loaded() && !_failed) {
|
||||
// Go download the contents file, so we can download the core DLL.
|
||||
string url = PANDA_PACKAGE_HOST_URL;
|
||||
if (!url.empty() && url[url.length() - 1] != '/') {
|
||||
url += '/';
|
||||
}
|
||||
_download_url_prefix = url;
|
||||
|
||||
if (!is_plugin_loaded() && !_failed) {
|
||||
// We need to read the contents.xml file. First, check to see if
|
||||
// the version on disk is already current enough.
|
||||
bool success = false;
|
||||
|
||||
string contents_filename = _root_dir + "/contents.xml";
|
||||
if (read_contents_file(contents_filename, false)) {
|
||||
if (time(NULL) < _contents_expiration) {
|
||||
// Got the file, and it's good.
|
||||
get_core_api();
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
// Go download the latest contents.xml file.
|
||||
ostringstream strm;
|
||||
strm << url << "contents.xml";
|
||||
strm << _download_url_prefix << "contents.xml";
|
||||
|
||||
// Append a uniquifying query string to the URL to force the
|
||||
// download to go all the way through any caches. We use the time
|
||||
@ -190,6 +206,7 @@ begin() {
|
||||
PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_contents_file);
|
||||
start_download(url, req);
|
||||
}
|
||||
}
|
||||
|
||||
handle_request_loop();
|
||||
}
|
||||
@ -562,7 +579,9 @@ url_notify(const char *url, NPReason reason, void *notifyData) {
|
||||
// there's an outstanding contents.xml file on disk, try to
|
||||
// load that one as a fallback.
|
||||
string contents_filename = _root_dir + "/contents.xml";
|
||||
if (!read_contents_file(contents_filename)) {
|
||||
if (read_contents_file(contents_filename, false)) {
|
||||
get_core_api();
|
||||
} else {
|
||||
nout << "Unable to read contents file " << contents_filename << "\n";
|
||||
set_failed();
|
||||
}
|
||||
@ -1134,18 +1153,58 @@ start_download(const string &url, PPDownloadRequest *req) {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PPInstance::read_contents_file
|
||||
// Access: Private
|
||||
// Description: Reads the contents.xml file and starts the core API
|
||||
// DLL downloading, if necessary.
|
||||
// Description: Attempts to open and read the contents.xml file on
|
||||
// disk. Copies the file to its standard location
|
||||
// on success. Returns true on success, false on
|
||||
// failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool PPInstance::
|
||||
read_contents_file(const string &contents_filename) {
|
||||
read_contents_file(const string &contents_filename, bool fresh_download) {
|
||||
TiXmlDocument doc(contents_filename.c_str());
|
||||
if (!doc.LoadFile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found_core_package = false;
|
||||
|
||||
TiXmlElement *xcontents = doc.FirstChildElement("contents");
|
||||
if (xcontents != NULL) {
|
||||
int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE;
|
||||
xcontents->Attribute("max_age", &max_age);
|
||||
|
||||
// Get the latest possible expiration time, based on the max_age
|
||||
// indication. Any expiration time later than this is in error.
|
||||
time_t now = time(NULL);
|
||||
_contents_expiration = now + (time_t)max_age;
|
||||
|
||||
if (fresh_download) {
|
||||
// Update the XML with the new download information.
|
||||
TiXmlElement *xorig = xcontents->FirstChildElement("orig");
|
||||
while (xorig != NULL) {
|
||||
xcontents->RemoveChild(xorig);
|
||||
xorig = xcontents->FirstChildElement("orig");
|
||||
}
|
||||
|
||||
xorig = new TiXmlElement("orig");
|
||||
xcontents->LinkEndChild(xorig);
|
||||
|
||||
xorig->SetAttribute("expiration", (int)_contents_expiration);
|
||||
|
||||
} else {
|
||||
// Read the expiration time from the XML.
|
||||
int expiration = 0;
|
||||
TiXmlElement *xorig = xcontents->FirstChildElement("orig");
|
||||
if (xorig != NULL) {
|
||||
xorig->Attribute("expiration", &expiration);
|
||||
}
|
||||
|
||||
_contents_expiration = min(_contents_expiration, (time_t)expiration);
|
||||
}
|
||||
|
||||
nout << "read contents.xml, max_age = " << max_age
|
||||
<< ", expires in " << max(_contents_expiration, now) - now
|
||||
<< " s\n";
|
||||
|
||||
// Look for the <host> entry; it might point us at a different
|
||||
// download URL, and it might mention some mirrors.
|
||||
find_host(xcontents);
|
||||
@ -1157,8 +1216,9 @@ read_contents_file(const string &contents_filename) {
|
||||
if (name != NULL && strcmp(name, "coreapi") == 0) {
|
||||
const char *platform = xpackage->Attribute("platform");
|
||||
if (platform != NULL && strcmp(platform, DTOOL_PLATFORM) == 0) {
|
||||
get_core_api(xpackage);
|
||||
return true;
|
||||
_core_api_dll.load_xml(xpackage);
|
||||
found_core_package = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1166,10 +1226,23 @@ read_contents_file(const string &contents_filename) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_core_package) {
|
||||
// Couldn't find the coreapi package description.
|
||||
nout << "No coreapi package defined in contents file for "
|
||||
<< DTOOL_PLATFORM << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Success. Now save the file in its proper place.
|
||||
string standard_filename = _root_dir + "/contents.xml";
|
||||
|
||||
mkfile_complete(standard_filename, nout);
|
||||
if (!doc.SaveFile(standard_filename.c_str())) {
|
||||
nout << "Couldn't rewrite " << standard_filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@ -1212,12 +1285,13 @@ void PPInstance::
|
||||
downloaded_file(PPDownloadRequest *req, const string &filename) {
|
||||
switch (req->_rtype) {
|
||||
case PPDownloadRequest::RT_contents_file:
|
||||
{
|
||||
// Now we have the contents.xml file. Read this to get the
|
||||
// filename and md5 hash of our core API DLL.
|
||||
if (read_contents_file(filename)) {
|
||||
// Successfully read. Copy it into its normal place.
|
||||
string contents_filename = _root_dir + "/contents.xml";
|
||||
copy_file(filename, contents_filename);
|
||||
if (read_contents_file(filename, true)) {
|
||||
// Successfully downloaded and read, and it has been written
|
||||
// into its normal place.
|
||||
get_core_api();
|
||||
|
||||
} else {
|
||||
// Error reading the contents.xml file, or in loading the core
|
||||
@ -1227,11 +1301,14 @@ downloaded_file(PPDownloadRequest *req, const string &filename) {
|
||||
// If there's an outstanding contents.xml file on disk, try to
|
||||
// load that one as a fallback.
|
||||
string contents_filename = _root_dir + "/contents.xml";
|
||||
if (!read_contents_file(contents_filename)) {
|
||||
if (read_contents_file(contents_filename, false)) {
|
||||
get_core_api();
|
||||
} else {
|
||||
nout << "Unable to read contents file " << contents_filename << "\n";
|
||||
set_failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PPDownloadRequest::RT_core_dll:
|
||||
@ -1350,9 +1427,7 @@ send_p3d_temp_file_data() {
|
||||
// if necessary.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void PPInstance::
|
||||
get_core_api(TiXmlElement *xpackage) {
|
||||
_core_api_dll.load_xml(xpackage);
|
||||
|
||||
get_core_api() {
|
||||
if (_core_api_dll.quick_verify(_root_dir)) {
|
||||
// The DLL file is good. Just load it.
|
||||
do_load_plugin();
|
||||
@ -1477,7 +1552,9 @@ do_load_plugin() {
|
||||
#endif // P3D_PLUGIN_P3D_PLUGIN
|
||||
|
||||
nout << "Attempting to load core API from " << pathname << "\n";
|
||||
if (!load_plugin(pathname, "", "", true, "", "", "", false, false,
|
||||
string contents_filename = _root_dir + "/contents.xml";
|
||||
if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
|
||||
true, "", "", "", false, false,
|
||||
_root_dir, nout)) {
|
||||
nout << "Unable to launch core API in " << pathname << "\n";
|
||||
set_failed();
|
||||
|
@ -84,8 +84,8 @@ private:
|
||||
void open_p3d_temp_file();
|
||||
void send_p3d_temp_file_data();
|
||||
|
||||
bool read_contents_file(const string &contents_filename);
|
||||
void get_core_api(TiXmlElement *xpackage);
|
||||
bool read_contents_file(const string &contents_filename, bool fresh_download);
|
||||
void get_core_api();
|
||||
void downloaded_plugin(const string &filename);
|
||||
void do_load_plugin();
|
||||
|
||||
@ -140,6 +140,7 @@ private:
|
||||
CoreUrls _core_urls;
|
||||
|
||||
FileSpec _core_api_dll;
|
||||
time_t _contents_expiration;
|
||||
bool _failed;
|
||||
bool _started;
|
||||
|
||||
|
@ -353,13 +353,19 @@ post_arg_processing() {
|
||||
bool Panda3D::
|
||||
get_plugin() {
|
||||
// First, look for the existing contents.xml file.
|
||||
bool success = false;
|
||||
|
||||
Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
|
||||
if (!_verify_contents && read_contents_file(contents_filename)) {
|
||||
if (read_contents_file(contents_filename, false)) {
|
||||
if (!_verify_contents || time(NULL) < _contents_expiration) {
|
||||
// Got the file, and it's good.
|
||||
return true;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't read it, so go get it.
|
||||
if (!success) {
|
||||
// Couldn't read it (or it wasn't current enough), so go get a new
|
||||
// one.
|
||||
HTTPClient *http = HTTPClient::get_global_ptr();
|
||||
|
||||
// Try the super_mirror first.
|
||||
@ -377,19 +383,13 @@ get_plugin() {
|
||||
cerr << "Unable to download " << url << "\n";
|
||||
tempfile.unlink();
|
||||
} else {
|
||||
// Successfully downloaded from the super_mirror; move it into
|
||||
// place and try to read it.
|
||||
contents_filename.make_dir();
|
||||
contents_filename.unlink();
|
||||
tempfile.rename_to(contents_filename);
|
||||
if (read_contents_file(contents_filename)) {
|
||||
return true;
|
||||
// Successfully downloaded from the super_mirror; try to read it.
|
||||
success = read_contents_file(tempfile, true);
|
||||
tempfile.unlink();
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to download from the super_mirror.
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
// Go download contents.xml from the actual host.
|
||||
ostringstream strm;
|
||||
strm << _host_url_prefix << "contents.xml";
|
||||
@ -407,45 +407,89 @@ get_plugin() {
|
||||
PT(HTTPChannel) channel = http->make_channel(false);
|
||||
channel->get_document(request);
|
||||
|
||||
// Since we have to download some of it, might as well ask the core
|
||||
// API to check all of it.
|
||||
_verify_contents = true;
|
||||
|
||||
// First, download it to a temporary file.
|
||||
Filename tempfile = Filename::temporary("", "p3d_");
|
||||
if (!channel->download_to_file(tempfile)) {
|
||||
cerr << "Unable to download " << url << "\n";
|
||||
tempfile.unlink();
|
||||
|
||||
// Couldn't download, but fall through and try to read the
|
||||
// contents.xml file anyway. Maybe it's good enough.
|
||||
// Couldn't download, but try to read the existing contents.xml
|
||||
// file anyway. Maybe it's good enough.
|
||||
success = read_contents_file(contents_filename, false);
|
||||
|
||||
} else {
|
||||
// Successfully downloaded; move the temporary file into place.
|
||||
contents_filename.make_dir();
|
||||
contents_filename.unlink();
|
||||
tempfile.rename_to(contents_filename);
|
||||
// Successfully downloaded; read it and move it into place.
|
||||
success = read_contents_file(tempfile, true);
|
||||
}
|
||||
|
||||
// Since we had to download some of it, might as well ask the core
|
||||
// API to check all of it.
|
||||
_verify_contents = true;
|
||||
tempfile.unlink();
|
||||
}
|
||||
}
|
||||
|
||||
return read_contents_file(contents_filename);
|
||||
if (success) {
|
||||
// Now that we've downloaded the contents file successfully, start
|
||||
// the Core API.
|
||||
success = get_core_api();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Panda3D::read_contents_file
|
||||
// Access: Protected
|
||||
// Description: Attempts to open and read the contents.xml file on
|
||||
// disk, and uses that data to load the plugin, if
|
||||
// possible. Returns true on success, false on failure.
|
||||
// disk. Copies the file to its standard location
|
||||
// on success. Returns true on success, false on
|
||||
// failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3D::
|
||||
read_contents_file(const Filename &contents_filename) {
|
||||
read_contents_file(const Filename &contents_filename, bool fresh_download) {
|
||||
string os_contents_filename = contents_filename.to_os_specific();
|
||||
TiXmlDocument doc(os_contents_filename.c_str());
|
||||
if (!doc.LoadFile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found_core_package = false;
|
||||
|
||||
TiXmlElement *xcontents = doc.FirstChildElement("contents");
|
||||
if (xcontents != NULL) {
|
||||
int max_age = P3D_CONTENTS_DEFAULT_MAX_AGE;
|
||||
xcontents->Attribute("max_age", &max_age);
|
||||
|
||||
// Get the latest possible expiration time, based on the max_age
|
||||
// indication. Any expiration time later than this is in error.
|
||||
time_t now = time(NULL);
|
||||
_contents_expiration = now + (time_t)max_age;
|
||||
|
||||
if (fresh_download) {
|
||||
// Update the XML with the new download information.
|
||||
TiXmlElement *xorig = xcontents->FirstChildElement("orig");
|
||||
while (xorig != NULL) {
|
||||
xcontents->RemoveChild(xorig);
|
||||
xorig = xcontents->FirstChildElement("orig");
|
||||
}
|
||||
|
||||
xorig = new TiXmlElement("orig");
|
||||
xcontents->LinkEndChild(xorig);
|
||||
|
||||
xorig->SetAttribute("expiration", (int)_contents_expiration);
|
||||
|
||||
} else {
|
||||
// Read the expiration time from the XML.
|
||||
int expiration = 0;
|
||||
TiXmlElement *xorig = xcontents->FirstChildElement("orig");
|
||||
if (xorig != NULL) {
|
||||
xorig->Attribute("expiration", &expiration);
|
||||
}
|
||||
|
||||
_contents_expiration = min(_contents_expiration, (time_t)expiration);
|
||||
}
|
||||
|
||||
// Look for the <host> entry; it might point us at a different
|
||||
// download URL, and it might mention some mirrors.
|
||||
find_host(xcontents);
|
||||
@ -457,7 +501,9 @@ read_contents_file(const Filename &contents_filename) {
|
||||
if (name != NULL && strcmp(name, "coreapi") == 0) {
|
||||
const char *platform = xpackage->Attribute("platform");
|
||||
if (platform != NULL && _this_platform == string(platform)) {
|
||||
return get_core_api(contents_filename, xpackage);
|
||||
_core_api_dll.load_xml(xpackage);
|
||||
found_core_package = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,10 +511,38 @@ read_contents_file(const Filename &contents_filename) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_core_package) {
|
||||
// Couldn't find the coreapi package description.
|
||||
nout << "No coreapi package defined in contents file for "
|
||||
<< _this_platform << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Success. Now copy the file into place.
|
||||
Filename standard_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
|
||||
if (fresh_download) {
|
||||
Filename tempfile = Filename::temporary("", "p3d_");
|
||||
string os_specific = tempfile.to_os_specific();
|
||||
if (!doc.SaveFile(os_specific.c_str())) {
|
||||
nout << "Couldn't write to " << tempfile << "\n";
|
||||
tempfile.unlink();
|
||||
return false;
|
||||
}
|
||||
tempfile.rename_to(standard_filename);
|
||||
nout << "rewrote " << standard_filename << "\n";
|
||||
|
||||
} else {
|
||||
if (contents_filename != standard_filename) {
|
||||
if (!contents_filename.rename_to(standard_filename)) {
|
||||
nout << "Couldn't move contents.xml to " << standard_filename << "\n";
|
||||
contents_filename.unlink();
|
||||
return false;
|
||||
}
|
||||
nout << "moved to " << standard_filename << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@ -598,9 +672,7 @@ choose_random_mirrors(vector_string &result, int num_mirrors) {
|
||||
// if necessary.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool Panda3D::
|
||||
get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) {
|
||||
_core_api_dll.load_xml(xpackage);
|
||||
|
||||
get_core_api() {
|
||||
if (!_core_api_dll.quick_verify(_root_dir)) {
|
||||
// The DLL file needs to be downloaded. Build up our list of
|
||||
// URL's to attempt to download it from, in reverse order.
|
||||
@ -690,6 +762,7 @@ get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) {
|
||||
|
||||
bool trusted_environment = !_enable_security;
|
||||
|
||||
Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
|
||||
if (!load_plugin(pathname, contents_filename.to_os_specific(),
|
||||
_host_url, _verify_contents, _this_platform, _log_dirname,
|
||||
_log_basename, trusted_environment, _console_environment,
|
||||
|
@ -37,12 +37,12 @@ public:
|
||||
protected:
|
||||
bool post_arg_processing();
|
||||
bool get_plugin();
|
||||
bool read_contents_file(const Filename &contents_filename);
|
||||
bool read_contents_file(const Filename &contents_filename, bool fresh_download);
|
||||
void find_host(TiXmlElement *xcontents);
|
||||
void read_xhost(TiXmlElement *xhost);
|
||||
void add_mirror(string mirror_url);
|
||||
void choose_random_mirrors(vector_string &result, int num_mirrors);
|
||||
bool get_core_api(const Filename &contents_filename, TiXmlElement *xplugin);
|
||||
bool get_core_api();
|
||||
|
||||
void usage();
|
||||
|
||||
|
@ -65,6 +65,7 @@ Panda3DBase(bool console_environment) {
|
||||
_host_url = PANDA_PACKAGE_HOST_URL;
|
||||
_this_platform = DTOOL_PLATFORM;
|
||||
_verify_contents = false;
|
||||
_contents_expiration = 0;
|
||||
|
||||
// Seed the lame random number generator in rand(); we use it to
|
||||
// select a mirror for downloading.
|
||||
|
@ -74,6 +74,8 @@ protected:
|
||||
string _log_basename;
|
||||
string _this_platform;
|
||||
bool _verify_contents;
|
||||
time_t _contents_expiration;
|
||||
|
||||
P3D_window_type _window_type;
|
||||
P3D_window_handle _parent_window;
|
||||
int _win_x, _win_y;
|
||||
|
Loading…
x
Reference in New Issue
Block a user