mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
bi-directional communication
This commit is contained in:
parent
3f42a08968
commit
ce182cb038
@ -6,6 +6,7 @@ from LoggerGlobal import defaultLogger
|
||||
from direct.showbase import PythonUtil
|
||||
import time
|
||||
import types
|
||||
import sys
|
||||
|
||||
class Notifier:
|
||||
serverDelta = 0
|
||||
@ -237,7 +238,7 @@ class Notifier:
|
||||
if self.streamWriter:
|
||||
self.streamWriter.appendData(string + '\n')
|
||||
else:
|
||||
print string
|
||||
print >> sys.stderr, string
|
||||
|
||||
def debugStateCall(self, obj=None, fsmMemberName='fsm',
|
||||
secondaryFsm='secondaryFSM'):
|
||||
|
@ -33,7 +33,7 @@
|
||||
#define USE_PACKAGES tinyxml python
|
||||
#define TARGET p3dpython
|
||||
|
||||
#define OTHER_LIBS \
|
||||
#define OTHER_LIBS \
|
||||
dtoolutil:c dtoolbase:c dtool:m \
|
||||
interrogatedb:c dconfig:c dtoolconfig:m \
|
||||
express:c pandaexpress:m \
|
||||
@ -51,11 +51,19 @@
|
||||
#end bin_target
|
||||
|
||||
#begin bin_target
|
||||
#define USE_PACKAGES openssl
|
||||
#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 \
|
||||
panda3d.cxx \
|
||||
$[if $[WINDOWS_PLATFORM],wingetopt.h wingetopt.c]
|
||||
panda3d.cxx
|
||||
|
||||
#define WIN_SYS_LIBS user32.lib gdi32.lib
|
||||
|
||||
|
@ -311,7 +311,7 @@ write_chars(const char *start, size_t length) {
|
||||
BOOL success = WriteFile(_handle, start, length, &bytes_written, NULL);
|
||||
if (!success) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != ERROR_BROKEN_PIPE) {
|
||||
if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) {
|
||||
cerr
|
||||
<< "Error writing " << length
|
||||
<< " bytes, windows error code 0x" << hex
|
||||
|
@ -153,6 +153,7 @@ get_request() {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
add_request(P3D_request *request) {
|
||||
cerr << "adding a request\n";
|
||||
assert(request->_instance == this);
|
||||
|
||||
ACQUIRE_LOCK(_request_lock);
|
||||
|
@ -23,14 +23,3 @@ INLINE int P3DInstanceManager::
|
||||
get_num_instances() const {
|
||||
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;
|
||||
}
|
||||
|
@ -46,15 +46,13 @@ P3DInstanceManager::
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::initialize
|
||||
// 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::
|
||||
initialize(const string &config_xml, const string &dll_filename) {
|
||||
_dll_filename = dll_filename;
|
||||
initialize() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ private:
|
||||
~P3DInstanceManager();
|
||||
|
||||
public:
|
||||
bool initialize(const string &config_xml, const string &dll_filename);
|
||||
bool initialize();
|
||||
|
||||
P3DInstance *
|
||||
create_instance(P3D_request_ready_func *func,
|
||||
@ -53,15 +53,12 @@ public:
|
||||
|
||||
INLINE int get_num_instances() const;
|
||||
|
||||
INLINE const string &get_dll_filename() const;
|
||||
|
||||
int get_unique_session_index();
|
||||
void signal_request_ready();
|
||||
|
||||
static P3DInstanceManager *get_global_ptr();
|
||||
|
||||
private:
|
||||
string _dll_filename;
|
||||
string _p3d_root_directory;
|
||||
|
||||
typedef set<P3DInstance *> Instances;
|
||||
|
@ -103,16 +103,17 @@ run_python() {
|
||||
PyErr_Print();
|
||||
return false;
|
||||
}
|
||||
_exit = PyObject_GetAttrString(appmf, "exit");
|
||||
if (_exit == NULL) {
|
||||
PyErr_Print();
|
||||
return false;
|
||||
}
|
||||
_setupWindow = PyObject_GetAttrString(appmf, "setupWindow");
|
||||
if (_setupWindow == NULL) {
|
||||
PyErr_Print();
|
||||
return false;
|
||||
}
|
||||
_taskMgr = PyObject_GetAttrString(appmf, "taskMgr");
|
||||
if (_taskMgr == NULL) {
|
||||
PyErr_Print();
|
||||
return false;
|
||||
}
|
||||
|
||||
Py_DECREF(appmf);
|
||||
|
||||
// Now add check_comm() as a task.
|
||||
@ -121,14 +122,8 @@ run_python() {
|
||||
task_mgr->add(_check_comm_task);
|
||||
|
||||
// Finally, get lost in taskMgr.run().
|
||||
|
||||
PyObject *taskMgr = PyObject_GetAttrString(appmf, "taskMgr");
|
||||
if (taskMgr == NULL) {
|
||||
PyErr_Print();
|
||||
return false;
|
||||
}
|
||||
cerr << "calling run()\n";
|
||||
PyObject *done = PyObject_CallMethod(taskMgr, "run", "");
|
||||
PyObject *done = PyObject_CallMethod(_taskMgr, "run", "");
|
||||
if (done == NULL) {
|
||||
PyErr_Print();
|
||||
return false;
|
||||
@ -136,8 +131,6 @@ run_python() {
|
||||
Py_DECREF(done);
|
||||
cerr << "done calling run()\n";
|
||||
|
||||
Py_DECREF(taskMgr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -165,6 +158,8 @@ handle_command(TiXmlDocument *doc) {
|
||||
if (xcommand->Attribute("id", &id)) {
|
||||
terminate_instance(id);
|
||||
}
|
||||
} else if (strcmp(cmd, "exit") == 0) {
|
||||
terminate_session();
|
||||
|
||||
} else {
|
||||
cerr << "Unhandled command " << cmd << "\n";
|
||||
@ -251,6 +246,7 @@ void P3DPythonRun::
|
||||
join_read_thread() {
|
||||
cerr << "waiting for thread\n";
|
||||
_read_thread_continue = false;
|
||||
_pipe_read.close();
|
||||
|
||||
#ifdef _WIN32
|
||||
assert(_read_thread != NULL);
|
||||
@ -346,11 +342,14 @@ terminate_session() {
|
||||
}
|
||||
_instances.clear();
|
||||
|
||||
PyObject *result = PyObject_CallFunction(_exit, "");
|
||||
cerr << "calling stop()\n";
|
||||
PyObject *result = PyObject_CallMethod(_taskMgr, "stop", "");
|
||||
if (result == NULL) {
|
||||
PyErr_Print();
|
||||
return;
|
||||
}
|
||||
Py_XDECREF(result);
|
||||
Py_DECREF(result);
|
||||
cerr << "done calling stop()\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@ -372,7 +371,21 @@ rt_thread_run() {
|
||||
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);
|
||||
_commands.push_back(doc);
|
||||
RELEASE_LOCK(_commands_lock);
|
||||
|
@ -90,8 +90,8 @@ private:
|
||||
char **_py_argv;
|
||||
|
||||
PyObject *_runPackedApp;
|
||||
PyObject *_exit;
|
||||
PyObject *_setupWindow;
|
||||
PyObject *_taskMgr;
|
||||
|
||||
PT(GenericAsyncTask) _check_comm_task;
|
||||
|
||||
|
@ -34,6 +34,10 @@ P3DSession(P3DInstance *inst) {
|
||||
_python_version = inst->get_python_version();
|
||||
_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 = _python_root_dir + "/p3dpython.exe";
|
||||
|
||||
@ -122,12 +126,15 @@ P3DSession(P3DInstance *inst) {
|
||||
_pipe_read.open_read(r_from);
|
||||
_pipe_write.open_write(w_to);
|
||||
#endif // _WIN32
|
||||
|
||||
if (!_pipe_read) {
|
||||
cerr << "unable to open read pipe\n";
|
||||
}
|
||||
if (!_pipe_write) {
|
||||
cerr << "unable to open write pipe\n";
|
||||
}
|
||||
|
||||
spawn_read_thread();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@ -139,13 +146,34 @@ P3DSession(P3DInstance *inst) {
|
||||
P3DSession::
|
||||
~P3DSession() {
|
||||
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.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_python_version() == _python_version);
|
||||
|
||||
ACQUIRE_LOCK(_instances_lock);
|
||||
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);
|
||||
|
||||
TiXmlDocument doc;
|
||||
@ -201,8 +231,118 @@ terminate_instance(P3DInstance *inst) {
|
||||
|
||||
_pipe_write << doc << flush;
|
||||
|
||||
ACQUIRE_LOCK(_instances_lock);
|
||||
if (inst->_session == this) {
|
||||
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
|
||||
|
@ -42,22 +42,43 @@ public:
|
||||
|
||||
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:
|
||||
string _session_key;
|
||||
string _python_version;
|
||||
|
||||
string _python_root_dir;
|
||||
|
||||
typedef set<P3DInstance *> Instances;
|
||||
typedef map<int, P3DInstance *> Instances;
|
||||
Instances _instances;
|
||||
LOCK _instances_lock;
|
||||
|
||||
// Members for communicating with the p3dpython child process.
|
||||
bool _started_p3dpython;
|
||||
#ifdef _WIN32
|
||||
PROCESS_INFORMATION _p3dpython;
|
||||
#endif
|
||||
|
||||
// The remaining members are manipulated by the read thread.
|
||||
HandleStream _pipe_read;
|
||||
HandleStream _pipe_write;
|
||||
|
||||
bool _read_thread_continue;
|
||||
#ifdef _WIN32
|
||||
HANDLE _read_thread;
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "p3dSession.I"
|
||||
|
@ -23,24 +23,14 @@ bool initialized_lock = false;
|
||||
LOCK _lock;
|
||||
|
||||
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) {
|
||||
INIT_LOCK(_lock);
|
||||
initialized_lock = true;
|
||||
}
|
||||
ACQUIRE_LOCK(_lock);
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
|
@ -74,22 +74,14 @@ extern "C" {
|
||||
to being specific to a particular instance. */
|
||||
|
||||
/* 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.
|
||||
If it returns false, the host should not call any more functions in
|
||||
this API, and should immediately unload the DLL and (if possible)
|
||||
download a new one. */
|
||||
typedef bool
|
||||
P3D_initialize_func(const char *config_xml, const char *dll_filename);
|
||||
P3D_initialize_func();
|
||||
|
||||
/* This function frees a pointer returned by
|
||||
P3D_instance_get_property(), or another similar function that
|
||||
@ -266,7 +258,6 @@ P3D_instance_set_property_func(P3D_instance *instance,
|
||||
typedef enum {
|
||||
P3D_RT_stop,
|
||||
P3D_RT_new_config_xml,
|
||||
P3D_RT_patch,
|
||||
P3D_RT_get_url,
|
||||
P3D_RT_post_url,
|
||||
} P3D_request_type;
|
||||
@ -291,16 +282,6 @@ typedef struct {
|
||||
const char *_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
|
||||
particular URL. The plugin is responsible for supplying a valid
|
||||
URL string, and a unique integer ID. The unique ID is needed to
|
||||
@ -334,7 +315,6 @@ typedef struct {
|
||||
union {
|
||||
P3D_request_stop _stop;
|
||||
P3D_request_new_config_xml _new_config_xml;
|
||||
P3D_request_patch _patch;
|
||||
P3D_request_get_url _get_url;
|
||||
P3D_request_post_url _post_url;
|
||||
} _request;
|
||||
|
@ -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
|
||||
#include "wingetopt.h"
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <getopt.h>
|
||||
#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";
|
||||
|
||||
@ -41,9 +55,88 @@ P3D_check_request_func *P3D_check_request;
|
||||
P3D_request_finish_func *P3D_request_finish;
|
||||
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
|
||||
load_plugin(const string &config_xml_filename,
|
||||
const string &p3d_plugin_filename) {
|
||||
load_plugin(const string &p3d_plugin_filename) {
|
||||
string filename = p3d_plugin_filename;
|
||||
if (filename.empty()) {
|
||||
// Look for the plugin along the path.
|
||||
@ -102,7 +195,7 @@ load_plugin(const string &config_xml_filename,
|
||||
}
|
||||
|
||||
// Successfully loaded.
|
||||
if (!P3D_initialize(NULL, filename.c_str())) {
|
||||
if (!P3D_initialize()) {
|
||||
// Oops, failure to initialize.
|
||||
return false;
|
||||
}
|
||||
@ -116,12 +209,40 @@ handle_request(P3D_request *request) {
|
||||
|
||||
switch (request->_request_type) {
|
||||
case P3D_RT_stop:
|
||||
cerr << "Got P3D_RT_stop\n";
|
||||
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;
|
||||
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:
|
||||
// Some request types are not handled.
|
||||
cerr << "Unhandled request: " << request->_request_type << "\n";
|
||||
break;
|
||||
};
|
||||
|
||||
@ -189,12 +310,6 @@ usage() {
|
||||
|
||||
<< "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"
|
||||
<< " Specify the full path to the particular Panda plugin DLL to\n"
|
||||
<< " run. Normally, this will be found by searching in the usual\n"
|
||||
@ -231,9 +346,8 @@ int
|
||||
main(int argc, char *argv[]) {
|
||||
extern char *optarg;
|
||||
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;
|
||||
P3D_window_type window_type = P3D_WT_toplevel;
|
||||
int win_x = 0, win_y = 0;
|
||||
@ -243,10 +357,6 @@ main(int argc, char *argv[]) {
|
||||
|
||||
while (flag != EOF) {
|
||||
switch (flag) {
|
||||
case 'c':
|
||||
config_xml_filename = optarg;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
p3d_plugin_filename = optarg;
|
||||
break;
|
||||
@ -297,7 +407,7 @@ main(int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!load_plugin(config_xml_filename, p3d_plugin_filename)) {
|
||||
if (!load_plugin(p3d_plugin_filename)) {
|
||||
cerr << "Unable to load Panda3D plugin.\n";
|
||||
return 1;
|
||||
}
|
||||
@ -342,50 +452,75 @@ main(int argc, char *argv[]) {
|
||||
int inst_x = win_x + xi * inst_width;
|
||||
int inst_y = win_y + yi * inst_height;
|
||||
|
||||
P3D_create_instance
|
||||
P3D_instance *inst = P3D_create_instance
|
||||
(NULL, argv[i + 1],
|
||||
P3D_WT_embedded, inst_x, inst_y, inst_width, inst_height, parent_window,
|
||||
NULL, 0);
|
||||
_instances.insert(inst);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Not an embedded window. Create each window with the same parameters.
|
||||
for (int i = 0; i < num_instances; ++i) {
|
||||
P3D_create_instance
|
||||
P3D_instance *inst = P3D_create_instance
|
||||
(NULL, argv[i + 1],
|
||||
window_type, win_x, win_y, win_width, win_height, parent_window,
|
||||
NULL, 0);
|
||||
_instances.insert(inst);
|
||||
}
|
||||
}
|
||||
|
||||
#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);
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
|
||||
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);
|
||||
}
|
||||
_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) {
|
||||
P3D_request *request = P3D_instance_get_request(inst);
|
||||
if (request != (P3D_request *)NULL) {
|
||||
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.
|
||||
P3D_instance *inst = P3D_check_request(true);
|
||||
@ -396,7 +531,6 @@ main(int argc, char *argv[]) {
|
||||
}
|
||||
inst = P3D_check_request(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
// All instances have finished; we can exit.
|
||||
|
||||
|
@ -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__ */
|
@ -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__ */
|
@ -230,7 +230,7 @@ class AppPacker:
|
||||
bamFile = BamFile()
|
||||
stream = StringStream()
|
||||
bamFile.openWrite(stream)
|
||||
bamFile.getWriter().setFileTextureMode(BTMUnchanged)
|
||||
bamFile.getWriter().setFileTextureMode(bamFile.BTMUnchanged)
|
||||
bamFile.writeObject(node)
|
||||
bamFile.close()
|
||||
|
||||
|
@ -44,6 +44,11 @@ def initPackedAppEnvironment():
|
||||
|
||||
__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()
|
||||
|
||||
# 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)
|
||||
|
||||
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__':
|
||||
try:
|
||||
runPackedApp(sys.argv[1:])
|
||||
|
Loading…
x
Reference in New Issue
Block a user