Browse Source

handle updates without touching python

David Rose 21 years ago
parent
commit
69e04be3af

+ 15 - 0
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

+ 2 - 0
direct/src/dcparser/dcFile.h

@@ -34,6 +34,8 @@ PUBLISHED:
   DCFile();
   ~DCFile();
 
+  void clear();
+
 #ifdef WITHIN_PANDA
   bool read_all();
 #endif

+ 17 - 17
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()

+ 23 - 18
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

+ 3 - 1
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 \

+ 112 - 0
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

+ 95 - 3
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  
+}

+ 31 - 1
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"