Browse Source

add set_handle_c_updates()

David Rose 18 years ago
parent
commit
52d2ba31fd

+ 133 - 2
direct/src/distributed/ClientRepositoryBase.py

@@ -33,6 +33,11 @@ class ClientRepositoryBase(ConnectionRepository):
         self.context=100000
         self.setClientDatagram(1)
 
+        self.deferredGenerates = []
+        self.deferredDoIds = {}
+        self.lastGenerate = 0
+        self.setDeferInterval(base.config.GetDouble('deferred-generate-interval', 0.2))
+
         self.recorder = base.recorder
 
         self.readDCFile(dcFileNames)
@@ -57,6 +62,23 @@ class ClientRepositoryBase(ConnectionRepository):
         self.heartbeatStarted = 0
         self.lastHeartbeat = 0
 
+    def setDeferInterval(self, deferInterval):
+        """Specifies the minimum amount of time, in seconds, that must
+        elapse before generating any two DistributedObjects whose
+        class type is marked "deferrable".  Set this to 0 to indicate
+        no deferring will occur."""
+
+        # Temporary condition for old Pandas.
+        if hasattr(self, 'setHandleCUpdates'):
+            self.deferInterval = deferInterval
+            self.setHandleCUpdates(self.deferInterval != 0)
+        else:
+            self.deferInterval = 0
+
+        if self.deferredGenerates:
+            taskMgr.remove('deferredGenerate')
+            taskMgr.doMethodLater(self.deferInterval, self.__doDeferredGenerate, 'deferredGenerate')
+
     ## def queryObjectAll(self, doID, context=0):
         ## """
         ## Get a one-time snapshot look at the object.
@@ -154,13 +176,98 @@ class ClientRepositoryBase(ConnectionRepository):
         classId = di.getUint16()
         # Get the DO Id
         doId = di.getUint32()
+
+        dclass = self.dclassesByNumber[classId]
+
+        deferrable = getattr(dclass.getClassDef(), 'deferrable', False)
+        if not self.deferInterval:
+            deferrable = False
+        
+        now = globalClock.getFrameTime()
+        if self.deferredGenerates or deferrable:
+            # This object is deferrable, or there are already deferred
+            # objects in the queue (so all objects have to be held
+            # up).
+            if self.deferredGenerates or now - self.lastGenerate < self.deferInterval:
+                # Queue it for later.
+                assert(self.notify.debug("deferring generate for %s %s" % (dclass.getName(), doId)))
+                self.deferredGenerates.append(doId)
+                
+                # Keep a copy of the datagram, and move the di to the copy
+                dg = Datagram(di.getDatagram())
+                di = DatagramIterator(dg, di.getCurrentIndex())
+            
+                self.deferredDoIds[doId] = ((parentId, zoneId, classId, doId, di), deferrable, dg, [])
+                if len(self.deferredGenerates) == 1:
+                    # We just deferred the first object on the queue;
+                    # start the task to generate it.
+                    taskMgr.remove('deferredGenerate')
+                    taskMgr.doMethodLater(self.deferInterval, self.__doDeferredGenerate, 'deferredGenerate')
+                    
+            else:
+                # We haven't generated any deferrable objects in a
+                # while, so it's safe to go ahead and generate this
+                # one immediately.
+                self.lastGenerate = now
+                self.__doGenerate(parentId, zoneId, classId, doId, di)
+                
+        else:
+            self.__doGenerate(parentId, zoneId, classId, doId, di)
+
+    def __doGenerate(self, parentId, zoneId, classId, doId, di):
         # Look up the dclass
         dclass = self.dclassesByNumber[classId]
+        assert(self.notify.debug("performing generate for %s %s" % (dclass.getName(), doId)))
         dclass.startGenerate()
         # Create a new distributed object, and put it in the dictionary
         distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
         dclass.stopGenerate()
 
+    def flushGenerates(self):
+        """ Forces all pending generates to be performed immediately. """
+        while self.deferredGenerates:
+            doId = self.deferredGenerates[0]
+            del self.deferredGenerates[0]
+            if doId in self.deferredDoIds:
+                args, deferrable, dg, updates = self.deferredDoIds[doId]
+                del self.deferredDoIds[doId]
+                self.__doGenerate(*args)
+                
+                for dg, di in updates:
+                    self.__doUpdate(doId, di)
+
+        taskMgr.remove('deferredGenerate')
+
+    def __doDeferredGenerate(self, task):
+        """ This is the task that generates an object on the deferred
+        queue. """
+        
+        now = globalClock.getFrameTime()
+        if now - self.lastGenerate < self.deferInterval:
+            # Come back later.
+            return Task.again
+
+        while self.deferredGenerates:
+            # Generate the next deferred object.
+            doId = self.deferredGenerates[0]
+            del self.deferredGenerates[0]
+            if doId in self.deferredDoIds:
+                args, deferrable, dg, updates = self.deferredDoIds[doId]
+                del self.deferredDoIds[doId]
+                self.__doGenerate(*args)
+                
+                for dg, di in updates:
+                    self.__doUpdate(doId, di)
+
+                if deferrable:
+                    # If this was an actual deferrable object, wait
+                    # for the next pass to generate any more.
+                    self.lastGenerate = now
+                    return Task.again
+
+        # All objects are generaetd.
+        return Task.done
+
     def handleGenerateWithRequiredOtherOwner(self, di):
         # Get the class Id
         classId = di.getUint16()
@@ -360,6 +467,16 @@ class ClientRepositoryBase(ConnectionRepository):
                 cache.cache(distObj)
             else:
                 distObj.deleteOrDelay()
+
+        elif self.deferredDoIds.has_key(doId):
+            # The object had been deferred.  Great; we don't even have
+            # to generate it now.
+            del self.deferredDoIds[doId]
+            i = self.deferredGenerates.index(doId)
+            del self.deferredGenerates[i]
+            if len(self.deferredGenerates) == 0:
+                taskMgr.remove('deferredGenerate')
+            
         else:
             self._logFailedDisable(doId, ownerView)
 
@@ -393,9 +510,23 @@ class ClientRepositoryBase(ConnectionRepository):
         """
         # Get the DO Id
         doId = di.getUint32()
-        #print("Updating " + str(doId))
-        # Find the DO
 
+        if doId in self.deferredDoIds:
+            # This object hasn't really been generated yet.  Sit on
+            # the update.
+            args, deferrable, dg0, updates = self.deferredDoIds[doId]
+
+            # Keep a copy of the datagram, and move the di to the copy
+            dg = Datagram(di.getDatagram())
+            di = DatagramIterator(dg, di.getCurrentIndex())
+
+            updates.append((dg, di))
+        else:
+            # This object has been fully generated.  It's OK to update.
+            self.__doUpdate(doId, di)
+
+    def __doUpdate(self, doId, di):
+        # Find the DO
         do = self.doId2do.get(doId)
         if do is not None:
             # Let the dclass finish the job

+ 24 - 0
direct/src/distributed/cConnectionRepository.I

@@ -39,6 +39,30 @@ has_owner_view() const {
   return _has_owner_view;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::set_handle_c_updates
+//       Access: Published
+//  Description: Set true to specify this repository should process
+//               distributed updates internally in C++ code, or false
+//               if it should return them to Python.
+////////////////////////////////////////////////////////////////////
+INLINE void CConnectionRepository::
+set_handle_c_updates(bool handle_c_updates) {
+  _handle_c_updates = handle_c_updates;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::get_handle_c_updates
+//       Access: Published
+//  Description: Returns true if this repository will process
+//               distributed updates internally in C++ code, or false
+//               if it will return them to Python.
+////////////////////////////////////////////////////////////////////
+INLINE bool CConnectionRepository::
+get_handle_c_updates() const {
+  return _handle_c_updates;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CConnectionRepository::set_client_datagram
 //       Access: Published

+ 14 - 8
direct/src/distributed/cConnectionRepository.cxx

@@ -70,7 +70,8 @@ CConnectionRepository(bool has_owner_view) :
 //  _msg_channels(),
   _msg_sender(0),
   _msg_type(0),
-  _has_owner_view(has_owner_view)
+  _has_owner_view(has_owner_view),
+  _handle_c_updates(true)
 {
 #if defined(HAVE_NET) && defined(SIMULATE_NETWORK_DELAY)
   if (min_lag != 0.0 || max_lag != 0.0) {
@@ -290,14 +291,19 @@ check_datagram() {
 #ifdef HAVE_PYTHON
     case CLIENT_OBJECT_UPDATE_FIELD:
     case STATESERVER_OBJECT_UPDATE_FIELD:
-      if (_has_owner_view) {
-        if (!handle_update_field_owner()) {
-          return false;
+      if (_handle_c_updates) {
+        if (_has_owner_view) {
+          if (!handle_update_field_owner()) {
+            return false;
+          }
+        } else {
+          if (!handle_update_field()) {
+            return false;
+          }
         }
       } else {
-        if (!handle_update_field()) {
-          return false;
-        }
+        // Let the caller (Python) deal with this update.
+        return true;
       }
       break;
 #endif  // HAVE_PYTHON
@@ -953,4 +959,4 @@ bool CConnectionRepository::handle_update_field_ai(PyObject *doId2do)
 }
 
 #endif  // #ifdef WANT_NATIVE_NET
-#endif  // #ifdef HAVE_PYTHON
+#endif  // #ifdef HAVE_PYTHON

+ 5 - 1
direct/src/distributed/cConnectionRepository.h

@@ -62,13 +62,16 @@ class SocketStream;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_DIRECT CConnectionRepository {
 PUBLISHED:
-  CConnectionRepository(bool has_owner_view=false);
+  CConnectionRepository(bool has_owner_view = false);
   ~CConnectionRepository();
 
   INLINE DCFile &get_dc_file();
 
   INLINE bool has_owner_view() const;
 
+  INLINE void set_handle_c_updates(bool handle_c_updates);
+  INLINE bool get_handle_c_updates() const;
+
   INLINE void set_client_datagram(bool client_datagram);
   INLINE bool get_client_datagram() const;
 
@@ -170,6 +173,7 @@ private:
 
   DCFile _dc_file;
   bool _has_owner_view;
+  bool _handle_c_updates;
   bool _client_datagram;
   bool _simulated_disconnect;
   bool _verbose;