diff --git a/direct/src/dcparser/dcFile.cxx b/direct/src/dcparser/dcFile.cxx index 1f56f9b22b..f009e9a7fa 100644 --- a/direct/src/dcparser/dcFile.cxx +++ b/direct/src/dcparser/dcFile.cxx @@ -45,10 +45,25 @@ DCFile() { //////////////////////////////////////////////////////////////////// DCFile:: ~DCFile() { + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCFile::clear +// Access: Published +// Description: Removes all of the classes defined within the DCFile +// and prepares it for reading a new file. +//////////////////////////////////////////////////////////////////// +void DCFile:: +clear() { Classes::iterator ci; for (ci = _classes.begin(); ci != _classes.end(); ++ci) { delete (*ci); } + + _classes.clear(); + _imports.clear(); + _classes_by_name.clear(); } #ifdef WITHIN_PANDA diff --git a/direct/src/dcparser/dcFile.h b/direct/src/dcparser/dcFile.h index ed30737dfa..48f06774b8 100644 --- a/direct/src/dcparser/dcFile.h +++ b/direct/src/dcparser/dcFile.h @@ -34,6 +34,8 @@ PUBLISHED: DCFile(); ~DCFile(); + void clear(); + #ifdef WITHIN_PANDA bool read_all(); #endif diff --git a/direct/src/distributed/ClientRepository.py b/direct/src/distributed/ClientRepository.py index 8c5e29fc7f..e8e7004a69 100644 --- a/direct/src/distributed/ClientRepository.py +++ b/direct/src/distributed/ClientRepository.py @@ -19,6 +19,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): def __init__(self): ConnectionRepository.ConnectionRepository.__init__(self, base.config) + self.setClientDatagram(1) self.recorder = base.recorder @@ -95,9 +96,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): def handleGenerateWithRequired(self, di): # Get the class Id - classId = di.getArg(STUint16); + classId = di.getUint16(); # Get the DO Id - doId = di.getArg(STUint32) + doId = di.getUint32() # Look up the dclass dclass = self.dclassesByNumber[classId] # Create a new distributed object, and put it in the dictionary @@ -105,9 +106,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): def handleGenerateWithRequiredOther(self, di): # Get the class Id - classId = di.getArg(STUint16); + classId = di.getUint16(); # Get the DO Id - doId = di.getArg(STUint32) + doId = di.getUint32() # Look up the dclass dclass = self.dclassesByNumber[classId] # Create a new distributed object, and put it in the dictionary @@ -116,9 +117,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): def handleQuietZoneGenerateWithRequired(self, di): # Special handler for quiet zone generates -- we need to filter # Get the class Id - classId = di.getArg(STUint16); + classId = di.getUint16(); # Get the DO Id - doId = di.getArg(STUint32) + doId = di.getUint32() # Look up the dclass dclass = self.dclassesByNumber[classId] # If the class is a neverDisable class (which implies uberzone) we @@ -130,9 +131,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): def handleQuietZoneGenerateWithRequiredOther(self, di): # Special handler for quiet zone generates -- we need to filter # Get the class Id - classId = di.getArg(STUint16); + classId = di.getUint16(); # Get the DO Id - doId = di.getArg(STUint32) + doId = di.getUint32() # Look up the dclass dclass = self.dclassesByNumber[classId] # If the class is a neverDisable class (which implies uberzone) we @@ -222,7 +223,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): def handleDisable(self, di): # Get the DO Id - doId = di.getArg(STUint32) + doId = di.getUint32() # disable it. self.disableDoId(doId) @@ -249,7 +250,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): def handleDelete(self, di): # Get the DO Id - doId = di.getArg(STUint32) + doId = di.getUint32() self.deleteObject(doId) def deleteObject(self, doId): @@ -285,7 +286,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): def handleUpdateField(self, di): # Get the DO Id - doId = di.getArg(STUint32) + doId = di.getUint32() #print("Updating " + str(doId)) # Find the DO @@ -368,14 +369,13 @@ class ClientRepository(ConnectionRepository.ConnectionRepository): # send the message self.send(datagram) - def handleDatagram(self, datagram): + def handleDatagram(self, di): if self.notify.getDebug(): print "ClientRepository received datagram:" - datagram.dumpHex(ostream) - di = PyDatagramIterator(datagram) - msgType = di.getUint16() - if self.notify.getDebug(): - self.notify.debug("handleDatagram: msgType: " + `msgType`) + di.getDatagram().dumpHex(ostream) + + msgType = self.getMsgType() + # watch for setZoneDones if msgType == CLIENT_DONE_SET_ZONE_RESP: self.handleSetZoneDone() diff --git a/direct/src/distributed/ConnectionRepository.py b/direct/src/distributed/ConnectionRepository.py index f77575f6c1..ad1b90d65f 100644 --- a/direct/src/distributed/ConnectionRepository.py +++ b/direct/src/distributed/ConnectionRepository.py @@ -3,6 +3,7 @@ import Task import DirectNotifyGlobal import DirectObject from PyDatagram import PyDatagram +from PyDatagramIterator import PyDatagramIterator import types @@ -20,6 +21,7 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository): def __init__(self, config): DirectObject.DirectObject.__init__(self) CConnectionRepository.__init__(self) + self.setPythonRepository(self) self.config = config @@ -44,44 +46,48 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository): self.connectHttp = None self.http = None + # This DatagramIterator is constructed once, and then re-used + # each time we read a datagram. + self.__di = PyDatagramIterator() + self.recorder = None def readDCFile(self, dcFileNames = None): """ Reads in the dc files listed in dcFileNames, or if dcFileNames is None, reads in all of the dc files listed in - the Configrc file. - - The resulting DCFile object is stored in self.dcFile. """ + the Configrc file. """ - self.dcFile = DCFile() + dcFile = self.getDcFile() + dcFile.clear() self.dclassesByName = {} self.dclassesByNumber = {} + self.hashVal = 0 dcImports = {} if dcFileNames == None: - readResult = self.dcFile.readAll() + readResult = dcFile.readAll() if not readResult: self.notify.error("Could not read dc file.") else: for dcFileName in dcFileNames: - readResult = self.dcFile.read(Filename(dcFileName)) + readResult = dcFile.read(Filename(dcFileName)) if not readResult: self.notify.error("Could not read dc file: %s" % (dcFileName)) - self.hashVal = self.dcFile.getHash() + self.hashVal = dcFile.getHash() # Now import all of the modules required by the DC file. - for n in range(self.dcFile.getNumImportModules()): - moduleName = self.dcFile.getImportModule(n) + for n in range(dcFile.getNumImportModules()): + moduleName = dcFile.getImportModule(n) moduleName = self.mangleDCName(moduleName) module = __import__(moduleName, globals(), locals()) - if self.dcFile.getNumImportSymbols(n) > 0: + if dcFile.getNumImportSymbols(n) > 0: # "from moduleName import symbolName, symbolName, ..." # Copy just the named symbols into the dictionary. - for i in range(self.dcFile.getNumImportSymbols(n)): - symbolName = self.dcFile.getImportSymbol(n, i) + for i in range(dcFile.getNumImportSymbols(n)): + symbolName = dcFile.getImportSymbol(n, i) if symbolName == '*': # Get all symbols. dcImports.update(module.__dict__) @@ -105,8 +111,8 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository): # Now get the class definition for the classes named in the DC # file. - for i in range(self.dcFile.getNumClasses()): - dclass = self.dcFile.getClass(i) + for i in range(dcFile.getNumClasses()): + dclass = dcFile.getClass(i) number = dclass.getNumber() className = dclass.getName() className = self.mangleDCName(className) @@ -298,9 +304,8 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository): def readerPollOnce(self): if self.checkDatagram(): - dg = PyDatagram() - self.getDatagram(dg) - self.handleDatagram(dg) + self.getDatagramIterator(self.__di) + self.handleDatagram(self.__di) return 1 # Unable to receive a datagram: did we lose the connection? @@ -314,7 +319,7 @@ class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository): # unexpectedly lost connection to the gameserver. self.notify.warning("Lost connection to gameserver.") - def handleDatagram(self, datagram): + def handleDatagram(self, di): # This class is meant to be pure virtual, and any classes that # inherit from it need to make their own handleDatagram method pass diff --git a/direct/src/distributed/Sources.pp b/direct/src/distributed/Sources.pp index 57e7735773..743d2a49ee 100644 --- a/direct/src/distributed/Sources.pp +++ b/direct/src/distributed/Sources.pp @@ -1,10 +1,12 @@ +#define C++FLAGS -DWITHIN_PANDA + #begin lib_target #define BUILD_TARGET $[HAVE_PYTHON] #define USE_PACKAGES ssl nspr #define TARGET distributed #define LOCAL_LIBS \ - directbase + directbase dcparser #define OTHER_LIBS \ downloader:c net:c panda:m express:c pandaexpress:m \ interrogatedb:c dconfig:c dtoolconfig:m \ diff --git a/direct/src/distributed/cConnectionRepository.I b/direct/src/distributed/cConnectionRepository.I index a835e8b994..abdcb4b4e0 100644 --- a/direct/src/distributed/cConnectionRepository.I +++ b/direct/src/distributed/cConnectionRepository.I @@ -17,6 +17,57 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::get_dc_file +// Access: Published +// Description: Returns the DCFile object associated with this +// repository. +//////////////////////////////////////////////////////////////////// +INLINE DCFile &CConnectionRepository:: +get_dc_file() { + return _dc_file; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::set_client_datagram +// Access: Published +// Description: Sets the client_datagram flag. If this is true, +// incoming datagrams are not expected to be prefixed +// with the server routing information like message +// sender, channel number, etc.; otherwise, these server +// fields are parsed and removed from each incoming +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void CConnectionRepository:: +set_client_datagram(bool client_datagram) { + _client_datagram = client_datagram; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::get_client_datagram +// Access: Published +// Description: Returns the client_datagram flag. +//////////////////////////////////////////////////////////////////// +INLINE bool CConnectionRepository:: +get_client_datagram() const { + return _client_datagram; +} + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::set_python_repository +// Access: Published +// Description: Records the pointer to the Python class that derives +// from CConnectionRepository. This allows the C++ +// implementation to directly manipulation some python +// structures on the repository. +//////////////////////////////////////////////////////////////////// +INLINE void CConnectionRepository:: +set_python_repository(PyObject *python_repository) { + _python_repository = python_repository; +} +#endif // HAVE_PYTHON + #ifdef HAVE_NSPR //////////////////////////////////////////////////////////////////// // Function: CConnectionRepository::get_qcm @@ -70,6 +121,67 @@ get_datagram(Datagram &dg) { dg = _dg; } +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::get_datagram_iterator +// Access: Published +// Description: Fills the DatagramIterator object with the iterator +// for the datagram most recently retrieved by +// check_datagram(). This iterator has already read +// past the datagram header and the message type, and is +// positioned at the beginning of data. +//////////////////////////////////////////////////////////////////// +INLINE void CConnectionRepository:: +get_datagram_iterator(DatagramIterator &di) { + di = _di; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::get_msg_channel +// Access: Published +// Description: Returns the channel from which the current message +// was sent, according to the datagram headers. This +// information is not available to the client. +//////////////////////////////////////////////////////////////////// +INLINE unsigned int CConnectionRepository:: +get_msg_channel() const { + return _msg_channel; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::get_msg_sender +// Access: Published +// Description: Returns the sender ID of the current message, +// according to the datagram headers. This information +// is not available to the client. +//////////////////////////////////////////////////////////////////// +INLINE unsigned int CConnectionRepository:: +get_msg_sender() const { + return _msg_sender; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::get_sec_code +// Access: Published +// Description: Returns the security code associated with the current +// message, according to the datagram headers. This +// information is not available to the client. +//////////////////////////////////////////////////////////////////// +INLINE unsigned char CConnectionRepository:: +get_sec_code() const { + return _sec_code; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::get_msg_type +// Access: Published +// Description: Returns the type ID of the current message, +// according to the datagram headers. +//////////////////////////////////////////////////////////////////// +INLINE unsigned int CConnectionRepository:: +get_msg_type() const { + return _msg_type; +} + //////////////////////////////////////////////////////////////////// // Function: CConnectionRepository::set_simulated_disconnect // Access: Published diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index 22eff8afa4..80542e4a1a 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -17,9 +17,13 @@ //////////////////////////////////////////////////////////////////// #include "cConnectionRepository.h" +#include "dcmsgtypes.h" +#include "dcClass.h" + #include "config_distributed.h" #include "httpChannel.h" #include "urlSpec.h" +#include "datagramIterator.h" //////////////////////////////////////////////////////////////////// // Function: CConnectionRepository::Constructor @@ -28,6 +32,9 @@ //////////////////////////////////////////////////////////////////// CConnectionRepository:: CConnectionRepository() : +#ifdef HAVE_PYTHON + _python_repository(NULL), +#endif #ifdef HAVE_SSL _http_conn(NULL), #endif @@ -35,7 +42,12 @@ CConnectionRepository() : _cw(&_qcm, 0), _qcr(&_qcm, 0), #endif - _simulated_disconnect(false) + _client_datagram(true), + _simulated_disconnect(false), + _msg_channel(0), + _msg_sender(0), + _sec_code(0), + _msg_type(0) { #ifdef HAVE_NSPR if (min_lag != 0.0 || max_lag != 0.0) { @@ -103,7 +115,9 @@ try_connect_nspr(const URLSpec &url) { // Access: Published // Description: Returns true if a new datagram is available, false // otherwise. If the return value is true, the new -// datagram may be retrieved via get_datagram(). +// datagram may be retrieved via get_datagram(), or +// preferably, with get_datagram_iterator() and +// get_msg_type(). //////////////////////////////////////////////////////////////////// bool CConnectionRepository:: check_datagram() { @@ -111,7 +125,46 @@ check_datagram() { return false; } - return do_check_datagram(); + while (do_check_datagram()) { + // Start breaking apart the datagram. + _di = DatagramIterator(_dg); + + if (!_client_datagram) { + _msg_channel = _di.get_uint32(); + _msg_sender = _di.get_uint32(); + _sec_code = _di.get_uint8(); + +#ifdef HAVE_PYTHON + // For now, we need to stuff this field onto the Python + // structure, to support legacy code that expects to find it + // there. + if (_python_repository != (PyObject *)NULL) { + PyObject *value = PyInt_FromLong(_msg_sender); + PyObject_SetAttrString(_python_repository, "msgSender", value); + Py_DECREF(value); + } +#endif // HAVE_PYTHON + } + + _msg_type = _di.get_uint16(); + + // Is this a message that we can process directly? + switch (_msg_type) { +#ifdef HAVE_PYTHON + case CLIENT_OBJECT_UPDATE_FIELD: + case STATESERVER_OBJECT_UPDATE_FIELD: + handle_update_field(); + break; +#endif // HAVE_PYTHON + + default: + // Some unknown message; let the caller deal with it. + return true; + } + } + + // No datagrams available. + return false; } //////////////////////////////////////////////////////////////////// @@ -303,3 +356,42 @@ do_check_datagram() { return false; } + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::handle_update_field +// Access: Private +// Description: Directly handles an update message on a field. +// Python never touches the datagram; it just gets its +// distributed method called with the appropriate +// parameters. +//////////////////////////////////////////////////////////////////// +void CConnectionRepository:: +handle_update_field() { +#ifdef HAVE_PYTHON + int do_id = _di.get_uint32(); + if (_python_repository != (PyObject *)NULL) { + PyObject *doId2do = + PyObject_GetAttrString(_python_repository, "doId2do"); + nassertv(doId2do != NULL); + + PyObject *doId = PyInt_FromLong(do_id); + PyObject *distobj = PyDict_GetItem(doId2do, doId); + Py_DECREF(doId); + Py_DECREF(doId2do); + + if (distobj != NULL) { + PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass"); + nassertv(dclass_obj != NULL); + + PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this"); + Py_DECREF(dclass_obj); + nassertv(dclass_this != NULL); + + DCClass *dclass = (DCClass *)PyInt_AsLong(dclass_this); + Py_DECREF(dclass_this); + + dclass->receive_update(distobj, _di); + } + } +#endif // HAVE_PYTHON +} diff --git a/direct/src/distributed/cConnectionRepository.h b/direct/src/distributed/cConnectionRepository.h index 804fd47ffd..9a808c87b6 100644 --- a/direct/src/distributed/cConnectionRepository.h +++ b/direct/src/distributed/cConnectionRepository.h @@ -21,6 +21,8 @@ #include "directbase.h" #include "pointerTo.h" +#include "dcFile.h" +#include "dcField.h" // to pick up Python.h #ifdef HAVE_NSPR #include "queuedConnectionManager.h" @@ -53,6 +55,15 @@ PUBLISHED: CConnectionRepository(); ~CConnectionRepository(); + INLINE DCFile &get_dc_file(); + + INLINE void set_client_datagram(bool client_datagram); + INLINE bool get_client_datagram() const; + +#ifdef HAVE_PYTHON + INLINE void set_python_repository(PyObject *python_repository); +#endif + #ifdef HAVE_SSL void set_connection_http(HTTPChannel *channel); #endif @@ -66,6 +77,12 @@ PUBLISHED: bool check_datagram(); INLINE void get_datagram(Datagram &dg); + INLINE void get_datagram_iterator(DatagramIterator &di); + INLINE unsigned int get_msg_channel() const; + INLINE unsigned int get_msg_sender() const; + INLINE unsigned char get_sec_code() const; + INLINE unsigned int get_msg_type() const; + bool is_connected(); bool send_datagram(const Datagram &dg); @@ -80,6 +97,11 @@ PUBLISHED: private: bool do_check_datagram(); + void handle_update_field(); + +#ifdef HAVE_PYTHON + PyObject *_python_repository; +#endif #ifdef HAVE_SSL SocketStream *_http_conn; @@ -92,8 +114,16 @@ private: PT(Connection) _nspr_conn; #endif - Datagram _dg; + DCFile _dc_file; + bool _client_datagram; bool _simulated_disconnect; + + Datagram _dg; + DatagramIterator _di; + unsigned int _msg_channel; + unsigned int _msg_sender; + unsigned char _sec_code; + unsigned int _msg_type; }; #include "cConnectionRepository.I"