Browse Source

make panda3d.exe download the core API too

David Rose 16 years ago
parent
commit
2d3fd6e709

+ 3 - 1
direct/src/plugin/make_contents.py

@@ -53,9 +53,11 @@ class ContentsMaker:
         f = open(contentsFilePathname.toOsSpecific(), 'w')
         print >> f, '<?xml version="1.0" ?>'
         print >> f, ''
+        print >> f, '<contents>'
         for packageName, packageVersion, packagePlatform, file in self.packages:
-            print >> f, '<package name="%s" version="%s" platform="%s" %s />' % (
+            print >> f, '  <package name="%s" version="%s" platform="%s" %s />' % (
                 packageName, packageVersion, packagePlatform or '', file.getParams())
+        print >> f, '</contents>'
         f.close()
 
     def scanDirectory(self):

+ 13 - 10
direct/src/plugin_npapi/ppInstance.cxx

@@ -703,18 +703,21 @@ read_contents_file(const string &filename) {
     return false;
   }
 
-  TiXmlElement *xpackage = doc.FirstChildElement("package");
-  while (xpackage != NULL) {
-    const char *name = xpackage->Attribute("name");
-    if (name != NULL && strcmp(name, "coreapi") == 0) {
-      const char *platform = xpackage->Attribute("platform");
-      if (platform != NULL && strcmp(platform, P3D_PLUGIN_PLATFORM) == 0) {
-        get_core_api(xpackage);
-        return true;
+  TiXmlElement *xcontents = doc.FirstChildElement("contents");
+  if (xcontents != NULL) {
+    TiXmlElement *xpackage = xcontents->FirstChildElement("package");
+    while (xpackage != NULL) {
+      const char *name = xpackage->Attribute("name");
+      if (name != NULL && strcmp(name, "coreapi") == 0) {
+        const char *platform = xpackage->Attribute("platform");
+        if (platform != NULL && strcmp(platform, P3D_PLUGIN_PLATFORM) == 0) {
+          get_core_api(xpackage);
+          return true;
+        }
       }
-    }
     
-    xpackage = xpackage->NextSiblingElement("package");
+      xpackage = xpackage->NextSiblingElement("package");
+    }
   }
 
   // Couldn't find the coreapi package description.

+ 151 - 68
direct/src/plugin_standalone/panda3d.cxx

@@ -15,7 +15,10 @@
 #include "panda3d.h"
 #include "httpClient.h"
 #include "load_plugin.h"
+#include "find_root_dir.h"
+#include "p3d_plugin_config.h"
 
+#include <sstream>
 #ifdef _WIN32
 #include <windows.h>
 #endif
@@ -36,6 +39,7 @@
 ////////////////////////////////////////////////////////////////////
 Panda3D::
 Panda3D() {
+  _root_dir = find_root_dir();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -48,9 +52,12 @@ int Panda3D::
 run(int argc, char *argv[]) {
   extern char *optarg;
   extern int optind;
-  const char *optstr = "p:l:t:s:o:h";
+  const char *optstr = "u:p:fl:t:s:o:h";
+
+  string root_url = P3D_PLUGIN_DOWNLOAD;
+  string this_platform = P3D_PLUGIN_PLATFORM;
+  bool force_download = false;
 
-  Filename p3d_plugin_filename;
   Filename output_filename;
   P3D_window_type window_type = P3D_WT_toplevel;
   int win_x = 0, win_y = 0;
@@ -60,8 +67,19 @@ run(int argc, char *argv[]) {
 
   while (flag != EOF) {
     switch (flag) {
+    case 'u':
+      root_url = optarg;
+      if (!root_url.empty() && root_url[root_url.length() - 1] != '/') {
+        root_url += '/';
+      }
+      break;
+
     case 'p':
-      p3d_plugin_filename = Filename::from_os_specific(optarg);
+      this_platform = optarg;
+      break;
+
+    case 'f':
+      force_download = true;
       break;
 
     case 'l':
@@ -114,7 +132,7 @@ run(int argc, char *argv[]) {
     return 1;
   }
 
-  if (!load_plugin(p3d_plugin_filename.to_os_specific())) {
+  if (!get_plugin(root_url, this_platform, force_download)) {
     cerr << "Unable to load Panda3D plugin.\n";
     return 1;
   }
@@ -257,66 +275,132 @@ run(int argc, char *argv[]) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::feed_file
+//     Function: Panda3D::get_plugin
 //       Access: Private
-//  Description: Opens the named file (extracted from a file:// URL)
-//               and feeds its contents to the plugin.
+//  Description: Downloads the contents.xml file from the named URL
+//               and attempts to use it to load the core API.  Returns
+//               true on success, false on failure.
 ////////////////////////////////////////////////////////////////////
-void Panda3D::
-feed_file(P3D_instance *inst, int unique_id, string filename) {
-#ifdef _WIN32 
-  // On Windows, we have to munge the filename specially, because it's
-  // been URL-munged.  It might begin with a leading slash as well as
-  // a drive letter.  Clean up that nonsense.
-  if (!filename.empty()) {
-    if (filename[0] == '/' || filename[0] == '\\') {
-      Filename fname = Filename::from_os_specific(filename.substr(1));
-      if (fname.is_local()) {
-        // Put the slash back on.
-        fname = string("/") + fname.get_fullpath();
+bool Panda3D::
+get_plugin(const string &root_url, const string &this_platform, bool force_download) {
+  // First, look for the existing contents.xml file.
+  Filename contents = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
+  if (!force_download && read_contents_file(contents, root_url, this_platform)) {
+    // Got the file, and it's good.
+    return true;
+  }
+
+  // Couldn't read it, so go get it.
+  string url = root_url;
+  url += "contents.xml";
+  cerr << "Getting URL " << root_url << "\n";
+  
+  HTTPClient *http = HTTPClient::get_global_ptr();
+  PT(HTTPChannel) channel = http->get_document(url);
+  Ramfile rf;
+  if (!channel->download_to_file(contents)) {
+    cerr << "Unable to download " << url << "\n";
+    return false;
+  }
+  
+  return read_contents_file(contents, root_url, this_platform);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::read_contents_file
+//       Access: Private
+//  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.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+read_contents_file(Filename contents, const string &root_url, 
+                   const string &this_platform) {
+  ifstream in;
+  contents.set_text();
+  if (!contents.open_read(in)) {
+    cerr << "Couldn't read " << contents.to_os_specific() << "\n";
+    return false;
+  }
+
+  TiXmlDocument doc;
+  in >> doc;
+
+  TiXmlElement *xcontents = doc.FirstChildElement("contents");
+  if (xcontents != NULL) {
+    TiXmlElement *xpackage = xcontents->FirstChildElement("package");
+    while (xpackage != NULL) {
+      const char *name = xpackage->Attribute("name");
+      if (name != NULL && strcmp(name, "coreapi") == 0) {
+        const char *xplatform = xpackage->Attribute("platform");
+        if (xplatform != NULL && strcmp(xplatform, this_platform.c_str()) == 0) {
+          return get_core_api(root_url, xpackage);
+        }
       }
-      filename = fname.to_os_specific();
+      
+      xpackage = xpackage->NextSiblingElement("package");
     }
   }
-#endif  // _WIN32
 
-  ifstream file(filename.c_str(), ios::in | ios::binary);
-
-  // First, seek to the end to get the file size.
-  file.seekg(0, ios::end);
-  size_t file_size = file.tellg();
+  // Couldn't find the coreapi package description.
+  cerr << "No coreapi package defined in contents file for "
+       << this_platform << "\n";
+  return false;
+}
 
-  // Then return to the beginning to read the data.
-  file.seekg(0, ios::beg);
 
-  static const size_t buffer_size = 4096;
-  char buffer[buffer_size];
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::get_core_api
+//       Access: Private
+//  Description: Checks the core API DLL file against the
+//               specification in the contents file, and downloads it
+//               if necessary.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+get_core_api(const string &root_url, TiXmlElement *xpackage) {
+  _core_api_dll.load_xml(xpackage);
 
-  file.read(buffer, buffer_size);
-  size_t count = file.gcount();
-  size_t total_count = 0;
-  while (count != 0) {
-    bool download_ok = P3D_instance_feed_url_stream
-      (inst, unique_id, P3D_RC_in_progress,
-       0, file_size, (const unsigned char *)buffer, count);
+  if (!_core_api_dll.quick_verify(_root_dir)) {
+    // The DLL file needs to be downloaded.  Go get it.
+    string url = root_url;
+    url += _core_api_dll.get_filename();
+    
+    Filename pathname = Filename::from_os_specific(_core_api_dll.get_pathname(_root_dir));
+    HTTPClient *http = HTTPClient::get_global_ptr();
+    PT(HTTPChannel) channel = http->get_document(url);
+    if (!channel->download_to_file(pathname)) {
+      cerr << "Unable to download " << url << "\n";
+      return false;
+    }
 
-    if (!download_ok) {
-      // Never mind.
-      return;
+    if (!_core_api_dll.quick_verify(_root_dir)) {
+      cerr << "Mismatched download for " << url << "\n";
+      return false;
     }
-    file.read(buffer, buffer_size);
-    count = file.gcount();
-    total_count += count;
   }
-  
-  P3D_result_code result = P3D_RC_done;
-  if (file.fail() && !file.eof()) {
-    // Got an error while reading.
-    result = P3D_RC_generic_error;
+
+  // Now we've got the DLL.  Load it.
+  string pathname = _core_api_dll.get_pathname(_root_dir);
+
+#ifdef P3D_PLUGIN_P3D_PLUGIN
+  // This is a convenience macro for development.  If defined and
+  // nonempty, it indicates the name of the plugin DLL that we will
+  // actually run, even after downloading a possibly different
+  // (presumably older) version.  Its purpose is to simplify iteration
+  // on the plugin DLL.
+  string override_filename = P3D_PLUGIN_P3D_PLUGIN;
+  if (!override_filename.empty()) {
+    pathname = override_filename;
   }
+#endif  // P3D_PLUGIN_P3D_PLUGIN
 
-  P3D_instance_feed_url_stream
-    (inst, unique_id, result, 0, total_count, NULL, 0);
+  if (!load_plugin(pathname)) {
+    cerr << "Unable to launch core API in " << pathname << "\n" << flush;
+    return false;
+  }
+
+  // Successfully loaded.
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -370,18 +454,9 @@ handle_request(P3D_request *request) {
     {
       int unique_id = request->_request._get_url._unique_id;
       const string &url = request->_request._get_url._url;
-      if (url.substr(0, 7) == "file://") {
-        // A special case: if the url begins with "file://", just open
-        // the named file and feed it directly to the plugin.
-        feed_file(request->_instance, unique_id, url.substr(7));
-
-      } else {
-        // Otherwise, assume it's a legitimate URL and pass it down to
-        // Panda's HTTPClient to read it.
-        URLGetter *getter = new URLGetter
-          (request->_instance, unique_id, URLSpec(url), "");
-        _url_getters.insert(getter);
-      }
+      URLGetter *getter = new URLGetter
+        (request->_instance, unique_id, URLSpec(url), "");
+      _url_getters.insert(getter);
       handled = true;
     }
     break;
@@ -570,11 +645,6 @@ usage() {
 
     << "Options:\n\n"
 
-    << "  -p " << get_plugin_basename() << "\n"
-    << "    Specify the full path to the particular Panda plugin DLL to\n"
-    << "    run.  Normally, this will be found by searching in the usual\n"
-    << "    places.\n\n"
-
     << "  -l output.log\n"
     << "    Specify the name of the file to receive the log output of the\n"
     << "    plugin process(es).  The default is to send this output to the\n"
@@ -589,7 +659,20 @@ usage() {
 
     << "  -o x,y\n"
     << "    Specify the position (origin) of the graphic window on the\n"
-    << "    screen, or on the parent window.\n\n";
+    << "    screen, or on the parent window.\n\n"
+
+    << "  -f\n"
+    << "    Force a HTTP contact to the Panda3D download server, to check\n"
+    << "    if a new version is available.  Normally, this is done only\n"
+    << "    if contents.xml cannot be read.\n\n"
+
+    << "  -u url\n"
+    << "    Specify the URL of the Panda3D download server.  The default is\n"
+    << "    " << P3D_PLUGIN_DOWNLOAD << "\n\n"
+
+    << "  -p platform\n"
+    << "    Specify the platform to masquerade as.  The default is "
+    << P3D_PLUGIN_PLATFORM << "\n\n";
 }
 
 ////////////////////////////////////////////////////////////////////

+ 9 - 1
direct/src/plugin_standalone/panda3d.h

@@ -27,6 +27,7 @@
 #include "p3d_plugin.h"
 #include "httpChannel.h"
 #include "ramfile.h"
+#include "fileSpec.h"
 #include "pset.h"
 
 ////////////////////////////////////////////////////////////////////
@@ -41,7 +42,11 @@ public:
   int run(int argc, char *argv[]);
 
 private:
-  void feed_file(P3D_instance *inst, int unique_id, string filename);
+  bool get_plugin(const string &root_url, const string &this_platform,
+                  bool force_download);
+  bool read_contents_file(Filename contents, const string &root_url, 
+                          const string &this_platform);
+  bool get_core_api(const string &root_url, TiXmlElement *xpackage);
   void run_getters();
   void handle_request(P3D_request *request);
   void make_parent_window(P3D_window_handle &parent_window, 
@@ -58,6 +63,9 @@ private:
   bool parse_int_pair(char *arg, int &x, int &y);
 
 private:
+  string _root_dir;
+  FileSpec _core_api_dll;
+
   typedef pset<P3D_instance *> Instances;
   Instances _instances;