diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 5e2fc6b4be..652cf04ce6 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -16,8 +16,10 @@ #include "p3dInstanceManager.h" #include "p3dDownload.h" #include "p3dSession.h" +#include "p3dPackage.h" #include +#include int P3DInstance::_next_instance_id = 0; @@ -61,6 +63,14 @@ P3DInstance:: DESTROY_LOCK(_request_lock); + // Tell all of the packages that we're no longer in business for + // them. + Packages::iterator pi; + for (pi = _packages.begin(); pi != _packages.end(); ++pi) { + (*pi)->cancel_instance(this); + } + _packages.clear(); + // TODO: empty _pending_requests queue and _downloads map. // TODO: Is it possible for someone to delete an instance while a @@ -239,6 +249,22 @@ feed_url_stream(int unique_id, return download_ok; } +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::add_package +// Access: Public +// Description: Adds the package to the list of packages used by this +// instance. The instance will share responsibility for +// downloading the package will any of the other +// instances that use the same package. +//////////////////////////////////////////////////////////////////// +void P3DInstance:: +add_package(P3DPackage *package) { + assert(find(_packages.begin(), _packages.end(), package) == _packages.end()); + + _packages.push_back(package); + package->set_instance(this); +} + //////////////////////////////////////////////////////////////////// // Function: P3DInstance::start_download // Access: Public diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index b1c2f8bdeb..149adfe43c 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -26,6 +26,7 @@ class P3DSession; class P3DDownload; +class P3DPackage; //////////////////////////////////////////////////////////////////// // Class : P3DInstance @@ -64,6 +65,8 @@ public: inline const string &get_session_key() const; inline const string &get_python_version() const; + void add_package(P3DPackage *package); + void start_download(P3DDownload *download); void request_stop(); @@ -80,6 +83,9 @@ private: string _python_version; P3DSession *_session; + typedef vector Packages; + Packages _packages; + typedef map Downloads; Downloads _downloads; diff --git a/direct/src/plugin/p3dInstanceManager.I b/direct/src/plugin/p3dInstanceManager.I index d4e3f0a593..4c69b03cfa 100644 --- a/direct/src/plugin/p3dInstanceManager.I +++ b/direct/src/plugin/p3dInstanceManager.I @@ -60,20 +60,6 @@ get_platform() const { return _platform; } -//////////////////////////////////////////////////////////////////// -// Function: P3DInstanceManager::get_command_instance -// Access: Public -// Description: Returns a global P3DInstance object that exists for -// the lifetime of the instance manager. This instance -// object has no reality onscreen, but can be used as a -// download host for downloading files that aren't -// specifically needed by any user-created instance. -//////////////////////////////////////////////////////////////////// -inline P3DInstance *P3DInstanceManager:: -get_command_instance() const { - return _command_instance; -} - //////////////////////////////////////////////////////////////////// // Function: P3DInstanceManager::get_num_instances // Access: Public @@ -84,3 +70,24 @@ inline int P3DInstanceManager:: get_num_instances() const { return _instances.size(); } + + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::is_pathsep +// Access: Private, Static +// Description: Returns true if the indicated character is a path +// separator character (e.g. slash or backslash), false +// otherwise. +//////////////////////////////////////////////////////////////////// +inline bool P3DInstanceManager:: +is_pathsep(char ch) { + if (ch == '/') { + return true; + } +#ifdef _WIN32 + if (ch == '\\') { + return true; + } +#endif + return false; +} diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx index bd29d8dafa..5249dbde49 100644 --- a/direct/src/plugin/p3dInstanceManager.cxx +++ b/direct/src/plugin/p3dInstanceManager.cxx @@ -54,8 +54,6 @@ P3DInstanceManager() { icc.dwICC = ICC_PROGRESS_CLASS; InitCommonControlsEx(&icc); #endif - - _command_instance = NULL; } //////////////////////////////////////////////////////////////////// @@ -71,8 +69,6 @@ P3DInstanceManager:: assert(_instances.empty()); assert(_sessions.empty()); - delete _command_instance; - #ifdef _WIN32 CloseHandle(_request_ready); #else @@ -145,8 +141,6 @@ create_instance(P3D_request_ready_func *func, //////////////////////////////////////////////////////////////////// void P3DInstanceManager:: finish_instance(P3DInstance *inst) { - assert(inst != _command_instance); - Instances::iterator ii; ii = _instances.find(inst); assert(ii != _instances.end()); @@ -183,10 +177,6 @@ check_request() { } } - if (_command_instance->has_request()) { - return _command_instance; - } - return NULL; } @@ -290,9 +280,11 @@ signal_request_ready() { // Function: P3DInstanceManager::mkdir_public // Access: Public, Static // Description: Creates a new directory with wide-open access -// priviledges. +// priviledges. Returns true on success, false on +// failure. Will create intervening directories if +// necessary. //////////////////////////////////////////////////////////////////// -void P3DInstanceManager:: +bool P3DInstanceManager:: mkdir_public(const string &dirname) { #ifdef _WIN32 SECURITY_DESCRIPTOR sd; @@ -302,10 +294,55 @@ mkdir_public(const string &dirname) { SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = &sd; - CreateDirectory(dirname.c_str(), &sa); + if (CreateDirectory(dirname.c_str(), &sa) != 0) { + // Success! + return true; + } + + // Failed. + DWORD last_error = GetLastError(); + if (last_error == ERROR_ALREADY_EXISTS) { + // Not really an error: the directory is already there. + return true; + } + + if (last_error == ERROR_PATH_NOT_FOUND) { + // We need to make the parent directory first. + string parent = get_dirname(dirname); + if (!parent.empty() && mkdir_public(parent)) { + // Parent successfully created. Try again to make the child. + if (CreateDirectory(dirname.c_str(), &sa) != 0) { + // Got it! + return true; + } + } + } + return false; #else //_WIN32 - mkdir(dirname.c_str(), 0777); + if (mkdir(dirname.c_str(), 0777) == 0) { + // Success! + return true; + } + + // Failed. + if (errno == EEXIST) { + // Not really an error: the directory is already there. + return true; + } + + if (errno == ENOENT) { + // We need to make the parent directory first. + string parent = get_dirname(dirname); + if (!parent.empty() && mkdir_public(parent)) { + // Parent successfully created. Try again to make the child. + if (mkdir(dirname.c_str(), 0777) == 0) { + // Got it! + return true; + } + } + } + return false; #endif // _WIN32 } @@ -314,7 +351,9 @@ mkdir_public(const string &dirname) { // Function: P3DInstanceManager::mkfile_public // Access: Public, Static // Description: Creates a new file with wide-open access -// priviledges. +// priviledges. Returns true on success, false on +// failure. This will create intervening directories if +// needed. //////////////////////////////////////////////////////////////////// bool P3DInstanceManager:: mkfile_public(const string &filename) { @@ -331,7 +370,17 @@ mkfile_public(const string &filename) { FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file == INVALID_HANDLE_VALUE) { - return false; + // Try to make the parent directory first. + string parent = get_dirname(filename); + if (!parent.empty() && mkdir_public(parent)) { + // Parent successfully created. Try again to make the file. + file = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + if (file == INVALID_HANDLE_VALUE) { + return false; + } } CloseHandle(file); return true; @@ -339,7 +388,15 @@ mkfile_public(const string &filename) { #else // _WIN32 int fd = creat(filename.c_str(), 0777); if (fd == -1) { - return false; + // Try to make the parent directory first. + string parent = get_dirname(filename); + if (!parent.empty() && mkdir_public(parent)) { + // Parent successfully created. Try again to make the file. + fd = creat(filename.c_str(), 0777); + } + if (fd == -1) { + return false; + } } close(fd); return true; @@ -356,23 +413,10 @@ P3DInstanceManager *P3DInstanceManager:: get_global_ptr() { if (_global_ptr == NULL) { _global_ptr = new P3DInstanceManager; - _global_ptr->create_command_instance(); } return _global_ptr; } -//////////////////////////////////////////////////////////////////// -// Function: P3DInstanceManager::create_command_instance; -// Access: Private -// Description: Create a command instance. This is used to handle -// requests that have nothing to do with any particular -// host-created instance. -//////////////////////////////////////////////////////////////////// -void P3DInstanceManager:: -create_command_instance() { - _command_instance = new P3DInstance(NULL, "", NULL, 0); -} - //////////////////////////////////////////////////////////////////// // Function: P3DInstanceManager::find_root_dir // Access: Private @@ -488,4 +532,23 @@ find_root_dir() const { #endif } +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::get_dirname +// Access: Private, Static +// Description: Returns the directory component of the indicated +// pathname, or the empty string if there is no +// directory prefix. +//////////////////////////////////////////////////////////////////// +string P3DInstanceManager:: +get_dirname(const string &filename) { + size_t p = filename.length(); + while (p > 0) { + --p; + if (is_pathsep(filename[p])) { + return filename.substr(0, p); + } + } + + return string(); +} diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h index dbb2f135c9..edf6f63556 100644 --- a/direct/src/plugin/p3dInstanceManager.h +++ b/direct/src/plugin/p3dInstanceManager.h @@ -58,20 +58,20 @@ public: const string &package_version, const string &package_display_name); - inline P3DInstance *get_command_instance() const; inline int get_num_instances() const; int get_unique_session_index(); void signal_request_ready(); - static void mkdir_public(const string &dirname); + static bool mkdir_public(const string &dirname); static bool mkfile_public(const string &dirname); static P3DInstanceManager *get_global_ptr(); private: - void create_command_instance(); string find_root_dir() const; + static inline bool is_pathsep(char ch); + static string get_dirname(const string &filename); private: bool _is_initialized; @@ -79,8 +79,6 @@ private: string _download_url; string _platform; - P3DInstance *_command_instance; - typedef set Instances; Instances _instances; diff --git a/direct/src/plugin/p3dPackage.cxx b/direct/src/plugin/p3dPackage.cxx index e35fbe8dc5..a89eb2b186 100755 --- a/direct/src/plugin/p3dPackage.cxx +++ b/direct/src/plugin/p3dPackage.cxx @@ -14,6 +14,7 @@ #include "p3dPackage.h" #include "p3dInstanceManager.h" +#include "p3dInstance.h" #include "p3dMultifileReader.h" #include "openssl/md5.h" @@ -81,23 +82,6 @@ P3DPackage(const string &package_name, const string &package_version, _desc_file_basename = _package_fullname + ".xml"; _desc_file_pathname = _package_dir + "/" + _desc_file_basename; - - // TODO: we should check the desc file for updates with the server. - // Perhaps this should be done in a parent class. - - // TODO: if the desc file exists, and is consistent with the server - // contents file, don't re-download it. - /* - // Load the desc file, if it exists. - TiXmlDocument doc(_desc_file_pathname.c_str()); - if (!doc.LoadFile()) { - download_desc_file(); - } else { - got_desc_file(&doc, false); - } - */ - - download_desc_file(); } //////////////////////////////////////////////////////////////////// @@ -116,6 +100,8 @@ P3DPackage:: delete _active_download; _active_download = NULL; } + + assert(_instances.empty()); } //////////////////////////////////////////////////////////////////// @@ -155,6 +141,87 @@ cancel_callback(Callback *callback) { } } +//////////////////////////////////////////////////////////////////// +// Function: P3DPackage::set_instance +// Access: Public +// Description: Specifies an instance that may be responsible for +// downloading this package. +//////////////////////////////////////////////////////////////////// +void P3DPackage:: +set_instance(P3DInstance *inst) { + _instances.push_back(inst); + + begin_download(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPackage::cancel_instance +// Access: Public +// Description: Indicates that the given instance will no longer be +// responsible for downloading this package. +//////////////////////////////////////////////////////////////////// +void P3DPackage:: +cancel_instance(P3DInstance *inst) { + assert(!_instances.empty()); + + if (inst == _instances[0]) { + // This was the primary instance. Cancel any pending download and + // move to the next instance. + if (_active_download != NULL) { + _active_download->cancel(); + delete _active_download; + _active_download = NULL; + } + } + + Instances::iterator ii = find(_instances.begin(), _instances.end(), inst); + assert(ii != _instances.end()); + _instances.erase(ii); + + begin_download(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPackage::begin_download +// Access: Public +// Description: Begins downloading and installing the package, if +// needed. +//////////////////////////////////////////////////////////////////// +void P3DPackage:: +begin_download() { + if (_instances.empty()) { + // Can't download without any instances. + return; + } + + if (_ready) { + // Already downloaded. + return; + } + + if (_active_download != NULL) { + // In the middle of downloading. + return; + } + + // TODO: we should check the desc file for updates with the server. + // Perhaps this should be done in a parent class. + + // TODO: if the desc file exists, and is consistent with the server + // contents file, don't re-download it. + /* + // Load the desc file, if it exists. + TiXmlDocument doc(_desc_file_pathname.c_str()); + if (!doc.LoadFile()) { + download_desc_file(); + } else { + got_desc_file(&doc, false); + } + */ + + download_desc_file(); +} + //////////////////////////////////////////////////////////////////// // Function: P3DPackage::download_desc_file // Access: Private @@ -554,8 +621,10 @@ start_download(P3DPackage::DownloadType dtype, const string &url, _active_download = download; _partial_download = false; - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - inst_mgr->get_command_instance()->start_download(download); + + assert(!_instances.empty()); + + _instances[0]->start_download(download); } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dPackage.h b/direct/src/plugin/p3dPackage.h index 1e8e362e23..9f6071b231 100755 --- a/direct/src/plugin/p3dPackage.h +++ b/direct/src/plugin/p3dPackage.h @@ -18,6 +18,8 @@ #include "p3d_plugin_common.h" #include "p3dFileDownload.h" +class P3DInstance; + //////////////////////////////////////////////////////////////////// // Class : P3DPackage // Description : This corresponds to a downloadable, patchable @@ -54,6 +56,9 @@ public: void set_callback(Callback *callback); void cancel_callback(Callback *callback); + void set_instance(P3DInstance *inst); + void cancel_instance(P3DInstance *inst); + private: enum DownloadType { DT_desc_file, @@ -73,6 +78,7 @@ private: DownloadType _dtype; }; + void begin_download(); void download_desc_file(); void desc_file_download_finished(bool success); void got_desc_file(TiXmlDocument *doc, bool freshly_downloaded); @@ -115,6 +121,9 @@ private: typedef vector Callbacks; Callbacks _callbacks; + typedef vector Instances; + Instances _instances; + enum { hash_size = 16 }; class FileSpec { diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 113d1f5975..6bf909f857 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -52,6 +52,8 @@ P3DSession(P3DInstance *inst) { P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); _panda3d = inst_mgr->get_package("panda3d", "dev", "Panda3D"); + inst->add_package(_panda3d); + _panda3d_callback = NULL; _python_root_dir = _panda3d->get_package_dir();