From 52d2ba31fdb5c0938285cced080e0ddb8bb5f1ef Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 17 May 2007 18:44:45 +0000 Subject: [PATCH] add set_handle_c_updates() --- .../src/distributed/ClientRepositoryBase.py | 135 +++++++++++++++++- .../src/distributed/cConnectionRepository.I | 24 ++++ .../src/distributed/cConnectionRepository.cxx | 22 +-- .../src/distributed/cConnectionRepository.h | 6 +- 4 files changed, 176 insertions(+), 11 deletions(-) diff --git a/direct/src/distributed/ClientRepositoryBase.py b/direct/src/distributed/ClientRepositoryBase.py index 9cac4aabf1..fdec7c9d73 100644 --- a/direct/src/distributed/ClientRepositoryBase.py +++ b/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 diff --git a/direct/src/distributed/cConnectionRepository.I b/direct/src/distributed/cConnectionRepository.I index f66c62dd4d..754c44b1cb 100644 --- a/direct/src/distributed/cConnectionRepository.I +++ b/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 diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index a8becf8b1d..3cc1080f78 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/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 \ No newline at end of file +#endif // #ifdef HAVE_PYTHON diff --git a/direct/src/distributed/cConnectionRepository.h b/direct/src/distributed/cConnectionRepository.h index 93c3edd751..c03199a617 100644 --- a/direct/src/distributed/cConnectionRepository.h +++ b/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;