Explorar o código

added parentMgr for more flexible distributed reparentTo's

Darren Ranalli %!s(int64=22) %!d(string=hai) anos
pai
achega
b64887d146

+ 5 - 0
direct/src/distributed/ClientRepository.py

@@ -8,6 +8,7 @@ import ClientDistClass
 import CRCache
 import ConnectionRepository
 import PythonUtil
+import ParentMgr
 import time
 
 class ClientRepository(ConnectionRepository.ConnectionRepository):
@@ -27,6 +28,10 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         self.bootedIndex = None
         self.bootedText = None
 
+        # create a parentMgr to handle distributed reparents
+        # this used to be 'token2nodePath'
+        self.parentMgr = ParentMgr.ParentMgr()
+
     def setServerDelta(self, delta):
         """
         Indicates the approximate difference in seconds between the

+ 3 - 7
direct/src/distributed/DistributedNode.py

@@ -48,15 +48,14 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath):
 
     def b_setParent(self, parentToken):
         self.setParent(parentToken)
+        # it's important to call the local setParent first.
         self.d_setParent(parentToken)
-        return None
 
     def d_setParent(self, parentToken):
         self.sendUpdate("setParent", [parentToken])
-        return None
 
     def setParent(self, parentToken):
-        return self.do_setParent(parentToken)
+        self.do_setParent(parentToken)
 
     def do_setParent(self, parentToken):
         """do_setParent(self, int parentToken)
@@ -66,10 +65,7 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath):
         desired.
         """
         if not self.isDisabled():
-            assert(self.cr.token2nodePath.has_key(parentToken))
-            parent = self.cr.token2nodePath[parentToken]
-            self.wrtReparentTo(parent)
-        return None
+            self.cr.parentMgr.requestReparent(self, parentToken)
 
     ###### set pos and hpr functions #######
 

+ 12 - 0
direct/src/distributed/DistributedSmoothNode.py

@@ -319,6 +319,18 @@ class DistributedSmoothNode(DistributedNode.DistributedNode):
         else:
             NodePath.wrtReparentTo(self, parent)
 
+    def d_setParent(self, parentToken):
+        # We override this DistributedNode method to force a full position
+        # update immediately after the distributed setParent is sent.
+        # See ParentMgr.py for an explanation.
+        DistributedNode.DistributedNode.d_setParent(self, parentToken)
+
+        self.forceToTruePosition()
+        xyz = self.getPos()
+        hpr = self.getHpr()
+        x=xyz[0]; y=xyz[1]; z=xyz[2]
+        h=hpr[0]; p=hpr[1]; r=hpr[2]
+        self.d_setSmPosHpr(x,y,z,h,p,r)
 
     ### Monitor clock sync ###
 

+ 100 - 0
direct/src/distributed/ParentMgr.py

@@ -0,0 +1,100 @@
+"""ParentMgr module: contains the ParentMgr class"""
+
+from ShowBaseGlobal import *
+from ToontownGlobals import *
+import DirectNotifyGlobal
+
+class ParentMgr:
+    """ParentMgr holds a table of nodes that avatars may be parented to
+    in a distributed manner. All clients within a particular zone maintain
+    identical tables of these nodes, and the nodes are referenced by 'tokens'
+    which the clients can pass to each other to communicate distributed
+    reparenting information.
+
+    The functionality of ParentMgr used to be implemented with a simple
+    token->node dictionary. As distributed 'parent' objects were manifested,
+    they would add themselves to the dictionary. Problems occured when
+    distributed avatars were manifested before the objects to which they
+    were parented to.
+
+    Since the order of object manifestation depends on the order of the
+    classes in the DC file, we could maintain an ordering of DC definitions
+    that ensures that the necessary objects are manifested before avatars.
+    However, it's easy enough to keep a list of pending reparents and thus
+    support the general case without requiring any strict ordering in the DC.
+    """
+
+    notify = DirectNotifyGlobal.directNotify.newCategory('ParentMgr')
+    
+    def __init__(self):
+        self.token2nodepath = {}
+        # these are nodepaths that have requested to be parented to
+        # a node that has not yet been registered
+        self.pendingChildren = {}
+
+    def destroy(self):
+        del self.token2nodepath
+        del self.pendingChildren
+
+    def requestReparent(self, child, parentToken):
+        if parentToken in self.token2nodepath.keys():
+            # this parent has registered
+            self.notify.debug('performing wrtReparent of %s to %s' %
+                              (child, parentToken))
+            child.wrtReparentTo(self.token2nodepath[parentToken])
+        else:
+            self.notify.warning(
+                "child %s requested reparent to '%s', not in list" %
+                (child, parentToken))
+            if not self.pendingChildren.has_key(parentToken):
+                self.pendingChildren[parentToken] = []
+            self.pendingChildren[parentToken].append(child)
+            
+    def registerParent(self, token, parent):
+        if token in self.token2nodepath.keys():
+            self.notify.error(
+                'token %s already in the table, referencing %s' %
+                (token, self.token2nodepath[token]))
+
+        self.notify.debug('registering %s as %s' % (parent, token))
+        self.token2nodepath[token] = parent
+
+        # if we have any pending children, add them
+        if token in self.pendingChildren.keys():
+            children = self.pendingChildren[token]
+            for child in children:
+                # NOTE: We do a plain-old reparentTo here (non-wrt)
+                # under the assumption that the node has been
+                # positioned as if it is already under the new parent.
+                #
+                # The only case that I can think of where the parent
+                # node would not have been registered at the time of
+                # the reparent request is when we're entering a new
+                # zone and manifesting remote toons along with
+                # other distributed objects, and a remote toon is
+                # requesting to be parented to geometry owned by a
+                # distributed object that has not yet been manifested.
+                #
+                # It is therefore important for that remote toon to
+                # have his position set as a required field, relative
+                # to the parent node, after the reparent request.
+                # If the node has already been registered, the toon will
+                # be in the correct position. Otherwise, the toon will
+                # have the correct position but the wrong parent node,
+                # until this code runs and corrects the toon's parent
+                # node. Since we don't start rendering until all objects
+                # in a new zone have been generated, all of that action
+                # will happen in a single frame, and the net result will
+                # be that the toon will be in the right place when
+                # rendering starts.
+                self.notify.debug('performing reparent of %s to %s' %
+                                  (child, token))
+                child.reparentTo(self.token2nodepath[token])
+            del self.pendingChildren[token]
+
+    def unregisterParent(self, token):
+        if token not in self.token2nodepath.keys():
+            self.notify.warning('unknown token %s' % token)
+            return
+        self.notify.debug("unregistering parent '%s'" % (token))
+        del self.token2nodepath[token]