Bläddra i källkod

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

Darren Ranalli 19 år sedan
förälder
incheckning
8933f78230

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

@@ -1,5 +1,6 @@
 """DistributedObject module: contains the DistributedObject class"""
 """DistributedObject module: contains the DistributedObject class"""
 
 
+from pandac.PandaModules import *
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 #from PyDatagram import PyDatagram
 #from PyDatagram import PyDatagram
@@ -95,6 +96,38 @@ class DistributedObject(DistributedObjectBase):
                 print
                 print
             except Exception, e: print "%serror printing status"%(spaces,), e
             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):
     def setNeverDisable(self, bool):
         assert bool == 1 or bool == 0
         assert bool == 1 or bool == 0
         self.neverDisable = bool
         self.neverDisable = bool
@@ -182,6 +215,7 @@ class DistributedObject(DistributedObjectBase):
         if self.activeState != ESDisabled:
         if self.activeState != ESDisabled:
             self.activeState = ESDisabled
             self.activeState = ESDisabled
             self.__callbacks = {}
             self.__callbacks = {}
+            self.cr.closeAutoInterests(self)
             #self.cr.deleteObjectLocation(self.doId, self.parentId, self.zoneId)
             #self.cr.deleteObjectLocation(self.doId, self.parentId, self.zoneId)
             self.setLocation(0,0)
             self.setLocation(0,0)
             # TODO: disable my children
             # TODO: disable my children
@@ -221,6 +255,9 @@ class DistributedObject(DistributedObjectBase):
         self.activeState = ESGenerating
         self.activeState = ESGenerating
         # this has already been set at this point
         # this has already been set at this point
         #self.cr.storeObjectLocation(self.doId, self.parentId, self.zoneId)
         #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):
     def generateInit(self):
         """
         """

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

@@ -11,13 +11,15 @@ from pandac.PandaModules import *
 from MsgTypes import *
 from MsgTypes import *
 from direct.showbase.PythonUtil import *
 from direct.showbase.PythonUtil import *
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
+from direct.showbase.DelayedCallback import DelayedCallback
 from PyDatagram import PyDatagram
 from PyDatagram import PyDatagram
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 
 
 class InterestState:
 class InterestState:
     StateActive = 'Active'
     StateActive = 'Active'
     StatePendingDel = 'PendingDel'
     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.desc = desc
         self.state = state
         self.state = state
         self.scope = scope
         self.scope = scope
@@ -26,14 +28,21 @@ class InterestState:
         # for removal of the same interest before we get a response for the
         # for removal of the same interest before we get a response for the
         # first interest removal, we now have two parts of the codebase
         # first interest removal, we now have two parts of the codebase
         # waiting for a response on the removal of a single interest.
         # 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.parentId = parentId
         self.zoneIdList = zoneIdList
         self.zoneIdList = zoneIdList
+        self.auto = auto
     def addEvent(self, event):
     def addEvent(self, event):
         self.events.append(event)
         self.events.append(event)
+        self.eventCounter.num += 1
     def getEvents(self):
     def getEvents(self):
         return list(self.events)
         return list(self.events)
     def clearEvents(self):
     def clearEvents(self):
+        self.eventCounter.num -= len(self.events)
+        assert self.eventCounter.num >= 0
         self.events = []
         self.events = []
     def sendEvents(self):
     def sendEvents(self):
         for event in self.events:
         for event in self.events:
@@ -86,9 +95,10 @@ class DoInterestManager(DirectObject.DirectObject):
     _interests = {}
     _interests = {}
     if __debug__:
     if __debug__:
         _debug_interestHistory = []
         _debug_interestHistory = []
-        _debug_maxDescriptionLen = 20
+        _debug_maxDescriptionLen = 35
 
 
-    _Serial = SerialNum()
+    _SerialGen = SerialNumGen()
+    _SerialNum = serialNum()
 
 
     def __init__(self):
     def __init__(self):
         assert DoInterestManager.notify.debugCall()
         assert DoInterestManager.notify.debugCall()
@@ -96,9 +106,13 @@ class DoInterestManager(DirectObject.DirectObject):
         self._addInterestEvent = uniqueName('DoInterestManager-Add')
         self._addInterestEvent = uniqueName('DoInterestManager-Add')
         self._removeInterestEvent = uniqueName('DoInterestManager-Remove')
         self._removeInterestEvent = uniqueName('DoInterestManager-Remove')
         self._noNewInterests = False
         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):
     def _getAnonymousEvent(self, desc):
-        return 'anonymous-%s-%s' % (desc, DoInterestManager._Serial.next())
+        return 'anonymous-%s-%s' % (desc, DoInterestManager._SerialGen.next())
 
 
     def setNoNewInterests(self, flag):
     def setNoNewInterests(self, flag):
         self._noNewInterests = flag
         self._noNewInterests = flag
@@ -106,7 +120,10 @@ class DoInterestManager(DirectObject.DirectObject):
     def noNewInterests(self):
     def noNewInterests(self):
         return self._noNewInterests
         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).
         Look into a (set of) zone(s).
         """
         """
@@ -117,33 +134,28 @@ class DoInterestManager(DirectObject.DirectObject):
             DoInterestManager.notify.warning(
             DoInterestManager.notify.warning(
                 "addInterest: addingInterests on delete: %s" % (handle))
                 "addInterest: addingInterests on delete: %s" % (handle))
             return
             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(
         DoInterestManager._interests[handle] = InterestState(
-            description, InterestState.StateActive, scopeId, event, parentId, zoneIdList)
+            description, InterestState.StateActive, scopeId, event, parentId, zoneIdList, self._completeEventCount)
         if self.InterestDebug:
         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:
         if event:
             messenger.send(self._getAddInterestEvent(), [event])
             messenger.send(self._getAddInterestEvent(), [event])
         assert self.printInterestsIfDebug()
         assert self.printInterestsIfDebug()
         return InterestHandle(handle)
         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)
         Stop looking in a (set of) zone(s)
         """
         """
@@ -183,7 +195,8 @@ class DoInterestManager(DirectObject.DirectObject):
                 if self.InterestDebug:
                 if self.InterestDebug:
                     print 'INTEREST DEBUG: removeInterest(): handle=%s, event=%s' % (
                     print 'INTEREST DEBUG: removeInterest(): handle=%s, event=%s' % (
                         handle, event)
                         handle, event)
-                self._sendRemoveInterest(handle, scopeId)
+                if not auto:
+                    self._sendRemoveInterest(handle, scopeId)
                 if event is None:
                 if event is None:
                     self._considerRemoveInterest(handle)
                     self._considerRemoveInterest(handle)
         else:
         else:
@@ -239,6 +252,25 @@ class DoInterestManager(DirectObject.DirectObject):
                 "alterInterest: handle not found: %s" % (handle))
                 "alterInterest: handle not found: %s" % (handle))
         return exists
         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
     # events for InterestWatcher
     def _getAddInterestEvent(self):
     def _getAddInterestEvent(self):
         return self._addInterestEvent
         return self._addInterestEvent
@@ -278,6 +310,7 @@ class DoInterestManager(DirectObject.DirectObject):
             if DoInterestManager._interests[handle].isPendingDelete():
             if DoInterestManager._interests[handle].isPendingDelete():
                 # make sure there is no pending event for this interest
                 # make sure there is no pending event for this interest
                 if DoInterestManager._interests[handle].scope == NO_SCOPE:
                 if DoInterestManager._interests[handle].scope == NO_SCOPE:
+                    assert len(DoInterestManager._interests[handle].events) == 0
                     del DoInterestManager._interests[handle]
                     del DoInterestManager._interests[handle]
 
 
     if __debug__:
     if __debug__:
@@ -413,6 +446,21 @@ class DoInterestManager(DirectObject.DirectObject):
         self._considerRemoveInterest(handle)
         self._considerRemoveInterest(handle)
         for event in eventsToSend:
         for event in eventsToSend:
             messenger.send(event)
             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()
         assert self.printInterestsIfDebug()
 
 
 if __debug__:
 if __debug__:

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

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