Browse Source

SerialNum->SerialNumGen, auto-interest tracking on the client

Darren Ranalli 19 years ago
parent
commit
8933f78230

+ 37 - 0
direct/src/distributed/DistributedObject.py

@@ -1,5 +1,6 @@
 """DistributedObject module: contains the DistributedObject class"""
 
+from pandac.PandaModules import *
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 #from PyDatagram import PyDatagram
@@ -95,6 +96,38 @@ class DistributedObject(DistributedObjectBase):
                 print
             except Exception, e: print "%serror printing status"%(spaces,), e
 
+    def getAutoInterests(self):
+        # returns the sub-zones under this object that are automatically
+        # opened for us by the server.
+        # have we already cached it?
+        def _getAutoInterests(cls):
+            # returns set of auto-interests for this class and all derived
+            # have we already computed this class's autoInterests?
+            if 'autoInterests' in cls.__dict__:
+                autoInterests = cls.autoInterests
+            else:
+                autoInterests = set()
+                # grab autoInterests from base classes
+                for base in cls.__bases__:
+                    autoInterests.update(_getAutoInterests(base))
+                # grab autoInterests from this class
+                if cls.__name__ in self.cr.dclassesByName:
+                    dclass = self.cr.dclassesByName[cls.__name__]
+                    field = dclass.getFieldByName('AutoInterest')
+                    if field is not None:
+                        p = DCPacker()
+                        p.setUnpackData(field.getDefaultValue())
+                        len = p.rawUnpackUint16()/4
+                        for i in xrange(len):
+                            zone = int(p.rawUnpackUint32())
+                            autoInterests.add(zone)
+                    autoInterests.update(autoInterests)
+                    cls.autoInterests = autoInterests
+            return set(autoInterests)
+        autoInterests = _getAutoInterests(self.__class__)
+        _getAutoInterests = None
+        return list(autoInterests)
+
     def setNeverDisable(self, bool):
         assert bool == 1 or bool == 0
         self.neverDisable = bool
@@ -182,6 +215,7 @@ class DistributedObject(DistributedObjectBase):
         if self.activeState != ESDisabled:
             self.activeState = ESDisabled
             self.__callbacks = {}
+            self.cr.closeAutoInterests(self)
             #self.cr.deleteObjectLocation(self.doId, self.parentId, self.zoneId)
             self.setLocation(0,0)
             # TODO: disable my children
@@ -221,6 +255,9 @@ class DistributedObject(DistributedObjectBase):
         self.activeState = ESGenerating
         # this has already been set at this point
         #self.cr.storeObjectLocation(self.doId, self.parentId, self.zoneId)
+        # HACK: we seem to be calling generate() more than once for objects that multiply-inherit
+        if not hasattr(self, '_autoInterestHandle'):
+            self.cr.openAutoInterests(self)
 
     def generateInit(self):
         """

+ 75 - 27
direct/src/distributed/DoInterestManager.py

@@ -11,13 +11,15 @@ from pandac.PandaModules import *
 from MsgTypes import *
 from direct.showbase.PythonUtil import *
 from direct.showbase import DirectObject
+from direct.showbase.DelayedCallback import DelayedCallback
 from PyDatagram import PyDatagram
 from direct.directnotify.DirectNotifyGlobal import directNotify
 
 class InterestState:
     StateActive = 'Active'
     StatePendingDel = 'PendingDel'
-    def __init__(self, desc, state, scope, event, parentId, zoneIdList):
+    def __init__(self, desc, state, scope, event, parentId, zoneIdList,
+                 eventCounter, auto=False):
         self.desc = desc
         self.state = state
         self.scope = scope
@@ -26,14 +28,21 @@ class InterestState:
         # for removal of the same interest before we get a response for the
         # first interest removal, we now have two parts of the codebase
         # waiting for a response on the removal of a single interest.
-        self.events = [event]
+        self.events = []
+        self.eventCounter = eventCounter
+        if event is not None:
+            self.addEvent(event)
         self.parentId = parentId
         self.zoneIdList = zoneIdList
+        self.auto = auto
     def addEvent(self, event):
         self.events.append(event)
+        self.eventCounter.num += 1
     def getEvents(self):
         return list(self.events)
     def clearEvents(self):
+        self.eventCounter.num -= len(self.events)
+        assert self.eventCounter.num >= 0
         self.events = []
     def sendEvents(self):
         for event in self.events:
@@ -86,9 +95,10 @@ class DoInterestManager(DirectObject.DirectObject):
     _interests = {}
     if __debug__:
         _debug_interestHistory = []
-        _debug_maxDescriptionLen = 20
+        _debug_maxDescriptionLen = 35
 
-    _Serial = SerialNum()
+    _SerialGen = SerialNumGen()
+    _SerialNum = serialNum()
 
     def __init__(self):
         assert DoInterestManager.notify.debugCall()
@@ -96,9 +106,13 @@ class DoInterestManager(DirectObject.DirectObject):
         self._addInterestEvent = uniqueName('DoInterestManager-Add')
         self._removeInterestEvent = uniqueName('DoInterestManager-Remove')
         self._noNewInterests = False
+        self._completeDelayedCallback = None
+        # keep track of request scopes that have not completed
+        self._outstandingScopes = set()
+        self._completeEventCount = ScratchPad(num=0)
 
     def _getAnonymousEvent(self, desc):
-        return 'anonymous-%s-%s' % (desc, DoInterestManager._Serial.next())
+        return 'anonymous-%s-%s' % (desc, DoInterestManager._SerialGen.next())
 
     def setNoNewInterests(self, flag):
         self._noNewInterests = flag
@@ -106,7 +120,10 @@ class DoInterestManager(DirectObject.DirectObject):
     def noNewInterests(self):
         return self._noNewInterests
 
-    def addInterest(self, parentId, zoneIdList, description, event=None):
+    def getAllInterestsCompleteEvent(self):
+        return 'allInterestsComplete-%s' % DoInterestManager._SerialNum
+
+    def addInterest(self, parentId, zoneIdList, description, event=None, auto=False):
         """
         Look into a (set of) zone(s).
         """
@@ -117,33 +134,28 @@ class DoInterestManager(DirectObject.DirectObject):
             DoInterestManager.notify.warning(
                 "addInterest: addingInterests on delete: %s" % (handle))
             return
-        
-        scopeId = self._getNextScopeId()
-        if event is None:
-            event = self._getAnonymousEvent('addInterest')
+
+        if auto:
+            scopeId = 0
+            event = None
+        else:
+            scopeId = self._getNextScopeId()
+            self._outstandingScopes.add(scopeId)
+            if event is None:
+                event = self._getAnonymousEvent('addInterest')
         DoInterestManager._interests[handle] = InterestState(
-            description, InterestState.StateActive, scopeId, event, parentId, zoneIdList)
+            description, InterestState.StateActive, scopeId, event, parentId, zoneIdList, self._completeEventCount)
         if self.InterestDebug:
-            print 'INTEREST DEBUG: addInterest(): handle=%s, parent=%s, zoneIds=%s, description=%s, event=%s' % (
-                handle, parentId, zoneIdList, description, event)
-        self._sendAddInterest(handle, scopeId, parentId, zoneIdList, description)
+            print 'INTEREST DEBUG: addInterest(): handle=%s, parent=%s, zoneIds=%s, description=%s, event=%s, auto=%s' % (
+                handle, parentId, zoneIdList, description, event, auto)
+        if not auto:
+            self._sendAddInterest(handle, scopeId, parentId, zoneIdList, description)
         if event:
             messenger.send(self._getAddInterestEvent(), [event])
         assert self.printInterestsIfDebug()
         return InterestHandle(handle)
 
-    def countOpenInterests(self):
-        openInterestsCount = 0
-        for interest in DoInterestManager._interests.values():
-            # I'm only getting NO_SCOPE interests, so for now I'll key off
-            # of events
-            #if interest.scope != NO_SCOPE:     
-            #    openInterestsCount += 1
-            #if len(interest.events) != 0:
-                openInterestsCount += 1
-        return openInterestsCount
-
-    def removeInterest(self, handle, event=None):
+    def removeInterest(self, handle, event=None, auto=False):
         """
         Stop looking in a (set of) zone(s)
         """
@@ -183,7 +195,8 @@ class DoInterestManager(DirectObject.DirectObject):
                 if self.InterestDebug:
                     print 'INTEREST DEBUG: removeInterest(): handle=%s, event=%s' % (
                         handle, event)
-                self._sendRemoveInterest(handle, scopeId)
+                if not auto:
+                    self._sendRemoveInterest(handle, scopeId)
                 if event is None:
                     self._considerRemoveInterest(handle)
         else:
@@ -239,6 +252,25 @@ class DoInterestManager(DirectObject.DirectObject):
                 "alterInterest: handle not found: %s" % (handle))
         return exists
 
+    def openAutoInterests(self, obj):
+        if hasattr(obj, '_autoInterestHandle'):
+            # must be multiple inheritance
+            self.notify.warning('openAutoInterests(%s): interests already open' % obj.__class__.__name__)
+            return
+        autoInterests = obj.getAutoInterests()
+        obj._autoInterestHandle = None
+        if not len(autoInterests):
+            return
+        obj._autoInterestHandle = self.addInterest(obj.doId, autoInterests, '%s-autoInterest' % obj.__class__.__name__, auto=True)
+    def closeAutoInterests(self, obj):
+        if not hasattr(obj, '_autoInterestHandle'):
+            # must be multiple inheritance
+            self.notify.warning('closeAutoInterests(%s): interests already closed' % obj)
+            return
+        if obj._autoInterestHandle is not None:
+            self.removeInterest(obj._autoInterestHandle, auto=True)
+        del obj._autoInterestHandle
+
     # events for InterestWatcher
     def _getAddInterestEvent(self):
         return self._addInterestEvent
@@ -278,6 +310,7 @@ class DoInterestManager(DirectObject.DirectObject):
             if DoInterestManager._interests[handle].isPendingDelete():
                 # make sure there is no pending event for this interest
                 if DoInterestManager._interests[handle].scope == NO_SCOPE:
+                    assert len(DoInterestManager._interests[handle].events) == 0
                     del DoInterestManager._interests[handle]
 
     if __debug__:
@@ -413,6 +446,21 @@ class DoInterestManager(DirectObject.DirectObject):
         self._considerRemoveInterest(handle)
         for event in eventsToSend:
             messenger.send(event)
+        # if there are no more outstanding interest-completes, send out global all-done event
+        if self._completeEventCount.num == 0:
+            # wait for 3 frames, if no new interests, send out all-done event
+            def checkMoreInterests():
+                return self._completeEventCount.num > 0
+            def sendEvent():
+                messenger.send(self.getAllInterestsCompleteEvent())
+            if self._completeDelayedCallback is not None:
+                self._completeDelayedCallback.destroy()
+            self._completeDelayedCallback = DelayedCallback(
+                3,
+                callback=sendEvent,
+                cancelFunc=checkMoreInterests)
+            checkMoreInterests = None
+            sendEvent = None
         assert self.printInterestsIfDebug()
 
 if __debug__:

+ 4 - 4
direct/src/showbase/EventGroup.py

@@ -1,12 +1,12 @@
 from direct.showbase import DirectObject
-from direct.showbase.PythonUtil import SerialNum, Functor
+from direct.showbase.PythonUtil import SerialNumGen, Functor
 
 class EventGroup(DirectObject.DirectObject):
     """This class allows you to group together multiple events and treat
     them as a single event. The EventGroup will not send out its event until
     all of its sub-events have occured."""
 
-    _SerialNum = SerialNum()
+    _SerialNumGen = SerialNumGen()
 
     def __init__(self, name, subEvents=None, doneEvent=None):
         """
@@ -34,7 +34,7 @@ class EventGroup(DirectObject.DirectObject):
         if doneEvent is None:
             # no doneEvent provided, allocate a unique event name
             doneEvent = 'EventGroup-%s-%s-Done' % (
-                EventGroup._SerialNum.next(), self._name)
+                EventGroup._SerialNumGen.next(), self._name)
         self._doneEvent = doneEvent
         self._completed = False
 
@@ -81,7 +81,7 @@ class EventGroup(DirectObject.DirectObject):
         name that is already in the name of the EventGroup object.
         Returns the new event name. """
         return self.addEvent('%s-SubEvent-%s-%s' % (
-            self._name, EventGroup._SerialNum.next(), name))
+            self._name, EventGroup._SerialNumGen.next(), name))
 
     def _subEventComplete(self, subEventName, *args, **kwArgs):
         if subEventName in self._completedEvents: