mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
RelatedObjectMgr
This commit is contained in:
parent
e534118821
commit
f367e64cb5
@ -9,6 +9,7 @@ import CRCache
|
||||
import ConnectionRepository
|
||||
import PythonUtil
|
||||
import ParentMgr
|
||||
import RelatedObjectMgr
|
||||
import time
|
||||
|
||||
class ClientRepository(ConnectionRepository.ConnectionRepository):
|
||||
@ -32,6 +33,10 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
|
||||
# this used to be 'token2nodePath'
|
||||
self.parentMgr = ParentMgr.ParentMgr()
|
||||
|
||||
# The RelatedObjectMgr helps distributed objects find each
|
||||
# other.
|
||||
self.relatedObjectMgr = RelatedObjectMgr.RelatedObjectMgr(self)
|
||||
|
||||
def setServerDelta(self, delta):
|
||||
"""
|
||||
Indicates the approximate difference in seconds between the
|
||||
|
204
direct/src/distributed/RelatedObjectMgr.py
Normal file
204
direct/src/distributed/RelatedObjectMgr.py
Normal file
@ -0,0 +1,204 @@
|
||||
"""RelatedObjectMgr module: contains the RelatedObjectMgr class"""
|
||||
|
||||
from ShowBaseGlobal import *
|
||||
from ToontownGlobals import *
|
||||
import DirectObject
|
||||
import DirectNotifyGlobal
|
||||
|
||||
class RelatedObjectMgr(DirectObject.DirectObject):
|
||||
"""
|
||||
|
||||
This class manages a relationship between DistributedObjects that
|
||||
know about each other, and are expected to be generated together.
|
||||
Ideally, we should be able to guarantee the ordering of the
|
||||
generate calls, but there are certain cases in which the objects
|
||||
may not be generated in the correct order as defined by the
|
||||
toon.dc file.
|
||||
|
||||
To handle cases like these robustly, it is necessary for each
|
||||
object to deal with the possibility that its companion object has
|
||||
not yet been generated. This may mean deferring some operations
|
||||
until the expected companion object has been generated.
|
||||
|
||||
This class helps manage that process. To use it, an object should
|
||||
register its desire to be associated with the other object's doId.
|
||||
When the other object is generated (or immediately, if the object
|
||||
already exists), the associated callback will be called. There is
|
||||
also a timeout callback in case the object never appears.
|
||||
"""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('RelatedObjectMgr')
|
||||
|
||||
doLaterSequence = 1
|
||||
|
||||
def __init__(self, cr):
|
||||
self.cr = cr
|
||||
self.pendingObjects = {}
|
||||
|
||||
def destroy(self):
|
||||
del self.cr
|
||||
del self.pendingObjects
|
||||
self.ignoreAll()
|
||||
|
||||
def requestObjects(self, doIdList, callback,
|
||||
timeout = None, timeoutCallback = None):
|
||||
"""
|
||||
Requests the indicated callback to be called when all the
|
||||
objects in the doIdList are generated. If all the objects
|
||||
already exist, the callback is called immediately. In either
|
||||
case, the list of objects, in the order given in doIdList, is
|
||||
passed to the callback.
|
||||
|
||||
If all of the objects are not generated within the indicated
|
||||
timeout time, the timeoutCallback is called instead, with the
|
||||
original doIdList as the parameter. If the timeoutCallback is
|
||||
None, then the original callback is called on timeout, with
|
||||
the list of objects that have been generated so far, and None
|
||||
for objects that have not been generated.
|
||||
|
||||
If any element of doIdList is None or 0, it is ignored, and
|
||||
None is passed in its place in the object list passed to the
|
||||
callback.
|
||||
|
||||
The return value may be saved and passed to a future call to
|
||||
abortRequest(), in order to abort a pending request before the
|
||||
timeout expires.
|
||||
|
||||
"""
|
||||
assert(self.notify.debug("requestObjects(%s, timeout=%s)" % (doIdList, timeout)))
|
||||
|
||||
# First, see if we have all of the objects already.
|
||||
objects, doIdsPending = self.__generateObjectList(doIdList)
|
||||
if len(doIdsPending) == 0:
|
||||
# All the objects exist, so just call the callback
|
||||
# immediately.
|
||||
assert(self.notify.debug("All objects already exist."))
|
||||
callback(objects)
|
||||
return
|
||||
|
||||
# Some objects don't exist yet, so start listening for them, and
|
||||
# also set a timeout in case they don't come.
|
||||
assert(self.notify.debug("Some objects pending: %s" % (doIdsPending)))
|
||||
|
||||
# Make a copy of the original doIdList, so we can save it over
|
||||
# a period of time without worrying about the caller modifying
|
||||
# it.
|
||||
doIdList = doIdList[:]
|
||||
|
||||
doLaterName = None
|
||||
if timeout != None:
|
||||
doLaterName = "RelatedObject-%s" % (RelatedObjectMgr.doLaterSequence)
|
||||
assert(self.notify.debug("doLaterName = %s" % (doLaterName)))
|
||||
|
||||
RelatedObjectMgr.doLaterSequence += 1
|
||||
|
||||
tuple = (callback, timeoutCallback, doIdsPending, doIdList, doLaterName)
|
||||
|
||||
for doId in doIdsPending:
|
||||
pendingList = self.pendingObjects.get(doId)
|
||||
if pendingList == None:
|
||||
pendingList = []
|
||||
self.pendingObjects[doId] = pendingList
|
||||
self.__listenFor(doId)
|
||||
|
||||
pendingList.append(tuple)
|
||||
|
||||
if doLaterName:
|
||||
# Now spawn a do-later to catch the timeout.
|
||||
taskMgr.doMethodLater(timeout, self.__timeoutExpired, doLaterName,
|
||||
extraArgs = [tuple])
|
||||
|
||||
return tuple
|
||||
|
||||
def abortRequest(self, tuple):
|
||||
"""
|
||||
Aborts a previous request. The parameter is the return value
|
||||
from a previous call to requestObjects(). The pending request
|
||||
is removed from the queue and no further callbacks will be called.
|
||||
"""
|
||||
callback, timeoutCallback, doIdsPending, doIdList, doLaterName = tuple
|
||||
assert(self.notify.debug("aborting request for %s (remaining: %s)" % (doIdList, doIdsPending)))
|
||||
|
||||
taskMgr.remove(doLaterName)
|
||||
self.__removePending(tuple, doIdsPending)
|
||||
|
||||
def __timeoutExpired(self, tuple):
|
||||
callback, timeoutCallback, doIdsPending, doIdList, doLaterName = tuple
|
||||
assert(self.notify.debug("timeout expired for %s (remaining: %s)" % (doIdList, doIdsPending)))
|
||||
|
||||
self.__removePending(tuple, doIdsPending)
|
||||
|
||||
if timeoutCallback:
|
||||
timeoutCallback(doIdList)
|
||||
else:
|
||||
objects, doIdsPending = self.__generateObjectList(doIdList)
|
||||
callback(objects)
|
||||
|
||||
def __removePending(self, tuple, doIdsPending):
|
||||
# Removes all the pending events for the doIdsPending list.
|
||||
while len(doIdsPending) > 0:
|
||||
# We pop doId's off the list instead of simply iterating
|
||||
# through the list, so that we will shorten the list (and
|
||||
# all other outstanding instances of the list) as we go.
|
||||
doId = doIdsPending.pop()
|
||||
pendingList = self.pendingObjects[doId]
|
||||
pendingList.remove(tuple)
|
||||
if len(pendingList) == 0:
|
||||
del self.pendingObjects[doId]
|
||||
self.__noListenFor(doId)
|
||||
|
||||
|
||||
def __listenFor(self, doId):
|
||||
# Start listening for the indicated object to be generated.
|
||||
assert(self.notify.debug("Now listening for generate from %s" % (doId)))
|
||||
announceGenerateName = "generate-%s" % (doId)
|
||||
self.acceptOnce(announceGenerateName, self.__generated)
|
||||
|
||||
def __noListenFor(self, doId):
|
||||
# Stop listening for the indicated object to be generated.
|
||||
assert(self.notify.debug("No longer listening for generate from %s" % (doId)))
|
||||
announceGenerateName = "generate-%s" % (doId)
|
||||
self.ignore(announceGenerateName)
|
||||
|
||||
def __generated(self, object):
|
||||
# The indicated object has been generated.
|
||||
doId = object.doId
|
||||
assert(self.notify.debug("Got generate from %s" % (doId)))
|
||||
pendingList = self.pendingObjects[doId]
|
||||
del self.pendingObjects[doId]
|
||||
|
||||
for tuple in pendingList:
|
||||
callback, timeoutCallback, doIdsPending, doIdList, doLaterName = tuple
|
||||
|
||||
# Here we are depending on Python to unify this one list
|
||||
# across all objects that share it. When we remove our
|
||||
# doId from our reference to the list, it is also removed
|
||||
# from all the other references.
|
||||
doIdsPending.remove(doId)
|
||||
|
||||
if len(doIdsPending) == 0:
|
||||
# That was the last doId on the list. Call the
|
||||
# callback!
|
||||
assert(self.notify.debug("All objects generated on list: %s" % (doIdList)))
|
||||
taskMgr.remove(doLaterName)
|
||||
|
||||
objects, doIdsPending = self.__generateObjectList(doIdList)
|
||||
callback(objects)
|
||||
|
||||
else:
|
||||
assert(self.notify.debug("Objects still pending: %s" % (doIdsPending)))
|
||||
|
||||
def __generateObjectList(self, doIdList):
|
||||
objects = []
|
||||
doIdsPending = []
|
||||
|
||||
for doId in doIdList:
|
||||
if doId:
|
||||
object = self.cr.doId2do.get(doId)
|
||||
objects.append(object)
|
||||
if object == None:
|
||||
doIdsPending.append(doId)
|
||||
else:
|
||||
objects.append(None)
|
||||
|
||||
return objects, doIdsPending
|
Loading…
x
Reference in New Issue
Block a user