diff --git a/direct/src/distributed/DistributedNodeAI.py b/direct/src/distributed/DistributedNodeAI.py index 9bd17fcd28..74f626568b 100644 --- a/direct/src/distributed/DistributedNodeAI.py +++ b/direct/src/distributed/DistributedNodeAI.py @@ -1,24 +1,42 @@ from AIBaseGlobal import * +from PandaModules import NodePath import DistributedObjectAI -import Task -class DistributedNodeAI(DistributedObjectAI.DistributedObjectAI): - def __init__(self, air): +class DistributedNodeAI(DistributedObjectAI.DistributedObjectAI, NodePath): + def __init__(self, air, name=None): DistributedObjectAI.DistributedObjectAI.__init__(self, air) + if name is None: + name = self.__class__.__name__ + NodePath.__init__(self, hidden.attachNewNode(name)) def delete(self): DistributedObjectAI.DistributedObjectAI.delete(self) ### setParent ### - def d_setParent(self, parentString): - if type(parentString) == type(''): - self.sendUpdate("setParentStr", [parentString]) + def b_setParent(self, parentToken): + if type(parentToken) == types.StringType: + self.setParentStr(parentToken) else: - self.sendUpdate("setParent", [parentString]) + self.setParent(parentToken) + self.d_setParent(parentToken) - def setParent(self, parentString): - pass + def d_setParent(self, parentToken): + if type(parentToken) == type(''): + self.sendUpdate("setParentStr", [parentToken]) + else: + self.sendUpdate("setParent", [parentToken]) + + def setParentStr(self, parentToken): + print 'setParentStr(%s): %s' % (self.doId, parentToken) + self.do_setParent(parentToken) + + def setParent(self, parentToken): + print 'setParent(%s): %s' % (self.doId, parentToken) + self.do_setParent(parentToken) + + def do_setParent(self, parentToken): + self.air.parentMgr.requestReparent(self, parentToken) ###### set pos and hpr functions ####### @@ -47,8 +65,8 @@ class DistributedNodeAI(DistributedObjectAI.DistributedObjectAI): self.sendUpdate("setR", [r]) def setXY(self, x, y): - pass - + self.setX(x) + self.setY(y) def d_setXY(self, x, y): self.sendUpdate("setXY", [x, y]) @@ -61,19 +79,18 @@ class DistributedNodeAI(DistributedObjectAI.DistributedObjectAI): self.sendUpdate("setHpr", [h, p, r]) def setXYH(self, x, y, h): - pass - + self.setX(x) + self.setY(y) + self.setH(h) def d_setXYH(self, x, y, h): self.sendUpdate("setXYH", [x, y, h]) def setXYZH(self, x, y, z, h): - pass - + self.setPos(x, y, z) + self.setH(h) def d_setXYZH(self, x, y, z, h): self.sendUpdate("setXYZH", [x, y, z, h]) # setPosHpr provided by NodePath def d_setPosHpr(self, x, y, z, h, p, r): self.sendUpdate("setPosHpr", [x, y, z, h, p, r]) - - diff --git a/direct/src/distributed/DistributedSmoothNode.py b/direct/src/distributed/DistributedSmoothNode.py index c4a8250d81..de3b3b34c5 100644 --- a/direct/src/distributed/DistributedSmoothNode.py +++ b/direct/src/distributed/DistributedSmoothNode.py @@ -3,6 +3,7 @@ from PandaModules import * from ClockDelta import * import DistributedNode +import DistributedSmoothNodeBase import Task # This number defines our tolerance for out-of-sync telemetry packets. @@ -67,7 +68,9 @@ def activateSmoothing(smoothing, prediction): -class DistributedSmoothNode(DistributedNode.DistributedNode): +class DistributedSmoothNode(DistributedNode.DistributedNode, + DistributedSmoothNodeBase.\ + DistributedSmoothNodeBase): """DistributedSmoothNode class: This specializes DistributedNode to add functionality to smooth @@ -81,11 +84,16 @@ class DistributedSmoothNode(DistributedNode.DistributedNode): except: self.DistributedSmoothNode_initialized = 1 DistributedNode.DistributedNode.__init__(self, cr) + DistributedSmoothNodeBase.DistributedSmoothNodeBase.__init__(self) self.smoother = SmoothMover() self.smoothStarted = 0 self.lastSuggestResync = 0 + def delete(self): + DistributedSmoothNodeBase.DistributedSmoothNodeBase.delete(self) + DistributedNode.DistributedNode.delete(self) + ### Methods to handle computing and updating of the smoothed ### position. @@ -162,76 +170,42 @@ class DistributedSmoothNode(DistributedNode.DistributedNode): self.smoother.setPhonyTimestamp() self.smoother.markPosition() - - - ### distributed set pos and hpr functions ### - - ### These functions send the distributed update to set the - ### appropriate values on the remote side. These are - ### composite fields, with all the likely combinations - ### defined; each function maps (via the dc file) to one or - ### more component operations on the remote client. - - def d_setSmStop(self): - self.sendUpdate("setSmStop", [globalClockDelta.getFrameNetworkTime()]) + # distributed set pos and hpr functions + # 'send' versions are inherited from DistributedSmoothNodeBase def setSmStop(self, timestamp): self.setComponentTLive(timestamp) - - def d_setSmH(self, h): - self.sendUpdate("setSmH", [h, globalClockDelta.getFrameNetworkTime()]) def setSmH(self, h, timestamp): self.setComponentH(h) self.setComponentTLive(timestamp) - - def d_setSmXY(self, x, y): - self.sendUpdate("setSmXY", [x, y, globalClockDelta.getFrameNetworkTime()]) def setSmXY(self, x, y, timestamp): self.setComponentX(x) self.setComponentY(y) self.setComponentTLive(timestamp) - - def d_setSmXZ(self, x, z): - self.sendUpdate("setSmXZ", [x, z, globalClockDelta.getFrameNetworkTime()]) def setSmXZ(self, x, z, timestamp): self.setComponentX(x) self.setComponentZ(z) self.setComponentTLive(timestamp) - - def d_setSmPos(self, x, y, z): - self.sendUpdate("setSmPos", [x, y, z, globalClockDelta.getFrameNetworkTime()]) def setSmPos(self, x, y, z, timestamp): self.setComponentX(x) self.setComponentY(y) self.setComponentZ(z) self.setComponentTLive(timestamp) - - def d_setSmHpr(self, h, p, r): - self.sendUpdate("setSmHpr", [h, p, r, globalClockDelta.getFrameNetworkTime()]) def setSmHpr(self, h, p, r, timestamp): self.setComponentH(h) self.setComponentP(p) self.setComponentR(r) self.setComponentTLive(timestamp) - - def d_setSmXYH(self, x, y, h): - self.sendUpdate("setSmXYH", [x, y, h, globalClockDelta.getFrameNetworkTime()]) def setSmXYH(self, x, y, h, timestamp): self.setComponentX(x) self.setComponentY(y) self.setComponentH(h) self.setComponentTLive(timestamp) - - def d_setSmXYZH(self, x, y, z, h): - self.sendUpdate("setSmXYZH", [x, y, z, h, globalClockDelta.getFrameNetworkTime()]) def setSmXYZH(self, x, y, z, h, timestamp): self.setComponentX(x) self.setComponentY(y) self.setComponentZ(z) self.setComponentH(h) self.setComponentTLive(timestamp) - - def d_setSmPosHpr(self, x, y, z, h, p, r): - self.sendUpdate("setSmPosHpr", [x, y, z, h, p, r, globalClockDelta.getFrameNetworkTime()]) def setSmPosHpr(self, x, y, z, h, p, r, timestamp): self.setComponentX(x) self.setComponentY(y) @@ -311,11 +285,6 @@ class DistributedSmoothNode(DistributedNode.DistributedNode): self.smoother.setTimestamp(local) self.smoother.markPosition() - def b_clearSmoothing(self): - self.d_clearSmoothing() - self.clearSmoothing() - def d_clearSmoothing(self): - self.sendUpdate("clearSmoothing", [0]) def clearSmoothing(self, bogus = None): # Call this to invalidate all the old position reports # (e.g. just before popping to a new position). diff --git a/direct/src/distributed/DistributedSmoothNodeAI.py b/direct/src/distributed/DistributedSmoothNodeAI.py new file mode 100755 index 0000000000..9b854cb86d --- /dev/null +++ b/direct/src/distributed/DistributedSmoothNodeAI.py @@ -0,0 +1,84 @@ +from AIBaseGlobal import * +import DistributedNodeAI +import DistributedSmoothNodeBase + +class DistributedSmoothNodeAI(DistributedNodeAI.DistributedNodeAI, + DistributedSmoothNodeBase.\ + DistributedSmoothNodeBase): + def __init__(self, air, name=None): + DistributedNodeAI.DistributedNodeAI.__init__(self, air, name) + DistributedSmoothNodeBase.DistributedSmoothNodeBase.__init__(self) + + def delete(self): + DistributedSmoothNodeBase.DistributedSmoothNodeBase.delete(self) + DistributedNodeAI.DistributedNodeAI.delete(self) + + # distributed set pos and hpr functions + # these are invoked by the DC system + # 'send' (d_set*) versions are inherited from DistributedSmoothNodeBase + def setSmStop(self, t): + self.setComponentT(t) + def setSmH(self, h, t): + self.setComponentH(h) + self.setComponentT(t) + def setSmXY(self, x, y, t): + self.setComponentX(x) + self.setComponentY(y) + self.setComponentT(t) + def setSmXZ(self, x, z, t): + self.setComponentX(x) + self.setComponentZ(z) + self.setComponentT(t) + def setSmPos(self, x, y, z, t): + self.setComponentX(x) + self.setComponentY(y) + self.setComponentZ(z) + self.setComponentT(t) + def setSmHpr(self, h, p, r, t): + self.setComponentH(h) + self.setComponentP(p) + self.setComponentR(r) + self.setComponentT(t) + def setSmXYH(self, x, y, h, t): + self.setComponentX(x) + self.setComponentY(y) + self.setComponentH(h) + self.setComponentT(t) + def setSmXYZH(self, x, y, z, h, t): + self.setComponentX(x) + self.setComponentY(y) + self.setComponentZ(z) + self.setComponentH(h) + self.setComponentT(t) + def setSmPosHpr(self, x, y, z, h, p, r, t): + self.setComponentX(x) + self.setComponentY(y) + self.setComponentZ(z) + self.setComponentH(h) + self.setComponentP(p) + self.setComponentR(r) + self.setComponentT(t) + + def clearSmoothing(self, bogus = None): + pass + + ### component set pos and hpr functions ### + + ### These are the component functions that are invoked + ### remotely by the above composite functions. + + # on the AI, the components are assigned immediately + def setComponentX(self, x): + self.setX(x) + def setComponentY(self, y): + self.setY(y) + def setComponentZ(self, z): + self.setZ(z) + def setComponentH(self, h): + self.setH(h) + def setComponentP(self, p): + self.setP(p) + def setComponentR(self, r): + self.setR(r) + def setComponentT(self, t): + pass diff --git a/direct/src/distributed/DistributedSmoothNodeBase.py b/direct/src/distributed/DistributedSmoothNodeBase.py new file mode 100755 index 0000000000..c58bf43039 --- /dev/null +++ b/direct/src/distributed/DistributedSmoothNodeBase.py @@ -0,0 +1,175 @@ +"""DistributedSmoothNodeBase module: contains the DistributedSmoothNodeBase class""" + +from ClockDelta import * +import Task + +class DistributedSmoothNodeBase: + """common base class for DistributedSmoothNode and DistributedSmoothNodeAI + """ + def __init__(self): + pass + + def delete(self): + pass + + ### distributed set pos and hpr functions ### + + ### These functions send the distributed update to set the + ### appropriate values on the remote side. These are + ### composite fields, with all the likely combinations + ### defined; each function maps (via the dc file) to one or + ### more component operations on the remote client. + + def d_setSmStop(self): + self.sendUpdate("setSmStop", [globalClockDelta.getFrameNetworkTime()]) + def d_setSmH(self, h): + self.sendUpdate("setSmH", [h, globalClockDelta.getFrameNetworkTime()]) + def d_setSmXY(self, x, y): + self.sendUpdate("setSmXY", [x, y, + globalClockDelta.getFrameNetworkTime()]) + def d_setSmXZ(self, x, z): + self.sendUpdate("setSmXZ", [x, z, + globalClockDelta.getFrameNetworkTime()]) + def d_setSmPos(self, x, y, z): + self.sendUpdate("setSmPos", [x, y, z, + globalClockDelta.getFrameNetworkTime()]) + def d_setSmHpr(self, h, p, r): + self.sendUpdate("setSmHpr", [h, p, r, + globalClockDelta.getFrameNetworkTime()]) + def d_setSmXYH(self, x, y, h): + self.sendUpdate("setSmXYH", [x, y, h, + globalClockDelta.getFrameNetworkTime()]) + def d_setSmXYZH(self, x, y, z, h): + self.sendUpdate("setSmXYZH", [x, y, z, h, + globalClockDelta.getFrameNetworkTime()]) + def d_setSmPosHpr(self, x, y, z, h, p, r): + self.sendUpdate("setSmPosHpr", [x, y, z, h, p, r, + globalClockDelta.getFrameNetworkTime()]) + + def b_clearSmoothing(self): + self.d_clearSmoothing() + self.clearSmoothing() + def d_clearSmoothing(self): + self.sendUpdate("clearSmoothing", [0]) + + ### posHprBroadcast ### + + def getPosHprBroadcastTaskName(self): + # presumably, we have a doId at this point + return "sendPosHpr-%s" % self.doId + + def stopPosHprBroadcast(self): + taskMgr.remove(self.getPosHprBroadcastTaskName()) + + def startPosHprBroadcast(self, period=.2): + taskName = self.getPosHprBroadcastTaskName() + # Set up telemetry optimization variables + xyz = self.getPos() + hpr = self.getHpr() + + self.__storeX = xyz[0] + self.__storeY = xyz[1] + self.__storeZ = xyz[2] + self.__storeH = hpr[0] + self.__storeP = hpr[1] + self.__storeR = hpr[2] + self.__storeStop = 0 + self.__epsilon = 0.01 + self.__broadcastPeriod = period + # Broadcast our initial position + self.b_clearSmoothing() + self.d_setSmPosHpr(self.__storeX, self.__storeY, self.__storeZ, + self.__storeH, self.__storeP, self.__storeR) + # remove any old tasks + taskMgr.remove(taskName) + # spawn the new task + taskMgr.doMethodLater(self.__broadcastPeriod, + self.posHprBroadcast, taskName) + + def posHprBroadcast(self, task): + self.d_broadcastPosHpr() + taskName = self.taskName("sendPosHpr") + taskMgr.doMethodLater(self.__broadcastPeriod, + self.posHprBroadcast, taskName) + return Task.done + + def d_broadcastPosHpr(self): + # send out the minimal bits to describe our new position + xyz = self.getPos() + hpr = self.getHpr() + + if abs(self.__storeX - xyz[0]) > self.__epsilon: + self.__storeX = xyz[0] + newX = 1 + else: + newX = 0 + + if abs(self.__storeY - xyz[1]) > self.__epsilon: + self.__storeY = xyz[1] + newY = 1 + else: + newY = 0 + + if abs(self.__storeZ - xyz[2]) > self.__epsilon: + self.__storeZ = xyz[2] + newZ = 1 + else: + newZ = 0 + + if abs(self.__storeH - hpr[0]) > self.__epsilon: + self.__storeH = hpr[0] + newH = 1 + else: + newH = 0 + + if abs(self.__storeP - hpr[1]) > self.__epsilon: + self.__storeP = hpr[1] + newP = 1 + else: + newP = 0 + + if abs(self.__storeR - hpr[2]) > self.__epsilon: + self.__storeR = hpr[2] + newR = 1 + else: + newR = 0 + + # Check for changes: + if not(newX or newY or newZ or newH or newP or newR): + # No change + # Send one and only one "stop" message. + if not self.__storeStop: + self.__storeStop = 1 + self.d_setSmStop() + # print 'no change' + elif (newH) and not(newX or newY or newZ or newP or newR): + # Only change in H + self.__storeStop = 0 + self.d_setSmH(self.__storeH) + # print ("H change") + elif (newX or newY) and not(newZ or newH or newP or newR): + # Only change in X, Y + self.__storeStop = 0 + self.d_setSmXY(self.__storeX, self.__storeY) + # print ("XY change") + elif (newX or newY or newZ) and not(newH or newP or newR): + # Only change in X, Y, Z + self.__storeStop = 0 + self.d_setSmPos(self.__storeX, self.__storeY, self.__storeZ) + # print ("XYZ change") + elif (newX or newY or newH) and not(newZ or newP or newR): + # Only change in X, Y, H + self.__storeStop = 0 + self.d_setSmXYH(self.__storeX, self.__storeY, self.__storeH) + # print ("XYH change") + elif (newX or newY or newZ or newH) and not(newP or newR): + # Only change in X, Y, Z, H + self.__storeStop = 0 + self.d_setSmXYZH(self.__storeX, self.__storeY, self.__storeZ, self.__storeH) + # print ("XYZH change") + else: + # Other changes + self.__storeStop = 0 + self.d_setSmPosHpr(self.__storeX, self.__storeY, self.__storeZ, + self.__storeH, self.__storeP, self.__storeR) + # print ("XYZHPR change") diff --git a/direct/src/distributed/ParentMgr.py b/direct/src/distributed/ParentMgr.py index 9833f3a254..77d45d024d 100644 --- a/direct/src/distributed/ParentMgr.py +++ b/direct/src/distributed/ParentMgr.py @@ -1,9 +1,9 @@ """ParentMgr module: contains the ParentMgr class""" -from ShowBaseGlobal import * import DirectNotifyGlobal class ParentMgr: + # This is now used on the AI as well. """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' diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 9286702afa..a0057351c6 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -71,6 +71,10 @@ class ShowBase(DirectObject.DirectObject): self.wantStats = self.config.GetBool('want-pstats', 0) + self.clientSleep = self.config.GetFloat('client-sleep', 0.) + # magic-word override + self.mwClientSleep = 0. + # Fill this in with a function to invoke when the user "exits" # the program by closing the main window. self.exitFunc = None @@ -1106,6 +1110,12 @@ class ShowBase(DirectObject.DirectObject): # minimized, not just the main window. But it will do for # now until someone complains. time.sleep(0.1) + else: + # magic word overrides config + if self.mwClientSleep: + time.sleep(self.mwClientSleep) + elif self.clientSleep: + time.sleep(self.clientSleep) # Lerp stuff needs this event, and it must be generated in # C++, not in Python.