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 ConnectionRepository
|
||||||
import PythonUtil
|
import PythonUtil
|
||||||
import ParentMgr
|
import ParentMgr
|
||||||
|
import RelatedObjectMgr
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class ClientRepository(ConnectionRepository.ConnectionRepository):
|
class ClientRepository(ConnectionRepository.ConnectionRepository):
|
||||||
@ -32,6 +33,10 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
|
|||||||
# this used to be 'token2nodePath'
|
# this used to be 'token2nodePath'
|
||||||
self.parentMgr = ParentMgr.ParentMgr()
|
self.parentMgr = ParentMgr.ParentMgr()
|
||||||
|
|
||||||
|
# The RelatedObjectMgr helps distributed objects find each
|
||||||
|
# other.
|
||||||
|
self.relatedObjectMgr = RelatedObjectMgr.RelatedObjectMgr(self)
|
||||||
|
|
||||||
def setServerDelta(self, delta):
|
def setServerDelta(self, delta):
|
||||||
"""
|
"""
|
||||||
Indicates the approximate difference in seconds between the
|
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