Browse Source

bi-directional communication

David Rose 16 years ago
parent
commit
ce182cb038

+ 2 - 1
direct/src/directnotify/Notifier.py

@@ -6,6 +6,7 @@ from LoggerGlobal import defaultLogger
 from direct.showbase import PythonUtil
 from direct.showbase import PythonUtil
 import time
 import time
 import types
 import types
+import sys
 
 
 class Notifier:
 class Notifier:
     serverDelta = 0
     serverDelta = 0
@@ -237,7 +238,7 @@ class Notifier:
         if self.streamWriter:
         if self.streamWriter:
             self.streamWriter.appendData(string + '\n')
             self.streamWriter.appendData(string + '\n')
         else:
         else:
-            print string
+            print >> sys.stderr, string
 
 
     def debugStateCall(self, obj=None, fsmMemberName='fsm',
     def debugStateCall(self, obj=None, fsmMemberName='fsm',
             secondaryFsm='secondaryFSM'):
             secondaryFsm='secondaryFSM'):

+ 11 - 3
direct/src/plugin/Sources.pp

@@ -33,7 +33,7 @@
   #define USE_PACKAGES tinyxml python
   #define USE_PACKAGES tinyxml python
   #define TARGET p3dpython
   #define TARGET p3dpython
 
 
-#define OTHER_LIBS \
+  #define OTHER_LIBS \
     dtoolutil:c dtoolbase:c dtool:m \
     dtoolutil:c dtoolbase:c dtool:m \
     interrogatedb:c dconfig:c dtoolconfig:m \
     interrogatedb:c dconfig:c dtoolconfig:m \
     express:c pandaexpress:m \
     express:c pandaexpress:m \
@@ -51,11 +51,19 @@
 #end bin_target
 #end bin_target
 
 
 #begin bin_target
 #begin bin_target
+  #define USE_PACKAGES openssl
   #define TARGET panda3d
   #define TARGET panda3d
 
 
+  #define OTHER_LIBS \
+    prc:c dtoolutil:c dtoolbase:c dtool:m \
+    interrogatedb:c dconfig:c dtoolconfig:m \
+    express:c pandaexpress:m \
+    pstatclient:c pandabase:c linmath:c putil:c \
+    pipeline:c panda:m \
+    pystub
+
   #define SOURCES \
   #define SOURCES \
-    panda3d.cxx \
-    $[if $[WINDOWS_PLATFORM],wingetopt.h wingetopt.c]
+    panda3d.cxx
 
 
   #define WIN_SYS_LIBS user32.lib gdi32.lib
   #define WIN_SYS_LIBS user32.lib gdi32.lib
 
 

+ 1 - 1
direct/src/plugin/handleStreamBuf.cxx

@@ -311,7 +311,7 @@ write_chars(const char *start, size_t length) {
   BOOL success = WriteFile(_handle, start, length, &bytes_written, NULL);
   BOOL success = WriteFile(_handle, start, length, &bytes_written, NULL);
   if (!success) {
   if (!success) {
     DWORD error = GetLastError();
     DWORD error = GetLastError();
-    if (error != ERROR_BROKEN_PIPE) {
+    if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) {
       cerr
       cerr
         << "Error writing " << length
         << "Error writing " << length
         << " bytes, windows error code 0x" << hex
         << " bytes, windows error code 0x" << hex

+ 1 - 0
direct/src/plugin/p3dInstance.cxx

@@ -153,6 +153,7 @@ get_request() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 void P3DInstance::
 add_request(P3D_request *request) {
 add_request(P3D_request *request) {
+  cerr << "adding a request\n";
   assert(request->_instance == this);
   assert(request->_instance == this);
 
 
   ACQUIRE_LOCK(_request_lock);
   ACQUIRE_LOCK(_request_lock);

+ 0 - 11
direct/src/plugin/p3dInstanceManager.I

@@ -23,14 +23,3 @@ INLINE int P3DInstanceManager::
 get_num_instances() const {
 get_num_instances() const {
   return _instances.size();
   return _instances.size();
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: P3DInstanceManager::get_dll_filename
-//       Access: Public
-//  Description: Returns the DLL filename that was passed to
-//               initialize().
-////////////////////////////////////////////////////////////////////
-INLINE const string &P3DInstanceManager::
-get_dll_filename() const {
-  return _dll_filename;
-}

+ 5 - 7
direct/src/plugin/p3dInstanceManager.cxx

@@ -46,15 +46,13 @@ P3DInstanceManager::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::initialize
 //     Function: P3DInstanceManager::initialize
 //       Access: Public
 //       Access: Public
-//  Description: Called by the host at application startup, this
-//               receives the name of the DLL that contains this code
-//               (for patching purposes).  It returns true if the DLL
-//               is successfully initialized, false if it should be
-//               immediately shut down and redownloaded.
+//  Description: Called by the host at application startup.  It
+//               returns true if the DLL is successfully initialized,
+//               false if it should be immediately shut down and
+//               redownloaded.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool P3DInstanceManager::
 bool P3DInstanceManager::
-initialize(const string &config_xml, const string &dll_filename) {
-  _dll_filename = dll_filename;
+initialize() {
   return true;
   return true;
 }
 }
 
 

+ 1 - 4
direct/src/plugin/p3dInstanceManager.h

@@ -34,7 +34,7 @@ private:
   ~P3DInstanceManager();
   ~P3DInstanceManager();
 
 
 public:
 public:
-  bool initialize(const string &config_xml, const string &dll_filename);
+  bool initialize();
 
 
   P3DInstance *
   P3DInstance *
   create_instance(P3D_request_ready_func *func,
   create_instance(P3D_request_ready_func *func,
@@ -53,15 +53,12 @@ public:
 
 
   INLINE int get_num_instances() const;
   INLINE int get_num_instances() const;
 
 
-  INLINE const string &get_dll_filename() const;
-
   int get_unique_session_index();
   int get_unique_session_index();
   void signal_request_ready();
   void signal_request_ready();
 
 
   static P3DInstanceManager *get_global_ptr();
   static P3DInstanceManager *get_global_ptr();
 
 
 private:
 private:
-  string _dll_filename;
   string _p3d_root_directory;
   string _p3d_root_directory;
 
 
   typedef set<P3DInstance *> Instances;
   typedef set<P3DInstance *> Instances;

+ 29 - 16
direct/src/plugin/p3dPythonRun.cxx

@@ -103,16 +103,17 @@ run_python() {
     PyErr_Print();
     PyErr_Print();
     return false;
     return false;
   }
   }
-  _exit = PyObject_GetAttrString(appmf, "exit");
-  if (_exit == NULL) {
+  _setupWindow = PyObject_GetAttrString(appmf, "setupWindow");
+  if (_setupWindow == NULL) {
     PyErr_Print();
     PyErr_Print();
     return false;
     return false;
   }
   }
-  _setupWindow = PyObject_GetAttrString(appmf, "setupWindow");
-  if (_setupWindow == NULL) {
+  _taskMgr = PyObject_GetAttrString(appmf, "taskMgr");
+  if (_taskMgr == NULL) {
     PyErr_Print();
     PyErr_Print();
     return false;
     return false;
   }
   }
+
   Py_DECREF(appmf);
   Py_DECREF(appmf);
 
 
   // Now add check_comm() as a task.
   // Now add check_comm() as a task.
@@ -121,14 +122,8 @@ run_python() {
   task_mgr->add(_check_comm_task);
   task_mgr->add(_check_comm_task);
 
 
   // Finally, get lost in taskMgr.run().
   // Finally, get lost in taskMgr.run().
-
-  PyObject *taskMgr = PyObject_GetAttrString(appmf, "taskMgr");
-  if (taskMgr == NULL) {
-    PyErr_Print();
-    return false;
-  }
   cerr << "calling run()\n";
   cerr << "calling run()\n";
-  PyObject *done = PyObject_CallMethod(taskMgr, "run", "");
+  PyObject *done = PyObject_CallMethod(_taskMgr, "run", "");
   if (done == NULL) {
   if (done == NULL) {
     PyErr_Print();
     PyErr_Print();
     return false;
     return false;
@@ -136,8 +131,6 @@ run_python() {
   Py_DECREF(done);
   Py_DECREF(done);
   cerr << "done calling run()\n";
   cerr << "done calling run()\n";
 
 
-  Py_DECREF(taskMgr);
-
   return true;
   return true;
 }
 }
 
 
@@ -165,6 +158,8 @@ handle_command(TiXmlDocument *doc) {
         if (xcommand->Attribute("id", &id)) {
         if (xcommand->Attribute("id", &id)) {
           terminate_instance(id);
           terminate_instance(id);
         }
         }
+      } else if (strcmp(cmd, "exit") == 0) {
+        terminate_session();
         
         
       } else {
       } else {
         cerr << "Unhandled command " << cmd << "\n";
         cerr << "Unhandled command " << cmd << "\n";
@@ -251,6 +246,7 @@ void P3DPythonRun::
 join_read_thread() {
 join_read_thread() {
   cerr << "waiting for thread\n";
   cerr << "waiting for thread\n";
   _read_thread_continue = false;
   _read_thread_continue = false;
+  _pipe_read.close();
   
   
 #ifdef _WIN32
 #ifdef _WIN32
   assert(_read_thread != NULL);
   assert(_read_thread != NULL);
@@ -346,11 +342,14 @@ terminate_session() {
   }
   }
   _instances.clear();
   _instances.clear();
 
 
-  PyObject *result = PyObject_CallFunction(_exit, "");
+  cerr << "calling stop()\n";
+  PyObject *result = PyObject_CallMethod(_taskMgr, "stop", "");
   if (result == NULL) {
   if (result == NULL) {
     PyErr_Print();
     PyErr_Print();
+    return;
   }
   }
-  Py_XDECREF(result);
+  Py_DECREF(result);
+  cerr << "done calling stop()\n";
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -372,7 +371,21 @@ rt_thread_run() {
       return;
       return;
     }
     }
 
 
-    // Successfully read an XML document.  Feed it to the parent.
+    // Successfully read an XML document.
+    
+    // Check for one special case: the "exit" command means we shut
+    // down the read thread along with everything else.
+    TiXmlElement *xcommand = doc->FirstChildElement("command");
+    if (xcommand != NULL) {
+      const char *cmd = xcommand->Attribute("cmd");
+      if (cmd != NULL) {
+        if (strcmp(cmd, "exit") == 0) {
+          _read_thread_continue = false;
+        }
+      }
+    }
+
+    // Feed the command up to the parent.
     ACQUIRE_LOCK(_commands_lock);
     ACQUIRE_LOCK(_commands_lock);
     _commands.push_back(doc);
     _commands.push_back(doc);
     RELEASE_LOCK(_commands_lock);
     RELEASE_LOCK(_commands_lock);

+ 1 - 1
direct/src/plugin/p3dPythonRun.h

@@ -90,8 +90,8 @@ private:
   char **_py_argv;
   char **_py_argv;
 
 
   PyObject *_runPackedApp;
   PyObject *_runPackedApp;
-  PyObject *_exit;
   PyObject *_setupWindow;
   PyObject *_setupWindow;
+  PyObject *_taskMgr;
 
 
   PT(GenericAsyncTask) _check_comm_task;
   PT(GenericAsyncTask) _check_comm_task;
 
 

+ 145 - 5
direct/src/plugin/p3dSession.cxx

@@ -34,6 +34,10 @@ P3DSession(P3DInstance *inst) {
   _python_version = inst->get_python_version();
   _python_version = inst->get_python_version();
   _python_root_dir = "C:/p3drun";
   _python_root_dir = "C:/p3drun";
 
 
+  INIT_LOCK(_instances_lock);
+
+  _read_thread_continue = false;
+
   string p3dpython = "c:/cygwin/home/drose/player/direct/built/bin/p3dpython.exe";
   string p3dpython = "c:/cygwin/home/drose/player/direct/built/bin/p3dpython.exe";
   //  string p3dpython = _python_root_dir + "/p3dpython.exe";
   //  string p3dpython = _python_root_dir + "/p3dpython.exe";
 
 
@@ -122,12 +126,15 @@ P3DSession(P3DInstance *inst) {
   _pipe_read.open_read(r_from);
   _pipe_read.open_read(r_from);
   _pipe_write.open_write(w_to);
   _pipe_write.open_write(w_to);
 #endif  // _WIN32
 #endif  // _WIN32
+
   if (!_pipe_read) {
   if (!_pipe_read) {
     cerr << "unable to open read pipe\n";
     cerr << "unable to open read pipe\n";
   }
   }
   if (!_pipe_write) {
   if (!_pipe_write) {
     cerr << "unable to open write pipe\n";
     cerr << "unable to open write pipe\n";
   }
   }
+
+  spawn_read_thread();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -139,13 +146,34 @@ P3DSession(P3DInstance *inst) {
 P3DSession::
 P3DSession::
 ~P3DSession() {
 ~P3DSession() {
   if (_started_p3dpython) {
   if (_started_p3dpython) {
-    cerr << "Terminating process.\n";
-    // Messy.  Shouldn't use TerminateProcess unless necessary.
-    TerminateProcess(_p3dpython.hProcess, 2);
+    // Tell the process we're going away.
+    TiXmlDocument doc;
+    TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", "");
+    TiXmlElement *xcommand = new TiXmlElement("command");
+    xcommand->SetAttribute("cmd", "exit");
+    doc.LinkEndChild(decl);
+    doc.LinkEndChild(xcommand);
+    _pipe_write << doc << flush;
+
+    // Also close the pipe, to help underscore the point.
+    _pipe_write.close();
+    _pipe_read.close();
+
+#ifdef _WIN32
+    // Now give the process a chance to terminate itself cleanly.
+    if (WaitForSingleObject(_p3dpython.hProcess, 2000) == WAIT_TIMEOUT) {
+      // It didn't shut down cleanly, so kill it the hard way.
+      cerr << "Terminating process.\n";
+      TerminateProcess(_p3dpython.hProcess, 2);
+    }
 
 
     CloseHandle(_p3dpython.hProcess);
     CloseHandle(_p3dpython.hProcess);
     CloseHandle(_p3dpython.hThread);
     CloseHandle(_p3dpython.hThread);
+#endif
   }
   }
+
+  join_read_thread();
+  DESTROY_LOCK(_instances_lock);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -164,8 +192,10 @@ start_instance(P3DInstance *inst) {
   assert(inst->get_session_key() == _session_key);
   assert(inst->get_session_key() == _session_key);
   assert(inst->get_python_version() == _python_version);
   assert(inst->get_python_version() == _python_version);
 
 
+  ACQUIRE_LOCK(_instances_lock);
   inst->_session = this;
   inst->_session = this;
-  bool inserted = _instances.insert(inst).second;
+  bool inserted = _instances.insert(Instances::value_type(inst->get_instance_id(), inst)).second;
+  RELEASE_LOCK(_instances_lock);
   assert(inserted);
   assert(inserted);
 
 
   TiXmlDocument doc;
   TiXmlDocument doc;
@@ -201,8 +231,118 @@ terminate_instance(P3DInstance *inst) {
 
 
   _pipe_write << doc << flush;
   _pipe_write << doc << flush;
 
 
+  ACQUIRE_LOCK(_instances_lock);
   if (inst->_session == this) {
   if (inst->_session == this) {
     inst->_session = NULL;
     inst->_session = NULL;
-    _instances.erase(inst);
+    _instances.erase(inst->get_instance_id());
   }
   }
+  RELEASE_LOCK(_instances_lock);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::spawn_read_thread
+//       Access: Private
+//  Description: Starts the read thread.  This thread is responsible
+//               for reading the standard input socket for XML
+//               requests and storing them in the _requests queue.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+spawn_read_thread() {
+  assert(!_read_thread_continue);
+
+  // We have to use direct OS calls to create the thread instead of
+  // Panda constructs, because it has to be an actual thread, not
+  // necessarily a Panda thread (we can't use Panda's simple threads
+  // implementation, because we can't get overlapped I/O on an
+  // anonymous pipe in Windows).
+
+  _read_thread_continue = true;
+#ifdef _WIN32
+  _read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
+#endif
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::join_read_thread
+//       Access: Private
+//  Description: Waits for the read thread to stop.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+join_read_thread() {
+  cerr << "session waiting for thread\n";
+  _read_thread_continue = false;
+  _pipe_read.close();
+  
+#ifdef _WIN32
+  assert(_read_thread != NULL);
+  WaitForSingleObject(_read_thread, INFINITE);
+  CloseHandle(_read_thread);
+  _read_thread = NULL;
+#endif
+  cerr << "session done waiting for thread\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::rt_thread_run
+//       Access: Private
+//  Description: The main function for the read thread.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+rt_thread_run() {
+  cerr << "session thread reading.\n";
+  while (_read_thread_continue) {
+    TiXmlDocument *doc = new TiXmlDocument;
+
+    _pipe_read >> *doc;
+    if (!_pipe_read || _pipe_read.eof()) {
+      // Some error on reading.  Abort.
+      cerr << "Error on session reading.\n";
+      rt_terminate();
+      return;
+    }
+
+    // Successfully read an XML document.
+    cerr << "Session got request: " << *doc << "\n";
+
+    // TODO: feed the request up to the parent.
+    delete doc;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::rt_terminate
+//       Access: Private
+//  Description: Got a closed pipe from the sub-process.  Send a
+//               terminate request for all instances.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+rt_terminate() {
+  Instances icopy;
+  ACQUIRE_LOCK(_instances_lock);
+  icopy = _instances;
+  RELEASE_LOCK(_instances_lock);
+
+  // TODO: got a race condition here.  What happens if someone deletes
+  // an instance while we're processing this loop?
+
+  for (Instances::iterator ii = icopy.begin(); ii != icopy.end(); ++ii) {
+    P3DInstance *inst = (*ii).second;
+    P3D_request *request = new P3D_request;
+    request->_instance = inst;
+    request->_request_type = P3D_RT_stop;
+    inst->add_request(request);
+  }
+}
+
+#ifdef _WIN32
+////////////////////////////////////////////////////////////////////
+//     Function: P3DPython::win_rt_thread_run
+//       Access: Private, Static
+//  Description: The Windows flavor of the thread callback function.
+////////////////////////////////////////////////////////////////////
+DWORD P3DSession::
+win_rt_thread_run(LPVOID data) {
+  ((P3DSession *)data)->rt_thread_run();
+  return 0;
+}
+#endif

+ 22 - 1
direct/src/plugin/p3dSession.h

@@ -42,22 +42,43 @@ public:
 
 
   INLINE int get_num_instances() const;
   INLINE int get_num_instances() const;
 
 
+private:
+  void spawn_read_thread();
+  void join_read_thread();
+
+private:
+  // These methods run only within the read thread.
+  void rt_thread_run();
+  void rt_terminate();
+
+#ifdef _WIN32
+  static DWORD WINAPI win_rt_thread_run(LPVOID data);
+#endif
+
 private:
 private:
   string _session_key;
   string _session_key;
   string _python_version;
   string _python_version;
 
 
   string _python_root_dir;
   string _python_root_dir;
 
 
-  typedef set<P3DInstance *> Instances;
+  typedef map<int, P3DInstance *> Instances;
   Instances _instances;
   Instances _instances;
+  LOCK _instances_lock;
 
 
   // Members for communicating with the p3dpython child process.
   // Members for communicating with the p3dpython child process.
   bool _started_p3dpython;
   bool _started_p3dpython;
 #ifdef _WIN32
 #ifdef _WIN32
   PROCESS_INFORMATION _p3dpython;
   PROCESS_INFORMATION _p3dpython;
 #endif
 #endif
+
+  // The remaining members are manipulated by the read thread.
   HandleStream _pipe_read;
   HandleStream _pipe_read;
   HandleStream _pipe_write;
   HandleStream _pipe_write;
+
+  bool _read_thread_continue;
+#ifdef _WIN32
+  HANDLE _read_thread;
+#endif
 };
 };
 
 
 #include "p3dSession.I"
 #include "p3dSession.I"

+ 2 - 12
direct/src/plugin/p3d_plugin.cxx

@@ -23,24 +23,14 @@ bool initialized_lock = false;
 LOCK _lock;
 LOCK _lock;
 
 
 bool 
 bool 
-P3D_initialize(const char *config_xml, const char *dll_filename) {
-  string config_xml_str;
-  if (config_xml != NULL) {
-    config_xml_str = config_xml;
-  }
-
-  string dll_filename_str;
-  if (dll_filename != NULL) {
-    dll_filename_str = dll_filename;
-  }
-
+P3D_initialize() {
   if (!initialized_lock) {
   if (!initialized_lock) {
     INIT_LOCK(_lock);
     INIT_LOCK(_lock);
     initialized_lock = true;
     initialized_lock = true;
   }
   }
   ACQUIRE_LOCK(_lock);
   ACQUIRE_LOCK(_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  bool result = inst_mgr->initialize(config_xml_str, dll_filename_str);
+  bool result = inst_mgr->initialize();
   RELEASE_LOCK(_lock);
   RELEASE_LOCK(_lock);
   return result;
   return result;
 }
 }

+ 2 - 22
direct/src/plugin/p3d_plugin.h

@@ -74,22 +74,14 @@ extern "C" {
    to being specific to a particular instance. */
    to being specific to a particular instance. */
 
 
 /* This function should be called immediately after the plugin is
 /* This function should be called immediately after the plugin is
-   loaded.  The config_xml parameter is the plugin configuration data,
-   formatted as an XML stream (it contains the XML data itself; it is
-   not a filename).  The exact contents of this XML stream are
-   documented elsewhere.  This config_xml parameter may be the empty
-   string or NULL; if so, the plugin will use its own internal default
-   configuration.
-
-   The dll_filename parameter is the filename of the plugin's dll
-   itself, which is needed for self-patching.
+   loaded.
 
 
    This function returns true if the plugin is valid, false otherwise.
    This function returns true if the plugin is valid, false otherwise.
    If it returns false, the host should not call any more functions in
    If it returns false, the host should not call any more functions in
    this API, and should immediately unload the DLL and (if possible)
    this API, and should immediately unload the DLL and (if possible)
    download a new one. */
    download a new one. */
 typedef bool 
 typedef bool 
-P3D_initialize_func(const char *config_xml, const char *dll_filename);
+P3D_initialize_func();
 
 
 /* This function frees a pointer returned by
 /* This function frees a pointer returned by
    P3D_instance_get_property(), or another similar function that
    P3D_instance_get_property(), or another similar function that
@@ -266,7 +258,6 @@ P3D_instance_set_property_func(P3D_instance *instance,
 typedef enum {
 typedef enum {
   P3D_RT_stop,
   P3D_RT_stop,
   P3D_RT_new_config_xml,
   P3D_RT_new_config_xml,
-  P3D_RT_patch,
   P3D_RT_get_url,
   P3D_RT_get_url,
   P3D_RT_post_url,
   P3D_RT_post_url,
 } P3D_request_type;
 } P3D_request_type;
@@ -291,16 +282,6 @@ typedef struct {
   const char *_config_xml;
   const char *_config_xml;
 } P3D_request_new_config_xml;
 } P3D_request_new_config_xml;
 
 
-/* A patch request.  The plugin has determined that it is out of date
-   and needs to be patched.  It has already applied the patch to
-   itself, and the resulting patched dll is referenced in the request
-   data.  The host should respond by finishing all active instances,
-   unloading the DLL, moving the patched dll onto the original DLL,
-   and reloading the DLL and (optionally) restarting the instances. */
-typedef struct {
-  const char *_patched_filename;
-} P3D_request_patch;
-
 /* A get_url request.  The plugin would like to retrieve data for a
 /* A get_url request.  The plugin would like to retrieve data for a
    particular URL.  The plugin is responsible for supplying a valid
    particular URL.  The plugin is responsible for supplying a valid
    URL string, and a unique integer ID.  The unique ID is needed to
    URL string, and a unique integer ID.  The unique ID is needed to
@@ -334,7 +315,6 @@ typedef struct {
   union {
   union {
     P3D_request_stop _stop;
     P3D_request_stop _stop;
     P3D_request_new_config_xml _new_config_xml;
     P3D_request_new_config_xml _new_config_xml;
-    P3D_request_patch _patch;
     P3D_request_get_url _get_url;
     P3D_request_get_url _get_url;
     P3D_request_post_url _post_url;
     P3D_request_post_url _post_url;
   } _request;
   } _request;

+ 178 - 44
direct/src/plugin/panda3d.cxx

@@ -12,20 +12,34 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-#include "p3d_plugin.h"
+// This program must link with Panda for HTTPClient support.  This
+// means it probably should be built with LINK_ALL_STATIC defined, so
+// we won't have to deal with confusing .dll or .so files that might
+// compete on the disk with the dynamically-loaded versions.  There's
+// no competition in memory address space, though, because
+// p3d_plugin--the only file we dynamically link in--doesn't itself
+// link with Panda.
 
 
-#include <iostream>
-#include <string>
-#include <math.h>
+#include "pandabase.h"
 
 
 #ifdef _WIN32
 #ifdef _WIN32
-#include "wingetopt.h"
 #include <windows.h>
 #include <windows.h>
-#else
-#include <getopt.h>
 #endif
 #endif
 
 
-using namespace std;
+#include "httpClient.h"
+#include "httpChannel.h"
+#include "Ramfile.h"
+#include "thread.h"
+#include "p3d_plugin.h"
+#include "pset.h"
+
+#ifndef HAVE_GETOPT
+  #include "gnu_getopt.h"
+#else
+  #ifdef HAVE_GETOPT_H
+    #include <getopt.h>
+  #endif
+#endif
 
 
 static const string default_plugin_filename = "libp3d_plugin";
 static const string default_plugin_filename = "libp3d_plugin";
 
 
@@ -41,9 +55,88 @@ P3D_check_request_func *P3D_check_request;
 P3D_request_finish_func *P3D_request_finish;
 P3D_request_finish_func *P3D_request_finish;
 P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream;
 P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream;
 
 
+typedef pset<P3D_instance *> Instances;
+Instances _instances;
+
+class URLGetterThread : public Thread {
+public:
+  URLGetterThread(P3D_instance *instance,
+                  int unique_id,
+                  const URLSpec &url,
+                  const string &post_data);
+protected:
+  virtual void thread_main();
+
+private:
+  P3D_instance *_instance;
+  int _unique_id;
+  URLSpec _url;
+  string _post_data;
+};
+
+URLGetterThread::
+URLGetterThread(P3D_instance *instance,
+                int unique_id,
+                const URLSpec &url,
+                const string &post_data) :
+  Thread(url, "URLGetter"),
+  _instance(instance),
+  _unique_id(unique_id),
+  _url(url),
+  _post_data(post_data)
+{
+}
+
+void URLGetterThread::
+thread_main() {
+  HTTPClient *http = HTTPClient::get_global_ptr();
+
+  cerr << "Getting URL " << _url << "\n";
+
+  PT(HTTPChannel) channel = http->make_channel(false);
+  if (_post_data.empty()) {
+    channel->begin_get_document(_url);
+  } else {
+    channel->begin_post_form(_url, _post_data);
+  }
+
+  Ramfile rf;
+  channel->download_to_ram(&rf);
+
+  size_t bytes_sent = 0;
+  while (channel->run()) {
+    if (rf.get_data_size() != 0) {
+      // Got some new data.
+      P3D_instance_feed_url_stream
+        (_instance, _unique_id, P3D_RC_in_progress,
+         channel->get_status_code(),
+         channel->get_file_size(),
+         (const unsigned char *)rf.get_data().data(), rf.get_data_size());
+      bytes_sent += rf.get_data_size();
+      rf.clear();
+    }
+  }
+
+  // All done.
+  P3D_result_code status = P3D_RC_done;
+  if (!channel->is_valid()) {
+    if (channel->get_status_code() != 0) {
+      status = P3D_RC_http_error;
+    } else {
+      status = P3D_RC_generic_error;
+    }
+  }
+
+  P3D_instance_feed_url_stream
+    (_instance, _unique_id, status,
+     channel->get_status_code(),
+     bytes_sent, NULL, 0);
+
+  cerr << "Done getting URL " << _url << ", got " << bytes_sent << " bytes\n";
+}
+
 bool
 bool
-load_plugin(const string &config_xml_filename,
-            const string &p3d_plugin_filename) {
+load_plugin(const string &p3d_plugin_filename) {
   string filename = p3d_plugin_filename;
   string filename = p3d_plugin_filename;
   if (filename.empty()) {
   if (filename.empty()) {
     // Look for the plugin along the path.
     // Look for the plugin along the path.
@@ -102,7 +195,7 @@ load_plugin(const string &config_xml_filename,
   }
   }
 
 
   // Successfully loaded.
   // Successfully loaded.
-  if (!P3D_initialize(NULL, filename.c_str())) {
+  if (!P3D_initialize()) {
     // Oops, failure to initialize.
     // Oops, failure to initialize.
     return false;
     return false;
   }
   }
@@ -116,12 +209,40 @@ handle_request(P3D_request *request) {
 
 
   switch (request->_request_type) {
   switch (request->_request_type) {
   case P3D_RT_stop:
   case P3D_RT_stop:
+    cerr << "Got P3D_RT_stop\n";
     P3D_instance_finish(request->_instance);
     P3D_instance_finish(request->_instance);
+    _instances.erase(request->_instance);
+#ifdef _WIN32
+    // Post a silly message to spin the event loop.
+    PostMessage(NULL, WM_USER, 0, 0);
+#endif
     handled = true;
     handled = true;
     break;
     break;
 
 
+  case P3D_RT_get_url:
+    cerr << "Got P3D_RT_get_url\n";
+    {
+      PT(URLGetterThread) thread = new URLGetterThread
+        (request->_instance, request->_request._get_url._unique_id,
+         URLSpec(request->_request._get_url._url), "");
+      thread->start(TP_normal, false);
+    }
+    break;
+
+  case P3D_RT_post_url:
+    cerr << "Got P3D_RT_post_url\n";
+    {
+      PT(URLGetterThread) thread = new URLGetterThread
+        (request->_instance, request->_request._post_url._unique_id,
+         URLSpec(request->_request._post_url._url), 
+         string(request->_request._post_url._post_data, request->_request._post_url._post_data_size));
+      thread->start(TP_normal, false);
+    }
+    break;
+
   default:
   default:
     // Some request types are not handled.
     // Some request types are not handled.
+    cerr << "Unhandled request: " << request->_request_type << "\n";
     break;
     break;
   };
   };
 
 
@@ -189,12 +310,6 @@ usage() {
 
 
     << "Options:\n\n"
     << "Options:\n\n"
 
 
-    << "  -c config.xml\n"
-    << "    Specify the name of the config.xml file that informs the Panda\n"
-    << "    plugin where to download patches and such.  This is normally\n"
-    << "    not necessary to specify, since it is already stored within\n"
-    << "    the Panda plugin itself.\n\n"
-
     << "  -p p3d_plugin.dll\n"
     << "  -p p3d_plugin.dll\n"
     << "    Specify the full path to the particular Panda plugin DLL to\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"
     << "    run.  Normally, this will be found by searching in the usual\n"
@@ -231,9 +346,8 @@ int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
   extern char *optarg;
   extern char *optarg;
   extern int optind;
   extern int optind;
-  const char *optstr = "c:p:t:s:o:h";
+  const char *optstr = "p:t:s:o:h";
 
 
-  string config_xml_filename;
   string p3d_plugin_filename;
   string p3d_plugin_filename;
   P3D_window_type window_type = P3D_WT_toplevel;
   P3D_window_type window_type = P3D_WT_toplevel;
   int win_x = 0, win_y = 0;
   int win_x = 0, win_y = 0;
@@ -243,10 +357,6 @@ main(int argc, char *argv[]) {
 
 
   while (flag != EOF) {
   while (flag != EOF) {
     switch (flag) {
     switch (flag) {
-    case 'c':
-      config_xml_filename = optarg;
-      break;
-
     case 'p':
     case 'p':
       p3d_plugin_filename = optarg;
       p3d_plugin_filename = optarg;
       break;
       break;
@@ -297,7 +407,7 @@ main(int argc, char *argv[]) {
     return 1;
     return 1;
   }
   }
 
 
-  if (!load_plugin(config_xml_filename, p3d_plugin_filename)) {
+  if (!load_plugin(p3d_plugin_filename)) {
     cerr << "Unable to load Panda3D plugin.\n";
     cerr << "Unable to load Panda3D plugin.\n";
     return 1;
     return 1;
   }
   }
@@ -342,50 +452,75 @@ main(int argc, char *argv[]) {
         int inst_x = win_x + xi * inst_width;
         int inst_x = win_x + xi * inst_width;
         int inst_y = win_y + yi * inst_height;
         int inst_y = win_y + yi * inst_height;
 
 
-        P3D_create_instance
+        P3D_instance *inst = P3D_create_instance
           (NULL, argv[i + 1], 
           (NULL, argv[i + 1], 
            P3D_WT_embedded, inst_x, inst_y, inst_width, inst_height, parent_window,
            P3D_WT_embedded, inst_x, inst_y, inst_width, inst_height, parent_window,
            NULL, 0);
            NULL, 0);
+        _instances.insert(inst);
       }
       }
     }
     }
 
 
   } else {
   } else {
     // Not an embedded window.  Create each window with the same parameters.
     // Not an embedded window.  Create each window with the same parameters.
     for (int i = 0; i < num_instances; ++i) {
     for (int i = 0; i < num_instances; ++i) {
-      P3D_create_instance
+      P3D_instance *inst = P3D_create_instance
         (NULL, argv[i + 1], 
         (NULL, argv[i + 1], 
          window_type, win_x, win_y, win_width, win_height, parent_window,
          window_type, win_x, win_y, win_width, win_height, parent_window,
          NULL, 0);
          NULL, 0);
+      _instances.insert(inst);
     }
     }
   }
   }
 
 
 #ifdef _WIN32
 #ifdef _WIN32
-  // Wait for new messages from Windows, and new requests from the
-  // plugin.
-  MSG msg;
-  int retval;
-  retval = GetMessage(&msg, NULL, 0, 0);
-  while (retval != 0) {
-    if (retval == -1) {
-      cerr << "Error processing message queue.\n";
-      exit(1);
+  if (window_type == P3D_WT_embedded) {
+    // Wait for new messages from Windows, and new requests from the
+    // plugin.
+    MSG msg;
+    int retval;
+    retval = GetMessage(&msg, NULL, 0, 0);
+    while (retval != 0 && !_instances.empty()) {
+      if (retval == -1) {
+        cerr << "Error processing message queue.\n";
+        exit(1);
+      }
+      TranslateMessage(&msg);
+      DispatchMessage(&msg);
+      
+      // Check for new requests from the Panda3D plugin.
+      P3D_instance *inst = P3D_check_request(false);
+      while (inst != (P3D_instance *)NULL) {
+        P3D_request *request = P3D_instance_get_request(inst);
+        if (request != (P3D_request *)NULL) {
+          handle_request(request);
+        }
+        inst = P3D_check_request(false);
+      }
+      retval = GetMessage(&msg, NULL, 0, 0);
+    }
+    
+    cerr << "WM_QUIT\n";
+    // WM_QUIT has been received.  Terminate all instances, and fall
+    // through.
+    Instances::iterator ii;
+    for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
+      P3D_instance_finish(*ii);
     }
     }
-    TranslateMessage(&msg);
-    DispatchMessage(&msg);
+    _instances.clear();
 
 
-    // Check for new requests from the Panda3D plugin.
-    P3D_instance *inst = P3D_check_request(false);
+  } else {
+    // Not an embedded window, so we don't have our own window to
+    // generate Windows events.  Instead, just wait for requests.
+    P3D_instance *inst = P3D_check_request(true);
     while (inst != (P3D_instance *)NULL) {
     while (inst != (P3D_instance *)NULL) {
       P3D_request *request = P3D_instance_get_request(inst);
       P3D_request *request = P3D_instance_get_request(inst);
       if (request != (P3D_request *)NULL) {
       if (request != (P3D_request *)NULL) {
         handle_request(request);
         handle_request(request);
       }
       }
-      inst = P3D_check_request(false);
+      inst = P3D_check_request(true);
     }
     }
-    retval = GetMessage(&msg, NULL, 0, 0);
   }
   }
-
-#else
+    
+#endif
 
 
   // Now wait while we process pending requests.
   // Now wait while we process pending requests.
   P3D_instance *inst = P3D_check_request(true);
   P3D_instance *inst = P3D_check_request(true);
@@ -396,7 +531,6 @@ main(int argc, char *argv[]) {
     }
     }
     inst = P3D_check_request(true);
     inst = P3D_check_request(true);
   }
   }
-#endif
 
 
   // All instances have finished; we can exit.
   // All instances have finished; we can exit.
 
 

+ 0 - 76
direct/src/plugin/wingetopt.c

@@ -1,76 +0,0 @@
-/*
-POSIX getopt for Windows
-
-AT&T Public License
-
-Code given out at the 1985 UNIFORUM conference in Dallas.  
-*/
-
-#ifndef __GNUC__
-
-#include "wingetopt.h"
-#include <stdio.h>
-#include <string.h>
-
-//#define NULL	0
-#define EOF	(-1)
-#define ERR(s, c)	if(opterr){\
-	char errbuf[2];\
-	errbuf[0] = c; errbuf[1] = '\n';\
-	fputs(argv[0], stderr);\
-	fputs(s, stderr);\
-	fputc(c, stderr);}
-	//(void) write(2, argv[0], (unsigned)strlen(argv[0]));\
-	//(void) write(2, s, (unsigned)strlen(s));\
-	//(void) write(2, errbuf, 2);}
-
-int	opterr = 1;
-int	optind = 1;
-int	optopt;
-char	*optarg;
-
-int
-getopt(int argc, char **argv, const char *opts)
-{
-	static int sp = 1;
-	register int c;
-	register char *cp;
-
-	if(sp == 1)
-		if(optind >= argc ||
-		   argv[optind][0] != '-' || argv[optind][1] == '\0')
-			return(EOF);
-		else if(strcmp(argv[optind], "--") == NULL) {
-			optind++;
-			return(EOF);
-		}
-	optopt = c = argv[optind][sp];
-	if(c == ':' || (cp=strchr(opts, c)) == NULL) {
-		ERR(": illegal option -- ", c);
-		if(argv[optind][++sp] == '\0') {
-			optind++;
-			sp = 1;
-		}
-		return('?');
-	}
-	if(*++cp == ':') {
-		if(argv[optind][sp+1] != '\0')
-			optarg = &argv[optind++][sp+1];
-		else if(++optind >= argc) {
-			ERR(": option requires an argument -- ", c);
-			sp = 1;
-			return('?');
-		} else
-			optarg = argv[optind++];
-		sp = 1;
-	} else {
-		if(argv[optind][++sp] == '\0') {
-			sp = 1;
-			optind++;
-		}
-		optarg = NULL;
-	}
-	return(c);
-}
-
-#endif  /* __GNUC__ */

+ 0 - 32
direct/src/plugin/wingetopt.h

@@ -1,32 +0,0 @@
-/*
-POSIX getopt for Windows
-
-AT&T Public License
-
-Code given out at the 1985 UNIFORUM conference in Dallas.  
-*/
-
-#ifdef __GNUC__
-#include <getopt.h>
-#endif
-#ifndef __GNUC__
-
-#ifndef _WINGETOPT_H_
-#define _WINGETOPT_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern int opterr;
-extern int optind;
-extern int optopt;
-extern char *optarg;
-extern int getopt(int argc, char **argv, const char *opts);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif  /* _GETOPT_H_ */
-#endif  /* __GNUC__ */

+ 1 - 1
direct/src/showbase/MakeAppMF.py

@@ -230,7 +230,7 @@ class AppPacker:
         bamFile = BamFile()
         bamFile = BamFile()
         stream = StringStream()
         stream = StringStream()
         bamFile.openWrite(stream)
         bamFile.openWrite(stream)
-        bamFile.getWriter().setFileTextureMode(BTMUnchanged)
+        bamFile.getWriter().setFileTextureMode(bamFile.BTMUnchanged)
         bamFile.writeObject(node)
         bamFile.writeObject(node)
         bamFile.close()
         bamFile.close()
 
 

+ 5 - 11
direct/src/showbase/RunAppMF.py

@@ -44,6 +44,11 @@ def initPackedAppEnvironment():
 
 
     __packedAppEnvironmentInitialized = True
     __packedAppEnvironmentInitialized = True
 
 
+    # We need to make sure sys.stdout maps to sys.stderr instead, so
+    # if someone makes an unadorned print command within Python code,
+    # it won't muck up the data stream between parent and child.
+    sys.stdout = sys.stderr
+
     vfs = VirtualFileSystem.getGlobalPtr()
     vfs = VirtualFileSystem.getGlobalPtr()
 
 
     # Clear *all* the mount points, including "/", so that we no
     # Clear *all* the mount points, including "/", so that we no
@@ -145,17 +150,6 @@ def setupWindow(windowType, x, y, width, height, parent):
 
 
     loadPrcFileData("setupWindow", data)
     loadPrcFileData("setupWindow", data)
 
 
-def add_check_comm(func, this):
-    """ This function is provided just a convenience for
-    p3dPythonRun.cxx.  It adds the indicated function to the task
-    manager with the appropriate parameters for the check_comm
-    task. """
-    return taskMgr.add(func, "check_comm", sort = -50, extraArgs = [this],
-                       appendTask = True)
-
-def exit():
-    taskMgr.stop()
-
 if __name__ == '__main__':
 if __name__ == '__main__':
     try:
     try:
         runPackedApp(sys.argv[1:])
         runPackedApp(sys.argv[1:])